]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/Model_ma.cpp
hello world
[icculus/iodoom3.git] / neo / renderer / Model_ma.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
23
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.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "Model_ma.h"
33
34 /*
35 ======================================================================
36
37         Parses Maya ASCII files.
38
39 ======================================================================
40 */
41         
42
43 #define MA_VERBOSE( x ) { if ( maGlobal.verbose ) { common->Printf x ; } }
44
45 // working variables used during parsing
46 typedef struct {
47         bool                    verbose;
48         maModel_t               *model;
49         maObject_t              *currentObject;
50 } ma_t;
51
52 static ma_t maGlobal;
53
54
55 void MA_ParseNodeHeader(idParser& parser, maNodeHeader_t* header) {
56
57         memset(header, 0, sizeof(maNodeHeader_t));
58
59         idToken token;
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());
69                         }
70                 } else if (!token.Icmp(";")) {
71                         break;
72                 }
73         }
74 }
75
76 bool MA_ParseHeaderIndex(maAttribHeader_t* header, int& minIndex, int& maxIndex, const char* headerType, const char* skipString) {
77         
78         idParser miniParse;
79         idToken token;
80
81         miniParse.LoadMemory(header->name, strlen(header->name), headerType);
82         if(skipString) {
83                 miniParse.SkipUntilString(skipString);
84         }
85
86         if(!miniParse.SkipUntilString("[")) {
87                 //This was just a header
88                 return false;
89         }
90         minIndex = miniParse.ParseInt();
91         miniParse.ReadToken(&token);
92         if(!token.Icmp("]")) {
93                 maxIndex = minIndex;
94         } else {
95                 maxIndex = miniParse.ParseInt();
96         }
97         return true;
98 }
99
100 bool MA_ParseAttribHeader(idParser &parser, maAttribHeader_t* header) {
101         
102         idToken token;
103
104         memset(header, 0, sizeof(maAttribHeader_t));
105
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);
112                 }
113         }
114         strcpy(header->name, token.c_str());
115         return true;
116 }
117
118 bool MA_ReadVec3(idParser& parser, idVec3& vec) {
119         idToken token;
120         if(!parser.SkipUntilString("double3")) {
121                 throw idException( va("Maya Loader '%s': Invalid Vec3", parser.GetFileName()) );
122                 return false;
123         }
124
125
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();
130
131         return true;
132 }
133
134 bool IsNodeComplete(idToken& token) {
135         if(!token.Icmp("createNode") || !token.Icmp("connectAttr") || !token.Icmp("select")) {
136                 return true;
137         }
138         return false;
139 }
140
141 bool MA_ParseTransform(idParser& parser) {
142
143         maNodeHeader_t  header;
144         maTransform_t*  transform;
145         memset(&header, 0, sizeof(header));
146
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;
151
152         //Get the header info from the transform
153         MA_ParseNodeHeader(parser, &header);
154
155         //Read the transform attributes
156         idToken token;
157         while(parser.ReadToken(&token)) {
158                 if(IsNodeComplete(token)) {
159                         parser.UnreadToken(&token);
160                         break;
161                 }
162                 if(!token.Icmp("setAttr")) {
163                         parser.ReadToken(&token);
164                         if(!token.Icmp(".t")) {
165                                 if(!MA_ReadVec3(parser, transform->translate)) {
166                                         return false;
167                                 }
168                                 transform->translate.y *=  -1;
169                         } else if (!token.Icmp(".r")) {
170                                 if(!MA_ReadVec3(parser, transform->rotate)) {
171                                         return false;
172                                 }
173                         } else if (!token.Icmp(".s")) {
174                                 if(!MA_ReadVec3(parser, transform->scale)) {
175                                         return false;
176                                 }
177                         } else {
178                                 parser.SkipRestOfLine();
179                         }
180                 }
181         }
182
183         if(header.parent[0] != 0) {
184                 //Find the parent
185                 maTransform_t** parent;
186                 maGlobal.model->transforms.Get(header.parent, &parent);
187                 if(parent) {
188                         transform->parent = *parent;
189                 }
190         }
191         
192         //Add this transform to the list
193         maGlobal.model->transforms.Set(header.name, transform);
194         return true;
195 }
196
197 bool MA_ParseVertex(idParser& parser, maAttribHeader_t* header) {
198
199         maMesh_t* pMesh = &maGlobal.currentObject->mesh;
200         idToken token;
201
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 );
206         }
207
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
212                 return true;
213         }
214         
215         //Read each vert
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();
220         }
221         
222         return true;
223 }
224
225 bool MA_ParseVertexTransforms(idParser& parser, maAttribHeader_t* header) {
226
227         maMesh_t* pMesh = &maGlobal.currentObject->mesh;
228         idToken token;
229
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) {
233                         header->size = 1;
234                 }
235
236                 pMesh->numVertTransforms = header->size;
237                 pMesh->vertTransforms = (idVec4 *)Mem_Alloc( sizeof( idVec4 ) * pMesh->numVertTransforms );
238                 pMesh->nextVertTransformIndex = 0;
239         }
240
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
245                 return true;
246         }
247
248         parser.ReadToken(&token);
249         if(!token.Icmp("-")) {
250                 idToken tk2;
251                 parser.ReadToken(&tk2);
252                 if(!tk2.Icmp("type")) {
253                         parser.SkipUntilString("float3");
254                 } else {
255                         parser.UnreadToken(&tk2);
256                         parser.UnreadToken(&token);
257                 }
258         } else {
259                 parser.UnreadToken(&token);
260         }
261
262         //Read each vert
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();
267
268                 //w hold the vert index
269                 pMesh->vertTransforms[pMesh->nextVertTransformIndex].w = i;
270
271                 pMesh->nextVertTransformIndex++;
272         }
273
274         return true;
275 }
276
277 bool MA_ParseEdge(idParser& parser, maAttribHeader_t* header) {
278
279         maMesh_t* pMesh = &maGlobal.currentObject->mesh;
280         idToken token;
281
282         //Allocate enough space for all the verts if this is the first attribute for verticies
283         if(!pMesh->edges) {
284                 pMesh->numEdges = header->size;
285                 pMesh->edges = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numEdges );
286         }
287
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
292                 return true;
293         }
294
295         //Read each vert
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();
300         }
301
302         return true;
303 }
304
305 bool MA_ParseNormal(idParser& parser, maAttribHeader_t* header) {
306
307         maMesh_t* pMesh = &maGlobal.currentObject->mesh;
308         idToken token;
309
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 );
314         }
315
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
320                 return true;
321         }
322
323         
324         parser.ReadToken(&token);
325         if(!token.Icmp("-")) {
326                 idToken tk2;
327                 parser.ReadToken(&tk2);
328                 if(!tk2.Icmp("type")) {
329                         parser.SkipUntilString("float3");
330                 } else {
331                         parser.UnreadToken(&tk2);
332                         parser.UnreadToken(&token);
333                 }
334         } else {
335                 parser.UnreadToken(&token);
336         }
337         
338
339         //Read each vert
340         for(int i = minIndex; i <= maxIndex; i++) {
341                 pMesh->normals[i].x = parser.ParseFloat();
342
343                 //Adjust the normals for the change in coordinate systems
344                 pMesh->normals[i].z = parser.ParseFloat();
345                 pMesh->normals[i].y = -parser.ParseFloat();
346
347                 pMesh->normals[i].Normalize();
348
349         }
350
351         pMesh->normalsParsed = true;
352         pMesh->nextNormal = 0;
353
354         return true;
355 }
356
357
358
359 bool MA_ParseFace(idParser& parser, maAttribHeader_t* header) {
360
361         maMesh_t* pMesh = &maGlobal.currentObject->mesh;
362         idToken token;
363
364         //Allocate enough space for all the verts if this is the first attribute for verticies
365         if(!pMesh->faces) {
366                 pMesh->numFaces = header->size;
367                 pMesh->faces = (maFace_t *)Mem_Alloc( sizeof( maFace_t ) * pMesh->numFaces );
368         }
369
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
374                 return true;
375         }
376
377         //Read the face data
378         int currentFace = minIndex-1;
379         while(parser.ReadToken(&token)) {
380                 if(IsNodeComplete(token)) {
381                         parser.UnreadToken(&token);
382                         break;
383                 }
384
385                 if(!token.Icmp("f")) {
386                         int count = parser.ParseInt();
387                         if(count != 3) {
388                                 throw idException(va("Maya Loader '%s': Face is not a triangle.", parser.GetFileName()));
389                                 return false;
390                         }
391                         //Increment the face number because a new face always starts with an "f" token
392                         currentFace++;
393
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();
399
400                         //Some more init stuff
401                         pMesh->faces[currentFace].vertexColors[0] = pMesh->faces[currentFace].vertexColors[1] = pMesh->faces[currentFace].vertexColors[2] = -1;
402
403                 } else if(!token.Icmp("mu")) {
404                         int uvstIndex = parser.ParseInt();
405                         int count = parser.ParseInt();
406                         if(count != 3) {
407                                 throw idException(va("Maya Loader '%s': Invalid texture coordinates.", parser.GetFileName()));
408                                 return false;
409                         }
410                         pMesh->faces[currentFace].tVertexNum[0] = parser.ParseInt();
411                         pMesh->faces[currentFace].tVertexNum[1] = parser.ParseInt();
412                         pMesh->faces[currentFace].tVertexNum[2] = parser.ParseInt();
413
414                 } else if(!token.Icmp("mf")) {
415                         int count = parser.ParseInt();
416                         if(count != 3) {
417                                 throw idException(va("Maya Loader '%s': Invalid texture coordinates.", parser.GetFileName()));
418                                 return false;
419                         }
420                         pMesh->faces[currentFace].tVertexNum[0] = parser.ParseInt();
421                         pMesh->faces[currentFace].tVertexNum[1] = parser.ParseInt();
422                         pMesh->faces[currentFace].tVertexNum[2] = parser.ParseInt();
423
424                 } else if(!token.Icmp("fc")) {
425
426                         int count = parser.ParseInt();
427                         if(count != 3) {
428                                 throw idException(va("Maya Loader '%s': Invalid vertex color.", parser.GetFileName()));
429                                 return false;
430                         }
431                         pMesh->faces[currentFace].vertexColors[0] = parser.ParseInt();
432                         pMesh->faces[currentFace].vertexColors[1] = parser.ParseInt();
433                         pMesh->faces[currentFace].vertexColors[2] = parser.ParseInt();
434
435                 }
436         }
437
438         return true;
439 }
440
441 bool MA_ParseColor(idParser& parser, maAttribHeader_t* header) {
442
443         maMesh_t* pMesh = &maGlobal.currentObject->mesh;
444         idToken token;
445
446         //Allocate enough space for all the verts if this is the first attribute for verticies
447         if(!pMesh->colors) {
448                 pMesh->numColors = header->size;
449                 pMesh->colors = (byte *)Mem_Alloc( sizeof( byte ) * pMesh->numColors * 4 );
450         }
451
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
456                 return true;
457         }
458
459         //Read each vert
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;
465         }
466
467         return true;
468 }
469
470 bool MA_ParseTVert(idParser& parser, maAttribHeader_t* header) {
471         
472         maMesh_t* pMesh = &maGlobal.currentObject->mesh;
473         idToken token;
474
475         //This is not the texture coordinates. It is just the name so ignore it
476         if(strstr(header->name, "uvsn")) {
477                 return true;
478         }
479
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 );
484         }
485
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
490                 return true;
491         }
492
493         parser.ReadToken(&token);
494         if(!token.Icmp("-")) {
495                 idToken tk2;
496                 parser.ReadToken(&tk2);
497                 if(!tk2.Icmp("type")) {
498                         parser.SkipUntilString("float2");
499                 } else {
500                         parser.UnreadToken(&tk2);
501                         parser.UnreadToken(&token);
502                 }
503         } else {
504                 parser.UnreadToken(&token);
505         }
506
507         //Read each tvert
508         for(int i = minIndex; i <= maxIndex; i++) {
509                 pMesh->tvertexes[i].x = parser.ParseFloat();
510                 pMesh->tvertexes[i].y = 1.0f - parser.ParseFloat();
511         }
512
513         return true;
514 }
515
516
517
518 /*
519 *       Quick check to see if the vert participates in a shared normal
520 */
521 bool MA_QuickIsVertShared(int faceIndex, int vertIndex) {
522         
523         maMesh_t* pMesh = &maGlobal.currentObject->mesh;
524         int vertNum = pMesh->faces[faceIndex].vertexNum[vertIndex];
525         
526         for( int i = 0; i < 3; i++) {
527                 int edge = pMesh->faces[faceIndex].edge[i];
528                 if(edge < 0) {
529                         edge = idMath::Fabs(edge)-1;
530                 }
531                 if(pMesh->edges[edge].z == 1 && (pMesh->edges[edge].x == vertNum || pMesh->edges[edge].y == vertNum)) {
532                         return true;
533                 }
534         }
535         return false;
536 }
537
538 void MA_GetSharedFace(int faceIndex, int vertIndex, int& sharedFace, int& sharedVert) {
539
540         maMesh_t* pMesh = &maGlobal.currentObject->mesh;
541         int vertNum = pMesh->faces[faceIndex].vertexNum[vertIndex];
542
543         sharedFace = -1;
544         sharedVert = -1;
545
546         //Find a shared edge on this face that contains the specified vert
547         for(int edgeIndex = 0; edgeIndex < 3; edgeIndex++) {
548
549                 int edge = pMesh->faces[faceIndex].edge[edgeIndex];
550                 if(edge < 0) {
551                         edge = idMath::Fabs(edge)-1;
552                 }
553
554                 if(pMesh->edges[edge].z == 1 && (pMesh->edges[edge].x == vertNum || pMesh->edges[edge].y == vertNum)) {
555
556                         for(int i = 0; i < faceIndex; i++) {
557
558                                 for(int j = 0; j < 3; j++) {
559                                         if(pMesh->faces[i].vertexNum[j] == vertNum) {
560                                                 sharedFace = i;
561                                                 sharedVert = j;
562                                                 break;
563                                         }
564                                 }
565                         }
566                 }
567                 if(sharedFace != -1)
568                         break;
569
570         }
571 }
572
573 void MA_ParseMesh(idParser& parser) {
574
575         maObject_t      *object;
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;
581
582
583         //Get the header info from the mesh
584         maNodeHeader_t  header;
585         MA_ParseNodeHeader(parser, &header);
586
587         //Find my parent
588         if(header.parent[0] != 0) {
589                 //Find the parent
590                 maTransform_t** parent;
591                 maGlobal.model->transforms.Get(header.parent, &parent);
592                 if(parent) {
593                         maGlobal.currentObject->mesh.transform = *parent;
594                 }
595         }
596
597         strcpy(object->name, header.name);
598
599         //Read the transform attributes
600         idToken token;
601         while(parser.ReadToken(&token)) {
602                 if(IsNodeComplete(token)) {
603                         parser.UnreadToken(&token);
604                         break;
605                 }
606                 if(!token.Icmp("setAttr")) {
607                         maAttribHeader_t header;
608                         MA_ParseAttribHeader(parser, &header);
609
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);
624                         } else {
625                                 parser.SkipRestOfLine();
626                         }
627                 }
628         }
629
630
631         maMesh_t* pMesh = &maGlobal.currentObject->mesh;
632
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];
637                         if(edge < 0) {
638                                 edge = idMath::Fabs(edge)-1;
639                                 pMesh->faces[i].vertexNum[j] = pMesh->edges[edge].y;
640                         } else {
641                                 pMesh->faces[i].vertexNum[j] = pMesh->edges[edge].x;
642                         }
643                 }
644         }
645
646         //Get the normals
647         if(pMesh->normalsParsed) {
648                 for(int i = 0; i < pMesh->numFaces; i++) {
649                         for(int j = 0; j < 3; j++) {
650
651                                 //Is this vertex shared
652                                 int sharedFace = -1;
653                                 int sharedVert = -1;
654
655                                 if(MA_QuickIsVertShared(i, j)) {
656                                         MA_GetSharedFace(i, j, sharedFace, sharedVert);
657                                 }
658                                 
659                                 if(sharedFace != -1) {
660                                         //Get the normal from the share
661                                         pMesh->faces[i].vertexNormals[j] = pMesh->faces[sharedFace].vertexNormals[sharedVert];
662
663                                 } else {
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()));
668                                         }
669                                         pMesh->faces[i].vertexNormals[j] = pMesh->normals[pMesh->nextNormal];
670                                         pMesh->nextNormal++;
671                                 }
672                         }
673                 }
674         }
675
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;
681                         
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;
685
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;
689
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;
693         }
694
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();
698         }
699         
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)));
703 }
704
705 void MA_ParseFileNode(idParser& parser) {
706         
707         //Get the header info from the node
708         maNodeHeader_t  header;
709         MA_ParseNodeHeader(parser, &header);
710
711         //Read the transform attributes
712         idToken token;
713         while(parser.ReadToken(&token)) {
714                 if(IsNodeComplete(token)) {
715                         parser.UnreadToken(&token);
716                         break;
717                 }
718                 if(!token.Icmp("setAttr")) {
719                         maAttribHeader_t attribHeader;
720                         MA_ParseAttribHeader(parser, &attribHeader);
721
722                         if(strstr(attribHeader.name, ".ftn")) {
723                                 parser.SkipUntilString("string");
724                                 parser.ReadToken(&token);
725                                 if(!token.Icmp("(")) {
726                                         parser.ReadToken(&token);
727                                 }
728
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());
733
734                                 maGlobal.model->fileNodes.Set(fileNode->name, fileNode);
735                         } else {
736                                 parser.SkipRestOfLine();
737                         }
738                 }
739         }
740 }
741
742 void MA_ParseMaterialNode(idParser& parser) {
743
744         //Get the header info from the node
745         maNodeHeader_t  header;
746         MA_ParseNodeHeader(parser, &header);
747
748         maMaterialNode_t* matNode;
749         matNode = (maMaterialNode_t*)Mem_Alloc( sizeof( maMaterialNode_t ) );
750         memset(matNode, 0, sizeof(maMaterialNode_t));
751
752         strcpy(matNode->name, header.name);
753         
754         maGlobal.model->materialNodes.Set(matNode->name, matNode);
755 }
756
757 void MA_ParseCreateNode(idParser& parser) {
758
759         idToken token;
760         parser.ReadToken(&token);
761
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);
770         }
771 }
772
773
774 int MA_AddMaterial(const char* materialName) {
775         
776
777         maMaterialNode_t**      destNode;
778         maGlobal.model->materialNodes.Get(materialName, &destNode);
779         if(destNode) {
780                 maMaterialNode_t* matNode = *destNode;
781
782                 //Iterate down the tree until we get a file
783                 while(matNode && !matNode->file) {
784                         matNode = matNode->child;
785                 }
786                 if(matNode && matNode->file) {
787                         
788                         //Got the file
789                         maMaterial_t    *material;
790                         material = (maMaterial_t *)Mem_Alloc( sizeof( maMaterial_t ) );
791                         memset( material, 0, sizeof( maMaterial_t ) );
792                         
793                         //Remove the OS stuff
794                         idStr qPath;
795                         qPath = fileSystem->OSPathToRelativePath( matNode->file->path );
796                         
797                         strcpy(material->name, qPath.c_str());
798
799                         maGlobal.model->materials.Append( material );
800                         return maGlobal.model->materials.Num()-1;
801                 }
802         }
803         return -1;
804 }
805
806 bool MA_ParseConnectAttr(idParser& parser) {
807
808         idStr temp;
809         idStr srcName;
810         idStr srcType;
811         idStr destName;
812         idStr destType;
813
814         idToken token;
815         parser.ReadToken(&token);
816         temp = token;
817         int dot = temp.Find(".");
818         if(dot == -1) {
819                 throw idException(va("Maya Loader '%s': Invalid Connect Attribute.", parser.GetFileName()));
820                 return false;
821         }
822         srcName = temp.Left(dot);
823         srcType = temp.Right(temp.Length()-dot-1);
824
825         parser.ReadToken(&token);
826         temp = token;
827         dot = temp.Find(".");
828         if(dot == -1) {
829                 throw idException(va("Maya Loader '%s': Invalid Connect Attribute.", parser.GetFileName()));
830                 return false;
831         }
832         destName = temp.Left(dot);
833         destType = temp.Right(temp.Length()-dot-1);
834
835         if(srcType.Find("oc") != -1) {
836                 
837                 //Is this attribute a material node attribute
838                 maMaterialNode_t**      matNode;
839                 maGlobal.model->materialNodes.Get(srcName, &matNode);
840                 if(matNode) {
841                         maMaterialNode_t**      destNode;
842                         maGlobal.model->materialNodes.Get(destName, &destNode);
843                         if(destNode) {
844                                 (*destNode)->child = *matNode;
845                         }
846                 }
847
848                 //Is this attribute a file node
849                 maFileNode_t** fileNode;
850                 maGlobal.model->fileNodes.Get(srcName, &fileNode);
851                 if(fileNode) {
852                         maMaterialNode_t**      destNode;
853                         maGlobal.model->materialNodes.Get(destName, &destNode);
854                         if(destNode) {
855                                 (*destNode)->file = *fileNode;
856                         }
857                 }
858         }
859
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); 
866                                 break;
867                         }
868                 }
869         }
870
871         return true;
872 }
873
874
875 void MA_BuildScale(idMat4& mat, float x, float y, float z) {
876         mat.Identity();
877         mat[0][0] = x;
878         mat[1][1] = y;
879         mat[2][2] = z;
880 }
881
882 void MA_BuildAxisRotation(idMat4& mat, float ang, int axis) {
883
884         float sinAng = idMath::Sin(ang);
885         float cosAng = idMath::Cos(ang);
886
887         mat.Identity();
888         switch(axis) {
889         case 0: //x
890                 mat[1][1] = cosAng;
891                 mat[1][2] = sinAng;
892                 mat[2][1] = -sinAng;
893                 mat[2][2] = cosAng;
894                 break;
895         case 1: //y
896                 mat[0][0] = cosAng;
897                 mat[0][2] = -sinAng;
898                 mat[2][0] = sinAng;
899                 mat[2][2] = cosAng;
900                 break;
901         case 2://z
902                 mat[0][0] = cosAng;
903                 mat[0][1] = sinAng;
904                 mat[1][0] = -sinAng;
905                 mat[1][1] = cosAng;
906                 break;
907         }
908 }
909
910 void MA_ApplyTransformation(maModel_t *model) {
911         
912         for(int i = 0; i < model->objects.Num(); i++) {
913                 maMesh_t* mesh = &model->objects[i]->mesh;
914                 maTransform_t* transform = mesh->transform;
915                 
916                 
917
918                 while(transform) {
919
920                         idMat4 rotx, roty, rotz;
921                         idMat4 scale;
922
923                         rotx.Identity();
924                         roty.Identity();
925                         rotz.Identity();
926
927                         if(fabs(transform->rotate.x) > 0.0f) {
928                                 MA_BuildAxisRotation(rotx, DEG2RAD(-transform->rotate.x), 0);
929                         }
930                         if(fabs(transform->rotate.y) > 0.0f) {
931                                 MA_BuildAxisRotation(roty, DEG2RAD(transform->rotate.y), 1);
932                         }
933                         if(fabs(transform->rotate.z) > 0.0f) {
934                                 MA_BuildAxisRotation(rotz, DEG2RAD(-transform->rotate.z), 2);
935                         }
936
937                         MA_BuildScale(scale, transform->scale.x, transform->scale.y, transform->scale.z);
938
939                         //Apply the transformation to each vert
940                         for(int j = 0; j < mesh->numVertexes; j++) {
941                                 mesh->vertexes[j] = scale * mesh->vertexes[j];
942
943                                 mesh->vertexes[j] = rotx * mesh->vertexes[j];
944                                 mesh->vertexes[j] = rotz * mesh->vertexes[j];
945                                 mesh->vertexes[j] = roty * mesh->vertexes[j];
946                                 
947                                 mesh->vertexes[j] = mesh->vertexes[j] + transform->translate;
948                         }
949                         
950                         transform = transform->parent;
951                 }
952         }
953 }
954
955 /*
956 =================
957 MA_Parse
958 =================
959 */
960 maModel_t *MA_Parse( const char *buffer, const char* filename, bool verbose ) {
961         memset( &maGlobal, 0, sizeof( maGlobal ) );
962
963         maGlobal.verbose = verbose;
964
965         
966         
967         
968         maGlobal.currentObject = NULL;
969
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 );
974         
975
976         idParser parser;
977         parser.SetFlags(LEXFL_NOSTRINGCONCAT);
978         parser.LoadMemory(buffer, strlen(buffer), filename);
979
980         idToken token;
981         while(parser.ReadToken(&token)) {
982
983                 if(!token.Icmp("createNode")) {
984                         MA_ParseCreateNode(parser);
985                 } else if(!token.Icmp("connectAttr")) {
986                         MA_ParseConnectAttr(parser);
987                 }
988         }
989
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);
993         }
994
995         
996
997         //Apply Transformation
998         MA_ApplyTransformation(maGlobal.model);
999
1000         return maGlobal.model;
1001 }
1002
1003 /*
1004 =================
1005 MA_Load
1006 =================
1007 */
1008 maModel_t *MA_Load( const char *fileName ) {
1009         char *buf;
1010         ID_TIME_T timeStamp;
1011         maModel_t *ma;
1012
1013         fileSystem->ReadFile( fileName, (void **)&buf, &timeStamp );
1014         if ( !buf ) {
1015                 return NULL;
1016         }
1017
1018         try {
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);
1025                 }
1026                 ma = NULL;
1027         }
1028
1029         fileSystem->FreeFile( buf );
1030
1031         return ma;
1032 }
1033
1034 /*
1035 =================
1036 MA_Free
1037 =================
1038 */
1039 void MA_Free( maModel_t *ma ) {
1040         int                                     i;
1041         maObject_t                      *obj;
1042         maMesh_t                        *mesh;
1043         maMaterial_t            *material;
1044
1045         if ( !ma ) {
1046                 return;
1047         }
1048         for ( i = 0; i < ma->objects.Num(); i++ ) {
1049                 obj = ma->objects[i];
1050                 
1051                 // free the base nesh
1052                 mesh = &obj->mesh;
1053                 
1054                 if ( mesh->vertexes ) {
1055                         Mem_Free( mesh->vertexes );
1056                 }
1057                 if ( mesh->vertTransforms ) {
1058                         Mem_Free( mesh->vertTransforms );
1059                 }
1060                 if ( mesh->normals ) {
1061                         Mem_Free( mesh->normals );
1062                 }
1063                 if ( mesh->tvertexes ) {
1064                         Mem_Free( mesh->tvertexes );
1065                 }
1066                 if ( mesh->edges ) {
1067                         Mem_Free( mesh->edges );
1068                 }
1069                 if ( mesh->colors ) {
1070                         Mem_Free( mesh->colors );
1071                 }
1072                 if ( mesh->faces ) {
1073                         Mem_Free( mesh->faces );
1074                 }
1075                 Mem_Free( obj );
1076         }
1077         ma->objects.Clear();
1078
1079         for ( i = 0; i < ma->materials.Num(); i++ ) {
1080                 material = ma->materials[i];
1081                 Mem_Free( material );
1082         }
1083         ma->materials.Clear();
1084
1085         maTransform_t** trans;
1086         for ( i = 0; i < ma->transforms.Num(); i++ ) {
1087                 trans = ma->transforms.GetIndex(i);
1088                 Mem_Free( *trans );
1089         }
1090         ma->transforms.Clear();
1091
1092
1093         maFileNode_t** fileNode;
1094         for ( i = 0; i < ma->fileNodes.Num(); i++ ) {
1095                 fileNode = ma->fileNodes.GetIndex(i);
1096                 Mem_Free( *fileNode );
1097         }
1098         ma->fileNodes.Clear();
1099
1100         maMaterialNode_t** matNode;
1101         for ( i = 0; i < ma->materialNodes.Num(); i++ ) {
1102                 matNode = ma->materialNodes.GetIndex(i);
1103                 Mem_Free( *matNode );
1104         }
1105         ma->materialNodes.Clear();
1106         delete ma;
1107 }