맥스플러그인을 위해서 우리가 반드시 알아야 할 것들을 한번 이곳에 정리해보도록 해보자.
인터넷 여기저기에 흩어져 있는 문서를 바탕으로 정리하는 것이므로 잘못된 점도 있을지 모르겠지만 이러한 자료를 다른 플머들도
좀 공유할 수 있는 세상이 오면 좋겠다는 지금 글에서 새어나가는 글을 한번 써본다.
사실 나도 실천을 제대로 한게 아니라서... 그럴 말할 입장은 아니지만 말이다.
그럼 다시 한번 시작해보자.
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값을 새로 초기화시킨다.
'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 |