파서를 직접 제작하신 분이라면, 문득 스키닝매쉬가 가지고 있는 뼈대 행렬을 구지 포함시켜야 하는가?

라는 생각을 한것은 아마도 저뿐만이 아닐겁니다. 에니메이션 행렬과 뼈대 행렬의 곱셈을 제거할 수 없

는가?... 결론을 내리자면, 제외 불가능입니다. 단순히 생각해서 뼈대행렬의 역행렬을 정점에 가중치로

곱해주는 방법을 떠올릴 수 있습니다. 버텍스의 영향을 주는 뼈가 한가지라면 이것은 물론 가능합니다.

( "스티칭" 에 경우입니다 ) 머리, 다리, 팔 등을 모두 토막내서 가지고 있다가 인형을 맞추면 모습을 복

원할 수 있습니다.. 그러나 한정점에 4개의 뼈가 영향을 줄때는( "스키닝" ) 이것을 억지로 한가지로 모

으는 일에 비유를 할 수 있습니다. 캔 로봇을 납작하게 찌그려트려서 부분부분 부위를 연결해도 그전의

모양은 나오지 않겠죠. 당연한 이치지만, 의심많은 제가 오랜시간 직접실험 해본결과 알아낸 결과입니다

ㅠ_ㅠ;

스키닝 매쉬를 읽어들일때 본래의 뼈대행렬정보(매트릭스, 혹은 벡터와 쿼터니온)를 포함시켜야 하고 에

니메이션 행렬곱셈 정점당(복수행렬곱)이 불가피 합니다. 이에 오프셋정점? 을 사용하는 방법을 생각해볼

수 있습니다. v1 * 'world, v2 * 'world, v3 * 'world, v4 * 'world 를 모두 미리 계산 해놓고 랜더링시에는

정점당 행렬곱셈 4번을 줄일수 있다는 얘깁니다. 이것은 매우 큰 이득임이 틀림 없습니다. 행렬이 16개의

float을 가지고 있지만, 오프셋벡터는 12개의 float이고 언뜻 보면 용량에서도 이득인듯 보이지만, 행렬은

뼈대갯수 예를들어 50개정도 만 있으면 되지만 오프셋정점은 정점마다 4개의 벡터가 필요함으로 저장공간이

너무 많이 필요하게 됩니다.


최근의 애플리케이션의 관점에서 보면 몇번의 실행연산을 추가하여 용량상의 이득을 보는 것이 최적화 하려다

실행파일크기 증가로 속도저하를 유발하는 것보다는 낳을 것이지만, 퍼포먼스를 중요시하는 게임같은 분야에서

는 충분히 토론과 연구분야가 될것입니다.


Leave a comment

ASE 익스포터 (플러그인) 수정 - 2. 몇가지 수정 & 피지크, 스킨 얻기



요즘들어 표준문서, 웹표준에 각광받고 있는 XML과 맥스 스크립트가 주요 이슈 인것 같습니다. 하지만

ASE파일도 장단점이 있는데, 가장 큰 장점은 파서를 구현하기가 쉽다는데 있고 아이러니 하게도 익스포터

를 수정하는 것은 쉽지 않고, 수정시마다 매번 컴파일후 테스트가 큰 단점이 될것 같습니다.


다음의 수정 사항들은, 파서에서 가공할 수 있는 데이터를 익스포터에서 처리해 줌으로써 최적화된 데이터

를 좀더 편하고 빠르게 처리할 수 있도록 합니다. 파서의 소스를 더욱 간결하게 만들 수 있지만, 기존의 파서

로는 더이상 ASE파일을 읽어 들일 수 없게 됩니다. 따라서 ASE인 ASCII Exporter 파일의 속성은 변함이 없

지만, 기존의 ASE파일 보다는 사용자 정의 파일에 가깝게 됩니다. 이러한 최적화가 끝난 후 데이터를 바이

너리로 변환해서 출력한다던가, 에니메이션 구조에 적합한 데이터로 변환하여 출력 한다면 여러분만의 모델

파일이 완성이 될것입니다. ( ASE파일은 분명히 TXT기반 이기 때문에 항시 공개되어 있습니다. 분명, 개발이

끝난 부분이라면 바이너리 출력으로 변환 하는것이 나을 것입니다. )



1. 메쉬의 정점정보를 로컬 좌표로 변환


01: // 변환함수
02: 
03: void AsciiExp::ExportMesh(INode* node, TimeValue t, int indentLevel)  
04: 
05: // 변환위치
06: 
07: for (i=0; igetNumVerts(); i++)
08: {
09:     Point3 v = mesh->verts[i]; // using node local space coordinate
10: 
11:     fprintf(pStream, "%s\t\t%s %4d\t%s\n",
12: 
13:     indent.data(), D_MESH_VERTEX, i, Format(v)); 
14: }
15:  
16: 
17: // 변환할 내용 
18: 
19: Point3 v = tm * mesh->verts[i]; 이부분을 Point3 v = mesh->verts[i];
20:  
21: 



출력을 위한 3D Max는 오브젝트를 화면에 출력하기 위해 최적화된 정보를 보여줍니다. 하지만 우리의 목

적은 바로 자유로운 움직임을 가능하게 하는 게임에서 쓸 목적을 가지고 있습니다. 간단히 TM을 빼버려서

로컬좌표로 출력되게 합니다. (스키닝을 위한 변환)



2. 노드에서 Local TM 가져오기


01: // 변환함수
02:
03: void AsciiExp::ExportNodeTM(INode* node, int indentLevel)
04:
05:
06: // 삽입내용
07:
08: INode *parent;
09: Matrix3 parentTM, nodeTM, localTM;
10: nodeTM = node->GetNodeTM(GetStaticFrame());
11: parent = node->GetParentNode();
12: parentTM = parent->GetNodeTM(GetStaticFrame());
13: localTM = nodeTM*Inverse(parentTM); //부모 tm의 역행렬을 곱해서 local tm을 얻는다.



Local TM = TM * Inv(ParentTM) 입니다, 즉 나중에 이 Local TM을 얻으려면 부모의 역행렬을 가져와 계

산해야 하기 때문에 계산의 편의성을 위해 Local TM을 함께 얻어 오는것이 좋습니다. DumpLocalMatrix3

함수를 변경하셔서 Local,World 모두를 얻어옵니다.




3. Rotate Sample에서 Quaternion값 변경


0
1: // 변환함수
02:
03: void AsciiExp::DumpRotSample(INode* node, int indentLevel) 
04:
05: // 변환위치
06:
07: Quat q = ap.q / prevQ;
08:
09: prevQ = ap.q;
10:
11:
12: // 변환할 내용
13:
14: Quat q = ap.q;로 고칩니다.
15:



Quaternion값이 변화량만 되있기 때문에 이것을 축적된 형태로 변환 합니다. 이것으로 간단하게 변환방법을

알아봤습니다. 부수적으로 좌표계 변환처리와 필요없는 정보의 제외등의 작업 만 한다면 깔끔한 모델파일이

출력된 것을 보실 수 있습니다.



*. Export Physique / Skin Data


Physique Modifier 정보를 찾는 함수,

01: Modifier* AsciiExp::FindPhysiqueModifier(INode *pNode)
02: {
03:   // Get object from node. Abort if no object.
04:   Object* ObjectPtr = pNode->GetObjectRef();
05: 
06: 
07:   if (!ObjectPtr) return NULL;
08: 
09:   // Is derived object ?
10:   while (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID && ObjectPtr)
11:   {
12:     // Yes -> Cast.
13:     IDerivedObject *DerivedObjectPtr = (IDerivedObject *)(ObjectPtr);
14: 
15:     // Iterate over all entries of the modifier stack.
16:     int ModStackIndex = 0;
17:     while (ModStackIndex < DerivedObjectPtr->NumModifiers())
18:     {
19:       // Get current modifier.
20:       Modifier* ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);
21: 
22:       // Is this Physique ?
23:       if (ModifierPtr->ClassID() == Class_ID(PHYSIQUE_CLASS_ID_A, 
PHYSIQUE_CLASS_ID_B)) 24: { 25: // Yes -> Exit. 26: return ModifierPtr; 27: } 28: 29: // Next modifier stack entry. 30: ModStackIndex++; 31: } 32: ObjectPtr = DerivedObjectPtr->GetObjRef(); 33: } 34: 35: // Not found. 36: return NULL; 37: }



Skin Modifier 정보를 찾는 함수,


01: Modifier* AsciiExp::FindSkinModifier(INode *pNode)
02: {
03:   // Get object from node. Abort if no object.
04:   Object* ObjectPtr = pNode->GetObjectRef();
05: 
06: 
07:   if (!ObjectPtr) return NULL;
08: 
09:   // Is derived object ?
10:   while (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID && ObjectPtr)
11:   {
12:     // Yes -> Cast.
13:     IDerivedObject *DerivedObjectPtr = (IDerivedObject *)(ObjectPtr);
14: 
15:     // Iterate over all entries of the modifier stack.
16:     int ModStackIndex = 0;
17:     while (ModStackIndex < DerivedObjectPtr->NumModifiers())
18:     {
19:       // Get current modifier.
20:       Modifier* ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);
21: 
22:       // Is this Skin ?
23:       if (ModifierPtr->ClassID() == SKIN_CLASSID)
24:       {
25:       // Yes -> Exit.
26:         return ModifierPtr;
27:       }
28: 
29:       // Next modifier stack entry.
30:       ModStackIndex++;
31:     }
32:     ObjectPtr = DerivedObjectPtr->GetObjRef();
33:   }
34: 
35:   // Not found.
36:   return NULL;
37: }


PhysiqueData를 익스포트 하는 함수,



001:
void AsciiExp::ExportPhysiqueData(INode* node, Modifier* mod)
002: {
003:   //get a pointer to the export interface
004:   IPhysiqueExport *phyExport = (IPhysiqueExport *)mod->GetInterface(I_PHYEXPORT); 
005:
006:   //get the physique version number. 
007:   //If the version number is > 30 you may have floating bones
008:   int ver = phyExport->Version();
009:
010:   //get a pointer to the export context interface
011:   IPhyContextExport *mcExport = (IPhyContextExport *)phyExport->GetContextInterface(node);
012:
013:   //convert to rigid for time independent vertex assignment
014:   mcExport->ConvertToRigid(true);
015:
016:   //allow blending to export multi-link assignments
017:   mcExport->AllowBlending(true);
018:
019:   int x = 0;
020:   float normalizedWeight;
021:   Point3 offsetVector;
022:
023:   //these are the different types of rigid vertices supported
024:   IPhyBlendedRigidVertex *rb_vtx;
025:   IPhyRigidVertex *r_vtx;
026:
027:
028:   //get the vertex count from the export interface
029:   int numverts = mcExport->GetNumberVertices(); // 노드의 버텍스 수
030:
031:   fprintf(pStream,"\t%s {\n" , "*PHYSIQUE"); // 피지크 정보 출력 시작
032:
033:   //gather the vertex-link assignment data
034:   for (int i = 0; i<numverts; i++)
035:   {
036:     //keep record of the total bone weight in order to normalize the flaoting bone weights later
037:     float totalWeight = 0.0f, weight = 0.0f;
038:     TSTR nodeName;
039:
040:     //Get the hierarchial vertex interface for this vertex
041:     //IPhyVertexExport* vi = mcExport->GetVertexInterface(i, HIERARCHIAL_VERTEX);
042:     IPhyVertexExport* vi = (IPhyVertexExport*)mcExport->GetVertexInterface(i);
043:     if (vi){
044:
045:       //check the vertex type and process accordingly
046:       int type = vi->GetVertexType();
047:       switch (type)
048:       {
049:         //we have a vertex assigned to more than one link
050:       case RIGID_BLENDED_TYPE:     // 블랜드 타입
051:         //fprintf(pStream, "\t\t%s\t%d {\n", "*BLENDED_RIGID", i);
052:
053:         //type-cast the node to the proper class 
054:         rb_vtx = (IPhyBlendedRigidVertex*)vi;
055:
056:         //iterate through all links this vertex is assigned to
057:         for (x = 0; x<rb_vtx->GetNumberNodes(); x++)
058:         {
059:           //Get the node and its name for export
060:           nodeName = rb_vtx->GetNode(x)->GetName();
061:
062:           //Get the returned normalized weight for export. 
063:           //Use the weight reference to keep track of the total weight.
064:                   //normalizedWeight = rb_vtx->GetWeight(x, &weight);
065:           normalizedWeight = rb_vtx->GetWeight(x);
066:           if( normalizedWeight < 0.000001 ) continue ;
067:           totalWeight += weight;
068:
069:           //Get the offset vector for export
070:           offsetVector = rb_vtx->GetOffsetVector(x);
071:          
072:           // 수정
073:           fprintf(pStream, "\t\t%s %d %s %0.6f %s \"%s\"\n",
074:             "*VERTEX ID", i, "Weight", normalizedWeight, "Bone", nodeName);
075:         }
076:         break;
077:
078:         // we have a vertex assigned to a single link
079:       case RIGID_TYPE:     // 논블랜드 타입
080:         {
081:           //type-cast the node to the proper class
082:           r_vtx = (IPhyRigidVertex*)vi;
083:
084:           //get the node and its name for export
085:           nodeName = r_vtx->GetNode()->GetName();
086:
087:           //the weight is 1.0f since it is not blended
088:           float NormalizedWeight = 1.0f;
089:           //add this vertex weight to the running total
090:           totalWeight += 1.0f;
091:
092:           //get the offset vector for export
093:           offsetVector = r_vtx->GetOffsetVector();
094:
095:           // 수정
096:           fprintf(pStream, "\t\t%s %d %s %0.6f %s \"%s\"\n",
097:             "*VERTEX ID", i, "Weight", NormalizedWeight, "Bone", nodeName);
098:         }
099:         break;
100:
101:         //Should not make it here since assignments were converted to Rigid. 
102:         //Should be one of the above two types
103:       default:
104:         break;
105:       }
106:       //release the vertex interface
107:       mcExport->ReleaseVertexInterface(vi);
108:     }
109:
110:   }
111:
112:   fprintf(pStream, "\t}\n");  // 피지크 정보 출력 끝
113:
114:   //release the context interface
115:   phyExport->ReleaseContextInterface(mcExport);
116:
117:   //Release the physique interface
118:   mod->ReleaseInterface(I_PHYINTERFACE, phyExport);
119: }



SkinData를 익스포트 하는 함수.


01: void AsciiExp::ExportSkinData(INode* node, Modifier* mod)
02: {
03:   ISkin *skin = (ISkin *)mod->GetInterface(I_SKIN);
04: 
05:   ISkinContextData *skin_data = 
06:           (ISkinContextData *)skin->GetContextInterface(node);
07: 
08: //  int numverts = skin_data->GetNumPoints( ) ;
09: 
10:   Object *obj = node->EvalWorldState(ip->GetTime()).obj;
11: 
12:   TriObject *tri = (TriObject*)obj->ConvertToType(ip->GetTime(),
13:           Class_ID(TRIOBJ_CLASS_ID,0));
14:   Mesh* mesh = &tri->GetMesh();
15: 
16:   int numverts = mesh->numVerts ;
17:   
18:   fprintf(pStream,"\t%s {\n" , "*SKIN"); // 스킨 정보 출력 시작
19: 
20:   for (int i = 0; i<numverts; i++)
21:   {
22:     float totalWeight = 0.0f, weight = 0.0f;
23:     TSTR nodeName;
24: 
25:     int numWeights = skin_data->GetNumAssignedBones(i);
26: 
27:     for(int j = 0 ; j < numWeights ; j ++ )
28:     {
29:       INode * pBone = skin->GetBone( skin_data->GetAssignedBone( i, j ) );
30:       weight = skin_data->GetBoneWeight( i, j );
31:       if( weight < 0.000001 ) continue ;
32:       nodeName = pBone->GetName();
33: 
34:       fprintf(pStream, "\t\t%s %d %s %0.6f %s \"%s\"\n", 
35:                   "*VERTEX ID", i, "Weight", weight, "Bone", nodeName); 
36:     }
37:   }
38:   fprintf(pStream, "\t}\n");  // 스킨 정보 출력 끝
39:   mod->ReleaseInterface( I_SKIN, skin );
40: }

Leave a comment


Recent Posts

  1. [참고자료] 모니터 해상도 도표
  2. [런타임에러] Expression: map/set it..
  3. *. 플러그인 랩퍼 사용하기 ( 3DS MAX..
  4. *. 스키닝 오프셋 벡터 [Offset Vecto..
  5. November 2007 DirectX SDK News

Recent Comments

  1. 관리자만 볼 수 있는 댓글입니다. 06/24

Recent Trackbacks

Calendar

«   2009/11   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          

Bookmarks

  1. Cafe24
  2. ForTheProgramming
  3. Tatter Tools
  4. 근석이
  5. 진호네

Site Stats

TOTAL 14477 HIT
TODAY 4 HIT
YESTERDAY 16 HIT
Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-동일조건변경허락 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.