밀어서 잠금해제

DirectX11에 Bullet Physics를 사용해 픽킹을 구현해보자. 본문

Programming||Study/DirectX

DirectX11에 Bullet Physics를 사용해 픽킹을 구현해보자.

HAYAN_DEV! 2014.09.17 19:10

으음. 참고로 저는 DirectXMath.h(Windows 8 or higher)를 사용합니다..'만'
옛날에 책 따라서 만든 Box 예제가 있길래 그 위에다가 대충 테스트 할려고 구현한거라 XNAMath를 사용하게 됐습니다 ㅇㅂㅇ;

XNAMath(혹은 DirectXMath.h를 include 한 후 using namespace DirectX를 타이핑 했다면)와 Bullet Physics를 동시에 사용하게 되면 C2084에러를 보게 되는데 이 때는 당황하지 마시고

#define BT_NO_SIMD_OPERATOR_OVERLOADS
#include <btBulletDynamicsCommon.h>

이렇게 처리해 주시면됩니다.

자 시작해볼까요?

먼저 엔진을 사용할 준비를 해야겠죠? Bullet Physics Hello World예제는 한번 보고 오셨을거라 생각합니다.
아직 안보신 분은 Hello World! <- 클릭.

btBroadphaseInterface* broadphase = new btDbvtBroadphase;
btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration;

btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);

// 제가 테스트 해볼 박스 위치가 (-1,-1,-1), (1,1,1)입니다. 그러니 아래처럼 해줘야죠.
btCollisionShape* boxCollisionShape = new btBoxShape(btVector3(1, 1, 1));
// 변환따위 하지 않습니다. 카메라는 돌아가지만 변환은 하지 않습니다.
btDefaultMotionState* motionStatenew btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, btVector3(0, 0, 0)));

btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(0, bulletUtil.motionState, bulletUtil.boxCollisionShape, btVector3(0, 0, 0))
btRigidBody* rigidBody =  new btRigidBody(rigidBodyCI);
dynamicsWord->addRigidBody(rigidBody);

이제 여러분의 DirectX 프레임워크에 있는 마우스 이벤트 처리 함수에 다음과 같이 코딩해봅니다.


void PickingTest::OnMouseDown(WPARAM btnState, int x, int y)
{
	XMMATRIX proj = XMLoadFloat4x4(&m_Proj); // Projection 행렬입니다.
	XMMATRIX view = XMLoadFloat4x4(&m_View); // View 행렬입니다.

	XMVECTOR rayOrigin = XMLoadFloat4(&XMFLOAT4(0, 0, 0, 1.0f));
	// 혹시 Origin 점 위치가 왜 원점인지 이해가 안가시나요?
	//      /                 |                                             |
	//    /                   |                                             |
	//  /                     |                                             |
	//뷰----광선---NEAR--------->물체                  FAR
	//  \                    |                                             |
	//     \                 |                                             |
	//        \              |                                             |
	//
	// 즉, 광선의 시작점은 카메라 공간(View)에서 카메라 위치인 0,0,0 에서 시작됩니다.
	//
	XMVECTOR rayDirection = XMLoadFloat4(&XMFLOAT4(
		((float)(2.0f * x) / (float)mScreenViewport.Width - 1.0f) / m_Proj._11,
		((float)(-2.0f * y) / (float)mScreenViewport.Height + 1.0f) / m_Proj._22,
		1.0f,
		0.0f));
	// 광선의 시작지점이 원점이므로, 이 원점에서 어떤 "방향"으로 광선이 발사될지 정해줘야합니다.
	// 광선의 방향은 Point(x, y, z) - O(0, 0, 0) = Point(x, y, z)이므로 
	// 저희는 스크린 포인트의 마우스 위치를 뷰 행렬까지 옮겨주면 될 듯 합니다.
	// 원래 Loacl * World * View * Proj * ViewPort 순서대로 곱한 결과가 최종 스크린 좌표이므로(마우스는 이미 최종 스크린 좌표입니다.)
	// Local * World * View = ScreenPos * ViewPort^-1 * Proj^-1 이겠네요.

	XMVECTOR det2 = XMMatrixDeterminant(view); // 만약 DirectXMath 가 아니라 XNAMath 를 사용하신다면 반드시 필요합니다.
	XMMATRIX invView = XMMatrixInverse(&det2, view); // XNAMath XMMatrixInverse는 첫번째 인자가 nullptr 일 수 없습니다. DirectXMath는 nullptr 가능.

	rayOrigin = XMVector4Transform(rayOrigin, invView);
	rayDirection = XMVector4Transform(rayDirection, invView);

	rayDirection = XMVector4Normalize(rayDirection) * 1000.0f;
	// 방향을 구한다음에 길이를 재설정해줍니다. 으음. 길이를 설정해줘야 하더군요. 노말라이즈 안하고 그냥 값 넣어도 작동은 됩니다.

	XMFLOAT3 rayOriginF;
	XMFLOAT3 rayDirectionF;

	XMStoreFloat3(&rayOriginF, rayOrigin);
	XMStoreFloat3(&rayDirectionF, rayDirection);

	// 아래 코드는 이제 보시면 이해 할 것입니다.
	// 자세한 함수 사용 방법은 Bullet Physics Document를 참고해주세요.
	btCollisionWorld::ClosestRayResultCallback rayCallback(
		btVector3(rayOriginF.x, rayOriginF.y, rayOriginF.z),
		btVector3(rayDirectionF.x, rayDirectionF.y, rayDirectionF.z));
	bulletUtil.dynamicsWorld->rayTest(
		btVector3(rayOriginF.x, rayOriginF.y, rayOriginF.z),
		btVector3(rayDirectionF.x, rayDirectionF.y, rayDirectionF.z),
		rayCallback);

	if (rayCallback.hasHit())
	{
		SetWindowText(mhMainWnd, L"성공");
	}
	else
	{
		SetWindowText(mhMainWnd, L"실패");
	}
}

아래는 결과. 마우스를 캡쳐 할 수 있는 캡쳐 프로그램이 없어서 그냥 그림판으로 대충 동그라미 그렸습니다. 동그라미 가운데 부분이 클릭지점 입니다.




고딩 수학을 말아먹은 관계로 뭔가 틀린 부분이 있다면 알려주시면 감사하겠습니다.

0 Comments
댓글쓰기 폼