DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Model_ase.cpp (22799B)


      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 
     33 #include "Model_ase.h"
     34 
     35 /*
     36 ======================================================================
     37 
     38 	Parses 3D Studio Max ASCII export files.
     39 	The goal is to parse the information into memory exactly as it is
     40 	represented in the file.  Users of the data will then move it
     41 	into a form that is more convenient for them.
     42 
     43 ======================================================================
     44 */
     45 	
     46 
     47 #define VERBOSE( x ) { if ( ase.verbose ) { common->Printf x ; } }
     48 
     49 // working variables used during parsing
     50 typedef struct {
     51 	const char	*buffer;
     52 	const char	*curpos;
     53 	int			len;
     54 	char		token[1024];
     55 
     56 	bool	verbose;
     57 
     58 	aseModel_t	*model;
     59 	aseObject_t	*currentObject;
     60 	aseMesh_t	*currentMesh;
     61 	aseMaterial_t	*currentMaterial;
     62 	int			currentFace;
     63 	int			currentVertex;
     64 } ase_t;
     65 
     66 static ase_t ase;
     67 
     68 
     69 static aseMesh_t *ASE_GetCurrentMesh()
     70 {
     71 	return ase.currentMesh;
     72 }
     73 
     74 static int CharIsTokenDelimiter( int ch )
     75 {
     76 	if ( ch <= 32 )
     77 		return 1;
     78 	return 0;
     79 }
     80 
     81 static int ASE_GetToken( bool restOfLine )
     82 {
     83 	int i = 0;
     84 
     85 	if ( ase.buffer == 0 )
     86 		return 0;
     87 
     88 	if ( ( ase.curpos - ase.buffer ) == ase.len )
     89 		return 0;
     90 
     91 	// skip over crap
     92 	while ( ( ( ase.curpos - ase.buffer ) < ase.len ) &&
     93 		    ( *ase.curpos <= 32 ) )
     94 	{
     95 		ase.curpos++;
     96 	}
     97 
     98 	while ( ( ase.curpos - ase.buffer ) < ase.len )
     99 	{
    100 		ase.token[i] = *ase.curpos;
    101 
    102 		ase.curpos++;
    103 		i++;
    104 
    105 		if ( ( CharIsTokenDelimiter( ase.token[i-1] ) && !restOfLine ) ||
    106 			 ( ( ase.token[i-1] == '\n' ) || ( ase.token[i-1] == '\r' ) ) )
    107 		{
    108 			ase.token[i-1] = 0;
    109 			break;
    110 		}
    111 	}
    112 
    113 	ase.token[i] = 0;
    114 
    115 	return 1;
    116 }
    117 
    118 static void ASE_ParseBracedBlock( void (*parser)( const char *token ) )
    119 {
    120 	int indent = 0;
    121 
    122 	while ( ASE_GetToken( false ) )
    123 	{
    124 		if ( !strcmp( ase.token, "{" ) )
    125 		{
    126 			indent++;
    127 		}
    128 		else if ( !strcmp( ase.token, "}" ) )
    129 		{
    130 			--indent;
    131 			if ( indent == 0 )
    132 				break;
    133 			else if ( indent < 0 )
    134 				common->Error( "Unexpected '}'" );
    135 		}
    136 		else
    137 		{
    138 			if ( parser )
    139 				parser( ase.token );
    140 		}
    141 	}
    142 }
    143 
    144 static void ASE_SkipEnclosingBraces()
    145 {
    146 	int indent = 0;
    147 
    148 	while ( ASE_GetToken( false ) )
    149 	{
    150 		if ( !strcmp( ase.token, "{" ) )
    151 		{
    152 			indent++;
    153 		}
    154 		else if ( !strcmp( ase.token, "}" ) )
    155 		{
    156 			indent--;
    157 			if ( indent == 0 )
    158 				break;
    159 			else if ( indent < 0 )
    160 				common->Error( "Unexpected '}'" );
    161 		}
    162 	}
    163 }
    164 
    165 static void ASE_SkipRestOfLine()
    166 {
    167 	ASE_GetToken( true );
    168 }
    169 
    170 static void ASE_KeyMAP_DIFFUSE( const char *token )
    171 {
    172 	aseMaterial_t	*material;
    173 
    174 	if ( !strcmp( token, "*BITMAP" ) )
    175 	{
    176 		idStr	qpath;
    177 		idStr	matname;
    178 
    179 		ASE_GetToken( false );
    180 
    181 		// remove the quotes
    182 		char *s = strstr( ase.token + 1, "\"" );
    183 		if ( s ) {
    184 			*s = 0;
    185 		}
    186 		matname = ase.token + 1;
    187 
    188 		// convert the 3DSMax material pathname to a qpath
    189 		matname.BackSlashesToSlashes();
    190 		qpath = fileSystem->OSPathToRelativePath( matname );
    191 		idStr::Copynz( ase.currentMaterial->name, qpath, sizeof( ase.currentMaterial->name ) );
    192 	}
    193 	else if ( !strcmp( token, "*UVW_U_OFFSET" ) )
    194 	{
    195 		material = ase.model->materials[ase.model->materials.Num() - 1];
    196 		ASE_GetToken( false );
    197 		material->uOffset = atof( ase.token );
    198 	}
    199 	else if ( !strcmp( token, "*UVW_V_OFFSET" ) )
    200 	{
    201 		material = ase.model->materials[ase.model->materials.Num() - 1];
    202 		ASE_GetToken( false );
    203 		material->vOffset = atof( ase.token );
    204 	}
    205 	else if ( !strcmp( token, "*UVW_U_TILING" ) )
    206 	{
    207 		material = ase.model->materials[ase.model->materials.Num() - 1];
    208 		ASE_GetToken( false );
    209 		material->uTiling = atof( ase.token );
    210 	}
    211 	else if ( !strcmp( token, "*UVW_V_TILING" ) )
    212 	{
    213 		material = ase.model->materials[ase.model->materials.Num() - 1];
    214 		ASE_GetToken( false );
    215 		material->vTiling = atof( ase.token );
    216 	}
    217 	else if ( !strcmp( token, "*UVW_ANGLE" ) )
    218 	{
    219 		material = ase.model->materials[ase.model->materials.Num() - 1];
    220 		ASE_GetToken( false );
    221 		material->angle = atof( ase.token );
    222 	}
    223 	else
    224 	{
    225 	}
    226 }
    227 
    228 static void ASE_KeyMATERIAL( const char *token )
    229 {
    230 	if ( !strcmp( token, "*MAP_DIFFUSE" ) )
    231 	{
    232 		ASE_ParseBracedBlock( ASE_KeyMAP_DIFFUSE );
    233 	}
    234 	else
    235 	{
    236 	}
    237 }
    238 
    239 static void ASE_KeyMATERIAL_LIST( const char *token )
    240 {
    241 	if ( !strcmp( token, "*MATERIAL_COUNT" ) )
    242 	{
    243 		ASE_GetToken( false );
    244 		VERBOSE( ( "..num materials: %s\n", ase.token ) );
    245 	}
    246 	else if ( !strcmp( token, "*MATERIAL" ) )
    247 	{
    248 		VERBOSE( ( "..material %d\n", ase.model->materials.Num() ) );
    249 
    250 		ase.currentMaterial = (aseMaterial_t *)Mem_Alloc( sizeof( aseMaterial_t ), TAG_MODEL );
    251 		memset( ase.currentMaterial, 0, sizeof( aseMaterial_t ) );
    252 		ase.currentMaterial->uTiling = 1;
    253 		ase.currentMaterial->vTiling = 1;
    254 		ase.model->materials.Append(ase.currentMaterial);
    255 
    256 		ASE_ParseBracedBlock( ASE_KeyMATERIAL );
    257 	}
    258 }
    259 
    260 static void ASE_KeyNODE_TM( const char *token )
    261 {
    262 	int		i;
    263 
    264 	if ( !strcmp( token, "*TM_ROW0" ) ) {
    265 		for ( i = 0 ; i < 3 ; i++ ) {
    266 			ASE_GetToken( false );
    267 			ase.currentObject->mesh.transform[0][i] = atof( ase.token );
    268 		}
    269 	} else if ( !strcmp( token, "*TM_ROW1" ) ) {
    270 		for ( i = 0 ; i < 3 ; i++ ) {
    271 			ASE_GetToken( false );
    272 			ase.currentObject->mesh.transform[1][i] = atof( ase.token );
    273 		}
    274 	} else if ( !strcmp( token, "*TM_ROW2" ) ) {
    275 		for ( i = 0 ; i < 3 ; i++ ) {
    276 			ASE_GetToken( false );
    277 			ase.currentObject->mesh.transform[2][i] = atof( ase.token );
    278 		}
    279 	} else if ( !strcmp( token, "*TM_ROW3" ) ) {
    280 		for ( i = 0 ; i < 3 ; i++ ) {
    281 			ASE_GetToken( false );
    282 			ase.currentObject->mesh.transform[3][i] = atof( ase.token );
    283 		}
    284 	}
    285 }
    286 
    287 static void ASE_KeyMESH_VERTEX_LIST( const char *token )
    288 {
    289 	aseMesh_t *pMesh = ASE_GetCurrentMesh();
    290 
    291 	if ( !strcmp( token, "*MESH_VERTEX" ) )
    292 	{
    293 		ASE_GetToken( false );		// skip number
    294 
    295 		ASE_GetToken( false );
    296 		pMesh->vertexes[ase.currentVertex].x = atof( ase.token );
    297 
    298 		ASE_GetToken( false );
    299 		pMesh->vertexes[ase.currentVertex].y = atof( ase.token );
    300 
    301 		ASE_GetToken( false );
    302 		pMesh->vertexes[ase.currentVertex].z = atof( ase.token );
    303 
    304 		ase.currentVertex++;
    305 
    306 		if ( ase.currentVertex > pMesh->numVertexes )
    307 		{
    308 			common->Error( "ase.currentVertex >= pMesh->numVertexes" );
    309 		}
    310 	}
    311 	else
    312 	{
    313 		common->Error( "Unknown token '%s' while parsing MESH_VERTEX_LIST", token );
    314 	}
    315 }
    316 
    317 static void ASE_KeyMESH_FACE_LIST( const char *token )
    318 {
    319 	aseMesh_t *pMesh = ASE_GetCurrentMesh();
    320 
    321 	if ( !strcmp( token, "*MESH_FACE" ) )
    322 	{
    323 		ASE_GetToken( false );	// skip face number
    324 
    325 		// we are flipping the order here to change the front/back facing
    326 		// from 3DS to our standard (clockwise facing out)
    327 		ASE_GetToken( false );	// skip label
    328 		ASE_GetToken( false );	// first vertex
    329 		pMesh->faces[ase.currentFace].vertexNum[0] = atoi( ase.token );
    330                 
    331 		ASE_GetToken( false );	// skip label
    332 		ASE_GetToken( false );	// second vertex
    333 		pMesh->faces[ase.currentFace].vertexNum[2] = atoi( ase.token );
    334 
    335 		ASE_GetToken( false );	// skip label
    336 		ASE_GetToken( false );	// third vertex
    337 		pMesh->faces[ase.currentFace].vertexNum[1] = atoi( ase.token );
    338 
    339 		ASE_GetToken( true );
    340 
    341 		// we could parse material id and smoothing groups here
    342 /*
    343 		if ( ( p = strstr( ase.token, "*MESH_MTLID" ) ) != 0 )
    344 		{
    345 			p += strlen( "*MESH_MTLID" ) + 1;
    346 			mtlID = atoi( p );
    347 		}
    348 		else
    349 		{
    350 			common->Error( "No *MESH_MTLID found for face!" );
    351 		}
    352 */
    353 
    354 		ase.currentFace++;
    355 	}
    356 	else
    357 	{
    358 		common->Error( "Unknown token '%s' while parsing MESH_FACE_LIST", token );
    359 	}
    360 }
    361 
    362 static void ASE_KeyTFACE_LIST( const char *token )
    363 {
    364 	aseMesh_t *pMesh = ASE_GetCurrentMesh();
    365 
    366 	if ( !strcmp( token, "*MESH_TFACE" ) )
    367 	{
    368 		int a, b, c;
    369 
    370 		ASE_GetToken( false );
    371 
    372 		ASE_GetToken( false );
    373 		a = atoi( ase.token );
    374 		ASE_GetToken( false );
    375 		c = atoi( ase.token );
    376 		ASE_GetToken( false );
    377 		b = atoi( ase.token );
    378 
    379 		pMesh->faces[ase.currentFace].tVertexNum[0] = a;
    380 		pMesh->faces[ase.currentFace].tVertexNum[1] = b;
    381 		pMesh->faces[ase.currentFace].tVertexNum[2] = c;
    382 
    383 		ase.currentFace++;
    384 	}
    385 	else
    386 	{
    387 		common->Error( "Unknown token '%s' in MESH_TFACE", token );
    388 	}
    389 }
    390 
    391 static void ASE_KeyCFACE_LIST( const char *token )
    392 {
    393 	aseMesh_t *pMesh = ASE_GetCurrentMesh();
    394 
    395 	if ( !strcmp( token, "*MESH_CFACE" ) )
    396 	{
    397 		ASE_GetToken( false );
    398 
    399 		for ( int i = 0 ; i < 3 ; i++ ) {
    400 			ASE_GetToken( false );
    401 			int a = atoi( ase.token );
    402 
    403 			// we flip the vertex order to change the face direction to our style
    404 			static int remap[3] = { 0, 2, 1 };
    405 			pMesh->faces[ase.currentFace].vertexColors[remap[i]][0] = pMesh->cvertexes[a][0] * 255;
    406 			pMesh->faces[ase.currentFace].vertexColors[remap[i]][1] = pMesh->cvertexes[a][1] * 255;
    407 			pMesh->faces[ase.currentFace].vertexColors[remap[i]][2] = pMesh->cvertexes[a][2] * 255;
    408 		}
    409 
    410 		ase.currentFace++;
    411 	}
    412 	else
    413 	{
    414 		common->Error( "Unknown token '%s' in MESH_CFACE", token );
    415 	}
    416 }
    417 
    418 static void ASE_KeyMESH_TVERTLIST( const char *token )
    419 {
    420 	aseMesh_t *pMesh = ASE_GetCurrentMesh();
    421 
    422 	if ( !strcmp( token, "*MESH_TVERT" ) )
    423 	{
    424 		const int maxLength = 80;
    425 		char u[maxLength], v[maxLength], w[maxLength];
    426 
    427 		ASE_GetToken( false );
    428 
    429 		ASE_GetToken( false );
    430 		strncpy( u, ase.token, maxLength ); u[maxLength-1] = '\0';
    431 
    432 		ASE_GetToken( false );
    433 		strncpy( v, ase.token, maxLength ); v[maxLength-1] = '\0';
    434 
    435 		ASE_GetToken( false );
    436 		strncpy( w, ase.token, maxLength ); w[maxLength-1] = '\0';
    437 
    438 		pMesh->tvertexes[ase.currentVertex].x = atof( u );
    439 		// our OpenGL second texture axis is inverted from MAX's sense
    440 		pMesh->tvertexes[ase.currentVertex].y = 1.0f - atof( v );
    441 
    442 		ase.currentVertex++;
    443 
    444 		if ( ase.currentVertex > pMesh->numTVertexes )
    445 		{
    446 			common->Error( "ase.currentVertex > pMesh->numTVertexes" );
    447 		}
    448 	}
    449 	else
    450 	{
    451 		common->Error( "Unknown token '%s' while parsing MESH_TVERTLIST", token );
    452 	}
    453 }
    454 
    455 static void ASE_KeyMESH_CVERTLIST( const char *token )
    456 {
    457 	aseMesh_t *pMesh = ASE_GetCurrentMesh();
    458 
    459 	pMesh->colorsParsed = true;
    460 
    461 	if ( !strcmp( token, "*MESH_VERTCOL" ) )
    462 	{
    463 		ASE_GetToken( false );
    464 
    465 		ASE_GetToken( false );
    466 		pMesh->cvertexes[ase.currentVertex][0] = atof( token );
    467 
    468 		ASE_GetToken( false );
    469 		pMesh->cvertexes[ase.currentVertex][1] = atof( token );
    470 
    471 		ASE_GetToken( false );
    472 		pMesh->cvertexes[ase.currentVertex][2] = atof( token );
    473 
    474 		ase.currentVertex++;
    475 
    476 		if ( ase.currentVertex > pMesh->numCVertexes )
    477 		{
    478 			common->Error( "ase.currentVertex > pMesh->numCVertexes" );
    479 		}
    480 	}
    481 	else {
    482 		common->Error( "Unknown token '%s' while parsing MESH_CVERTLIST", token );
    483 	}
    484 }
    485 
    486 static void ASE_KeyMESH_NORMALS( const char *token )
    487 {
    488 	aseMesh_t *pMesh = ASE_GetCurrentMesh();
    489 	aseFace_t	*f;
    490 	idVec3		n;
    491 
    492 	pMesh->normalsParsed = true;
    493 	f = &pMesh->faces[ase.currentFace];
    494 
    495 	if ( !strcmp( token, "*MESH_FACENORMAL" ) )
    496 	{
    497 		int	num;
    498 
    499 		ASE_GetToken( false );
    500 		num = atoi( ase.token );
    501 
    502 		if ( num >= pMesh->numFaces || num < 0 ) {
    503 			common->Error( "MESH_NORMALS face index out of range: %i", num );
    504 		}
    505 
    506 		if ( num != ase.currentFace ) {
    507 			common->Error( "MESH_NORMALS face index != currentFace" );
    508 		}
    509 
    510 		ASE_GetToken( false );
    511 		n[0] = atof( ase.token );
    512 		ASE_GetToken( false );
    513 		n[1] = atof( ase.token );
    514 		ASE_GetToken( false );
    515 		n[2]= atof( ase.token );
    516 
    517 		f->faceNormal[0] = n[0] * pMesh->transform[0][0] + n[1] * pMesh->transform[1][0] + n[2] * pMesh->transform[2][0];
    518 		f->faceNormal[1] = n[0] * pMesh->transform[0][1] + n[1] * pMesh->transform[1][1] + n[2] * pMesh->transform[2][1];
    519 		f->faceNormal[2] = n[0] * pMesh->transform[0][2] + n[1] * pMesh->transform[1][2] + n[2] * pMesh->transform[2][2];
    520 
    521 		f->faceNormal.Normalize();
    522 
    523 		ase.currentFace++;
    524 	}
    525 	else if ( !strcmp( token, "*MESH_VERTEXNORMAL" ) )
    526 	{
    527 		int	num;
    528 		int	v;
    529 
    530 		ASE_GetToken( false );
    531 		num = atoi( ase.token );
    532 
    533 		if ( num >= pMesh->numVertexes || num < 0 ) {
    534 			common->Error( "MESH_NORMALS vertex index out of range: %i", num );
    535 		}
    536 
    537 		f = &pMesh->faces[ ase.currentFace - 1 ];
    538 
    539 		for ( v = 0 ; v < 3 ; v++ ) {
    540 			if ( num == f->vertexNum[ v ] ) {
    541 				break;
    542 			}
    543 		}
    544 
    545 		if ( v >= 3 ) {
    546 			common->Error( "MESH_NORMALS vertex index doesn't match face" );
    547 			return;
    548 		}
    549 
    550 		ASE_GetToken( false );
    551 		n[0] = atof( ase.token );
    552 		ASE_GetToken( false );
    553 		n[1] = atof( ase.token );
    554 		ASE_GetToken( false );
    555 		n[2]= atof( ase.token );
    556 
    557 		f->vertexNormals[ v ][0] = n[0] * pMesh->transform[0][0] + n[1] * pMesh->transform[1][0] + n[2] * pMesh->transform[2][0];
    558 		f->vertexNormals[ v ][1] = n[0] * pMesh->transform[0][1] + n[1] * pMesh->transform[1][1] + n[2] * pMesh->transform[2][1];
    559 		f->vertexNormals[ v ][2] = n[0] * pMesh->transform[0][2] + n[1] * pMesh->transform[1][2] + n[2] * pMesh->transform[2][2];
    560 
    561 		f->vertexNormals[v].Normalize();
    562 	}
    563 }
    564 
    565 static void ASE_KeyMESH( const char *token )
    566 {
    567 	aseMesh_t *pMesh = ASE_GetCurrentMesh();
    568 
    569 	if ( !strcmp( token, "*TIMEVALUE" ) )
    570 	{
    571 		ASE_GetToken( false );
    572 
    573 		pMesh->timeValue = atoi( ase.token );
    574 		VERBOSE( ( ".....timevalue: %d\n", pMesh->timeValue ) );
    575 	}
    576 	else if ( !strcmp( token, "*MESH_NUMVERTEX" ) )
    577 	{
    578 		ASE_GetToken( false );
    579 
    580 		pMesh->numVertexes = atoi( ase.token );
    581 		VERBOSE( ( ".....num vertexes: %d\n", pMesh->numVertexes ) );
    582 	}
    583 	else if ( !strcmp( token, "*MESH_NUMTVERTEX" ) )
    584 	{
    585 		ASE_GetToken( false );
    586 
    587 		pMesh->numTVertexes = atoi( ase.token );
    588 		VERBOSE( ( ".....num tvertexes: %d\n", pMesh->numTVertexes ) );
    589 	}
    590 	else if ( !strcmp( token, "*MESH_NUMCVERTEX" ) )
    591 	{
    592 		ASE_GetToken( false );
    593 
    594 		pMesh->numCVertexes = atoi( ase.token );
    595 		VERBOSE( ( ".....num cvertexes: %d\n", pMesh->numCVertexes ) );
    596 	}
    597 	else if ( !strcmp( token, "*MESH_NUMFACES" ) )
    598 	{
    599 		ASE_GetToken( false );
    600 
    601 		pMesh->numFaces = atoi( ase.token );
    602 		VERBOSE( ( ".....num faces: %d\n", pMesh->numFaces ) );
    603 	}
    604 	else if ( !strcmp( token, "*MESH_NUMTVFACES" ) )
    605 	{
    606 		ASE_GetToken( false );
    607 
    608 		pMesh->numTVFaces = atoi( ase.token );
    609 		VERBOSE( ( ".....num tvfaces: %d\n", pMesh->numTVFaces ) );
    610 
    611 		if ( pMesh->numTVFaces != pMesh->numFaces )
    612 		{
    613 			common->Error( "MESH_NUMTVFACES != MESH_NUMFACES" );
    614 		}
    615 	}
    616 	else if ( !strcmp( token, "*MESH_NUMCVFACES" ) )
    617 	{
    618 		ASE_GetToken( false );
    619 
    620 		pMesh->numCVFaces = atoi( ase.token );
    621 		VERBOSE( ( ".....num cvfaces: %d\n", pMesh->numCVFaces ) );
    622 
    623 		if ( pMesh->numTVFaces != pMesh->numFaces )
    624 		{
    625 			common->Error( "MESH_NUMCVFACES != MESH_NUMFACES" );
    626 		}
    627 	}
    628 	else if ( !strcmp( token, "*MESH_VERTEX_LIST" ) )
    629 	{
    630 		pMesh->vertexes = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numVertexes, TAG_MODEL );
    631 		ase.currentVertex = 0;
    632 		VERBOSE( ( ".....parsing MESH_VERTEX_LIST\n" ) );
    633 		ASE_ParseBracedBlock( ASE_KeyMESH_VERTEX_LIST );
    634 	}
    635 	else if ( !strcmp( token, "*MESH_TVERTLIST" ) )
    636 	{
    637 		ase.currentVertex = 0;
    638 		pMesh->tvertexes = (idVec2 *)Mem_Alloc( sizeof( idVec2 ) * pMesh->numTVertexes, TAG_MODEL );
    639 		VERBOSE( ( ".....parsing MESH_TVERTLIST\n" ) );
    640 		ASE_ParseBracedBlock( ASE_KeyMESH_TVERTLIST );
    641 	}
    642 	else if ( !strcmp( token, "*MESH_CVERTLIST" ) )
    643 	{
    644 		ase.currentVertex = 0;
    645 		pMesh->cvertexes = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numCVertexes, TAG_MODEL );
    646 		VERBOSE( ( ".....parsing MESH_CVERTLIST\n" ) );
    647 		ASE_ParseBracedBlock( ASE_KeyMESH_CVERTLIST );
    648 	}
    649 	else if ( !strcmp( token, "*MESH_FACE_LIST" ) )
    650 	{
    651 		pMesh->faces = (aseFace_t *)Mem_Alloc( sizeof( aseFace_t ) * pMesh->numFaces, TAG_MODEL );
    652 		ase.currentFace = 0;
    653 		VERBOSE( ( ".....parsing MESH_FACE_LIST\n" ) );
    654 		ASE_ParseBracedBlock( ASE_KeyMESH_FACE_LIST );
    655 	}
    656 	else if ( !strcmp( token, "*MESH_TFACELIST" ) )
    657 	{
    658 		if ( !pMesh->faces ) {
    659 			common->Error( "*MESH_TFACELIST before *MESH_FACE_LIST" );
    660 		}
    661 		ase.currentFace = 0;
    662 		VERBOSE( ( ".....parsing MESH_TFACE_LIST\n" ) );
    663 		ASE_ParseBracedBlock( ASE_KeyTFACE_LIST );
    664 	}
    665 	else if ( !strcmp( token, "*MESH_CFACELIST" ) )
    666 	{
    667 		if ( !pMesh->faces ) {
    668 			common->Error( "*MESH_CFACELIST before *MESH_FACE_LIST" );
    669 		}
    670 		ase.currentFace = 0;
    671 		VERBOSE( ( ".....parsing MESH_CFACE_LIST\n" ) );
    672 		ASE_ParseBracedBlock( ASE_KeyCFACE_LIST );
    673 	}
    674 	else if ( !strcmp( token, "*MESH_NORMALS" ) )
    675 	{
    676 		if ( !pMesh->faces ) {
    677 			common->Warning( "*MESH_NORMALS before *MESH_FACE_LIST" );
    678 		}
    679 		ase.currentFace = 0;
    680 		VERBOSE( ( ".....parsing MESH_NORMALS\n" ) );
    681 		ASE_ParseBracedBlock( ASE_KeyMESH_NORMALS );
    682 	}
    683 }
    684 
    685 static void ASE_KeyMESH_ANIMATION( const char *token )
    686 {
    687 	aseMesh_t *mesh;
    688 
    689 	// loads a single animation frame
    690 	if ( !strcmp( token, "*MESH" ) )
    691 	{
    692 		VERBOSE( ( "...found MESH\n" ) );
    693 
    694 		mesh = (aseMesh_t *)Mem_Alloc( sizeof( aseMesh_t ), TAG_MODEL );
    695 		memset( mesh, 0, sizeof( aseMesh_t ) );
    696 		ase.currentMesh = mesh;
    697 
    698 		ase.currentObject->frames.Append( mesh );
    699 
    700 		ASE_ParseBracedBlock( ASE_KeyMESH );
    701 	}
    702 	else
    703 	{
    704 		common->Error( "Unknown token '%s' while parsing MESH_ANIMATION", token );
    705 	}
    706 }
    707 
    708 static void ASE_KeyGEOMOBJECT( const char *token )
    709 {
    710 	aseObject_t	*object;
    711 
    712 	object = ase.currentObject;
    713 
    714 	if ( !strcmp( token, "*NODE_NAME" ) )
    715 	{
    716 		ASE_GetToken( true );
    717 		VERBOSE( ( " %s\n", ase.token ) );
    718 		idStr::Copynz( object->name, ase.token, sizeof( object->name ) );
    719 	}
    720 	else if ( !strcmp( token, "*NODE_PARENT" ) )
    721 	{
    722 		ASE_SkipRestOfLine();
    723 	}
    724 	// ignore unused data blocks
    725 	else if ( !strcmp( token, "*NODE_TM" ) ||
    726 		      !strcmp( token, "*TM_ANIMATION" ) )
    727 	{
    728 		ASE_ParseBracedBlock( ASE_KeyNODE_TM );
    729 	}
    730 	// ignore regular meshes that aren't part of animation
    731 	else if ( !strcmp( token, "*MESH" ) )
    732 	{
    733 		ase.currentMesh = &ase.currentObject->mesh;
    734 		idVec3	transforms[ 4 ];
    735 		for ( int i = 0; i < 4; ++i ) {
    736 			transforms[ i ] = ase.currentMesh->transform[ i ];
    737 		}
    738 
    739 		memset( ase.currentMesh, 0, sizeof( *ase.currentMesh ) );
    740 		for ( int i = 0; i < 4; ++i ) {
    741 			ase.currentMesh->transform[ i ] = transforms[ i ];
    742 		}
    743 
    744 		ASE_ParseBracedBlock( ASE_KeyMESH );
    745 	}
    746 	// according to spec these are obsolete
    747 	else if ( !strcmp( token, "*MATERIAL_REF" ) )
    748 	{
    749 		ASE_GetToken( false );
    750 
    751 		object->materialRef = atoi( ase.token );
    752 	}
    753 	// loads a sequence of animation frames
    754 	else if ( !strcmp( token, "*MESH_ANIMATION" ) )
    755 	{
    756 		VERBOSE( ( "..found MESH_ANIMATION\n" ) );
    757 
    758 		ASE_ParseBracedBlock( ASE_KeyMESH_ANIMATION );
    759 	}
    760 	// skip unused info
    761 	else if ( !strcmp( token, "*PROP_MOTIONBLUR" ) ||
    762 		      !strcmp( token, "*PROP_CASTSHADOW" ) ||
    763 			  !strcmp( token, "*PROP_RECVSHADOW" ) )
    764 	{
    765 		ASE_SkipRestOfLine();
    766 	}
    767 
    768 }
    769 
    770 void ASE_ParseGeomObject() {
    771 	aseObject_t	*object;
    772 
    773 	VERBOSE( ("GEOMOBJECT" ) );
    774 
    775 	object = (aseObject_t *)Mem_Alloc( sizeof( aseObject_t ), TAG_MODEL );
    776 	memset( object, 0, sizeof( aseObject_t ) );
    777 	ase.model->objects.Append( object );
    778 	ase.currentObject = object;
    779 
    780 	object->frames.Resize(32, 32);
    781 
    782 	ASE_ParseBracedBlock( ASE_KeyGEOMOBJECT );
    783 }
    784 
    785 static void ASE_KeyGROUP( const char *token )
    786 {
    787 	if ( !strcmp( token, "*GEOMOBJECT" ) ) {
    788 		ASE_ParseGeomObject();
    789 	}
    790 }
    791 
    792 /*
    793 =================
    794 ASE_Parse
    795 =================
    796 */
    797 aseModel_t *ASE_Parse( const char *buffer, bool verbose ) {
    798 	memset( &ase, 0, sizeof( ase ) );
    799 
    800 	ase.verbose = verbose;
    801 
    802 	ase.buffer = buffer;
    803 	ase.len = strlen( buffer );
    804 	ase.curpos = ase.buffer;
    805 	ase.currentObject = NULL;
    806 
    807 	// NOTE: using new operator because aseModel_t contains idList class objects
    808 	ase.model = new (TAG_MODEL) aseModel_t;
    809 	memset( ase.model, 0, sizeof( aseModel_t ) );
    810 	ase.model->objects.Resize( 32, 32 );
    811 	ase.model->materials.Resize( 32, 32 );
    812 
    813 	while ( ASE_GetToken( false ) ) {
    814 		if ( !strcmp( ase.token, "*3DSMAX_ASCIIEXPORT" ) ||
    815 			 !strcmp( ase.token, "*COMMENT" ) ) {
    816 			ASE_SkipRestOfLine();
    817 		} else if ( !strcmp( ase.token, "*SCENE" ) ) {
    818 			ASE_SkipEnclosingBraces();
    819 		} else if ( !strcmp( ase.token, "*GROUP" ) ) {
    820 			ASE_GetToken( false );		// group name
    821 			ASE_ParseBracedBlock( ASE_KeyGROUP );
    822 		} else if ( !strcmp( ase.token, "*SHAPEOBJECT" ) ) {
    823 			ASE_SkipEnclosingBraces();
    824 		} else if ( !strcmp( ase.token, "*CAMERAOBJECT" ) ) {
    825 			ASE_SkipEnclosingBraces();
    826 		} else if ( !strcmp( ase.token, "*MATERIAL_LIST" ) ) {
    827 			VERBOSE( ("MATERIAL_LIST\n") );
    828 
    829 			ASE_ParseBracedBlock( ASE_KeyMATERIAL_LIST );
    830 		} else if ( !strcmp( ase.token, "*GEOMOBJECT" ) ) {
    831 			ASE_ParseGeomObject();
    832 		} else if ( ase.token[0] ) {
    833 			common->Printf( "Unknown token '%s'\n", ase.token );
    834 		}
    835 	}
    836 
    837 	return ase.model;
    838 }
    839 
    840 /*
    841 =================
    842 ASE_Load
    843 =================
    844 */
    845 aseModel_t *ASE_Load( const char *fileName ) {
    846 	char *buf;
    847 	ID_TIME_T timeStamp;
    848 	aseModel_t *ase;
    849 
    850 	fileSystem->ReadFile( fileName, (void **)&buf, &timeStamp );
    851 	if ( !buf ) {
    852 		return NULL;
    853 	}
    854 
    855 	ase = ASE_Parse( buf, false );
    856 	ase->timeStamp = timeStamp;
    857 
    858 	fileSystem->FreeFile( buf );
    859 
    860 	return ase;
    861 }
    862 
    863 /*
    864 =================
    865 ASE_Free
    866 =================
    867 */
    868 void ASE_Free( aseModel_t *ase ) {
    869 	int					i, j;
    870 	aseObject_t			*obj;
    871 	aseMesh_t			*mesh;
    872 	aseMaterial_t		*material;
    873 
    874 	if ( !ase ) {
    875 		return;
    876 	}
    877 	for ( i = 0; i < ase->objects.Num(); i++ ) {
    878 		obj = ase->objects[i];
    879 		for ( j = 0; j < obj->frames.Num(); j++ ) {
    880 			mesh = obj->frames[j];
    881 			if ( mesh->vertexes ) {
    882 				Mem_Free( mesh->vertexes );
    883 			}
    884 			if ( mesh->tvertexes ) {
    885 				Mem_Free( mesh->tvertexes );
    886 			}
    887 			if ( mesh->cvertexes ) {
    888 				Mem_Free( mesh->cvertexes );
    889 			}
    890 			if ( mesh->faces ) {
    891 				Mem_Free( mesh->faces );
    892 			}
    893 			Mem_Free( mesh );
    894 		}
    895 
    896 		obj->frames.Clear();
    897 
    898 		// free the base nesh
    899 		mesh = &obj->mesh;
    900 		if ( mesh->vertexes ) {
    901 			Mem_Free( mesh->vertexes );
    902 		}
    903 		if ( mesh->tvertexes ) {
    904 			Mem_Free( mesh->tvertexes );
    905 		}
    906 		if ( mesh->cvertexes ) {
    907 			Mem_Free( mesh->cvertexes );
    908 		}
    909 		if ( mesh->faces ) {
    910 			Mem_Free( mesh->faces );
    911 		}
    912 		Mem_Free( obj );
    913 	}
    914 	ase->objects.Clear();
    915 
    916 	for ( i = 0; i < ase->materials.Num(); i++ ) {
    917 		material = ase->materials[i];
    918 		Mem_Free( material );
    919 	}
    920 	ase->materials.Clear();
    921 
    922 	delete ase;
    923 }