2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "Model_ase.h"
35 ======================================================================
37 Parses 3D Studio Max ASCII export files.
38 The goal is to parse the information into memory exactly as it is
39 represented in the file. Users of the data will then move it
40 into a form that is more convenient for them.
42 ======================================================================
46 #define VERBOSE( x ) { if ( ase.verbose ) { common->Printf x ; } }
48 // working variables used during parsing
58 aseObject_t *currentObject;
59 aseMesh_t *currentMesh;
60 aseMaterial_t *currentMaterial;
68 static aseMesh_t *ASE_GetCurrentMesh( void )
70 return ase.currentMesh;
73 static int CharIsTokenDelimiter( int ch )
80 static int ASE_GetToken( bool restOfLine )
84 if ( ase.buffer == 0 )
87 if ( ( ase.curpos - ase.buffer ) == ase.len )
91 while ( ( ( ase.curpos - ase.buffer ) < ase.len ) &&
92 ( *ase.curpos <= 32 ) )
97 while ( ( ase.curpos - ase.buffer ) < ase.len )
99 ase.token[i] = *ase.curpos;
104 if ( ( CharIsTokenDelimiter( ase.token[i-1] ) && !restOfLine ) ||
105 ( ( ase.token[i-1] == '\n' ) || ( ase.token[i-1] == '\r' ) ) )
117 static void ASE_ParseBracedBlock( void (*parser)( const char *token ) )
121 while ( ASE_GetToken( false ) )
123 if ( !strcmp( ase.token, "{" ) )
127 else if ( !strcmp( ase.token, "}" ) )
132 else if ( indent < 0 )
133 common->Error( "Unexpected '}'" );
143 static void ASE_SkipEnclosingBraces( void )
147 while ( ASE_GetToken( false ) )
149 if ( !strcmp( ase.token, "{" ) )
153 else if ( !strcmp( ase.token, "}" ) )
158 else if ( indent < 0 )
159 common->Error( "Unexpected '}'" );
164 static void ASE_SkipRestOfLine( void )
166 ASE_GetToken( true );
169 static void ASE_KeyMAP_DIFFUSE( const char *token )
171 aseMaterial_t *material;
173 if ( !strcmp( token, "*BITMAP" ) )
178 ASE_GetToken( false );
181 char *s = strstr( ase.token + 1, "\"" );
185 matname = ase.token + 1;
187 // convert the 3DSMax material pathname to a qpath
188 matname.BackSlashesToSlashes();
189 qpath = fileSystem->OSPathToRelativePath( matname );
190 idStr::Copynz( ase.currentMaterial->name, qpath, sizeof( ase.currentMaterial->name ) );
192 else if ( !strcmp( token, "*UVW_U_OFFSET" ) )
194 material = ase.model->materials[ase.model->materials.Num() - 1];
195 ASE_GetToken( false );
196 material->uOffset = atof( ase.token );
198 else if ( !strcmp( token, "*UVW_V_OFFSET" ) )
200 material = ase.model->materials[ase.model->materials.Num() - 1];
201 ASE_GetToken( false );
202 material->vOffset = atof( ase.token );
204 else if ( !strcmp( token, "*UVW_U_TILING" ) )
206 material = ase.model->materials[ase.model->materials.Num() - 1];
207 ASE_GetToken( false );
208 material->uTiling = atof( ase.token );
210 else if ( !strcmp( token, "*UVW_V_TILING" ) )
212 material = ase.model->materials[ase.model->materials.Num() - 1];
213 ASE_GetToken( false );
214 material->vTiling = atof( ase.token );
216 else if ( !strcmp( token, "*UVW_ANGLE" ) )
218 material = ase.model->materials[ase.model->materials.Num() - 1];
219 ASE_GetToken( false );
220 material->angle = atof( ase.token );
227 static void ASE_KeyMATERIAL( const char *token )
229 if ( !strcmp( token, "*MAP_DIFFUSE" ) )
231 ASE_ParseBracedBlock( ASE_KeyMAP_DIFFUSE );
238 static void ASE_KeyMATERIAL_LIST( const char *token )
240 if ( !strcmp( token, "*MATERIAL_COUNT" ) )
242 ASE_GetToken( false );
243 VERBOSE( ( "..num materials: %s\n", ase.token ) );
245 else if ( !strcmp( token, "*MATERIAL" ) )
247 VERBOSE( ( "..material %d\n", ase.model->materials.Num() ) );
249 ase.currentMaterial = (aseMaterial_t *)Mem_Alloc( sizeof( aseMaterial_t ) );
250 memset( ase.currentMaterial, 0, sizeof( aseMaterial_t ) );
251 ase.currentMaterial->uTiling = 1;
252 ase.currentMaterial->vTiling = 1;
253 ase.model->materials.Append(ase.currentMaterial);
255 ASE_ParseBracedBlock( ASE_KeyMATERIAL );
259 static void ASE_KeyNODE_TM( const char *token )
263 if ( !strcmp( token, "*TM_ROW0" ) ) {
264 for ( i = 0 ; i < 3 ; i++ ) {
265 ASE_GetToken( false );
266 ase.currentObject->mesh.transform[0][i] = atof( ase.token );
268 } else if ( !strcmp( token, "*TM_ROW1" ) ) {
269 for ( i = 0 ; i < 3 ; i++ ) {
270 ASE_GetToken( false );
271 ase.currentObject->mesh.transform[1][i] = atof( ase.token );
273 } else if ( !strcmp( token, "*TM_ROW2" ) ) {
274 for ( i = 0 ; i < 3 ; i++ ) {
275 ASE_GetToken( false );
276 ase.currentObject->mesh.transform[2][i] = atof( ase.token );
278 } else if ( !strcmp( token, "*TM_ROW3" ) ) {
279 for ( i = 0 ; i < 3 ; i++ ) {
280 ASE_GetToken( false );
281 ase.currentObject->mesh.transform[3][i] = atof( ase.token );
286 static void ASE_KeyMESH_VERTEX_LIST( const char *token )
288 aseMesh_t *pMesh = ASE_GetCurrentMesh();
290 if ( !strcmp( token, "*MESH_VERTEX" ) )
292 ASE_GetToken( false ); // skip number
294 ASE_GetToken( false );
295 pMesh->vertexes[ase.currentVertex].x = atof( ase.token );
297 ASE_GetToken( false );
298 pMesh->vertexes[ase.currentVertex].y = atof( ase.token );
300 ASE_GetToken( false );
301 pMesh->vertexes[ase.currentVertex].z = atof( ase.token );
305 if ( ase.currentVertex > pMesh->numVertexes )
307 common->Error( "ase.currentVertex >= pMesh->numVertexes" );
312 common->Error( "Unknown token '%s' while parsing MESH_VERTEX_LIST", token );
316 static void ASE_KeyMESH_FACE_LIST( const char *token )
318 aseMesh_t *pMesh = ASE_GetCurrentMesh();
320 if ( !strcmp( token, "*MESH_FACE" ) )
322 ASE_GetToken( false ); // skip face number
324 // we are flipping the order here to change the front/back facing
325 // from 3DS to our standard (clockwise facing out)
326 ASE_GetToken( false ); // skip label
327 ASE_GetToken( false ); // first vertex
328 pMesh->faces[ase.currentFace].vertexNum[0] = atoi( ase.token );
330 ASE_GetToken( false ); // skip label
331 ASE_GetToken( false ); // second vertex
332 pMesh->faces[ase.currentFace].vertexNum[2] = atoi( ase.token );
334 ASE_GetToken( false ); // skip label
335 ASE_GetToken( false ); // third vertex
336 pMesh->faces[ase.currentFace].vertexNum[1] = atoi( ase.token );
338 ASE_GetToken( true );
340 // we could parse material id and smoothing groups here
342 if ( ( p = strstr( ase.token, "*MESH_MTLID" ) ) != 0 )
344 p += strlen( "*MESH_MTLID" ) + 1;
349 common->Error( "No *MESH_MTLID found for face!" );
357 common->Error( "Unknown token '%s' while parsing MESH_FACE_LIST", token );
361 static void ASE_KeyTFACE_LIST( const char *token )
363 aseMesh_t *pMesh = ASE_GetCurrentMesh();
365 if ( !strcmp( token, "*MESH_TFACE" ) )
369 ASE_GetToken( false );
371 ASE_GetToken( false );
372 a = atoi( ase.token );
373 ASE_GetToken( false );
374 c = atoi( ase.token );
375 ASE_GetToken( false );
376 b = atoi( ase.token );
378 pMesh->faces[ase.currentFace].tVertexNum[0] = a;
379 pMesh->faces[ase.currentFace].tVertexNum[1] = b;
380 pMesh->faces[ase.currentFace].tVertexNum[2] = c;
386 common->Error( "Unknown token '%s' in MESH_TFACE", token );
390 static void ASE_KeyCFACE_LIST( const char *token )
392 aseMesh_t *pMesh = ASE_GetCurrentMesh();
394 if ( !strcmp( token, "*MESH_CFACE" ) )
396 ASE_GetToken( false );
398 for ( int i = 0 ; i < 3 ; i++ ) {
399 ASE_GetToken( false );
400 int a = atoi( ase.token );
402 // we flip the vertex order to change the face direction to our style
403 static int remap[3] = { 0, 2, 1 };
404 pMesh->faces[ase.currentFace].vertexColors[remap[i]][0] = pMesh->cvertexes[a][0] * 255;
405 pMesh->faces[ase.currentFace].vertexColors[remap[i]][1] = pMesh->cvertexes[a][1] * 255;
406 pMesh->faces[ase.currentFace].vertexColors[remap[i]][2] = pMesh->cvertexes[a][2] * 255;
413 common->Error( "Unknown token '%s' in MESH_CFACE", token );
417 static void ASE_KeyMESH_TVERTLIST( const char *token )
419 aseMesh_t *pMesh = ASE_GetCurrentMesh();
421 if ( !strcmp( token, "*MESH_TVERT" ) )
423 char u[80], v[80], w[80];
425 ASE_GetToken( false );
427 ASE_GetToken( false );
428 strcpy( u, ase.token );
430 ASE_GetToken( false );
431 strcpy( v, ase.token );
433 ASE_GetToken( false );
434 strcpy( w, ase.token );
436 pMesh->tvertexes[ase.currentVertex].x = atof( u );
437 // our OpenGL second texture axis is inverted from MAX's sense
438 pMesh->tvertexes[ase.currentVertex].y = 1.0f - atof( v );
442 if ( ase.currentVertex > pMesh->numTVertexes )
444 common->Error( "ase.currentVertex > pMesh->numTVertexes" );
449 common->Error( "Unknown token '%s' while parsing MESH_TVERTLIST", token );
453 static void ASE_KeyMESH_CVERTLIST( const char *token )
455 aseMesh_t *pMesh = ASE_GetCurrentMesh();
457 pMesh->colorsParsed = true;
459 if ( !strcmp( token, "*MESH_VERTCOL" ) )
461 ASE_GetToken( false );
463 ASE_GetToken( false );
464 pMesh->cvertexes[ase.currentVertex][0] = atof( token );
466 ASE_GetToken( false );
467 pMesh->cvertexes[ase.currentVertex][1] = atof( token );
469 ASE_GetToken( false );
470 pMesh->cvertexes[ase.currentVertex][2] = atof( token );
474 if ( ase.currentVertex > pMesh->numCVertexes )
476 common->Error( "ase.currentVertex > pMesh->numCVertexes" );
480 common->Error( "Unknown token '%s' while parsing MESH_CVERTLIST", token );
484 static void ASE_KeyMESH_NORMALS( const char *token )
486 aseMesh_t *pMesh = ASE_GetCurrentMesh();
490 pMesh->normalsParsed = true;
491 f = &pMesh->faces[ase.currentFace];
493 if ( !strcmp( token, "*MESH_FACENORMAL" ) )
497 ASE_GetToken( false );
498 num = atoi( ase.token );
500 if ( num >= pMesh->numFaces || num < 0 ) {
501 common->Error( "MESH_NORMALS face index out of range: %i", num );
504 if ( num != ase.currentFace ) {
505 common->Error( "MESH_NORMALS face index != currentFace" );
508 ASE_GetToken( false );
509 n[0] = atof( ase.token );
510 ASE_GetToken( false );
511 n[1] = atof( ase.token );
512 ASE_GetToken( false );
513 n[2]= atof( ase.token );
515 f->faceNormal[0] = n[0] * pMesh->transform[0][0] + n[1] * pMesh->transform[1][0] + n[2] * pMesh->transform[2][0];
516 f->faceNormal[1] = n[0] * pMesh->transform[0][1] + n[1] * pMesh->transform[1][1] + n[2] * pMesh->transform[2][1];
517 f->faceNormal[2] = n[0] * pMesh->transform[0][2] + n[1] * pMesh->transform[1][2] + n[2] * pMesh->transform[2][2];
519 f->faceNormal.Normalize();
523 else if ( !strcmp( token, "*MESH_VERTEXNORMAL" ) )
528 ASE_GetToken( false );
529 num = atoi( ase.token );
531 if ( num >= pMesh->numVertexes || num < 0 ) {
532 common->Error( "MESH_NORMALS vertex index out of range: %i", num );
535 f = &pMesh->faces[ ase.currentFace - 1 ];
537 for ( v = 0 ; v < 3 ; v++ ) {
538 if ( num == f->vertexNum[ v ] ) {
544 common->Error( "MESH_NORMALS vertex index doesn't match face" );
547 ASE_GetToken( false );
548 n[0] = atof( ase.token );
549 ASE_GetToken( false );
550 n[1] = atof( ase.token );
551 ASE_GetToken( false );
552 n[2]= atof( ase.token );
554 f->vertexNormals[ v ][0] = n[0] * pMesh->transform[0][0] + n[1] * pMesh->transform[1][0] + n[2] * pMesh->transform[2][0];
555 f->vertexNormals[ v ][1] = n[0] * pMesh->transform[0][1] + n[1] * pMesh->transform[1][1] + n[2] * pMesh->transform[2][1];
556 f->vertexNormals[ v ][2] = n[0] * pMesh->transform[0][2] + n[1] * pMesh->transform[1][2] + n[2] * pMesh->transform[2][2];
558 f->vertexNormals[v].Normalize();
562 static void ASE_KeyMESH( const char *token )
564 aseMesh_t *pMesh = ASE_GetCurrentMesh();
566 if ( !strcmp( token, "*TIMEVALUE" ) )
568 ASE_GetToken( false );
570 pMesh->timeValue = atoi( ase.token );
571 VERBOSE( ( ".....timevalue: %d\n", pMesh->timeValue ) );
573 else if ( !strcmp( token, "*MESH_NUMVERTEX" ) )
575 ASE_GetToken( false );
577 pMesh->numVertexes = atoi( ase.token );
578 VERBOSE( ( ".....num vertexes: %d\n", pMesh->numVertexes ) );
580 else if ( !strcmp( token, "*MESH_NUMTVERTEX" ) )
582 ASE_GetToken( false );
584 pMesh->numTVertexes = atoi( ase.token );
585 VERBOSE( ( ".....num tvertexes: %d\n", pMesh->numTVertexes ) );
587 else if ( !strcmp( token, "*MESH_NUMCVERTEX" ) )
589 ASE_GetToken( false );
591 pMesh->numCVertexes = atoi( ase.token );
592 VERBOSE( ( ".....num cvertexes: %d\n", pMesh->numCVertexes ) );
594 else if ( !strcmp( token, "*MESH_NUMFACES" ) )
596 ASE_GetToken( false );
598 pMesh->numFaces = atoi( ase.token );
599 VERBOSE( ( ".....num faces: %d\n", pMesh->numFaces ) );
601 else if ( !strcmp( token, "*MESH_NUMTVFACES" ) )
603 ASE_GetToken( false );
605 pMesh->numTVFaces = atoi( ase.token );
606 VERBOSE( ( ".....num tvfaces: %d\n", pMesh->numTVFaces ) );
608 if ( pMesh->numTVFaces != pMesh->numFaces )
610 common->Error( "MESH_NUMTVFACES != MESH_NUMFACES" );
613 else if ( !strcmp( token, "*MESH_NUMCVFACES" ) )
615 ASE_GetToken( false );
617 pMesh->numCVFaces = atoi( ase.token );
618 VERBOSE( ( ".....num cvfaces: %d\n", pMesh->numCVFaces ) );
620 if ( pMesh->numTVFaces != pMesh->numFaces )
622 common->Error( "MESH_NUMCVFACES != MESH_NUMFACES" );
625 else if ( !strcmp( token, "*MESH_VERTEX_LIST" ) )
627 pMesh->vertexes = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numVertexes );
628 ase.currentVertex = 0;
629 VERBOSE( ( ".....parsing MESH_VERTEX_LIST\n" ) );
630 ASE_ParseBracedBlock( ASE_KeyMESH_VERTEX_LIST );
632 else if ( !strcmp( token, "*MESH_TVERTLIST" ) )
634 ase.currentVertex = 0;
635 pMesh->tvertexes = (idVec2 *)Mem_Alloc( sizeof( idVec2 ) * pMesh->numTVertexes );
636 VERBOSE( ( ".....parsing MESH_TVERTLIST\n" ) );
637 ASE_ParseBracedBlock( ASE_KeyMESH_TVERTLIST );
639 else if ( !strcmp( token, "*MESH_CVERTLIST" ) )
641 ase.currentVertex = 0;
642 pMesh->cvertexes = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numCVertexes );
643 VERBOSE( ( ".....parsing MESH_CVERTLIST\n" ) );
644 ASE_ParseBracedBlock( ASE_KeyMESH_CVERTLIST );
646 else if ( !strcmp( token, "*MESH_FACE_LIST" ) )
648 pMesh->faces = (aseFace_t *)Mem_Alloc( sizeof( aseFace_t ) * pMesh->numFaces );
650 VERBOSE( ( ".....parsing MESH_FACE_LIST\n" ) );
651 ASE_ParseBracedBlock( ASE_KeyMESH_FACE_LIST );
653 else if ( !strcmp( token, "*MESH_TFACELIST" ) )
655 if ( !pMesh->faces ) {
656 common->Error( "*MESH_TFACELIST before *MESH_FACE_LIST" );
659 VERBOSE( ( ".....parsing MESH_TFACE_LIST\n" ) );
660 ASE_ParseBracedBlock( ASE_KeyTFACE_LIST );
662 else if ( !strcmp( token, "*MESH_CFACELIST" ) )
664 if ( !pMesh->faces ) {
665 common->Error( "*MESH_CFACELIST before *MESH_FACE_LIST" );
668 VERBOSE( ( ".....parsing MESH_CFACE_LIST\n" ) );
669 ASE_ParseBracedBlock( ASE_KeyCFACE_LIST );
671 else if ( !strcmp( token, "*MESH_NORMALS" ) )
673 if ( !pMesh->faces ) {
674 common->Warning( "*MESH_NORMALS before *MESH_FACE_LIST" );
677 VERBOSE( ( ".....parsing MESH_NORMALS\n" ) );
678 ASE_ParseBracedBlock( ASE_KeyMESH_NORMALS );
682 static void ASE_KeyMESH_ANIMATION( const char *token )
686 // loads a single animation frame
687 if ( !strcmp( token, "*MESH" ) )
689 VERBOSE( ( "...found MESH\n" ) );
691 mesh = (aseMesh_t *)Mem_Alloc( sizeof( aseMesh_t ) );
692 memset( mesh, 0, sizeof( aseMesh_t ) );
693 ase.currentMesh = mesh;
695 ase.currentObject->frames.Append( mesh );
697 ASE_ParseBracedBlock( ASE_KeyMESH );
701 common->Error( "Unknown token '%s' while parsing MESH_ANIMATION", token );
705 static void ASE_KeyGEOMOBJECT( const char *token )
709 object = ase.currentObject;
711 if ( !strcmp( token, "*NODE_NAME" ) )
713 ASE_GetToken( true );
714 VERBOSE( ( " %s\n", ase.token ) );
715 idStr::Copynz( object->name, ase.token, sizeof( object->name ) );
717 else if ( !strcmp( token, "*NODE_PARENT" ) )
719 ASE_SkipRestOfLine();
721 // ignore unused data blocks
722 else if ( !strcmp( token, "*NODE_TM" ) ||
723 !strcmp( token, "*TM_ANIMATION" ) )
725 ASE_ParseBracedBlock( ASE_KeyNODE_TM );
727 // ignore regular meshes that aren't part of animation
728 else if ( !strcmp( token, "*MESH" ) )
730 ase.currentMesh = &ase.currentObject->mesh;
731 memset( ase.currentMesh, 0, sizeof( ase.currentMesh ) );
733 ASE_ParseBracedBlock( ASE_KeyMESH );
735 // according to spec these are obsolete
736 else if ( !strcmp( token, "*MATERIAL_REF" ) )
738 ASE_GetToken( false );
740 object->materialRef = atoi( ase.token );
742 // loads a sequence of animation frames
743 else if ( !strcmp( token, "*MESH_ANIMATION" ) )
745 VERBOSE( ( "..found MESH_ANIMATION\n" ) );
747 ASE_ParseBracedBlock( ASE_KeyMESH_ANIMATION );
750 else if ( !strcmp( token, "*PROP_MOTIONBLUR" ) ||
751 !strcmp( token, "*PROP_CASTSHADOW" ) ||
752 !strcmp( token, "*PROP_RECVSHADOW" ) )
754 ASE_SkipRestOfLine();
759 void ASE_ParseGeomObject( void ) {
762 VERBOSE( ("GEOMOBJECT" ) );
764 object = (aseObject_t *)Mem_Alloc( sizeof( aseObject_t ) );
765 memset( object, 0, sizeof( aseObject_t ) );
766 ase.model->objects.Append( object );
767 ase.currentObject = object;
769 object->frames.Resize(32, 32);
771 ASE_ParseBracedBlock( ASE_KeyGEOMOBJECT );
774 static void ASE_KeyGROUP( const char *token )
776 if ( !strcmp( token, "*GEOMOBJECT" ) ) {
777 ASE_ParseGeomObject();
786 aseModel_t *ASE_Parse( const char *buffer, bool verbose ) {
787 memset( &ase, 0, sizeof( ase ) );
789 ase.verbose = verbose;
792 ase.len = strlen( buffer );
793 ase.curpos = ase.buffer;
794 ase.currentObject = NULL;
796 // NOTE: using new operator because aseModel_t contains idList class objects
797 ase.model = new aseModel_t;
798 memset( ase.model, 0, sizeof( aseModel_t ) );
799 ase.model->objects.Resize( 32, 32 );
800 ase.model->materials.Resize( 32, 32 );
802 while ( ASE_GetToken( false ) ) {
803 if ( !strcmp( ase.token, "*3DSMAX_ASCIIEXPORT" ) ||
804 !strcmp( ase.token, "*COMMENT" ) ) {
805 ASE_SkipRestOfLine();
806 } else if ( !strcmp( ase.token, "*SCENE" ) ) {
807 ASE_SkipEnclosingBraces();
808 } else if ( !strcmp( ase.token, "*GROUP" ) ) {
809 ASE_GetToken( false ); // group name
810 ASE_ParseBracedBlock( ASE_KeyGROUP );
811 } else if ( !strcmp( ase.token, "*SHAPEOBJECT" ) ) {
812 ASE_SkipEnclosingBraces();
813 } else if ( !strcmp( ase.token, "*CAMERAOBJECT" ) ) {
814 ASE_SkipEnclosingBraces();
815 } else if ( !strcmp( ase.token, "*MATERIAL_LIST" ) ) {
816 VERBOSE( ("MATERIAL_LIST\n") );
818 ASE_ParseBracedBlock( ASE_KeyMATERIAL_LIST );
819 } else if ( !strcmp( ase.token, "*GEOMOBJECT" ) ) {
820 ASE_ParseGeomObject();
821 } else if ( ase.token[0] ) {
822 common->Printf( "Unknown token '%s'\n", ase.token );
834 aseModel_t *ASE_Load( const char *fileName ) {
839 fileSystem->ReadFile( fileName, (void **)&buf, &timeStamp );
844 ase = ASE_Parse( buf, false );
845 ase->timeStamp = timeStamp;
847 fileSystem->FreeFile( buf );
857 void ASE_Free( aseModel_t *ase ) {
861 aseMaterial_t *material;
866 for ( i = 0; i < ase->objects.Num(); i++ ) {
867 obj = ase->objects[i];
868 for ( j = 0; j < obj->frames.Num(); j++ ) {
869 mesh = obj->frames[j];
870 if ( mesh->vertexes ) {
871 Mem_Free( mesh->vertexes );
873 if ( mesh->tvertexes ) {
874 Mem_Free( mesh->tvertexes );
876 if ( mesh->cvertexes ) {
877 Mem_Free( mesh->cvertexes );
880 Mem_Free( mesh->faces );
887 // free the base nesh
889 if ( mesh->vertexes ) {
890 Mem_Free( mesh->vertexes );
892 if ( mesh->tvertexes ) {
893 Mem_Free( mesh->tvertexes );
895 if ( mesh->cvertexes ) {
896 Mem_Free( mesh->cvertexes );
899 Mem_Free( mesh->faces );
903 ase->objects.Clear();
905 for ( i = 0; i < ase->materials.Num(); i++ ) {
906 material = ase->materials[i];
907 Mem_Free( material );
909 ase->materials.Clear();