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 "sys/platform.h"
30 #include "idlib/Parser.h"
31 #include "framework/Common.h"
32 #include "framework/FileSystem.h"
33 #include "framework/Session.h"
34
35 #include "renderer/Model_ma.h"
36
37 /*
38 ======================================================================
39
40 Parses Maya ASCII files.
41
42 ======================================================================
43 */
44
45
46 #define MA_VERBOSE( x ) { if ( maGlobal.verbose ) { common->Printf x ; } }
47
48 // working variables used during parsing
49 typedef struct {
50 bool verbose;
51 maModel_t *model;
52 maObject_t *currentObject;
53 } ma_t;
54
55 static ma_t maGlobal;
56
57
MA_ParseNodeHeader(idParser & parser,maNodeHeader_t * header)58 void MA_ParseNodeHeader(idParser& parser, maNodeHeader_t* header) {
59
60 memset(header, 0, sizeof(maNodeHeader_t));
61
62 idToken token;
63 while(parser.ReadToken(&token)) {
64 if(!token.Icmp("-")) {
65 parser.ReadToken(&token);
66 if (!token.Icmp("n")) {
67 parser.ReadToken(&token);
68 strcpy(header->name, token.c_str());
69 } else if (!token.Icmp("p")) {
70 parser.ReadToken(&token);
71 strcpy(header->parent, token.c_str());
72 }
73 } else if (!token.Icmp(";")) {
74 break;
75 }
76 }
77 }
78
MA_ParseHeaderIndex(maAttribHeader_t * header,int & minIndex,int & maxIndex,const char * headerType,const char * skipString)79 bool MA_ParseHeaderIndex(maAttribHeader_t* header, int& minIndex, int& maxIndex, const char* headerType, const char* skipString) {
80
81 idParser miniParse;
82 idToken token;
83
84 miniParse.LoadMemory(header->name, strlen(header->name), headerType);
85 if(skipString) {
86 miniParse.SkipUntilString(skipString);
87 }
88
89 if(!miniParse.SkipUntilString("[")) {
90 //This was just a header
91 return false;
92 }
93 minIndex = miniParse.ParseInt();
94 miniParse.ReadToken(&token);
95 if(!token.Icmp("]")) {
96 maxIndex = minIndex;
97 } else {
98 maxIndex = miniParse.ParseInt();
99 }
100 return true;
101 }
102
MA_ParseAttribHeader(idParser & parser,maAttribHeader_t * header)103 bool MA_ParseAttribHeader(idParser &parser, maAttribHeader_t* header) {
104
105 idToken token;
106
107 memset(header, 0, sizeof(maAttribHeader_t));
108
109 parser.ReadToken(&token);
110 if(!token.Icmp("-")) {
111 parser.ReadToken(&token);
112 if (!token.Icmp("s")) {
113 header->size = parser.ParseInt();
114 parser.ReadToken(&token);
115 }
116 }
117 strcpy(header->name, token.c_str());
118 return true;
119 }
120
MA_ReadVec3(idParser & parser,idVec3 & vec)121 bool MA_ReadVec3(idParser& parser, idVec3& vec) {
122 idToken token;
123 if(!parser.SkipUntilString("double3")) {
124 throw idException( va("Maya Loader '%s': Invalid Vec3", parser.GetFileName()) );
125 }
126
127
128 //We need to flip y and z because of the maya coordinate system
129 vec.x = parser.ParseFloat();
130 vec.z = parser.ParseFloat();
131 vec.y = parser.ParseFloat();
132
133 return true;
134 }
135
IsNodeComplete(idToken & token)136 bool IsNodeComplete(idToken& token) {
137 if(!token.Icmp("createNode") || !token.Icmp("connectAttr") || !token.Icmp("select")) {
138 return true;
139 }
140 return false;
141 }
142
MA_ParseTransform(idParser & parser)143 bool MA_ParseTransform(idParser& parser) {
144
145 maNodeHeader_t header;
146 maTransform_t* transform;
147 memset(&header, 0, sizeof(header));
148
149 //Allocate room for the transform
150 transform = (maTransform_t *)Mem_Alloc( sizeof( maTransform_t ) );
151 memset(transform, 0, sizeof(maTransform_t));
152 transform->scale.x = transform->scale.y = transform->scale.z = 1;
153
154 //Get the header info from the transform
155 MA_ParseNodeHeader(parser, &header);
156
157 //Read the transform attributes
158 idToken token;
159 while(parser.ReadToken(&token)) {
160 if(IsNodeComplete(token)) {
161 parser.UnreadToken(&token);
162 break;
163 }
164 if(!token.Icmp("setAttr")) {
165 parser.ReadToken(&token);
166 if(!token.Icmp(".t")) {
167 if(!MA_ReadVec3(parser, transform->translate)) {
168 return false;
169 }
170 transform->translate.y *= -1;
171 } else if (!token.Icmp(".r")) {
172 if(!MA_ReadVec3(parser, transform->rotate)) {
173 return false;
174 }
175 } else if (!token.Icmp(".s")) {
176 if(!MA_ReadVec3(parser, transform->scale)) {
177 return false;
178 }
179 } else {
180 parser.SkipRestOfLine();
181 }
182 }
183 }
184
185 if(header.parent[0] != 0) {
186 //Find the parent
187 maTransform_t** parent;
188 maGlobal.model->transforms.Get(header.parent, &parent);
189 if(parent) {
190 transform->parent = *parent;
191 }
192 }
193
194 //Add this transform to the list
195 maGlobal.model->transforms.Set(header.name, transform);
196 return true;
197 }
198
MA_ParseVertex(idParser & parser,maAttribHeader_t * header)199 bool MA_ParseVertex(idParser& parser, maAttribHeader_t* header) {
200
201 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
202 idToken token;
203
204 //Allocate enough space for all the verts if this is the first attribute for verticies
205 if(!pMesh->vertexes) {
206 pMesh->numVertexes = header->size; // XXX: +1?
207 pMesh->vertexes = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numVertexes );
208 }
209
210 //Get the start and end index for this attribute
211 int minIndex, maxIndex;
212 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "VertexHeader", NULL)) {
213 //This was just a header
214 return true;
215 }
216
217 //Read each vert
218 for(int i = minIndex; i <= maxIndex; i++) {
219 pMesh->vertexes[i].x = parser.ParseFloat();
220 pMesh->vertexes[i].z = parser.ParseFloat();
221 pMesh->vertexes[i].y = -parser.ParseFloat();
222 }
223
224 return true;
225 }
226
MA_ParseVertexTransforms(idParser & parser,maAttribHeader_t * header)227 bool MA_ParseVertexTransforms(idParser& parser, maAttribHeader_t* header) {
228
229 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
230 idToken token;
231
232 //Allocate enough space for all the verts if this is the first attribute for verticies
233 if(!pMesh->vertTransforms) {
234 if(header->size == 0) {
235 header->size = 1;
236 }
237
238 pMesh->numVertTransforms = header->size;
239 pMesh->vertTransforms = (idVec4 *)Mem_Alloc( sizeof( idVec4 ) * pMesh->numVertTransforms );
240 pMesh->nextVertTransformIndex = 0;
241 }
242
243 //Get the start and end index for this attribute
244 int minIndex, maxIndex;
245 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "VertexTransformHeader", NULL)) {
246 //This was just a header
247 return true;
248 }
249
250 parser.ReadToken(&token);
251 if(!token.Icmp("-")) {
252 idToken tk2;
253 parser.ReadToken(&tk2);
254 if(!tk2.Icmp("type")) {
255 parser.SkipUntilString("float3");
256 } else {
257 parser.UnreadToken(&tk2);
258 parser.UnreadToken(&token);
259 }
260 } else {
261 parser.UnreadToken(&token);
262 }
263
264 //Read each vert
265 for(int i = minIndex; i <= maxIndex; i++) {
266 pMesh->vertTransforms[pMesh->nextVertTransformIndex].x = parser.ParseFloat();
267 pMesh->vertTransforms[pMesh->nextVertTransformIndex].z = parser.ParseFloat();
268 pMesh->vertTransforms[pMesh->nextVertTransformIndex].y = -parser.ParseFloat();
269
270 //w hold the vert index
271 pMesh->vertTransforms[pMesh->nextVertTransformIndex].w = i;
272
273 pMesh->nextVertTransformIndex++;
274 }
275
276 return true;
277 }
278
MA_ParseEdge(idParser & parser,maAttribHeader_t * header)279 bool MA_ParseEdge(idParser& parser, maAttribHeader_t* header) {
280
281 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
282 idToken token;
283
284 //Allocate enough space for all the verts if this is the first attribute for verticies
285 if(!pMesh->edges) {
286 pMesh->numEdges = header->size;
287 pMesh->edges = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numEdges );
288 }
289
290 //Get the start and end index for this attribute
291 int minIndex, maxIndex;
292 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "EdgeHeader", NULL)) {
293 //This was just a header
294 return true;
295 }
296
297 //Read each vert
298 for(int i = minIndex; i <= maxIndex; i++) {
299 pMesh->edges[i].x = parser.ParseFloat();
300 pMesh->edges[i].y = parser.ParseFloat();
301 pMesh->edges[i].z = parser.ParseFloat();
302 }
303
304 return true;
305 }
306
MA_ParseNormal(idParser & parser,maAttribHeader_t * header)307 bool MA_ParseNormal(idParser& parser, maAttribHeader_t* header) {
308
309 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
310 idToken token;
311
312 //Allocate enough space for all the verts if this is the first attribute for verticies
313 if(!pMesh->normals) {
314 pMesh->numNormals = header->size;
315 pMesh->normals = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numNormals );
316 }
317
318 //Get the start and end index for this attribute
319 int minIndex, maxIndex;
320 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "NormalHeader", NULL)) {
321 //This was just a header
322 return true;
323 }
324
325
326 parser.ReadToken(&token);
327 if(!token.Icmp("-")) {
328 idToken tk2;
329 parser.ReadToken(&tk2);
330 if(!tk2.Icmp("type")) {
331 parser.SkipUntilString("float3");
332 } else {
333 parser.UnreadToken(&tk2);
334 parser.UnreadToken(&token);
335 }
336 } else {
337 parser.UnreadToken(&token);
338 }
339
340
341 //Read each vert
342 for(int i = minIndex; i <= maxIndex; i++) {
343 pMesh->normals[i].x = parser.ParseFloat();
344
345 //Adjust the normals for the change in coordinate systems
346 pMesh->normals[i].z = parser.ParseFloat();
347 pMesh->normals[i].y = -parser.ParseFloat();
348
349 pMesh->normals[i].Normalize();
350
351 }
352
353 pMesh->normalsParsed = true;
354 pMesh->nextNormal = 0;
355
356 return true;
357 }
358
359
360
MA_ParseFace(idParser & parser,maAttribHeader_t * header)361 bool MA_ParseFace(idParser& parser, maAttribHeader_t* header) {
362
363 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
364 idToken token;
365
366 //Allocate enough space for all the verts if this is the first attribute for verticies
367 if(!pMesh->faces) {
368 pMesh->numFaces = header->size;
369 pMesh->faces = (maFace_t *)Mem_Alloc( sizeof( maFace_t ) * pMesh->numFaces );
370 }
371
372 //Get the start and end index for this attribute
373 int minIndex, maxIndex;
374 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "FaceHeader", NULL)) {
375 //This was just a header
376 return true;
377 }
378
379 //Read the face data
380 int currentFace = minIndex-1;
381 while(parser.ReadToken(&token)) {
382 if(IsNodeComplete(token)) {
383 parser.UnreadToken(&token);
384 break;
385 }
386
387 if(!token.Icmp("f")) {
388 int count = parser.ParseInt();
389 if(count != 3) {
390 throw idException(va("Maya Loader '%s': Face is not a triangle.", parser.GetFileName()));
391 }
392 //Increment the face number because a new face always starts with an "f" token
393 currentFace++;
394
395 //We cannot reorder edges until later because the normal processing
396 //assumes the edges are in the original order
397 pMesh->faces[currentFace].edge[0] = parser.ParseInt();
398 pMesh->faces[currentFace].edge[1] = parser.ParseInt();
399 pMesh->faces[currentFace].edge[2] = parser.ParseInt();
400
401 //Some more init stuff
402 pMesh->faces[currentFace].vertexColors[0] = pMesh->faces[currentFace].vertexColors[1] = pMesh->faces[currentFace].vertexColors[2] = -1;
403
404 } else if(!token.Icmp("mu")) {
405 /* int uvstIndex = */ parser.ParseInt();
406 int count = parser.ParseInt();
407 if(count != 3) {
408 throw idException(va("Maya Loader '%s': Invalid texture coordinates.", parser.GetFileName()));
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 }
419 pMesh->faces[currentFace].tVertexNum[0] = parser.ParseInt();
420 pMesh->faces[currentFace].tVertexNum[1] = parser.ParseInt();
421 pMesh->faces[currentFace].tVertexNum[2] = parser.ParseInt();
422
423 } else if(!token.Icmp("fc")) {
424
425 int count = parser.ParseInt();
426 if(count != 3) {
427 throw idException(va("Maya Loader '%s': Invalid vertex color.", parser.GetFileName()));
428 }
429 pMesh->faces[currentFace].vertexColors[0] = parser.ParseInt();
430 pMesh->faces[currentFace].vertexColors[1] = parser.ParseInt();
431 pMesh->faces[currentFace].vertexColors[2] = parser.ParseInt();
432
433 }
434 }
435
436 return true;
437 }
438
MA_ParseColor(idParser & parser,maAttribHeader_t * header)439 bool MA_ParseColor(idParser& parser, maAttribHeader_t* header) {
440
441 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
442 idToken token;
443
444 //Allocate enough space for all the verts if this is the first attribute for verticies
445 if(!pMesh->colors) {
446 pMesh->numColors = header->size;
447 pMesh->colors = (byte *)Mem_Alloc( sizeof( byte ) * pMesh->numColors * 4 );
448 }
449
450 //Get the start and end index for this attribute
451 int minIndex, maxIndex;
452 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "ColorHeader", NULL)) {
453 //This was just a header
454 return true;
455 }
456
457 //Read each vert
458 for(int i = minIndex; i <= maxIndex; i++) {
459 pMesh->colors[i*4] = parser.ParseFloat() * 255;
460 pMesh->colors[i*4+1] = parser.ParseFloat() * 255;
461 pMesh->colors[i*4+2] = parser.ParseFloat() * 255;
462 pMesh->colors[i*4+3] = parser.ParseFloat() * 255;
463 }
464
465 return true;
466 }
467
MA_ParseTVert(idParser & parser,maAttribHeader_t * header)468 bool MA_ParseTVert(idParser& parser, maAttribHeader_t* header) {
469
470 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
471 idToken token;
472
473 //This is not the texture coordinates. It is just the name so ignore it
474 if(strstr(header->name, "uvsn")) {
475 return true;
476 }
477
478 //Allocate enough space for all the data
479 if(!pMesh->tvertexes) {
480 pMesh->numTVertexes = header->size;
481 pMesh->tvertexes = (idVec2 *)Mem_Alloc( sizeof( idVec2 ) * pMesh->numTVertexes );
482 }
483
484 //Get the start and end index for this attribute
485 int minIndex, maxIndex;
486 if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "TextureCoordHeader", "uvsp")) {
487 //This was just a header
488 return true;
489 }
490
491 parser.ReadToken(&token);
492 if(!token.Icmp("-")) {
493 idToken tk2;
494 parser.ReadToken(&tk2);
495 if(!tk2.Icmp("type")) {
496 parser.SkipUntilString("float2");
497 } else {
498 parser.UnreadToken(&tk2);
499 parser.UnreadToken(&token);
500 }
501 } else {
502 parser.UnreadToken(&token);
503 }
504
505 //Read each tvert
506 for(int i = minIndex; i <= maxIndex; i++) {
507 pMesh->tvertexes[i].x = parser.ParseFloat();
508 pMesh->tvertexes[i].y = 1.0f - parser.ParseFloat();
509 }
510
511 return true;
512 }
513
514
515
516 /*
517 * Quick check to see if the vert participates in a shared normal
518 */
MA_QuickIsVertShared(int faceIndex,int vertIndex)519 bool MA_QuickIsVertShared(int faceIndex, int vertIndex) {
520
521 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
522 int vertNum = pMesh->faces[faceIndex].vertexNum[vertIndex];
523
524 for( int i = 0; i < 3; i++) {
525 int edge = pMesh->faces[faceIndex].edge[i];
526 if(edge < 0) {
527 edge = idMath::Fabs(edge)-1;
528 }
529 if(pMesh->edges[edge].z == 1 && (pMesh->edges[edge].x == vertNum || pMesh->edges[edge].y == vertNum)) {
530 return true;
531 }
532 }
533 return false;
534 }
535
MA_GetSharedFace(int faceIndex,int vertIndex,int & sharedFace,int & sharedVert)536 void MA_GetSharedFace(int faceIndex, int vertIndex, int& sharedFace, int& sharedVert) {
537
538 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
539 int vertNum = pMesh->faces[faceIndex].vertexNum[vertIndex];
540
541 sharedFace = -1;
542 sharedVert = -1;
543
544 //Find a shared edge on this face that contains the specified vert
545 for(int edgeIndex = 0; edgeIndex < 3; edgeIndex++) {
546
547 int edge = pMesh->faces[faceIndex].edge[edgeIndex];
548 if(edge < 0) {
549 edge = idMath::Fabs(edge)-1;
550 }
551
552 if(pMesh->edges[edge].z == 1 && (pMesh->edges[edge].x == vertNum || pMesh->edges[edge].y == vertNum)) {
553
554 for(int i = 0; i < faceIndex; i++) {
555
556 for(int j = 0; j < 3; j++) {
557 if(pMesh->faces[i].vertexNum[j] == vertNum) {
558 sharedFace = i;
559 sharedVert = j;
560 break;
561 }
562 }
563 }
564 }
565 if(sharedFace != -1)
566 break;
567
568 }
569 }
570
MA_ParseMesh(idParser & parser)571 void MA_ParseMesh(idParser& parser) {
572
573 maObject_t *object;
574 object = (maObject_t *)Mem_Alloc( sizeof( maObject_t ) );
575 memset( object, 0, sizeof( maObject_t ) );
576 maGlobal.model->objects.Append( object );
577 maGlobal.currentObject = object;
578 object->materialRef = -1;
579
580
581 //Get the header info from the mesh
582 maNodeHeader_t header;
583 MA_ParseNodeHeader(parser, &header);
584
585 //Find my parent
586 if(header.parent[0] != 0) {
587 //Find the parent
588 maTransform_t** parent;
589 maGlobal.model->transforms.Get(header.parent, &parent);
590 if(parent) {
591 maGlobal.currentObject->mesh.transform = *parent;
592 }
593 }
594
595 strcpy(object->name, header.name);
596
597 //Read the transform attributes
598 idToken token;
599 while(parser.ReadToken(&token)) {
600 if(IsNodeComplete(token)) {
601 parser.UnreadToken(&token);
602 break;
603 }
604 if(!token.Icmp("setAttr")) {
605 maAttribHeader_t header;
606 MA_ParseAttribHeader(parser, &header);
607
608 if(strstr(header.name, ".vt")) {
609 MA_ParseVertex(parser, &header);
610 } else if (strstr(header.name, ".ed")) {
611 MA_ParseEdge(parser, &header);
612 } else if (strstr(header.name, ".pt")) {
613 MA_ParseVertexTransforms(parser, &header);
614 } else if (strstr(header.name, ".n")) {
615 MA_ParseNormal(parser, &header);
616 } else if (strstr(header.name, ".fc")) {
617 MA_ParseFace(parser, &header);
618 } else if (strstr(header.name, ".clr")) {
619 MA_ParseColor(parser, &header);
620 } else if (strstr(header.name, ".uvst")) {
621 MA_ParseTVert(parser, &header);
622 } else {
623 parser.SkipRestOfLine();
624 }
625 }
626 }
627
628
629 maMesh_t* pMesh = &maGlobal.currentObject->mesh;
630
631 //Get the verts from the edge
632 for(int i = 0; i < pMesh->numFaces; i++) {
633 for(int j = 0; j < 3; j++) {
634 int edge = pMesh->faces[i].edge[j];
635 if(edge < 0) {
636 edge = idMath::Fabs(edge)-1;
637 pMesh->faces[i].vertexNum[j] = pMesh->edges[edge].y;
638 } else {
639 pMesh->faces[i].vertexNum[j] = pMesh->edges[edge].x;
640 }
641 }
642 }
643
644 //Get the normals
645 if(pMesh->normalsParsed) {
646 for(int i = 0; i < pMesh->numFaces; i++) {
647 for(int j = 0; j < 3; j++) {
648
649 //Is this vertex shared
650 int sharedFace = -1;
651 int sharedVert = -1;
652
653 if(MA_QuickIsVertShared(i, j)) {
654 MA_GetSharedFace(i, j, sharedFace, sharedVert);
655 }
656
657 if(sharedFace != -1) {
658 //Get the normal from the share
659 pMesh->faces[i].vertexNormals[j] = pMesh->faces[sharedFace].vertexNormals[sharedVert];
660
661 } else {
662 //The vertex is not shared so get the next normal
663 if(pMesh->nextNormal >= pMesh->numNormals) {
664 //We are using more normals than exist
665 throw idException(va("Maya Loader '%s': Invalid Normals Index.", parser.GetFileName()));
666 }
667 pMesh->faces[i].vertexNormals[j] = pMesh->normals[pMesh->nextNormal];
668 pMesh->nextNormal++;
669 }
670 }
671 }
672 }
673
674 //Now that the normals are good...lets reorder the verts to make the tris face the right way
675 for(int i = 0; i < pMesh->numFaces; i++) {
676 int tmp = pMesh->faces[i].vertexNum[1];
677 pMesh->faces[i].vertexNum[1] = pMesh->faces[i].vertexNum[2];
678 pMesh->faces[i].vertexNum[2] = tmp;
679
680 idVec3 tmpVec = pMesh->faces[i].vertexNormals[1];
681 pMesh->faces[i].vertexNormals[1] = pMesh->faces[i].vertexNormals[2];
682 pMesh->faces[i].vertexNormals[2] = tmpVec;
683
684 tmp = pMesh->faces[i].tVertexNum[1];
685 pMesh->faces[i].tVertexNum[1] = pMesh->faces[i].tVertexNum[2];
686 pMesh->faces[i].tVertexNum[2] = tmp;
687
688 tmp = pMesh->faces[i].vertexColors[1];
689 pMesh->faces[i].vertexColors[1] = pMesh->faces[i].vertexColors[2];
690 pMesh->faces[i].vertexColors[2] = tmp;
691 }
692
693 //Now apply the pt transformations
694 for(int i = 0; i < pMesh->numVertTransforms; i++) {
695 int idx = (int)pMesh->vertTransforms[i].w;
696 if(idx < 0 || idx >= pMesh->numVertexes)
697 {
698 // this happens with d3xp/models/david/hell_h7.ma in the d3xp hell level
699 // TODO: if it happens for other models, too, maybe it's intended and the .ma parsing is broken
700 common->Warning( "Model %s tried to set an out-of-bounds vertex transform (%d, but max vert. index is %d)!",
701 parser.GetFileName(), idx, pMesh->numVertexes-1 );
702 continue;
703 }
704 pMesh->vertexes[idx] += pMesh->vertTransforms[i].ToVec3();
705 }
706
707 MA_VERBOSE((va("MESH %s - parent %s\n", header.name, header.parent)));
708 MA_VERBOSE((va("\tverts:%d\n",maGlobal.currentObject->mesh.numVertexes)));
709 MA_VERBOSE((va("\tfaces:%d\n",maGlobal.currentObject->mesh.numFaces)));
710 }
711
MA_ParseFileNode(idParser & parser)712 void MA_ParseFileNode(idParser& parser) {
713
714 //Get the header info from the node
715 maNodeHeader_t header;
716 MA_ParseNodeHeader(parser, &header);
717
718 //Read the transform attributes
719 idToken token;
720 while(parser.ReadToken(&token)) {
721 if(IsNodeComplete(token)) {
722 parser.UnreadToken(&token);
723 break;
724 }
725 if(!token.Icmp("setAttr")) {
726 maAttribHeader_t attribHeader;
727 MA_ParseAttribHeader(parser, &attribHeader);
728
729 if(strstr(attribHeader.name, ".ftn")) {
730 parser.SkipUntilString("string");
731 parser.ReadToken(&token);
732 if(!token.Icmp("(")) {
733 parser.ReadToken(&token);
734 }
735
736 maFileNode_t* fileNode;
737 fileNode = (maFileNode_t*)Mem_Alloc( sizeof( maFileNode_t ) );
738 strcpy(fileNode->name, header.name);
739 strcpy(fileNode->path, token.c_str());
740
741 maGlobal.model->fileNodes.Set(fileNode->name, fileNode);
742 } else {
743 parser.SkipRestOfLine();
744 }
745 }
746 }
747 }
748
MA_ParseMaterialNode(idParser & parser)749 void MA_ParseMaterialNode(idParser& parser) {
750
751 //Get the header info from the node
752 maNodeHeader_t header;
753 MA_ParseNodeHeader(parser, &header);
754
755 maMaterialNode_t* matNode;
756 matNode = (maMaterialNode_t*)Mem_Alloc( sizeof( maMaterialNode_t ) );
757 memset(matNode, 0, sizeof(maMaterialNode_t));
758
759 strcpy(matNode->name, header.name);
760
761 maGlobal.model->materialNodes.Set(matNode->name, matNode);
762 }
763
MA_ParseCreateNode(idParser & parser)764 void MA_ParseCreateNode(idParser& parser) {
765
766 idToken token;
767 parser.ReadToken(&token);
768
769 if(!token.Icmp("transform")) {
770 MA_ParseTransform(parser);
771 } else if(!token.Icmp("mesh")) {
772 MA_ParseMesh(parser);
773 } else if(!token.Icmp("file")) {
774 MA_ParseFileNode(parser);
775 } else if(!token.Icmp("shadingEngine") || !token.Icmp("lambert") || !token.Icmp("phong") || !token.Icmp("blinn") ) {
776 MA_ParseMaterialNode(parser);
777 }
778 }
779
780
MA_AddMaterial(const char * materialName)781 int MA_AddMaterial(const char* materialName) {
782
783
784 maMaterialNode_t** destNode;
785 maGlobal.model->materialNodes.Get(materialName, &destNode);
786 if(destNode) {
787 maMaterialNode_t* matNode = *destNode;
788
789 //Iterate down the tree until we get a file
790 while(matNode && !matNode->file) {
791 matNode = matNode->child;
792 }
793 if(matNode && matNode->file) {
794
795 //Got the file
796 maMaterial_t *material;
797 material = (maMaterial_t *)Mem_Alloc( sizeof( maMaterial_t ) );
798 memset( material, 0, sizeof( maMaterial_t ) );
799
800 //Remove the OS stuff
801 idStr qPath;
802 qPath = fileSystem->OSPathToRelativePath( matNode->file->path );
803
804 strcpy(material->name, qPath.c_str());
805
806 maGlobal.model->materials.Append( material );
807 return maGlobal.model->materials.Num()-1;
808 }
809 }
810 return -1;
811 }
812
MA_ParseConnectAttr(idParser & parser)813 bool MA_ParseConnectAttr(idParser& parser) {
814
815 idStr temp;
816 idStr srcName;
817 idStr srcType;
818 idStr destName;
819 idStr destType;
820
821 idToken token;
822 parser.ReadToken(&token);
823 temp = token;
824 int dot = temp.Find(".");
825 if(dot == -1) {
826 throw idException(va("Maya Loader '%s': Invalid Connect Attribute.", parser.GetFileName()));
827 }
828 srcName = temp.Left(dot);
829 srcType = temp.Right(temp.Length()-dot-1);
830
831 parser.ReadToken(&token);
832 temp = token;
833 dot = temp.Find(".");
834 if(dot == -1) {
835 throw idException(va("Maya Loader '%s': Invalid Connect Attribute.", parser.GetFileName()));
836 }
837 destName = temp.Left(dot);
838 destType = temp.Right(temp.Length()-dot-1);
839
840 if(srcType.Find("oc") != -1) {
841
842 //Is this attribute a material node attribute
843 maMaterialNode_t** matNode;
844 maGlobal.model->materialNodes.Get(srcName, &matNode);
845 if(matNode) {
846 maMaterialNode_t** destNode;
847 maGlobal.model->materialNodes.Get(destName, &destNode);
848 if(destNode) {
849 (*destNode)->child = *matNode;
850 }
851 }
852
853 //Is this attribute a file node
854 maFileNode_t** fileNode;
855 maGlobal.model->fileNodes.Get(srcName, &fileNode);
856 if(fileNode) {
857 maMaterialNode_t** destNode;
858 maGlobal.model->materialNodes.Get(destName, &destNode);
859 if(destNode) {
860 (*destNode)->file = *fileNode;
861 }
862 }
863 }
864
865 if(srcType.Find("iog") != -1) {
866 //Is this an attribute for one of our meshes
867 for(int i = 0; i < maGlobal.model->objects.Num(); i++) {
868 if(!strcmp(maGlobal.model->objects[i]->name, srcName)) {
869 //maGlobal.model->objects[i]->materialRef = MA_AddMaterial(destName);
870 strcpy(maGlobal.model->objects[i]->materialName, destName);
871 break;
872 }
873 }
874 }
875
876 return true;
877 }
878
879
MA_BuildScale(idMat4 & mat,float x,float y,float z)880 void MA_BuildScale(idMat4& mat, float x, float y, float z) {
881 mat.Identity();
882 mat[0][0] = x;
883 mat[1][1] = y;
884 mat[2][2] = z;
885 }
886
MA_BuildAxisRotation(idMat4 & mat,float ang,int axis)887 void MA_BuildAxisRotation(idMat4& mat, float ang, int axis) {
888
889 float sinAng = idMath::Sin(ang);
890 float cosAng = idMath::Cos(ang);
891
892 mat.Identity();
893 switch(axis) {
894 case 0: //x
895 mat[1][1] = cosAng;
896 mat[1][2] = sinAng;
897 mat[2][1] = -sinAng;
898 mat[2][2] = cosAng;
899 break;
900 case 1: //y
901 mat[0][0] = cosAng;
902 mat[0][2] = -sinAng;
903 mat[2][0] = sinAng;
904 mat[2][2] = cosAng;
905 break;
906 case 2://z
907 mat[0][0] = cosAng;
908 mat[0][1] = sinAng;
909 mat[1][0] = -sinAng;
910 mat[1][1] = cosAng;
911 break;
912 }
913 }
914
MA_ApplyTransformation(maModel_t * model)915 void MA_ApplyTransformation(maModel_t *model) {
916
917 for(int i = 0; i < model->objects.Num(); i++) {
918 maMesh_t* mesh = &model->objects[i]->mesh;
919 maTransform_t* transform = mesh->transform;
920
921
922
923 while(transform) {
924
925 idMat4 rotx, roty, rotz;
926 idMat4 scale;
927
928 rotx.Identity();
929 roty.Identity();
930 rotz.Identity();
931
932 if(fabs(transform->rotate.x) > 0.0f) {
933 MA_BuildAxisRotation(rotx, DEG2RAD(-transform->rotate.x), 0);
934 }
935 if(fabs(transform->rotate.y) > 0.0f) {
936 MA_BuildAxisRotation(roty, DEG2RAD(transform->rotate.y), 1);
937 }
938 if(fabs(transform->rotate.z) > 0.0f) {
939 MA_BuildAxisRotation(rotz, DEG2RAD(-transform->rotate.z), 2);
940 }
941
942 MA_BuildScale(scale, transform->scale.x, transform->scale.y, transform->scale.z);
943
944 //Apply the transformation to each vert
945 for(int j = 0; j < mesh->numVertexes; j++) {
946 mesh->vertexes[j] = scale * mesh->vertexes[j];
947
948 mesh->vertexes[j] = rotx * mesh->vertexes[j];
949 mesh->vertexes[j] = rotz * mesh->vertexes[j];
950 mesh->vertexes[j] = roty * mesh->vertexes[j];
951
952 mesh->vertexes[j] = mesh->vertexes[j] + transform->translate;
953 }
954
955 transform = transform->parent;
956 }
957 }
958 }
959
960 /*
961 =================
962 MA_Parse
963 =================
964 */
MA_Parse(const char * buffer,const char * filename,bool verbose)965 maModel_t *MA_Parse( const char *buffer, const char* filename, bool verbose ) {
966 memset( &maGlobal, 0, sizeof( maGlobal ) );
967
968 maGlobal.verbose = verbose;
969
970
971
972
973 maGlobal.currentObject = NULL;
974
975 // NOTE: using new operator because aseModel_t contains idList class objects
976 maGlobal.model = new maModel_t;
977 maGlobal.model->objects.Resize( 32, 32 );
978 maGlobal.model->materials.Resize( 32, 32 );
979
980
981 idParser parser;
982 parser.SetFlags(LEXFL_NOSTRINGCONCAT);
983 parser.LoadMemory(buffer, strlen(buffer), filename);
984
985 idToken token;
986 while(parser.ReadToken(&token)) {
987
988 if(!token.Icmp("createNode")) {
989 MA_ParseCreateNode(parser);
990 } else if(!token.Icmp("connectAttr")) {
991 MA_ParseConnectAttr(parser);
992 }
993 }
994
995 //Resolve The Materials
996 for(int i = 0; i < maGlobal.model->objects.Num(); i++) {
997 maGlobal.model->objects[i]->materialRef = MA_AddMaterial(maGlobal.model->objects[i]->materialName);
998 }
999
1000
1001
1002 //Apply Transformation
1003 MA_ApplyTransformation(maGlobal.model);
1004
1005 return maGlobal.model;
1006 }
1007
1008 /*
1009 =================
1010 MA_Load
1011 =================
1012 */
MA_Load(const char * fileName)1013 maModel_t *MA_Load( const char *fileName ) {
1014 char *buf;
1015 ID_TIME_T timeStamp;
1016 maModel_t *ma;
1017
1018 fileSystem->ReadFile( fileName, (void **)&buf, &timeStamp );
1019 if ( !buf ) {
1020 return NULL;
1021 }
1022
1023 try {
1024 ma = MA_Parse( buf, fileName, false );
1025 ma->timeStamp = timeStamp;
1026 } catch( idException &e ) {
1027 common->Warning("%s", e.error);
1028 if(maGlobal.model) {
1029 MA_Free(maGlobal.model);
1030 }
1031 ma = NULL;
1032 }
1033
1034 fileSystem->FreeFile( buf );
1035
1036 return ma;
1037 }
1038
1039 /*
1040 =================
1041 MA_Free
1042 =================
1043 */
MA_Free(maModel_t * ma)1044 void MA_Free( maModel_t *ma ) {
1045 int i;
1046 maObject_t *obj;
1047 maMesh_t *mesh;
1048 maMaterial_t *material;
1049
1050 if ( !ma ) {
1051 return;
1052 }
1053 for ( i = 0; i < ma->objects.Num(); i++ ) {
1054 obj = ma->objects[i];
1055
1056 // free the base nesh
1057 mesh = &obj->mesh;
1058
1059 if ( mesh->vertexes ) {
1060 Mem_Free( mesh->vertexes );
1061 }
1062 if ( mesh->vertTransforms ) {
1063 Mem_Free( mesh->vertTransforms );
1064 }
1065 if ( mesh->normals ) {
1066 Mem_Free( mesh->normals );
1067 }
1068 if ( mesh->tvertexes ) {
1069 Mem_Free( mesh->tvertexes );
1070 }
1071 if ( mesh->edges ) {
1072 Mem_Free( mesh->edges );
1073 }
1074 if ( mesh->colors ) {
1075 Mem_Free( mesh->colors );
1076 }
1077 if ( mesh->faces ) {
1078 Mem_Free( mesh->faces );
1079 }
1080 Mem_Free( obj );
1081 }
1082 ma->objects.Clear();
1083
1084 for ( i = 0; i < ma->materials.Num(); i++ ) {
1085 material = ma->materials[i];
1086 Mem_Free( material );
1087 }
1088 ma->materials.Clear();
1089
1090 maTransform_t** trans;
1091 for ( i = 0; i < ma->transforms.Num(); i++ ) {
1092 trans = ma->transforms.GetIndex(i);
1093 Mem_Free( *trans );
1094 }
1095 ma->transforms.Clear();
1096
1097
1098 maFileNode_t** fileNode;
1099 for ( i = 0; i < ma->fileNodes.Num(); i++ ) {
1100 fileNode = ma->fileNodes.GetIndex(i);
1101 Mem_Free( *fileNode );
1102 }
1103 ma->fileNodes.Clear();
1104
1105 maMaterialNode_t** matNode;
1106 for ( i = 0; i < ma->materialNodes.Num(); i++ ) {
1107 matNode = ma->materialNodes.GetIndex(i);
1108 Mem_Free( *matNode );
1109 }
1110 ma->materialNodes.Clear();
1111 delete ma;
1112 }
1113