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