[PhysX] 2화 Mesh Shape

Grphics 2009. 5. 10. 14:49
그림 1. 실행 화면

지난 시간에는 PhysX에서 제공하는 구, 캡슐, 박스, 평면 형태의 기본 모양에 대해서 살펴보았습니다. 이번에 공부할 내용은 임의의 형태를 가지는 충돌개체를 만드는 것입니다. 임의의 형태의 충돌 개체는 기본 도형으로 처리하기 힘든 개체를 표현할 때 사용합니다. 하지만 Mesh-Mesh 충돌검사는 비용이 비싼 계산입니다. 그렇기 때문에 되도록 단순하게 만들어야 합니다.

임의의 형태를 가지는 메쉬를 만드는 데는 두가지 방법이 있습니다. 첫번째는 Convex Mesh를 생성하는 방법입니다. 여기서 Convex는 볼록다각형을 의미합니다. 이에 반대말은 Concave입니다.

그림 2. Convex와 Concave

Concave는 3D 그래픽스에서 많은 단점을 가집니다. 먼저 내부를 색칠해야 하는 문제에 있어서도 Convex 도형에 비해서 알고리즘이 복잡합니다. 그래서 보통은 Concave 도형을 쪼해서 Convex 도형으로 만들어 Convex 도형의 집합으로서 처리합니다.

아무튼 Convex Mesh는 구현하기가 쉽습니다. 왜나하면 정점(Vertex)정보만 넘겨주면 알아서 메쉬를 구성하기 때문이죠. 하지만 이에 상응하는 단점이 있는데요, 그건 정점은 최대 256개를 넘어선 안된다는 겁니다. 즉, 복잡한 Convex Mesh는 만들지 못하는 겁니다.

百聞不如一見, 소스를 봅시다.

소스 1. CreateConvexMesh() 함수

Convex Mesh를 만드는데는 다음과 같은 순서로 생성합니다.
  1. 정점 정보 만들기
  2. NxConvexMeshDesc 객체 생성하기
  3. NxConvexMeshDesc(gPhysicsSDK->createConvexMesh) 객체를 바탕으로 ConvexMesh 생성하기
  4. 생성된 NxConvexMesh(.meshData)와 NxConvexMeshDesc(.userData)를 이용해서 NxConvexMeshShapeDesc 객체 생성하기
  5. NxBodyDesc(.body)와 NxConvexMeshShapeDesc(.shapes)를 이용해서 NxActorDesc 생성하기
  6. NxActorDesc(gScene->createActor)로 Actor만들기
위에서 괄호안에 있는 것은 생성하고 하는 객체에 대응되는 멤버나 함수를 적은 것입니다.

참 쉽죠..?(@_@)

두번째 방법은 Triangle Mesh를 생성하는 방법입니다. 이 방법은 개수의 제한은 없지만, 생성하는 방법이 조금 까다롭습니다. Convex Mesh는 정점 정보만 받았다면, 이 방법은 정점과 인덱스 정보도 주어야 합니다.

잠깐, 정점과 인덱스와의 관계를 설명하겠습니다. 정점(Vertex, 복수는 Vertices)는 말 그대로, 3차원 공간에서의 위치입니다(주로 3D에서의 위치를 Vertex라고 하고, 2D에서는 Point라고 합니다). 인덱스(Index, 복수는 Indices)는 폴리곤의 정보 - 폴리곤은 삼각형, 사각형 등 어떤 기본 도형이든 될 수 있다 - 를 저장하는 개체입니다. 예를 볼까요...?

그림 3. 정점과 인덱스의 관계(폴리곤은 삼각형)

정점을 정의하면 이는 단순히 공간상의 4개의 위치를 의미하는 것입니다. 인덱스는 정의된 정점의 번호를 나열하여, 하나의 다각형을 만드는 것입니다. 위의 예에서 0, 1, 2라고 정의했기 때문에 0, 1, 2번 점을 이어서 하나의 삼각형을 만드는 것입니다. 마찬가지로 3, 0, 2도 해당하는 인덱스에 있는 정점을 이어서 삼각형을 만든 것입니다.

하지만 여기에도 규칙이 있어야 합니다. PhysX에서는 노말을 계산하기 위해서 다음의 공식을 사용합니다.
Normal = (v1 - v0) x (v2 - v0)
즉, 만들고자 하는 면의 방향을 바라본 상태에서, 반시계 방향으로 삼각형을 만들어야 합니다. 그렇지 않으면 Normal이 뒤집혀서 계산될 수 있습니다. 이러면, 제대로된 충돌 검사가 이루어질 수 없죠. 이 부분은 모눈종이에 정점을 적어서 한번 생각해보기시 바랍니다.

그리고, 정점을 넘길 때, 같은 위치를 가리키고 있는 정점을 두개 이상 선언하면 안됩니다. 예를 들어 (0, 0, 0) 위치가 배열에 0번째에 있었는데, 배열의 10번째 위치도 (0, 0, 0)이라면, PhysX는 이러한 중복을 체크하지 않기 때문에 예기치 못한 결과를 야기할 수 있습니다.

자, 그럼 서론은 마무리 짓고, 본격적인 소스를 보겠습니다.

소스 2. CreateTriangleMesh() 함수


기본적인 순서는 Convex Mesh와 매우 유사합니다.
  1. 정점 정보 만들기
  2. 인덱스 정보 만들기
  3. 정점 정보(.numVerices, .pointStridebytes, .points)와 인덱스 정보(.numTriangle, .triangleStrideBytes, .triangles)를 이용해서 NxTriangleMeshDesc 객체 생성하기
  4. NxTriangleMeshDesc(gPhysicsSDK->createTriangleMesh) 객체를 바탕으로 NxTriangleMesh 생성하기
  5. NxTriangleMeshDesc(.userData), NxTriangleMesh(.meshData)를 이용해서 NxTriangleMeshShapeDesc 객체 생성하기
  6. NxTriangleMeshShapeDesc(.shapes)와 NxBodyDesc(.body) 객체를 이용해서 NxActorDesc 생성하기
  7. NxActorDesc(gScene->createActor)를 이용해서 NxActor 생성하기
인덱스는 NxU32 말고 NxU16을 사용할 수 있다. 이럴 경우라면, 폴리곤의 정점이 2^16개를 넘어서지 않는 경우일겁니다. 이럴 때는 위의 소스와 약간 다른데, 그때의 성정하는 방법은 주석으로 적어놓았으니 참고하세요.

자 그럼, 마지막으로 생성한 녀석들을 삽입하기 위해서 InitNx()함수에 추가하겠습니다.
소스 3. InitNx()에 객체 추가하기

setGlobalPosition함수를 이용해서  Triangle Mesh를 (5, 0, 0)으로 이동시킨 코드입니다. 아마 쉽게 이해가 되실 겁니다.

자 이렇게 이제 여러분은 임의의 형태의 충동 객체를 만드실 수 있습니다. 축하합니다. > _<

ps. Triangle이나 Convex Mesh를 생성할 때, 같은 모양의 객체를 여러개 만들고자 할 때는 NxTrangleMeshShape이나 NxConvexMeshShape을 여러개 생성하여 만드시길 바랍니다.



posted by 스펜서.