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