DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Model_ma.cpp (28584B)


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