본문 바로가기

DirectX/3D

맥스플러그인을 위해서 우리가 반드시 알아야 할 것들을 한번

맥스플러그인을 위해서 우리가 반드시 알아야 할 것들을 한번 이곳에 정리해보도록 해보자.

인터넷 여기저기에 흩어져 있는 문서를 바탕으로 정리하는 것이므로 잘못된 점도 있을지 모르겠지만 이러한 자료를 다른 플머들도

좀 공유할 수 있는 세상이 오면 좋겠다는 지금 글에서 새어나가는 글을 한번 써본다.

사실 나도 실천을 제대로 한게 아니라서... 그럴 말할 입장은 아니지만 말이다.

그럼 다시 한번 시작해보자.

 

class Interface

이 클래스는 3DS Max에서 실행할 수 있는 Export함수들을 포함하고 있다.

virtual HWND GetMAXHwnd() = 0;

  • Max의 윈도우 Handle을 리턴한다.

virtual BOOL ProgressStart( TCHAR* title, BOOL dispBar, LPTHREAD_START_RUNTINE fn, LPVOID arg ) = 0;

  • Max의 Plug-in을 처리할 때 Progress bar에 어떻게 출력되는지를 표현하는 함수이다.
  • title은 실행하고 있는 동안 출력할 메시지이다.
  • dispBar는 Progress  bar를 그릴지 말지를 제어할 변수이다. TRUE는 그리지 않고, FALSE은 그린다.
  • fn은 DWORD WINAPI fn( LPVOID arg )형태의 함수를 인자로 사용한다.

virtual void ProgressEnd() = 0;

  • Plug-in의 처리를 종료하고 Progress bar를 해제하는 함수이다. 즉 로딩한 메모리들을 모두 해제한다.

virtual INode* GetRootNode() = 0;

  • Root Node를 리턴한다. 즉 최상위 노드를 리한다는 뜻이다.

참고로 INode의 멤버함수인 NumberOfChildren()을 이용하여 자식의 개수를 리턴하며,

GetChildNode( int nIndex )은 해당 번째의 자식 노드의 INode*를 리턴받는다.

virtual BOOL GetCancel() = 0;

  • Plug-in을 처리 도중, 취소를 했을 경우, TRUE를 리턴하고, 아닌 경우에는 FALSE을 리턴한다.

virtual void ProgressUpdate( int pct, BOOL showPct = TRUE, TCHAR* tile = NULL ) = 0;

  • Progress bar를 업데이트한다.
  • pct의 크기범위는 0~99인데, 진행정도를 표시하는 데 사용한다. 즉 시작할때는 0, 완료될때는 99를 쓰면 된다.

virtual TCHAR* GetDir( int which ) = 0;

  • 3DS Max의 디렉토리의 pathname을 리턴한다.

 

class INode : public ReferenceTarget

INode클래스는 Interface 클래스의 Node에 연결된 클래스로 각각의 멤버함수는 Node의 이름[Name], 변환[Transformation], 계층구조[Parent and children], status표시를 포함하고 있다.

virtual int NumberOfChildren() = 0;

  • Node의 자식 개수를 리턴한다.

virutal INode* GetChildNode( int i ) = 0;

  • i에 지정된 Node를 읽어온다.

virtual BOOL IsGroupHead() = 0;

  •  Node가 Group의 처음이면 TRUE을 리턴하며, 아니면 FASE을 리턴한다.

virtual const ObjectState& EvalWorldState( TimeValue time, BOOL evalHidden = TRUE ) = 0;

  •  Object의 파이프라인[pipeline]정보를 읽어온다. 이 정보는 ObjectState 클래스에 저장된다.

virtual Matrix3 GetObjTMBeforeWSM( TimeValue time, Interval* valid = NULL ) = 0;

  • Object의 월드 행렬을 리턴한다.

virtual TCHAR* GetName() = 0;

  •  Node의 이름을 리턴한다.

virtual INode* GetParentNode() = 0;

  • Node의 부모 노드를 리턴한다.

virtual int IsRootNode() = 0;

  •  리턴 값이 0이 아니면 RootNode이다.

virtual Matrix3 GetNodeTM( TimeValue t, Interval* valid = NULL ) = 0;

  • Node의 특별한 시간( 애니메이션 시간 )에 월드공간의 변환 행렬을 리턴한다. 즉 월드 행렬을 리턴한다.
  • 애니메이션할 때 사용한다.

virtual Matrix3 GetParentTM( TimeValue t ) = 0;

- 부모 노드의 변환 행렬을 구한다. 즉 부모 노드의 월드 행렬을 리턴한다.

 

class Mesh

Mesh는 Vertices, faces, texture등의 정보를 가지고 있으며 이들 정보가 가진 멤버함수로 이루어져 있다.

즉, 아래와 같은 포인터 배열들을 가지고 있다.

Face *faces;

  • Face를 저장하고 있는 배열

Point3* verts;

  • Vertices를 저장하고 있는 배열

UVVert* tVerts;

  • Texture Vertices를 저장하고 있는 배열

TVFace* tvFace;

  •  Texture Face를 저장하고 있는 배열

int getNumVerts() const;

  • Vertices의 수를 리턴한다.

int getNumFaces() const;

  • Faces의 수를 리턴한다.

int getNumTVerts() const;

  • Texture의 수를 리턴한다.

void buildNormals();

  • Normal의 값을 생성한다.

Point3& getFaceNormal( int i );

  • Face의 Normal값을 리턴한다.

 

class Matrix3

Matrix3는 4x3의 변환 행렬을 멤버 변수로 가지고 있으며, 여러가지의 행렬에 관한 멤버함수들로 이루어져 있다.

float m[4][3];

  •  변환 행렬 정보

Point3 GetRow( int i ) const;

  •  i의 값이 가리키는 row matrix를 리턴한다.

Matrix3 Inverse( const Matrix3 M );

  • matrix의 Inverse값을 리턴한다. 역행렬이라는 건 아시겠지예.

 

class Point3

3D의 좌표 X,Y,Z를 표현하는 클래스이다. 멤버 변수로는 float x, y, z가 있다.

 

실제 코딩에서 그럼 어떻게 써먹을까

1. Node의 수 구하기.

Interface ip;

int nNumChildren = ip->GetRootNode()->NumberOfChildren();

nNumChildren은 Node의 자식의 노드수를 얻는다.

2. GetCancel()함수를 사용하여 Export하는 도중에 Export를 멈추게 하는 방법. 
 Interface* ip;
 int idex;
 // 루트 노드의 자식 개수를 얻는다.
 int nNumChildren = ip->GetRootNode()->NumberOfChildren();
 for( idx = 0; idx < nNumChildren; ++idx )
 {
  if( ip->GetCancel() ) break;

  // Export할 함수를 여기에서 호출한다.
 }

만약 Export도중에 Cancel을 호출하면 ip->GetCancel()에 1을 리턴한다.

   

3. Vertices 읽기
 // 삼각형 정보를 읽는다.
 TriObject* tri = GetTriObjectFromNode( ip->GetRootNode()->GetChildNode(idx), 1, FALSE );
 if( !tri ) return;

 Mesh* mesh = &tri->mesh;

 // 월드 행렬을 얻는다.
 Matrix3 tm = node->GetObjTMAfterWSM(1);

 for( i = 0; i < mesh->getNumVerts(); ++i )
 {

  // 정점 정보를 쓴다.
  Point3 v = ( tm * mesh->verts[i] );
  printf( "X : %f Y : %f Z : %f", v.x, v.y, v.z );
 }

GetTriObjectFromNode()

  • GetTriObjectFromNode()함수는 지정된 Node로부터 TriObject를 읽어오는 함수이다.
  • TriObject는 Node를 가리키고 있는 Object에 대한 기본적인 정보를 가지고 있는 클래스이다.
  •  ip는 interface클래스이다.
  • idx는 몇 번째 Node인가를 지정하는 변수이다.
  • 1은 Frame 수이다. 보통 1로 한다.

 

if( !tri ) return

  • 만약 tri이 NULL이라면 Node로부터 Object에 대한 정보를 읽을 수 없다.

Mesh* mesh = &tri->mesh;

Matrix3 tm = node->GetObjTMAfterWSM(1);

  • TriObejct로부터 Mesh정보를 읽어온다.
  • Node로부터 World Matrix를 읽어온다.
  • 여기서 1은 Fram수이며, 보통 1로 한다.

mesh->getNumVerts()

  •  vertices의 수를 구한다.

Point3 v = ( tm * mesh->verts[i] );

  • mesh->verts[i]배열에는 Vertices의 값을 가지고 있다.
  • Vertices값에 tm(World Matrix)을 곱한 것은 이 점을 World 좌표로 바꾸어서 출력하겠다는 뜻이다.
  • 즉 이 말인즉슨, mesh->verts[i]는 로컬 정점을 가지고 있다는 뜻으로 해석된다.

4. Faces 읽기 
TriObject* tri = GetTriObjectFromNode( ip->GetRootNode()->GetChildNode(idx), 1, FALSE );
Mesh* mesh = &tri->mesh;

for( i = 0; i < mesh->getNumFaces(); ++i )
{
   printf( "A : %d B : %d C : %d", mesh->faces[i].v[0], mesh->faces[i].v[1], mesh->faces[i].v[2] );
}


mesh->getNumFaces()
_ Faces의 수를 구한다.
mesh->faces[i].v[0], mesh->faces[i].v[1], mesh->faces[i].v[2]
- 모든 Object의 Face는 삼각형으로 만들어진다.
- 그래서 3개의 점으로 이루어져 있다.
_ 하나의 Faces는 3개의 점 A,B,C를 가지고 있다.

5. Texture U,V좌표를 읽는다.
TriObject* tri = GetTriObjectFromNode( ip->GetRootNode()->GetChildNode(idx), 1, FALSE );
Mesh* mesh = &tri->mesh;
int nNumTVx = mesh->getNumTVerts();

if( nNumTVx )
{
   for( i = 0; i < nNumTVx; ++i )
      UVVert tv = mesh->tVerts[i];
   printf( "U : %f V : %f", tv.x, tv.y );
}


int nNumTVx = mesh->getNumTVerts();
- mesh->getNumTVerts()함수는 Texture의 Vertices의 개수를 읽어온다.
if( nNumTVx )
- nNumTVx가 0과 같으면 Texture Vertices는 없는 것이다. 따라서 0이 아닌 경우에만 Texture Vertices가 존재한다.
UVVert tv = mesh->tVerts[i];
- mesh의 tVerts[]배열로부터 Texture vertices를 읽어온다.

6. Texture Face를 읽기
TriObject* tri = GetTriObjectFromNode( ip->GetRootNode()->GetChildNode(idx), 1, FALSE );
Mesh* mesh = &tri->mesh;
int nNumTVx = mesh->getNumTVerts();

if( nNumTVx )
{
   for( i = 0; i < mesh->getNumFaces(); ++i )
   {
      printf( "A : %d B : %d C : %d", mesh->tvFace[i].t[0], mesh->tvFace[i].t[1], mesh->tvFace[i].t[2] );
   }
}


if( nNumTVx ) {
- Texture Vertices가 존재하지 않으면 Texture Face도 존재하지 않는다.
mesh->getNumFaces();
- Faces의 숫자나, Texture Face의 숫자나 같다. 따라서 Faces의 개수를 읽어온다.

7. Normals 읽기
TriObject* tri = GetTriObjectFromNode( ip->GetRootNode()->GetChildNode(idx), 1, FALSE );
 Mesh* mesh = &tri->mesh;
mesh->buildNormals();

Point3 fn; // Face normal
Point3 vn; // Vertex normal
int vert;
Face* f;

for( i = 0; i < mesh->getNumFaces(); ++i )
{
   f = &mesh->faces[i];
   fn = mesh->getFaceNormal(i);
   printf( "Faces Normal = X : %f Y : %f Z : %f", fn.x, fn.y. fn.z );

   vert = f->getVert(0);
   vn = GetVertexNormal( mesh, i, mesh->getRVertPtr(vert));
   printf( "Vertex : %d X : %f Y : %f Z : %f", vert, vn.x, vn.y, vn.z );

   vert = f->getVert(1);
   vn = GetVertexNormal( mesh, i, mesh->getRVertPtr(vert));
   printf("Vertex: %d X : %f Y : %f Z: %f", vert, vn.x, vn.y, vn.z );

   vert = f->getVert(2);
   vn = GetVertexNormal( mesh, i, mesh->getRVertPtr(vert));
   printf("Vertex : %d X: %f Y: %f Z: %f", vert, vn.x, vn.y, vn.z );
};


mesh->buildNormals();
- Vertices, Faces의 Normals값을 생성한다.
fn = mesh->getFaceNormal(i);
- Face의 Normals 값을 읽어온다.
vert = f->getVert(0), vert = f->getVert(1), vert = f->getVert(2);
- DWORD getVert(int nIndex)함수는 face클래스의 멤버 함수이다.
- 기능은 face에 들어있는 Vertex의 값을 리턴한다.
- index는 0, 1, 2의 값을 가지며, 하나의 Face는 3개의 Vertex로 이루어져 있다.
GetVertexNormal( mesh, i, mesh->getRVertPtr(vert));
- RVertex * getRVertPtr( int i )함수는 mesh클래스의 멤버함수이다.
- int i의 값에 대한 포인터를 리턴한다.
- GetVertexNormal( mesh, i, mesh->getRVertPtr(vert) );는 특정 Face에 대한 Vertex의 Normal값을 리턴한다.

8. Animation-> Rot Sample 읽기
Interface*ip;
TimeValue start = ip->GetAnimRange().Start();
TimeValue end = ip->GetAnimRange().End();
TimeValue t;
int delta = GetTicksPerFrame();
Matrix3 tm;
AffineParts ap;
Quat prevQ;

prevQ.Identity();

for( t = start; t < end; t += delta )
{
   tm = node->GetNodeTM( t ) * Inverse( node->GetParentTM(t) );
   decomp_affine( tm, &ap );
   
   Quat q = ap.q / prevQ;
   prevQ = ap.q;

   if( q.IsIdentity() )
   {
      contine;
   }
Point3 axis;
float angle;

AngAxisFromQ( q, &angle, axis );
AngAxis( axis, angle );

printf( "X : %f Y : %f Z : %f Angle : %f", axis.x, axis.y, axis.z, angle );


TimeValue start = ip->GetAnimRange().Start();
TimeValue end = ip->GetAnimRange().End();

- GetAnimRange().Start는 Interface의 멤버함수로서, 애니메이션의 시작 프레임을 리턴한다.
- GetAnimRange().End는 Interface의 멤버함수로서, 애니메이션의 마지막 프레임을 리턴한다.
int delta = GetTickPerFrame();
- Frame의 간격을 리턴한다.
AffineParts ap;
- AffineParts는 구조체로 선언되어 있는데, Point3 t, Quat q, Quat u, Point3 k, float f로 이루어져 있다.
- t는 translation, q는 원래의 rotation, u는  변형된 rotation, k는 변형된 factors, f는 하나의 determinant이다.
Quat prevQ
- 멤버변수 float x,y,z,w가 가지고 이으며 Quaternion에 관련된 클래스이다.
prevQ.Identity();
- Quat의 멤버함수로서, Quaternion을 초기화시킨다. ( x = y = z = 0.0f; w = 1.0f )
for( t = start; t <= end; t+= delta ) {
- start(시작 프레임)에서 end(마지막 프레임)까지 간격 delta동안 돌아간다.
tm = node->GetNodeTM(t) * Inverse( node->GetParentTM(t) );
- GetNodeTM()함수는 특정한 시간에 World transformation matrix[월드행렬]을 리턴한다.
- GetParentTM()함수는 특정한 시간에 Parent의 World transformation matrix[월드행렬]을 리턴한다.
- Inverse함수는 Matrix를 Inverse한다.
decomp_affine( tm, &ap );
- Matrix에서 translation, rotation등을 분해하는 함수이다.
if( q.IsIdentity() )
- 만약 IsIdentity()함수는 quternion이 0일 경우 0을 리턴한다. 다른 경우에는 0이 아닌 값을 리턴한다.
AngAxisFromeQ( q, &angle, axis );
- quaternion으로부터 angle과 axis로 변환하는 기능을 한다.
AngAxis( axis, angle );
- axis, angle값을 새로 초기화시킨다.

 
출저 : http://coreafive.springnote.com/pages/5537131

'DirectX > 3D' 카테고리의 다른 글

노말맵이란  (0) 2010.10.01
Max Export  (0) 2010.09.27
ASE 파서 파싱  (1) 2010.07.27
ASE 에니메이션을 하기위한 이론정리  (0) 2010.07.26
Directx의 파이프라인을 따라 구현  (1) 2010.07.19