ASE 익스포터 (플러그인) 수정 - 2. 몇가지 수정 & 피지크, 스킨 얻어오기
Posted 2007/05/17 01:52, Filed under: 3D Programming/ASE Paser
요즘들어 표준문서, 웹표준에 각광받고 있는 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 가져오기
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값 변경
01: // 변환함수
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: }
