2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
35 ======================================================================
37 Parses Maya ASCII files.
39 ======================================================================
43 #define MA_VERBOSE( x ) { if ( maGlobal.verbose ) { common->Printf x ; } }
45 // working variables used during parsing
49 maObject_t *currentObject;
55 void MA_ParseNodeHeader(idParser& parser, maNodeHeader_t* header) {
57 memset(header, 0, sizeof(maNodeHeader_t));
60 while(parser.ReadToken(&token)) {
61 if(!token.Icmp("-")) {
62 parser.ReadToken(&token);
63 if (!token.Icmp("n")) {
64 parser.ReadToken(&token);
65 strcpy(header->name, token.c_str());
66 } else if (!token.Icmp("p")) {
67 parser.ReadToken(&token);
68 strcpy(header->parent, token.c_str());
70 } else if (!token.Icmp(";")) {
76 bool MA_ParseHeaderIndex(maAttribHeader_t* header, int& minIndex, int& maxIndex, const char* headerType, const char* skipString) {
81 miniParse.LoadMemory(header->name, strlen(header->name), headerType);
83 miniParse.SkipUntilString(skipString);
86 if(!miniParse.SkipUntilString("[")) {
87 //This was just a header
90 minIndex = miniParse.ParseInt();
91 miniParse.ReadToken(&token);
92 if(!token.Icmp("]")) {
95 maxIndex = miniParse.ParseInt();
100 bool MA_ParseAttribHeader(idParser &parser, maAttribHeader_t* header) {
104 memset(header, 0, sizeof(maAttribHeader_t));
106 parser.ReadToken(&token);
107 if(!token.Icmp("-")) {
108 parser.ReadToken(&token);
109 if (!token.Icmp("s")) {
110 header->size = parser.ParseInt();
111 parser.ReadToken(&token);
114 strcpy(header->name, token.c_str());
118 bool MA_ReadVec3(idParser& parser, idVec3& vec) {
120 if(!parser.SkipUntilString("double3")) {
121 throw idException( va("Maya Loader '%s': Invalid Vec3", parser.GetFileName()) );
126 //We need to flip y and z because of the maya coordinate system
127 vec.x = parser.ParseFloat();
128 vec.z = parser.ParseFloat();
129 vec.y = parser.ParseFloat();
134 bool IsNodeComplete(idToken& token) {
135 if(!token.Icmp("createNode") || !token.Icmp("connectAttr") || !token.Icmp("select")) {
141 bool MA_ParseTransform(idParser& parser) {
143 maNodeHeader_t header;
144 maTransform_t* transform;
145 memset(&header, 0, sizeof(header));
147 //Allocate room for the transform
148 transform = (maTransform_t *)Mem_Alloc( sizeof( maTransform_t ) );
149 memset(transform, 0, sizeof(maTransform_t));
150 transform->scale.x = transform->scale.y = transform->scale.z = 1;
152 //Get the header info from the transform
153 MA_ParseNodeHeader(parser, &header);
155 //Read the transform attributes
157 while(parser.ReadToken(&token)) {
158 if(IsNodeComplete(token)) {
159 parser.UnreadToken(&token);
162 if(!token.Icmp("setAttr")) {
163 parser.ReadToken(&token);
164 if(!token.Icmp(".t")) {
165 if(!MA_ReadVec3(parser, transform->translate)) {
168 transform->translate.y *= -1;
169 } else if (!token.Icmp(".r")) {
170 if(!MA_ReadVec3(parser, transform->rotate)) {
173 } else if (!token.Icmp(".s")) {
174 if(!MA_ReadVec3(parser, transform->scale)) {
178 parser.SkipRestOfLine();
183 if(header.parent[0] != 0) {
185 maTransform_t** parent;
186 maGlobal.model->transforms.Get(header.parent, &parent);
188 transform->parent = *parent;
192 //Add this transform to the list
193 maGlobal.model->transforms.Set(header.name, transform);
197 bool MA_ParseVertex(idParser& parser, maAttribHeader_t* header) {
199 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
202 //Allocate enough space for all the verts if this is the first attribute for verticies
203 if(!pMesh->vertexes) {
204 pMesh->numVertexes = header->size;
205 pMesh->vertexes = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numVertexes );
208 //Get the start and end index for this attribute
209 int minIndex, maxIndex;
210 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "VertexHeader", NULL)) {
211 //This was just a header
216 for(int i = minIndex; i <= maxIndex; i++) {
217 pMesh->vertexes[i].x = parser.ParseFloat();
218 pMesh->vertexes[i].z = parser.ParseFloat();
219 pMesh->vertexes[i].y = -parser.ParseFloat();
225 bool MA_ParseVertexTransforms(idParser& parser, maAttribHeader_t* header) {
227 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
230 //Allocate enough space for all the verts if this is the first attribute for verticies
231 if(!pMesh->vertTransforms) {
232 if(header->size == 0) {
236 pMesh->numVertTransforms = header->size;
237 pMesh->vertTransforms = (idVec4 *)Mem_Alloc( sizeof( idVec4 ) * pMesh->numVertTransforms );
238 pMesh->nextVertTransformIndex = 0;
241 //Get the start and end index for this attribute
242 int minIndex, maxIndex;
243 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "VertexTransformHeader", NULL)) {
244 //This was just a header
248 parser.ReadToken(&token);
249 if(!token.Icmp("-")) {
251 parser.ReadToken(&tk2);
252 if(!tk2.Icmp("type")) {
253 parser.SkipUntilString("float3");
255 parser.UnreadToken(&tk2);
256 parser.UnreadToken(&token);
259 parser.UnreadToken(&token);
263 for(int i = minIndex; i <= maxIndex; i++) {
264 pMesh->vertTransforms[pMesh->nextVertTransformIndex].x = parser.ParseFloat();
265 pMesh->vertTransforms[pMesh->nextVertTransformIndex].z = parser.ParseFloat();
266 pMesh->vertTransforms[pMesh->nextVertTransformIndex].y = -parser.ParseFloat();
268 //w hold the vert index
269 pMesh->vertTransforms[pMesh->nextVertTransformIndex].w = i;
271 pMesh->nextVertTransformIndex++;
277 bool MA_ParseEdge(idParser& parser, maAttribHeader_t* header) {
279 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
282 //Allocate enough space for all the verts if this is the first attribute for verticies
284 pMesh->numEdges = header->size;
285 pMesh->edges = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numEdges );
288 //Get the start and end index for this attribute
289 int minIndex, maxIndex;
290 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "EdgeHeader", NULL)) {
291 //This was just a header
296 for(int i = minIndex; i <= maxIndex; i++) {
297 pMesh->edges[i].x = parser.ParseFloat();
298 pMesh->edges[i].y = parser.ParseFloat();
299 pMesh->edges[i].z = parser.ParseFloat();
305 bool MA_ParseNormal(idParser& parser, maAttribHeader_t* header) {
307 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
310 //Allocate enough space for all the verts if this is the first attribute for verticies
311 if(!pMesh->normals) {
312 pMesh->numNormals = header->size;
313 pMesh->normals = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numNormals );
316 //Get the start and end index for this attribute
317 int minIndex, maxIndex;
318 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "NormalHeader", NULL)) {
319 //This was just a header
324 parser.ReadToken(&token);
325 if(!token.Icmp("-")) {
327 parser.ReadToken(&tk2);
328 if(!tk2.Icmp("type")) {
329 parser.SkipUntilString("float3");
331 parser.UnreadToken(&tk2);
332 parser.UnreadToken(&token);
335 parser.UnreadToken(&token);
340 for(int i = minIndex; i <= maxIndex; i++) {
341 pMesh->normals[i].x = parser.ParseFloat();
343 //Adjust the normals for the change in coordinate systems
344 pMesh->normals[i].z = parser.ParseFloat();
345 pMesh->normals[i].y = -parser.ParseFloat();
347 pMesh->normals[i].Normalize();
351 pMesh->normalsParsed = true;
352 pMesh->nextNormal = 0;
359 bool MA_ParseFace(idParser& parser, maAttribHeader_t* header) {
361 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
364 //Allocate enough space for all the verts if this is the first attribute for verticies
366 pMesh->numFaces = header->size;
367 pMesh->faces = (maFace_t *)Mem_Alloc( sizeof( maFace_t ) * pMesh->numFaces );
370 //Get the start and end index for this attribute
371 int minIndex, maxIndex;
372 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "FaceHeader", NULL)) {
373 //This was just a header
378 int currentFace = minIndex-1;
379 while(parser.ReadToken(&token)) {
380 if(IsNodeComplete(token)) {
381 parser.UnreadToken(&token);
385 if(!token.Icmp("f")) {
386 int count = parser.ParseInt();
388 throw idException(va("Maya Loader '%s': Face is not a triangle.", parser.GetFileName()));
391 //Increment the face number because a new face always starts with an "f" token
394 //We cannot reorder edges until later because the normal processing
395 //assumes the edges are in the original order
396 pMesh->faces[currentFace].edge[0] = parser.ParseInt();
397 pMesh->faces[currentFace].edge[1] = parser.ParseInt();
398 pMesh->faces[currentFace].edge[2] = parser.ParseInt();
400 //Some more init stuff
401 pMesh->faces[currentFace].vertexColors[0] = pMesh->faces[currentFace].vertexColors[1] = pMesh->faces[currentFace].vertexColors[2] = -1;
403 } else if(!token.Icmp("mu")) {
404 int uvstIndex = parser.ParseInt();
405 int count = parser.ParseInt();
407 throw idException(va("Maya Loader '%s': Invalid texture coordinates.", parser.GetFileName()));
410 pMesh->faces[currentFace].tVertexNum[0] = parser.ParseInt();
411 pMesh->faces[currentFace].tVertexNum[1] = parser.ParseInt();
412 pMesh->faces[currentFace].tVertexNum[2] = parser.ParseInt();
414 } else if(!token.Icmp("mf")) {
415 int count = parser.ParseInt();
417 throw idException(va("Maya Loader '%s': Invalid texture coordinates.", parser.GetFileName()));
420 pMesh->faces[currentFace].tVertexNum[0] = parser.ParseInt();
421 pMesh->faces[currentFace].tVertexNum[1] = parser.ParseInt();
422 pMesh->faces[currentFace].tVertexNum[2] = parser.ParseInt();
424 } else if(!token.Icmp("fc")) {
426 int count = parser.ParseInt();
428 throw idException(va("Maya Loader '%s': Invalid vertex color.", parser.GetFileName()));
431 pMesh->faces[currentFace].vertexColors[0] = parser.ParseInt();
432 pMesh->faces[currentFace].vertexColors[1] = parser.ParseInt();
433 pMesh->faces[currentFace].vertexColors[2] = parser.ParseInt();
441 bool MA_ParseColor(idParser& parser, maAttribHeader_t* header) {
443 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
446 //Allocate enough space for all the verts if this is the first attribute for verticies
448 pMesh->numColors = header->size;
449 pMesh->colors = (byte *)Mem_Alloc( sizeof( byte ) * pMesh->numColors * 4 );
452 //Get the start and end index for this attribute
453 int minIndex, maxIndex;
454 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "ColorHeader", NULL)) {
455 //This was just a header
460 for(int i = minIndex; i <= maxIndex; i++) {
461 pMesh->colors[i*4] = parser.ParseFloat() * 255;
462 pMesh->colors[i*4+1] = parser.ParseFloat() * 255;
463 pMesh->colors[i*4+2] = parser.ParseFloat() * 255;
464 pMesh->colors[i*4+3] = parser.ParseFloat() * 255;
470 bool MA_ParseTVert(idParser& parser, maAttribHeader_t* header) {
472 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
475 //This is not the texture coordinates. It is just the name so ignore it
476 if(strstr(header->name, "uvsn")) {
480 //Allocate enough space for all the data
481 if(!pMesh->tvertexes) {
482 pMesh->numTVertexes = header->size;
483 pMesh->tvertexes = (idVec2 *)Mem_Alloc( sizeof( idVec2 ) * pMesh->numTVertexes );
486 //Get the start and end index for this attribute
487 int minIndex, maxIndex;
488 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "TextureCoordHeader", "uvsp")) {
489 //This was just a header
493 parser.ReadToken(&token);
494 if(!token.Icmp("-")) {
496 parser.ReadToken(&tk2);
497 if(!tk2.Icmp("type")) {
498 parser.SkipUntilString("float2");
500 parser.UnreadToken(&tk2);
501 parser.UnreadToken(&token);
504 parser.UnreadToken(&token);
508 for(int i = minIndex; i <= maxIndex; i++) {
509 pMesh->tvertexes[i].x = parser.ParseFloat();
510 pMesh->tvertexes[i].y = 1.0f - parser.ParseFloat();
519 * Quick check to see if the vert participates in a shared normal
521 bool MA_QuickIsVertShared(int faceIndex, int vertIndex) {
523 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
524 int vertNum = pMesh->faces[faceIndex].vertexNum[vertIndex];
526 for( int i = 0; i < 3; i++) {
527 int edge = pMesh->faces[faceIndex].edge[i];
529 edge = idMath::Fabs(edge)-1;
531 if(pMesh->edges[edge].z == 1 && (pMesh->edges[edge].x == vertNum || pMesh->edges[edge].y == vertNum)) {
538 void MA_GetSharedFace(int faceIndex, int vertIndex, int& sharedFace, int& sharedVert) {
540 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
541 int vertNum = pMesh->faces[faceIndex].vertexNum[vertIndex];
546 //Find a shared edge on this face that contains the specified vert
547 for(int edgeIndex = 0; edgeIndex < 3; edgeIndex++) {
549 int edge = pMesh->faces[faceIndex].edge[edgeIndex];
551 edge = idMath::Fabs(edge)-1;
554 if(pMesh->edges[edge].z == 1 && (pMesh->edges[edge].x == vertNum || pMesh->edges[edge].y == vertNum)) {
556 for(int i = 0; i < faceIndex; i++) {
558 for(int j = 0; j < 3; j++) {
559 if(pMesh->faces[i].vertexNum[j] == vertNum) {
573 void MA_ParseMesh(idParser& parser) {
576 object = (maObject_t *)Mem_Alloc( sizeof( maObject_t ) );
577 memset( object, 0, sizeof( maObject_t ) );
578 maGlobal.model->objects.Append( object );
579 maGlobal.currentObject = object;
580 object->materialRef = -1;
583 //Get the header info from the mesh
584 maNodeHeader_t header;
585 MA_ParseNodeHeader(parser, &header);
588 if(header.parent[0] != 0) {
590 maTransform_t** parent;
591 maGlobal.model->transforms.Get(header.parent, &parent);
593 maGlobal.currentObject->mesh.transform = *parent;
597 strcpy(object->name, header.name);
599 //Read the transform attributes
601 while(parser.ReadToken(&token)) {
602 if(IsNodeComplete(token)) {
603 parser.UnreadToken(&token);
606 if(!token.Icmp("setAttr")) {
607 maAttribHeader_t header;
608 MA_ParseAttribHeader(parser, &header);
610 if(strstr(header.name, ".vt")) {
611 MA_ParseVertex(parser, &header);
612 } else if (strstr(header.name, ".ed")) {
613 MA_ParseEdge(parser, &header);
614 } else if (strstr(header.name, ".pt")) {
615 MA_ParseVertexTransforms(parser, &header);
616 } else if (strstr(header.name, ".n")) {
617 MA_ParseNormal(parser, &header);
618 } else if (strstr(header.name, ".fc")) {
619 MA_ParseFace(parser, &header);
620 } else if (strstr(header.name, ".clr")) {
621 MA_ParseColor(parser, &header);
622 } else if (strstr(header.name, ".uvst")) {
623 MA_ParseTVert(parser, &header);
625 parser.SkipRestOfLine();
631 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
633 //Get the verts from the edge
634 for(int i = 0; i < pMesh->numFaces; i++) {
635 for(int j = 0; j < 3; j++) {
636 int edge = pMesh->faces[i].edge[j];
638 edge = idMath::Fabs(edge)-1;
639 pMesh->faces[i].vertexNum[j] = pMesh->edges[edge].y;
641 pMesh->faces[i].vertexNum[j] = pMesh->edges[edge].x;
647 if(pMesh->normalsParsed) {
648 for(int i = 0; i < pMesh->numFaces; i++) {
649 for(int j = 0; j < 3; j++) {
651 //Is this vertex shared
655 if(MA_QuickIsVertShared(i, j)) {
656 MA_GetSharedFace(i, j, sharedFace, sharedVert);
659 if(sharedFace != -1) {
660 //Get the normal from the share
661 pMesh->faces[i].vertexNormals[j] = pMesh->faces[sharedFace].vertexNormals[sharedVert];
664 //The vertex is not shared so get the next normal
665 if(pMesh->nextNormal >= pMesh->numNormals) {
666 //We are using more normals than exist
667 throw idException(va("Maya Loader '%s': Invalid Normals Index.", parser.GetFileName()));
669 pMesh->faces[i].vertexNormals[j] = pMesh->normals[pMesh->nextNormal];
676 //Now that the normals are good...lets reorder the verts to make the tris face the right way
677 for(int i = 0; i < pMesh->numFaces; i++) {
678 int tmp = pMesh->faces[i].vertexNum[1];
679 pMesh->faces[i].vertexNum[1] = pMesh->faces[i].vertexNum[2];
680 pMesh->faces[i].vertexNum[2] = tmp;
682 idVec3 tmpVec = pMesh->faces[i].vertexNormals[1];
683 pMesh->faces[i].vertexNormals[1] = pMesh->faces[i].vertexNormals[2];
684 pMesh->faces[i].vertexNormals[2] = tmpVec;
686 tmp = pMesh->faces[i].tVertexNum[1];
687 pMesh->faces[i].tVertexNum[1] = pMesh->faces[i].tVertexNum[2];
688 pMesh->faces[i].tVertexNum[2] = tmp;
690 tmp = pMesh->faces[i].vertexColors[1];
691 pMesh->faces[i].vertexColors[1] = pMesh->faces[i].vertexColors[2];
692 pMesh->faces[i].vertexColors[2] = tmp;
695 //Now apply the pt transformations
696 for(int i = 0; i < pMesh->numVertTransforms; i++) {
697 pMesh->vertexes[(int)pMesh->vertTransforms[i].w] += pMesh->vertTransforms[i].ToVec3();
700 MA_VERBOSE((va("MESH %s - parent %s\n", header.name, header.parent)));
701 MA_VERBOSE((va("\tverts:%d\n",maGlobal.currentObject->mesh.numVertexes)));
702 MA_VERBOSE((va("\tfaces:%d\n",maGlobal.currentObject->mesh.numFaces)));
705 void MA_ParseFileNode(idParser& parser) {
707 //Get the header info from the node
708 maNodeHeader_t header;
709 MA_ParseNodeHeader(parser, &header);
711 //Read the transform attributes
713 while(parser.ReadToken(&token)) {
714 if(IsNodeComplete(token)) {
715 parser.UnreadToken(&token);
718 if(!token.Icmp("setAttr")) {
719 maAttribHeader_t attribHeader;
720 MA_ParseAttribHeader(parser, &attribHeader);
722 if(strstr(attribHeader.name, ".ftn")) {
723 parser.SkipUntilString("string");
724 parser.ReadToken(&token);
725 if(!token.Icmp("(")) {
726 parser.ReadToken(&token);
729 maFileNode_t* fileNode;
730 fileNode = (maFileNode_t*)Mem_Alloc( sizeof( maFileNode_t ) );
731 strcpy(fileNode->name, header.name);
732 strcpy(fileNode->path, token.c_str());
734 maGlobal.model->fileNodes.Set(fileNode->name, fileNode);
736 parser.SkipRestOfLine();
742 void MA_ParseMaterialNode(idParser& parser) {
744 //Get the header info from the node
745 maNodeHeader_t header;
746 MA_ParseNodeHeader(parser, &header);
748 maMaterialNode_t* matNode;
749 matNode = (maMaterialNode_t*)Mem_Alloc( sizeof( maMaterialNode_t ) );
750 memset(matNode, 0, sizeof(maMaterialNode_t));
752 strcpy(matNode->name, header.name);
754 maGlobal.model->materialNodes.Set(matNode->name, matNode);
757 void MA_ParseCreateNode(idParser& parser) {
760 parser.ReadToken(&token);
762 if(!token.Icmp("transform")) {
763 MA_ParseTransform(parser);
764 } else if(!token.Icmp("mesh")) {
765 MA_ParseMesh(parser);
766 } else if(!token.Icmp("file")) {
767 MA_ParseFileNode(parser);
768 } else if(!token.Icmp("shadingEngine") || !token.Icmp("lambert") || !token.Icmp("phong") || !token.Icmp("blinn") ) {
769 MA_ParseMaterialNode(parser);
774 int MA_AddMaterial(const char* materialName) {
777 maMaterialNode_t** destNode;
778 maGlobal.model->materialNodes.Get(materialName, &destNode);
780 maMaterialNode_t* matNode = *destNode;
782 //Iterate down the tree until we get a file
783 while(matNode && !matNode->file) {
784 matNode = matNode->child;
786 if(matNode && matNode->file) {
789 maMaterial_t *material;
790 material = (maMaterial_t *)Mem_Alloc( sizeof( maMaterial_t ) );
791 memset( material, 0, sizeof( maMaterial_t ) );
793 //Remove the OS stuff
795 qPath = fileSystem->OSPathToRelativePath( matNode->file->path );
797 strcpy(material->name, qPath.c_str());
799 maGlobal.model->materials.Append( material );
800 return maGlobal.model->materials.Num()-1;
806 bool MA_ParseConnectAttr(idParser& parser) {
815 parser.ReadToken(&token);
817 int dot = temp.Find(".");
819 throw idException(va("Maya Loader '%s': Invalid Connect Attribute.", parser.GetFileName()));
822 srcName = temp.Left(dot);
823 srcType = temp.Right(temp.Length()-dot-1);
825 parser.ReadToken(&token);
827 dot = temp.Find(".");
829 throw idException(va("Maya Loader '%s': Invalid Connect Attribute.", parser.GetFileName()));
832 destName = temp.Left(dot);
833 destType = temp.Right(temp.Length()-dot-1);
835 if(srcType.Find("oc") != -1) {
837 //Is this attribute a material node attribute
838 maMaterialNode_t** matNode;
839 maGlobal.model->materialNodes.Get(srcName, &matNode);
841 maMaterialNode_t** destNode;
842 maGlobal.model->materialNodes.Get(destName, &destNode);
844 (*destNode)->child = *matNode;
848 //Is this attribute a file node
849 maFileNode_t** fileNode;
850 maGlobal.model->fileNodes.Get(srcName, &fileNode);
852 maMaterialNode_t** destNode;
853 maGlobal.model->materialNodes.Get(destName, &destNode);
855 (*destNode)->file = *fileNode;
860 if(srcType.Find("iog") != -1) {
861 //Is this an attribute for one of our meshes
862 for(int i = 0; i < maGlobal.model->objects.Num(); i++) {
863 if(!strcmp(maGlobal.model->objects[i]->name, srcName)) {
864 //maGlobal.model->objects[i]->materialRef = MA_AddMaterial(destName);
865 strcpy(maGlobal.model->objects[i]->materialName, destName);
875 void MA_BuildScale(idMat4& mat, float x, float y, float z) {
882 void MA_BuildAxisRotation(idMat4& mat, float ang, int axis) {
884 float sinAng = idMath::Sin(ang);
885 float cosAng = idMath::Cos(ang);
910 void MA_ApplyTransformation(maModel_t *model) {
912 for(int i = 0; i < model->objects.Num(); i++) {
913 maMesh_t* mesh = &model->objects[i]->mesh;
914 maTransform_t* transform = mesh->transform;
920 idMat4 rotx, roty, rotz;
927 if(fabs(transform->rotate.x) > 0.0f) {
928 MA_BuildAxisRotation(rotx, DEG2RAD(-transform->rotate.x), 0);
930 if(fabs(transform->rotate.y) > 0.0f) {
931 MA_BuildAxisRotation(roty, DEG2RAD(transform->rotate.y), 1);
933 if(fabs(transform->rotate.z) > 0.0f) {
934 MA_BuildAxisRotation(rotz, DEG2RAD(-transform->rotate.z), 2);
937 MA_BuildScale(scale, transform->scale.x, transform->scale.y, transform->scale.z);
939 //Apply the transformation to each vert
940 for(int j = 0; j < mesh->numVertexes; j++) {
941 mesh->vertexes[j] = scale * mesh->vertexes[j];
943 mesh->vertexes[j] = rotx * mesh->vertexes[j];
944 mesh->vertexes[j] = rotz * mesh->vertexes[j];
945 mesh->vertexes[j] = roty * mesh->vertexes[j];
947 mesh->vertexes[j] = mesh->vertexes[j] + transform->translate;
950 transform = transform->parent;
960 maModel_t *MA_Parse( const char *buffer, const char* filename, bool verbose ) {
961 memset( &maGlobal, 0, sizeof( maGlobal ) );
963 maGlobal.verbose = verbose;
968 maGlobal.currentObject = NULL;
970 // NOTE: using new operator because aseModel_t contains idList class objects
971 maGlobal.model = new maModel_t;
972 maGlobal.model->objects.Resize( 32, 32 );
973 maGlobal.model->materials.Resize( 32, 32 );
977 parser.SetFlags(LEXFL_NOSTRINGCONCAT);
978 parser.LoadMemory(buffer, strlen(buffer), filename);
981 while(parser.ReadToken(&token)) {
983 if(!token.Icmp("createNode")) {
984 MA_ParseCreateNode(parser);
985 } else if(!token.Icmp("connectAttr")) {
986 MA_ParseConnectAttr(parser);
990 //Resolve The Materials
991 for(int i = 0; i < maGlobal.model->objects.Num(); i++) {
992 maGlobal.model->objects[i]->materialRef = MA_AddMaterial(maGlobal.model->objects[i]->materialName);
997 //Apply Transformation
998 MA_ApplyTransformation(maGlobal.model);
1000 return maGlobal.model;
1008 maModel_t *MA_Load( const char *fileName ) {
1010 ID_TIME_T timeStamp;
1013 fileSystem->ReadFile( fileName, (void **)&buf, &timeStamp );
1019 ma = MA_Parse( buf, fileName, false );
1020 ma->timeStamp = timeStamp;
1021 } catch( idException &e ) {
1022 common->Warning("%s", e.error);
1023 if(maGlobal.model) {
1024 MA_Free(maGlobal.model);
1029 fileSystem->FreeFile( buf );
1039 void MA_Free( maModel_t *ma ) {
1043 maMaterial_t *material;
1048 for ( i = 0; i < ma->objects.Num(); i++ ) {
1049 obj = ma->objects[i];
1051 // free the base nesh
1054 if ( mesh->vertexes ) {
1055 Mem_Free( mesh->vertexes );
1057 if ( mesh->vertTransforms ) {
1058 Mem_Free( mesh->vertTransforms );
1060 if ( mesh->normals ) {
1061 Mem_Free( mesh->normals );
1063 if ( mesh->tvertexes ) {
1064 Mem_Free( mesh->tvertexes );
1066 if ( mesh->edges ) {
1067 Mem_Free( mesh->edges );
1069 if ( mesh->colors ) {
1070 Mem_Free( mesh->colors );
1072 if ( mesh->faces ) {
1073 Mem_Free( mesh->faces );
1077 ma->objects.Clear();
1079 for ( i = 0; i < ma->materials.Num(); i++ ) {
1080 material = ma->materials[i];
1081 Mem_Free( material );
1083 ma->materials.Clear();
1085 maTransform_t** trans;
1086 for ( i = 0; i < ma->transforms.Num(); i++ ) {
1087 trans = ma->transforms.GetIndex(i);
1090 ma->transforms.Clear();
1093 maFileNode_t** fileNode;
1094 for ( i = 0; i < ma->fileNodes.Num(); i++ ) {
1095 fileNode = ma->fileNodes.GetIndex(i);
1096 Mem_Free( *fileNode );
1098 ma->fileNodes.Clear();
1100 maMaterialNode_t** matNode;
1101 for ( i = 0; i < ma->materialNodes.Num(); i++ ) {
1102 matNode = ma->materialNodes.GetIndex(i);
1103 Mem_Free( *matNode );
1105 ma->materialNodes.Clear();