]> git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3data/models.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake3 / q3data / models.c
1 #include <assert.h>\r
2 #include "q3data.h"\r
3 \r
4 //=================================================================\r
5 \r
6 static void OrderSurfaces( void );\r
7 static void LoadBase( const char *filename );\r
8 static int      LoadModelFile( const char *filename, polyset_t **ppsets, int *pnumpolysets );\r
9 \r
10 #define MAX_SURFACE_TRIS        (SHADER_MAX_INDEXES / 3)\r
11 #define MAX_SURFACE_VERTS       SHADER_MAX_VERTEXES\r
12 \r
13 #define MD3_TYPE_UNKNOWN 0\r
14 #define MD3_TYPE_BASE3DS 1\r
15 #define MD3_TYPE_SPRITE  2\r
16 #define MD3_TYPE_ASE     3\r
17 \r
18 #define MAX_ANIM_FRAMES         512\r
19 #define MAX_ANIM_SURFACES       32\r
20 \r
21 typedef struct\r
22 {\r
23         polyset_t *frames;\r
24         int numFrames;\r
25 } SurfaceAnimation_t;\r
26 \r
27 typedef struct\r
28 {\r
29         polyset_t *surfaces[MAX_ANIM_SURFACES];\r
30         int numSurfaces;\r
31 } ObjectAnimationFrame_t;\r
32 \r
33 typedef struct {\r
34         vec3_t          xyz;\r
35         vec3_t          normal;\r
36         vec3_t          color;\r
37         float           st[2];\r
38         int                     index;\r
39 } baseVertex_t;\r
40         \r
41 typedef struct {\r
42         baseVertex_t    v[3];\r
43 } baseTriangle_t;\r
44 \r
45 //================================================================\r
46 \r
47 typedef struct\r
48 {\r
49         md3Surface_t    header;\r
50         md3Shader_t             shaders[MD3_MAX_SHADERS];\r
51         // all verts (xyz_normal)\r
52         float   *verts[MD3_MAX_FRAMES];\r
53 \r
54         baseTriangle_t  baseTriangles[MD3_MAX_TRIANGLES];\r
55 \r
56         // the triangles will be sorted so that they form long generalized tristrips\r
57         int                             orderedTriangles[MD3_MAX_TRIANGLES][3];\r
58         int                             lodTriangles[MD3_MAX_TRIANGLES][3];\r
59         baseVertex_t    baseVertexes[MD3_MAX_VERTS];\r
60 \r
61 } md3SurfaceData_t;\r
62 \r
63 typedef struct\r
64 {\r
65         int                     skinwidth, skinheight;\r
66         \r
67         md3SurfaceData_t surfData[MD3_MAX_SURFACES];\r
68 \r
69         md3Tag_t                tags[MD3_MAX_FRAMES][MD3_MAX_TAGS];\r
70         md3Frame_t              frames[MD3_MAX_FRAMES];\r
71 \r
72         md3Header_t     model;\r
73         float           scale_up;                       // set by $scale\r
74         vec3_t          adjust;                         // set by $origin\r
75         vec3_t          aseAdjust;\r
76         int                     fixedwidth, fixedheight;        // set by $skinsize\r
77 \r
78         int                     maxSurfaceTris;\r
79 \r
80         int                     lowerSkipFrameStart, lowerSkipFrameEnd;\r
81         int                     maxUpperFrames;\r
82         int                     maxHeadFrames;\r
83         int                     currentLod;\r
84         float           lodBias;\r
85 \r
86         int                     type;           // MD3_TYPE_BASE, MD3_TYPE_OLDBASE, MD3_TYPE_ASE, or MD3_TYPE_SPRITE\r
87 \r
88 } q3data;\r
89 \r
90 q3data g_data;\r
91 \r
92 // the command list holds counts, the count * 3 xyz, st, normal indexes\r
93 // that are valid for every frame\r
94 char            g_cddir[1024];\r
95 char            g_modelname[1024];\r
96 \r
97 //==============================================================\r
98 \r
99 /*\r
100 ===============\r
101 ClearModel\r
102 ===============\r
103 */\r
104 void ClearModel (void)\r
105 {\r
106         int i;\r
107 \r
108         g_data.type = MD3_TYPE_UNKNOWN;\r
109 \r
110         for ( i = 0; i < MD3_MAX_SURFACES; i++ )\r
111         {\r
112                 memset( &g_data.surfData[i].header, 0, sizeof( g_data.surfData[i].header ) );\r
113                 memset( &g_data.surfData[i].shaders, 0, sizeof( g_data.surfData[i].shaders ) );\r
114                 memset( &g_data.surfData[i].verts, 0, sizeof( g_data.surfData[i].verts ) );\r
115         }\r
116 \r
117         memset( g_data.tags, 0, sizeof( g_data.tags ) );\r
118 \r
119         for ( i = 0; i < g_data.model.numSurfaces; i++ )\r
120         {\r
121                 int j;\r
122 \r
123                 for ( j = 0; j < g_data.surfData[i].header.numShaders; j++ )\r
124                 {\r
125                         memset( &g_data.surfData[i].shaders[j], 0, sizeof( g_data.surfData[i].shaders[j] ) );\r
126                 }\r
127         }\r
128         memset (&g_data.model, 0, sizeof(g_data.model));\r
129         memset (g_cddir, 0, sizeof(g_cddir));\r
130 \r
131         g_modelname[0] = 0;\r
132         g_data.scale_up = 1.0;  \r
133         memset( &g_data.model, 0, sizeof( g_data.model ) );\r
134         VectorCopy (vec3_origin, g_data.adjust);\r
135         g_data.fixedwidth = g_data.fixedheight = 0;\r
136         g_skipmodel = qfalse;\r
137 }\r
138 \r
139 /*\r
140 ** void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData )\r
141 **\r
142 ** This routine assumes that the file position has been adjusted\r
143 ** properly prior to entry to point at the beginning of the surface.\r
144 **\r
145 ** Since surface header information is completely relative, we can't\r
146 ** just randomly seek to an arbitrary surface location right now.  Is\r
147 ** this something we should add?\r
148 */\r
149 void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData )\r
150 {\r
151         md3Surface_t    *pSurf = &pSurfData->header;\r
152         md3Shader_t             *pShader = pSurfData->shaders;\r
153         baseVertex_t    *pBaseVertex = pSurfData->baseVertexes;\r
154         float                   **verts = pSurfData->verts;\r
155 \r
156         short xyznormals[MD3_MAX_VERTS][4];\r
157 \r
158         float base_st[MD3_MAX_VERTS][2];\r
159         md3Surface_t surftemp;\r
160 \r
161         int f, i, j, k;\r
162 \r
163         if ( strstr( pSurf->name, "tag_" ) == pSurf->name )\r
164                 return;\r
165 \r
166         //\r
167         // write out the header\r
168         //\r
169         surftemp = *pSurf;\r
170         surftemp.ident = LittleLong( MD3_IDENT );\r
171         surftemp.flags = LittleLong( pSurf->flags );\r
172         surftemp.numFrames = LittleLong( pSurf->numFrames );\r
173         surftemp.numShaders = LittleLong( pSurf->numShaders );\r
174 \r
175         surftemp.ofsShaders = LittleLong( pSurf->ofsShaders );\r
176 \r
177         surftemp.ofsTriangles = LittleLong( pSurf->ofsTriangles );\r
178         surftemp.numTriangles = LittleLong( pSurf->numTriangles );\r
179 \r
180         surftemp.ofsSt = LittleLong( pSurf->ofsSt );\r
181         surftemp.ofsXyzNormals = LittleLong( pSurf->ofsXyzNormals );\r
182         surftemp.ofsEnd = LittleLong( pSurf->ofsEnd );\r
183 \r
184         SafeWrite( modelouthandle, &surftemp, sizeof( surftemp ) );\r
185 \r
186         if ( g_verbose )\r
187         {\r
188                 printf( "surface '%s'\n", pSurf->name );\r
189                 printf( "...num shaders: %d\n", pSurf->numShaders );\r
190         }\r
191 \r
192         //\r
193         // write out shaders\r
194         //\r
195         for ( i = 0; i < pSurf->numShaders; i++ )\r
196         {\r
197                 md3Shader_t shadertemp;\r
198 \r
199                 if ( g_verbose )\r
200                         printf( "......'%s'\n", pShader[i].name );\r
201 \r
202                 shadertemp = pShader[i];\r
203                 shadertemp.shaderIndex = LittleLong( shadertemp.shaderIndex );\r
204                 SafeWrite( modelouthandle, &shadertemp, sizeof( shadertemp ) );\r
205         }\r
206 \r
207         //\r
208         // write out the triangles\r
209         //\r
210         for ( i = 0 ; i < pSurf->numTriangles ; i++ ) \r
211         {\r
212                 for (j = 0 ; j < 3 ; j++) \r
213                 {\r
214                         int ivalue = LittleLong( pSurfData->orderedTriangles[i][j] );\r
215                         pSurfData->orderedTriangles[i][j] = ivalue;\r
216                 }\r
217         }\r
218 \r
219         SafeWrite( modelouthandle, pSurfData->orderedTriangles, pSurf->numTriangles * sizeof( g_data.surfData[0].orderedTriangles[0] ) );\r
220 \r
221         if ( g_verbose )\r
222         {\r
223                 printf( "\n...num verts: %d\n", pSurf->numVerts );\r
224                 printf( "...TEX COORDINATES\n" );\r
225         }\r
226 \r
227         //\r
228         // write out the texture coordinates\r
229         //\r
230         for ( i = 0; i < pSurf->numVerts ; i++) {\r
231                 base_st[i][0] = LittleFloat( pBaseVertex[i].st[0] );\r
232                 base_st[i][1] = LittleFloat( pBaseVertex[i].st[1] );\r
233                 if ( g_verbose )\r
234                         printf( "......%d: %f,%f\n", i, base_st[i][0], base_st[i][1] );\r
235         }\r
236         SafeWrite( modelouthandle, base_st, pSurf->numVerts * sizeof(base_st[0]));\r
237 \r
238         //\r
239         // write the xyz_normal\r
240         //\r
241         if ( g_verbose )\r
242                 printf( "...XYZNORMALS\n" );\r
243         for ( f = 0; f < g_data.model.numFrames; f++ )\r
244         {\r
245                 for (j=0 ; j< pSurf->numVerts; j++) \r
246                 {\r
247                         short value;\r
248 \r
249                         for (k=0 ; k < 3 ; k++) \r
250                         {\r
251                                 value = ( short ) ( verts[f][j*6+k] / MD3_XYZ_SCALE );\r
252                                 xyznormals[j][k] = LittleShort( value );\r
253                         }\r
254                         NormalToLatLong( &verts[f][j*6+3], (byte *)&xyznormals[j][3] );\r
255                 }\r
256                 SafeWrite( modelouthandle, xyznormals, pSurf->numVerts * sizeof( short ) * 4 );\r
257         }\r
258 }\r
259 \r
260 /*\r
261 ** void WriteModelFile( FILE *modelouthandle )\r
262 **\r
263 ** CHUNK                        SIZE\r
264 ** header                       sizeof( md3Header_t )\r
265 ** frames                       sizeof( md3Frame_t ) * numFrames\r
266 ** tags                         sizeof( md3Tag_t ) * numFrames * numTags\r
267 ** surfaces                     surfaceSum\r
268 */\r
269 void WriteModelFile( FILE *modelouthandle )\r
270 {\r
271         int                             f;\r
272         int                             i, j;\r
273         md3Header_t             modeltemp;\r
274         long                    surfaceSum = 0;\r
275         int                             numRealSurfaces = 0;\r
276         int                             numFrames = g_data.model.numFrames;\r
277 \r
278         // compute offsets for all surfaces, sum their total size\r
279         for ( i = 0; i < g_data.model.numSurfaces; i++ )\r
280         {\r
281                 if ( strstr( g_data.surfData[i].header.name, "tag_" ) != g_data.surfData[i].header.name )\r
282                 {\r
283                         md3Surface_t *psurf = &g_data.surfData[i].header;\r
284 \r
285                         if ( psurf->numTriangles == 0 || psurf->numVerts == 0 )\r
286                                 continue;\r
287 \r
288                         //\r
289                         // the triangle and vertex split threshold is controlled by a parameter\r
290                         // to $base, a la $base blah.3ds 1900, where "1900" determines the number\r
291                         // of triangles to split on\r
292                         //\r
293                         else if ( psurf->numVerts > MAX_SURFACE_VERTS )\r
294                         {\r
295                                 Error( "too many vertices\n" );\r
296                         }\r
297 \r
298                         psurf->numFrames = numFrames;\r
299 \r
300                         psurf->ofsShaders = sizeof( md3Surface_t );\r
301 \r
302                         if ( psurf->numTriangles > MAX_SURFACE_TRIS  ) \r
303                         {\r
304                                 Error( "too many faces\n" );\r
305                         }\r
306 \r
307                         psurf->ofsTriangles = psurf->ofsShaders + psurf->numShaders * sizeof( md3Shader_t );\r
308 \r
309                         psurf->ofsSt = psurf->ofsTriangles + psurf->numTriangles * sizeof( md3Triangle_t );\r
310                         psurf->ofsXyzNormals = psurf->ofsSt + psurf->numVerts * sizeof( md3St_t );\r
311                         psurf->ofsEnd = psurf->ofsXyzNormals + psurf->numFrames * psurf->numVerts * ( sizeof( short ) * 4 );\r
312 \r
313                         surfaceSum += psurf->ofsEnd;\r
314 \r
315                         numRealSurfaces++;\r
316                 }\r
317         }\r
318 \r
319         g_data.model.ident = MD3_IDENT;\r
320         g_data.model.version = MD3_VERSION;\r
321 \r
322         g_data.model.ofsFrames = sizeof(md3Header_t);\r
323         g_data.model.ofsTags = g_data.model.ofsFrames + numFrames*sizeof(md3Frame_t);\r
324         g_data.model.ofsSurfaces = g_data.model.ofsTags + numFrames*g_data.model.numTags*sizeof(md3Tag_t);\r
325         g_data.model.ofsEnd = g_data.model.ofsSurfaces + surfaceSum;\r
326 \r
327         //\r
328         // write out the model header\r
329         //\r
330         modeltemp = g_data.model;\r
331         modeltemp.ident = LittleLong( modeltemp.ident );\r
332         modeltemp.version = LittleLong( modeltemp.version );\r
333         modeltemp.numFrames = LittleLong( modeltemp.numFrames );\r
334         modeltemp.numTags = LittleLong( modeltemp.numTags );\r
335         modeltemp.numSurfaces = LittleLong( numRealSurfaces );\r
336         modeltemp.ofsFrames = LittleLong( modeltemp.ofsFrames );\r
337         modeltemp.ofsTags = LittleLong( modeltemp.ofsTags );\r
338         modeltemp.ofsSurfaces = LittleLong( modeltemp.ofsSurfaces );\r
339         modeltemp.ofsEnd = LittleLong( modeltemp.ofsEnd );\r
340 \r
341         SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));\r
342 \r
343         //\r
344         // write out the frames\r
345         //\r
346         for (i=0 ; i < numFrames ; i++) \r
347         {\r
348                 vec3_t tmpVec;\r
349                 float maxRadius = 0;\r
350 \r
351                 //\r
352                 // compute localOrigin and radius\r
353                 //\r
354                 g_data.frames[i].localOrigin[0] =\r
355                 g_data.frames[i].localOrigin[1] =\r
356                 g_data.frames[i].localOrigin[2] = 0;\r
357 \r
358                 for ( j = 0; j < 8; j++ )\r
359                 {\r
360                         tmpVec[0] = g_data.frames[i].bounds[(j&1)!=0][0];\r
361                         tmpVec[1] = g_data.frames[i].bounds[(j&2)!=0][1];\r
362                         tmpVec[2] = g_data.frames[i].bounds[(j&4)!=0][2];\r
363 \r
364                         if ( VectorLength( tmpVec ) > maxRadius )\r
365                                 maxRadius = VectorLength( tmpVec );\r
366                 }\r
367 \r
368                 g_data.frames[i].radius = LittleFloat( maxRadius );\r
369 \r
370                 // swap\r
371                 for (j=0 ; j<3 ; j++) {\r
372                         g_data.frames[i].bounds[0][j] = LittleFloat( g_data.frames[i].bounds[0][j] );\r
373                         g_data.frames[i].bounds[1][j] = LittleFloat( g_data.frames[i].bounds[1][j] );\r
374                         g_data.frames[i].localOrigin[j] = LittleFloat( g_data.frames[i].localOrigin[j] );\r
375                 }\r
376         }\r
377         fseek (modelouthandle, g_data.model.ofsFrames, SEEK_SET);\r
378         SafeWrite( modelouthandle, g_data.frames, numFrames * sizeof(g_data.frames[0]) );\r
379 \r
380         //\r
381         // write out the tags\r
382         //\r
383         fseek( modelouthandle, g_data.model.ofsTags, SEEK_SET );\r
384         for (f=0 ; f<g_data.model.numFrames; f++) \r
385         {\r
386                 int t;\r
387 \r
388                 for ( t = 0; t < g_data.model.numTags; t++ )\r
389                 {\r
390                         g_data.tags[f][t].origin[0] = LittleFloat(g_data.tags[f][t].origin[0]);\r
391                         g_data.tags[f][t].origin[1] = LittleFloat(g_data.tags[f][t].origin[1]);\r
392                         g_data.tags[f][t].origin[2] = LittleFloat(g_data.tags[f][t].origin[2]);\r
393 \r
394                         for (j=0 ; j<3 ; j++) \r
395                         {\r
396                                 g_data.tags[f][t].axis[0][j] = LittleFloat(g_data.tags[f][t].axis[0][j]);\r
397                                 g_data.tags[f][t].axis[1][j] = LittleFloat(g_data.tags[f][t].axis[1][j]);\r
398                                 g_data.tags[f][t].axis[2][j] = LittleFloat(g_data.tags[f][t].axis[2][j]);\r
399                         }\r
400                 }\r
401                 SafeWrite( modelouthandle, g_data.tags[f], g_data.model.numTags * sizeof(md3Tag_t) );\r
402         }\r
403 \r
404         //\r
405         // write out the surfaces\r
406         //\r
407         fseek( modelouthandle, g_data.model.ofsSurfaces, SEEK_SET );\r
408         for ( i = 0; i < g_data.model.numSurfaces; i++ )\r
409         {\r
410                 WriteModelSurface( modelouthandle, &g_data.surfData[i] );\r
411         }\r
412 }\r
413 \r
414 \r
415 /*\r
416 ===============\r
417 FinishModel\r
418 ===============\r
419 */\r
420 void FinishModel ( int type )\r
421 {\r
422         FILE            *modelouthandle;\r
423         FILE            *defaultSkinHandle;\r
424         char            name[1024];\r
425         int                     i;\r
426 \r
427         if (!g_data.model.numFrames)\r
428                 return;\r
429 \r
430         //\r
431         // build generalized triangle strips\r
432         //\r
433         OrderSurfaces();\r
434 \r
435         if ( type == TYPE_PLAYER )\r
436         {\r
437                 sprintf( name, "%s%s", writedir, g_modelname );\r
438                 *strrchr( name, '.' ) = 0;\r
439                 strcat( name, "_default.skin" );\r
440 \r
441                 defaultSkinHandle = fopen( name, "wt" );\r
442                 for ( i = 0; i < g_data.model.numSurfaces; i++ )\r
443                 {\r
444                         fprintf( defaultSkinHandle, "%s,%s\n", g_data.surfData[i].header.name, g_data.surfData[i].shaders[0].name );\r
445                 }\r
446                 fclose( defaultSkinHandle );\r
447         }\r
448 \r
449         sprintf (name, "%s%s", writedir, g_modelname);\r
450 \r
451         //\r
452         // copy the model and its shaders to release directory tree \r
453         // if doing a release build\r
454         //\r
455         if ( g_release ) {\r
456                 int                     i, j;\r
457                 md3SurfaceData_t *pSurf;\r
458 \r
459                 ReleaseFile( g_modelname );\r
460 \r
461                 for ( i = 0; i < g_data.model.numSurfaces; i++ ) {\r
462                         pSurf = &g_data.surfData[i];\r
463                         for ( j = 0; j < g_data.model.numSkins; j++ ) {\r
464                                 ReleaseShader( pSurf->shaders[j].name );\r
465                         }\r
466                 }               \r
467                 return;\r
468         }\r
469         \r
470         //\r
471         // write the model output file\r
472         //\r
473         printf ("saving to %s\n", name);\r
474         CreatePath (name);\r
475         modelouthandle = SafeOpenWrite (name);\r
476 \r
477         WriteModelFile (modelouthandle);\r
478         \r
479         printf ("%4d surfaces\n", g_data.model.numSurfaces);\r
480         printf ("%4d frames\n", g_data.model.numFrames);\r
481         printf ("%4d tags\n", g_data.model.numTags);\r
482         printf ("file size: %d\n", (int)ftell (modelouthandle) );\r
483         printf ("---------------------\n");\r
484         \r
485         fclose (modelouthandle);\r
486 }\r
487 \r
488 /*\r
489 ** OrderSurfaces\r
490 **\r
491 ** Reorders triangles in all the surfaces.\r
492 */\r
493 static void OrderSurfaces( void )\r
494 {\r
495         int s;\r
496         extern qboolean g_stripify;\r
497 \r
498         // go through each surface and find best strip/fans possible\r
499         for ( s = 0; s < g_data.model.numSurfaces; s++ )\r
500         {\r
501                 int mesh[MD3_MAX_TRIANGLES][3];\r
502                 int i;\r
503 \r
504                 printf( "stripifying surface %d/%d with %d tris\n", s, g_data.model.numSurfaces, g_data.surfData[s].header.numTriangles );\r
505 \r
506                 for ( i = 0; i < g_data.surfData[s].header.numTriangles; i++ )\r
507                 {\r
508                         mesh[i][0] = g_data.surfData[s].lodTriangles[i][0];\r
509                         mesh[i][1] = g_data.surfData[s].lodTriangles[i][1];\r
510                         mesh[i][2] = g_data.surfData[s].lodTriangles[i][2];\r
511                 }\r
512 \r
513                 if ( g_stripify )\r
514                 {\r
515                         OrderMesh( mesh,                                                                        // input\r
516                                            g_data.surfData[s].orderedTriangles,         // output\r
517                                            g_data.surfData[s].header.numTriangles );\r
518                 }\r
519                 else\r
520                 {\r
521                         memcpy( g_data.surfData[s].orderedTriangles, mesh, sizeof( int ) * 3 * g_data.surfData[s].header.numTriangles );\r
522                 }\r
523         }\r
524 }\r
525 \r
526 \r
527 /*\r
528 ===============================================================\r
529 \r
530 BASE FRAME SETUP\r
531 \r
532 ===============================================================\r
533 */\r
534 /*\r
535 ============\r
536 CopyTrianglesToBaseTriangles\r
537 \r
538 ============\r
539 */\r
540 static void CopyTrianglesToBaseTriangles(triangle_t *ptri, int numtri, baseTriangle_t *bTri )\r
541 {\r
542         int                     i;\r
543 //      int                     width, height, iwidth, iheight, swidth;\r
544 //      float           s_scale, t_scale;\r
545 //      float           scale;\r
546 //      vec3_t          mins, maxs;\r
547         float           *pbasevert;\r
548 \r
549 /*\r
550         //\r
551         // find bounds of all the verts on the base frame\r
552         //\r
553         ClearBounds (mins, maxs);\r
554         \r
555         for (i=0 ; i<numtri ; i++)\r
556                 for (j=0 ; j<3 ; j++)\r
557                         AddPointToBounds (ptri[i].verts[j], mins, maxs);\r
558         \r
559         for (i=0 ; i<3 ; i++)\r
560         {\r
561                 mins[i] = floor(mins[i]);\r
562                 maxs[i] = ceil(maxs[i]);\r
563         }\r
564         \r
565         width = maxs[0] - mins[0];\r
566         height = maxs[2] - mins[2];\r
567 \r
568         if (!g_data.fixedwidth)\r
569         {       // old style\r
570                 scale = 8;\r
571                 if (width*scale >= 150)\r
572                         scale = 150.0 / width;  \r
573                 if (height*scale >= 190)\r
574                         scale = 190.0 / height;\r
575 \r
576                 s_scale = t_scale = scale;\r
577 \r
578                 iwidth = ceil(width*s_scale);\r
579                 iheight = ceil(height*t_scale);\r
580 \r
581                 iwidth += 4;\r
582                 iheight += 4;\r
583         }\r
584         else\r
585         {       // new style\r
586                 iwidth = g_data.fixedwidth / 2;\r
587                 iheight = g_data.fixedheight;\r
588 \r
589                 s_scale = (float)(iwidth-4) / width;\r
590                 t_scale = (float)(iheight-4) / height;\r
591         }\r
592 \r
593         // make the width a multiple of 4; some hardware requires this, and it ensures\r
594         // dword alignment for each scan\r
595         swidth = iwidth*2;\r
596         g_data.skinwidth = (swidth + 3) & ~3;\r
597         g_data.skinheight = iheight;\r
598 */\r
599 \r
600         for (i=0; i<numtri ; i++, ptri++, bTri++)\r
601         {\r
602                 int j;\r
603 \r
604                 for (j=0 ; j<3 ; j++) \r
605                 {\r
606                         pbasevert = ptri->verts[j];\r
607 \r
608                         VectorCopy( ptri->verts[j], bTri->v[j].xyz);\r
609                         VectorCopy( ptri->normals[j], bTri->v[j].normal );\r
610 \r
611                         bTri->v[j].st[0] = ptri->texcoords[j][0];\r
612                         bTri->v[j].st[1] = ptri->texcoords[j][1];\r
613                 }\r
614         }\r
615 }\r
616 \r
617 static void BuildBaseFrame( const char *filename, ObjectAnimationFrame_t *pOAF )\r
618 {\r
619         baseTriangle_t  *bTri;\r
620         baseVertex_t    *bVert;\r
621         int i, j;\r
622 \r
623         // calculate the base triangles\r
624         for ( i = 0; i < g_data.model.numSurfaces; i++ )\r
625         {\r
626                 CopyTrianglesToBaseTriangles( pOAF->surfaces[i]->triangles, \r
627                                                         pOAF->surfaces[i]->numtriangles,\r
628                                                                         g_data.surfData[i].baseTriangles );\r
629 \r
630                 strcpy( g_data.surfData[i].header.name, pOAF->surfaces[i]->name );\r
631 \r
632                 g_data.surfData[i].header.numTriangles = pOAF->surfaces[i]->numtriangles;\r
633                 g_data.surfData[i].header.numVerts = 0;\r
634 \r
635 /*\r
636                 if ( strstr( filename, gamedir + 1 ) )\r
637                 {\r
638                         strcpy( shaderName, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );\r
639                 }\r
640                 else\r
641                 {\r
642                         strcpy( shaderName, filename );\r
643                 }\r
644 \r
645                 if ( strrchr( shaderName, '/' ) )\r
646                         *( strrchr( shaderName, '/' ) + 1 ) = 0;\r
647 \r
648 \r
649                 strcpy( shaderName, pOAF->surfaces[i]->materialname );\r
650 */\r
651                 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, pOAF->surfaces[i]->materialname );\r
652 \r
653                 g_data.surfData[i].header.numShaders++;\r
654         }\r
655 \r
656         //\r
657         // compute unique vertices for each polyset\r
658         //\r
659         for ( i = 0; i < g_data.model.numSurfaces; i++ )\r
660         {\r
661                 int t;\r
662 \r
663                 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )\r
664                 {\r
665                         bTri = &g_data.surfData[i].baseTriangles[t];\r
666 \r
667                         for (j=0 ; j<3 ; j++)\r
668                         {\r
669                                 int k;\r
670 \r
671                                 bVert = &bTri->v[j];\r
672 \r
673                                 // get the xyz index\r
674                                 for ( k = 0; k < g_data.surfData[i].header.numVerts; k++ )\r
675                                 {\r
676                                         if ( ( g_data.surfData[i].baseVertexes[k].st[0] == bVert->st[0] ) &&\r
677                                                  ( g_data.surfData[i].baseVertexes[k].st[1] == bVert->st[1] ) &&\r
678                                                  ( VectorCompare (bVert->xyz, g_data.surfData[i].baseVertexes[k].xyz) ) &&\r
679                                                  ( VectorCompare (bVert->normal, g_data.surfData[i].baseVertexes[k].normal) ) )\r
680                                         {\r
681                                                 break;  // this vertex is already in the base vertex list\r
682                                         }\r
683                                 }\r
684 \r
685                                 if (k == g_data.surfData[i].header.numVerts)    { // new index\r
686                                         g_data.surfData[i].baseVertexes[g_data.surfData[i].header.numVerts] = *bVert;\r
687                                         g_data.surfData[i].header.numVerts++;\r
688                                 }\r
689 \r
690                                 bVert->index = k;\r
691 \r
692                                 g_data.surfData[i].lodTriangles[t][j] = k;\r
693                         }\r
694                 }\r
695         }\r
696 \r
697         //\r
698         // find tags\r
699         //\r
700         for ( i = 0; i < g_data.model.numSurfaces; i++ )\r
701         {\r
702                 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name )\r
703                 {\r
704                         if ( pOAF->surfaces[i]->numtriangles != 1 )\r
705                         {\r
706                                 Error( "tag polysets must consist of only one triangle" );\r
707                         }\r
708                         if ( strstr( filename, "_flash.md3" ) && !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) )\r
709                                 continue;\r
710                         printf( "found tag '%s'\n", pOAF->surfaces[i]->name );\r
711                         g_data.model.numTags++;\r
712                 }\r
713         }\r
714 \r
715 }\r
716 \r
717 static int LoadModelFile( const char *filename, polyset_t **psets, int *numpolysets )\r
718 {\r
719         int                     time1;\r
720         char            file1[1024];\r
721         const char                      *frameFile;\r
722 \r
723         printf ("---------------------\n");\r
724         if ( filename[1] != ':' )\r
725         {\r
726                 frameFile = filename;\r
727                 sprintf( file1, "%s/%s", g_cddir, frameFile );\r
728         }\r
729         else\r
730         {\r
731                 strcpy( file1, filename );\r
732         }\r
733 \r
734         time1 = FileTime (file1);\r
735         if (time1 == -1)\r
736                 Error ("%s doesn't exist", file1);\r
737 \r
738         //\r
739         // load the base triangles\r
740         //\r
741         *psets = Polyset_LoadSets( file1, numpolysets, g_data.maxSurfaceTris );\r
742 \r
743         //\r
744         // snap polysets\r
745         //\r
746         Polyset_SnapSets( *psets, *numpolysets );\r
747 \r
748         if ( strstr( file1, ".3ds" ) || strstr( file1, ".3DS" ) )\r
749                 return MD3_TYPE_BASE3DS;\r
750 \r
751         Error( "Unknown model file type" );\r
752 \r
753         return MD3_TYPE_UNKNOWN;\r
754 }\r
755 \r
756 /*\r
757 =================\r
758 Cmd_Base\r
759 =================\r
760 */\r
761 void Cmd_Base( void )\r
762 {\r
763         char filename[1024];\r
764 \r
765         GetToken( qfalse );\r
766         sprintf( filename, "%s/%s", g_cddir, token );\r
767         LoadBase( filename );\r
768 }\r
769 \r
770 static void LoadBase( const char *filename )\r
771 {\r
772         int numpolysets;\r
773         polyset_t *psets;\r
774         int                     i;\r
775         ObjectAnimationFrame_t oaf;\r
776 \r
777         // determine polyset splitting threshold\r
778         if ( TokenAvailable() )\r
779         {\r
780                 GetToken( qfalse );\r
781                 g_data.maxSurfaceTris = atoi( token );\r
782         }\r
783         else\r
784         {\r
785                 g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;\r
786         }\r
787 \r
788         g_data.type = LoadModelFile( filename, &psets, &numpolysets );\r
789 \r
790         Polyset_ComputeNormals( psets, numpolysets );\r
791 \r
792         g_data.model.numSurfaces = numpolysets;\r
793 \r
794         memset( &oaf, 0, sizeof( oaf ) );\r
795 \r
796         for ( i = 0; i < numpolysets; i++ )\r
797         {\r
798                 oaf.surfaces[i] = &psets[i];\r
799                 oaf.numSurfaces = numpolysets;\r
800         }\r
801 \r
802         BuildBaseFrame( filename, &oaf );\r
803 \r
804         free( psets[0].triangles );\r
805         free( psets );\r
806 }\r
807 \r
808 /*\r
809 =================\r
810 Cmd_SpriteBase\r
811 \r
812 $spritebase xorg yorg width height\r
813 \r
814 Generate a single square for the model\r
815 =================\r
816 */\r
817 void Cmd_SpriteBase (void)\r
818 {\r
819         float           xl, yl, width, height;\r
820 \r
821         g_data.type = MD3_TYPE_SPRITE;\r
822 \r
823         GetToken (qfalse);\r
824         xl = atof(token);\r
825         GetToken (qfalse);\r
826         yl = atof(token);\r
827         GetToken (qfalse);\r
828         width = atof(token);\r
829         GetToken (qfalse);\r
830         height = atof(token);\r
831 \r
832 //      if (g_skipmodel || g_release || g_archive)\r
833 //              return;\r
834 \r
835         printf ("---------------------\n");\r
836 \r
837         g_data.surfData[0].verts[0] = ( float * ) calloc( 1, sizeof( float ) * 6 * 4 );\r
838 \r
839         g_data.surfData[0].header.numVerts = 4;\r
840 \r
841         g_data.surfData[0].verts[0][0+0] = 0;\r
842         g_data.surfData[0].verts[0][0+1] = -xl;\r
843         g_data.surfData[0].verts[0][0+2] = yl + height;\r
844 \r
845         g_data.surfData[0].verts[0][0+3] = -1;\r
846         g_data.surfData[0].verts[0][0+4] = 0;\r
847         g_data.surfData[0].verts[0][0+5] = 0;\r
848         g_data.surfData[0].baseVertexes[0].st[0] = 0;\r
849         g_data.surfData[0].baseVertexes[0].st[1] = 0;\r
850 \r
851 \r
852         g_data.surfData[0].verts[0][6+0] = 0;\r
853         g_data.surfData[0].verts[0][6+1] = -xl - width;\r
854         g_data.surfData[0].verts[0][6+2] = yl + height;\r
855         \r
856         g_data.surfData[0].verts[0][6+3] = -1;\r
857         g_data.surfData[0].verts[0][6+4] = 0;\r
858         g_data.surfData[0].verts[0][6+5] = 0;\r
859         g_data.surfData[0].baseVertexes[1].st[0] = 1;\r
860         g_data.surfData[0].baseVertexes[1].st[1] = 0;\r
861 \r
862 \r
863         g_data.surfData[0].verts[0][12+0] = 0;\r
864         g_data.surfData[0].verts[0][12+1] = -xl - width;\r
865         g_data.surfData[0].verts[0][12+2] = yl;\r
866 \r
867         g_data.surfData[0].verts[0][12+3] = -1;\r
868         g_data.surfData[0].verts[0][12+4] = 0;\r
869         g_data.surfData[0].verts[0][12+5] = 0;\r
870         g_data.surfData[0].baseVertexes[2].st[0] = 1;\r
871         g_data.surfData[0].baseVertexes[2].st[1] = 1;\r
872 \r
873 \r
874         g_data.surfData[0].verts[0][18+0] = 0;\r
875         g_data.surfData[0].verts[0][18+1] = -xl;\r
876         g_data.surfData[0].verts[0][18+2] = yl;\r
877 \r
878         g_data.surfData[0].verts[0][18+3] = -1;\r
879         g_data.surfData[0].verts[0][18+4] = 0;\r
880         g_data.surfData[0].verts[0][18+5] = 0;\r
881         g_data.surfData[0].baseVertexes[3].st[0] = 0;\r
882         g_data.surfData[0].baseVertexes[3].st[1] = 1;\r
883 \r
884         g_data.surfData[0].lodTriangles[0][0] = 0;\r
885         g_data.surfData[0].lodTriangles[0][1] = 1;\r
886         g_data.surfData[0].lodTriangles[0][2] = 2;\r
887 \r
888         g_data.surfData[0].lodTriangles[1][0] = 2;\r
889         g_data.surfData[0].lodTriangles[1][1] = 3;\r
890         g_data.surfData[0].lodTriangles[1][2] = 0;\r
891 \r
892         g_data.model.numSurfaces = 1;\r
893 \r
894         g_data.surfData[0].header.numTriangles = 2;\r
895         g_data.surfData[0].header.numVerts = 4;\r
896 \r
897         g_data.model.numFrames = 1;\r
898 }\r
899 \r
900 /*\r
901 ===========================================================================\r
902 \r
903   FRAME GRABBING\r
904 \r
905 ===========================================================================\r
906 */\r
907 \r
908 /*\r
909 ===============\r
910 GrabFrame\r
911 ===============\r
912 */\r
913 void GrabFrame (const char *frame)\r
914 {\r
915         int                     i, j, k;\r
916         char            file1[1024];\r
917         md3Frame_t              *fr;\r
918         md3Tag_t                tagParent;\r
919         float           *frameXyz;\r
920         float           *frameNormals;\r
921         const char      *framefile;\r
922         polyset_t               *psets;\r
923         qboolean         parentTagExists = qfalse;\r
924         int                      numpolysets;\r
925         int                     numtags = 0;\r
926         int                     tagcount;\r
927 \r
928         // the frame 'run1' will be looked for as either\r
929         // run.1 or run1.tri, so the new alias sequence save\r
930         // feature an be used\r
931         if ( frame[1] != ':' )\r
932         {\r
933 //              framefile = FindFrameFile (frame);\r
934                 framefile = frame;\r
935                 sprintf (file1, "%s/%s",g_cddir, framefile);\r
936         }\r
937         else\r
938         {\r
939                 strcpy( file1, frame );\r
940         }\r
941         printf ("grabbing %s\n", file1);\r
942 \r
943         if (g_data.model.numFrames >= MD3_MAX_FRAMES)\r
944                 Error ("model.numFrames >= MD3_MAX_FRAMES");\r
945         fr = &g_data.frames[g_data.model.numFrames];\r
946 \r
947         strcpy (fr->name, frame);\r
948 \r
949         psets = Polyset_LoadSets( file1, &numpolysets, g_data.maxSurfaceTris );\r
950 \r
951         //\r
952         // snap polysets\r
953         //\r
954         Polyset_SnapSets( psets, numpolysets );\r
955 \r
956         //\r
957         // compute vertex normals\r
958         //\r
959         Polyset_ComputeNormals( psets, numpolysets );\r
960 \r
961         //\r
962         // flip everything to compensate for the alias coordinate system\r
963         // and perform global scale and adjust\r
964         //\r
965         for ( i = 0; i < g_data.model.numSurfaces; i++ )\r
966         {\r
967                 triangle_t *ptri = psets[i].triangles;\r
968                 int t;\r
969 \r
970                 for ( t = 0; t < psets[i].numtriangles; t++ )\r
971                 {\r
972 \r
973                         for ( j = 0; j < 3; j++ )\r
974                         {\r
975 \r
976                                 // scale and adjust\r
977                                 for ( k = 0 ; k < 3 ; k++ ) {\r
978                                         ptri[t].verts[j][k] = ptri[t].verts[j][k] * g_data.scale_up +\r
979                                                 g_data.adjust[k];\r
980 \r
981                                         if ( ptri[t].verts[j][k] > 1023 ||\r
982                                                  ptri[t].verts[j][k] < -1023 )\r
983                                         {\r
984                                                 Error( "Model extents too large" );\r
985                                         }\r
986                                 }\r
987                         }\r
988                 }\r
989         }\r
990 \r
991         //\r
992         // find and count tags, locate parent tag\r
993         //\r
994         for ( i = 0; i < numpolysets; i++ )\r
995         {\r
996                 if ( strstr( psets[i].name, "tag_" ) == psets[i].name )\r
997                 {\r
998                         if ( strstr( psets[i].name, "tag_parent" ) == psets[i].name )\r
999                         {\r
1000                                 if ( strstr( psets[i].name, "tag_parent" ) )\r
1001                                 {\r
1002                                         float tri[3][3];\r
1003 \r
1004                                         if ( parentTagExists )\r
1005                                                 Error( "Multiple parent tags not allowed" );\r
1006 \r
1007                                         memcpy( tri[0], psets[i].triangles[0].verts[0], sizeof( float ) * 3 );\r
1008                                         memcpy( tri[1], psets[i].triangles[0].verts[1], sizeof( float ) * 3 );\r
1009                                         memcpy( tri[2], psets[i].triangles[0].verts[2], sizeof( float ) * 3 );\r
1010 \r
1011                                         MD3_ComputeTagFromTri( &tagParent, tri );\r
1012                                         strcpy( tagParent.name, psets[i].name );\r
1013                                         g_data.tags[g_data.model.numFrames][numtags] = tagParent;\r
1014                                         parentTagExists = qtrue;\r
1015 \r
1016                                 }\r
1017                         }\r
1018                         numtags++;\r
1019                 }\r
1020 \r
1021                 if ( strcmp( psets[i].name, g_data.surfData[i].header.name ) )\r
1022                 {\r
1023                         Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, psets[i].name, g_modelname );\r
1024                 }\r
1025         }\r
1026 \r
1027         if ( numtags != g_data.model.numTags )\r
1028         {\r
1029                 Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );\r
1030         }\r
1031 \r
1032         if ( numpolysets != g_data.model.numSurfaces )\r
1033         {\r
1034                 Error( "mismatched number of surfaces in frame(%d) vs. base(%d)", numpolysets-numtags, g_data.model.numSurfaces );\r
1035         }\r
1036         \r
1037         //\r
1038         // prepare to accumulate bounds and normals\r
1039         //\r
1040         ClearBounds( fr->bounds[0], fr->bounds[1] );\r
1041 \r
1042         //\r
1043         // store the frame's vertices in the same order as the base. This assumes the\r
1044         // triangles and vertices in this frame are in exactly the same order as in the\r
1045         // base\r
1046         //\r
1047         for ( i = 0, tagcount = 0; i < numpolysets; i++ )\r
1048         {\r
1049                 int t;\r
1050                 triangle_t *pTris = psets[i].triangles;\r
1051 \r
1052                 strcpy( g_data.surfData[i].header.name, psets[i].name );\r
1053 \r
1054                 //\r
1055                 // parent tag adjust\r
1056                 //\r
1057                 if ( parentTagExists ) {\r
1058                         for ( t = 0; t < psets[i].numtriangles; t++ )\r
1059                         {\r
1060                                 for ( j = 0; j < 3 ; j++ )\r
1061                                 {\r
1062                                         vec3_t tmp;\r
1063                                         \r
1064                                         VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );\r
1065 \r
1066                                         pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );\r
1067                                         pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );\r
1068                                         pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );\r
1069 \r
1070                                         VectorCopy( pTris[t].normals[j], tmp );\r
1071                                         pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );\r
1072                                         pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );\r
1073                                         pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );\r
1074                                 }\r
1075                         }\r
1076                 }\r
1077 \r
1078                 //\r
1079                 // compute tag data\r
1080                 //\r
1081                 if ( strstr( psets[i].name, "tag_" ) == psets[i].name )\r
1082                 {\r
1083                         md3Tag_t *pTag = &g_data.tags[g_data.model.numFrames][tagcount];\r
1084                         float tri[3][3];\r
1085 \r
1086                         strcpy( pTag->name, psets[i].name );\r
1087 \r
1088                         memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );\r
1089                         memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );\r
1090                         memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );\r
1091 \r
1092                         MD3_ComputeTagFromTri( pTag, tri );\r
1093                         tagcount++;\r
1094                 }\r
1095                 else\r
1096                 {\r
1097                         if ( g_data.surfData[i].verts[g_data.model.numFrames] )\r
1098                                 free( g_data.surfData[i].verts[g_data.model.numFrames] );\r
1099                         frameXyz = g_data.surfData[i].verts[g_data.model.numFrames] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );\r
1100                         frameNormals = frameXyz + 3;\r
1101 \r
1102                         for ( t = 0; t < psets[i].numtriangles; t++ )\r
1103                         {\r
1104                                 for ( j = 0; j < 3 ; j++ )\r
1105                                 {\r
1106                                         int index;\r
1107 \r
1108                                         index = g_data.surfData[i].baseTriangles[t].v[j].index;\r
1109                                         frameXyz[index*6+0] = pTris[t].verts[j][0];\r
1110                                         frameXyz[index*6+1] = pTris[t].verts[j][1];\r
1111                                         frameXyz[index*6+2] = pTris[t].verts[j][2];\r
1112                                         frameNormals[index*6+0] =  pTris[t].normals[j][0];\r
1113                                         frameNormals[index*6+1] =  pTris[t].normals[j][1];\r
1114                                         frameNormals[index*6+2] =  pTris[t].normals[j][2];\r
1115                                         AddPointToBounds (&frameXyz[index*6], fr->bounds[0], fr->bounds[1] );\r
1116                                 }\r
1117                         }\r
1118                 }\r
1119         }\r
1120 \r
1121         g_data.model.numFrames++;\r
1122 \r
1123         // only free the first triangle array, all of the psets in this array share the\r
1124         // same triangle pool!!!\r
1125 //      free( psets[0].triangles );\r
1126 //      free( psets );\r
1127 }\r
1128 \r
1129 //===========================================================================\r
1130 \r
1131 \r
1132 \r
1133 /*\r
1134 ===============\r
1135 Cmd_Frame       \r
1136 ===============\r
1137 */\r
1138 void Cmd_Frame (void)\r
1139 {\r
1140         while (TokenAvailable())\r
1141         {\r
1142                 GetToken (qfalse);\r
1143                 if (g_skipmodel)\r
1144                         continue;\r
1145                 if (g_release || g_archive)\r
1146                 {\r
1147                         g_data.model.numFrames = 1;     // don't skip the writeout\r
1148                         continue;\r
1149                 }\r
1150 \r
1151                 GrabFrame( token );\r
1152         }\r
1153 }\r
1154 \r
1155 \r
1156 /*\r
1157 ===============\r
1158 Cmd_Skin\r
1159 \r
1160 ===============\r
1161 */\r
1162 void SkinFrom3DS( const char *filename )\r
1163 {\r
1164         polyset_t *psets;\r
1165         char name[1024];\r
1166         int numPolysets;\r
1167         int i;\r
1168 \r
1169         _3DS_LoadPolysets( filename, &psets, &numPolysets, g_verbose );\r
1170 \r
1171         for ( i = 0; i < numPolysets; i++ )\r
1172         {\r
1173 /*\r
1174                 if ( strstr( filename, gamedir + 1 ) )\r
1175                 {\r
1176                         strcpy( name, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );\r
1177                 }\r
1178                 else\r
1179                 {\r
1180                         strcpy( name, filename );\r
1181                 }\r
1182 \r
1183                 if ( strrchr( name, '/' ) )\r
1184                         *( strrchr( name, '/' ) + 1 ) = 0;\r
1185 */\r
1186                 strcpy( name, psets[i].materialname );\r
1187                 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, name );\r
1188 \r
1189                 g_data.surfData[i].header.numShaders++;\r
1190         }\r
1191 \r
1192         free( psets[0].triangles );\r
1193         free( psets );\r
1194 }\r
1195 \r
1196 void Cmd_Skin (void)\r
1197 {\r
1198         char skinfile[1024];\r
1199 \r
1200         if ( g_data.type == MD3_TYPE_BASE3DS )\r
1201         {\r
1202                 GetToken( qfalse );\r
1203 \r
1204                 sprintf( skinfile, "%s/%s", g_cddir, token );\r
1205 \r
1206                 if ( strstr( token, ".3ds" ) || strstr( token, ".3DS" ) )\r
1207                 {\r
1208                         SkinFrom3DS( skinfile );\r
1209                 }\r
1210                 else\r
1211                 {\r
1212                         Error( "Unknown file format for $skin '%s'\n", skinfile );\r
1213                 }\r
1214         }\r
1215         else\r
1216         {\r
1217                 Error( "invalid model type while processing $skin" );\r
1218         }\r
1219 \r
1220         g_data.model.numSkins++;\r
1221 }\r
1222 \r
1223 /*\r
1224 =================\r
1225 Cmd_SpriteShader\r
1226 =================\r
1227 \r
1228 This routine is also called for $oldskin\r
1229 \r
1230 */\r
1231 void Cmd_SpriteShader()\r
1232 {\r
1233         GetToken( qfalse );\r
1234         strcpy( g_data.surfData[0].shaders[g_data.surfData[0].header.numShaders].name, token );\r
1235         g_data.surfData[0].header.numShaders++;\r
1236         g_data.model.numSkins++;\r
1237 }\r
1238 \r
1239 /*\r
1240 =================\r
1241 Cmd_Origin\r
1242 =================\r
1243 */\r
1244 void Cmd_Origin (void)\r
1245 {\r
1246         // rotate points into frame of reference so model points down the\r
1247         // positive x axis\r
1248         // FIXME: use alias native coordinate system\r
1249         GetToken (qfalse);\r
1250         g_data.adjust[1] = -atof (token);\r
1251 \r
1252         GetToken (qfalse);\r
1253         g_data.adjust[0] = atof (token);\r
1254 \r
1255         GetToken (qfalse);\r
1256         g_data.adjust[2] = -atof (token);\r
1257 }\r
1258 \r
1259 \r
1260 /*\r
1261 =================\r
1262 Cmd_ScaleUp\r
1263 =================\r
1264 */\r
1265 void Cmd_ScaleUp (void)\r
1266 {\r
1267         GetToken (qfalse);\r
1268         g_data.scale_up = atof (token);\r
1269         if (g_skipmodel || g_release || g_archive)\r
1270                 return;\r
1271 \r
1272         printf ("Scale up: %f\n", g_data.scale_up);\r
1273 }\r
1274 \r
1275 \r
1276 /*\r
1277 =================\r
1278 Cmd_Skinsize\r
1279 \r
1280 Set a skin size other than the default\r
1281 QUAKE3: not needed\r
1282 =================\r
1283 */\r
1284 void Cmd_Skinsize (void)\r
1285 {\r
1286         GetToken (qfalse);\r
1287         g_data.fixedwidth = atoi(token);\r
1288         GetToken (qfalse);\r
1289         g_data.fixedheight = atoi(token);\r
1290 }\r
1291 \r
1292 /*\r
1293 =================\r
1294 Cmd_Modelname\r
1295 \r
1296 Begin creating a model of the given name\r
1297 =================\r
1298 */\r
1299 void Cmd_Modelname (void)\r
1300 {\r
1301         FinishModel ( TYPE_UNKNOWN );\r
1302         ClearModel ();\r
1303 \r
1304         GetToken (qfalse);\r
1305         strcpy (g_modelname, token);\r
1306         StripExtension (g_modelname);\r
1307         strcat (g_modelname, ".md3");\r
1308         strcpy (g_data.model.name, g_modelname);\r
1309 }\r
1310 \r
1311 /*\r
1312 ===============\r
1313 fCmd_Cd\r
1314 ===============\r
1315 */\r
1316 void Cmd_Cd (void)\r
1317 {\r
1318         if ( g_cddir[0]) {\r
1319                 Error ("$cd command without a $modelname");\r
1320         }\r
1321 \r
1322         GetToken (qfalse);\r
1323 \r
1324         sprintf ( g_cddir, "%s%s", gamedir, token);\r
1325 \r
1326         // if -only was specified and this cd doesn't match,\r
1327         // skip the model (you only need to match leading chars,\r
1328         // so you could regrab all monsters with -only models/monsters)\r
1329         if (!g_only[0])\r
1330                 return;\r
1331         if (strncmp(token, g_only, strlen(g_only)))\r
1332         {\r
1333                 g_skipmodel = qtrue;\r
1334                 printf ("skipping %s\n", token);\r
1335         }\r
1336 }\r
1337 \r
1338 void Convert3DStoMD3( const char *file )\r
1339 {\r
1340         LoadBase( file );\r
1341         GrabFrame( file );\r
1342         SkinFrom3DS( file );\r
1343 \r
1344         strcpy( g_data.model.name, g_modelname );\r
1345 \r
1346         FinishModel( TYPE_UNKNOWN );\r
1347         ClearModel();\r
1348 }\r
1349 \r
1350 /*\r
1351 ** Cmd_3DSConvert\r
1352 */\r
1353 void Cmd_3DSConvert()\r
1354 {\r
1355         char file[1024];\r
1356 \r
1357         FinishModel( TYPE_UNKNOWN );\r
1358         ClearModel();\r
1359 \r
1360         GetToken( qfalse );\r
1361 \r
1362         sprintf( file, "%s%s", gamedir, token );\r
1363         strcpy( g_modelname, token );\r
1364         if ( strrchr( g_modelname, '.' ) )\r
1365                 *strrchr( g_modelname, '.' ) = 0;\r
1366         strcat( g_modelname, ".md3" );\r
1367 \r
1368         if ( FileTime( file ) == -1 )\r
1369                 Error( "%s doesn't exist", file );\r
1370 \r
1371         if ( TokenAvailable() )\r
1372         {\r
1373                 GetToken( qfalse );\r
1374                 g_data.scale_up = atof( token );\r
1375         }\r
1376 \r
1377         Convert3DStoMD3( file );\r
1378 }\r
1379 \r
1380 static void ConvertASE( const char *filename, int type, qboolean grabAnims );\r
1381 \r
1382 /*\r
1383 ** Cmd_ASEConvert\r
1384 */\r
1385 void Cmd_ASEConvert( qboolean grabAnims )\r
1386 {\r
1387         char filename[1024];\r
1388         int type = TYPE_ITEM;\r
1389 \r
1390         FinishModel( TYPE_UNKNOWN );\r
1391         ClearModel();\r
1392 \r
1393         GetToken( qfalse );\r
1394         sprintf( filename, "%s%s", gamedir, token );\r
1395 \r
1396         strcpy (g_modelname, token);\r
1397         StripExtension (g_modelname);\r
1398         strcat (g_modelname, ".md3");\r
1399         strcpy (g_data.model.name, g_modelname);\r
1400 \r
1401         if ( !strstr( filename, ".ase" ) && !strstr( filename, ".ASE" ) )\r
1402                 strcat( filename, ".ASE" );\r
1403 \r
1404         g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;\r
1405 \r
1406         while ( TokenAvailable() )\r
1407         {\r
1408                 GetToken( qfalse );\r
1409                 if ( !strcmp( token, "-origin" ) )\r
1410                 {\r
1411                         if ( !TokenAvailable() )\r
1412                                 Error( "missing parameter for -origin" );\r
1413                         GetToken( qfalse );\r
1414                         g_data.aseAdjust[1] = -atof( token );\r
1415 \r
1416                         if ( !TokenAvailable() )\r
1417                                 Error( "missing parameter for -origin" );\r
1418                         GetToken( qfalse );\r
1419                         g_data.aseAdjust[0] = atof (token);\r
1420 \r
1421                         if ( !TokenAvailable() )\r
1422                                 Error( "missing parameter for -origin" );\r
1423                         GetToken( qfalse );\r
1424                         g_data.aseAdjust[2] = -atof (token);\r
1425                 }\r
1426                 else if ( !strcmp( token, "-lod" ) )\r
1427                 {\r
1428                         if ( !TokenAvailable() )\r
1429                                 Error( "No parameter for -lod" );\r
1430                         GetToken( qfalse );\r
1431                         g_data.currentLod = atoi( token );\r
1432                         if ( g_data.currentLod > MD3_MAX_LODS - 1 )\r
1433                         {\r
1434                                 Error( "-lod parameter too large! (%d)\n", g_data.currentLod );\r
1435                         }\r
1436 \r
1437                         if ( !TokenAvailable() )\r
1438                                 Error( "No second parameter for -lod" );\r
1439                         GetToken( qfalse );\r
1440                         g_data.lodBias = atof( token );\r
1441                 }\r
1442                 else if ( !strcmp( token, "-maxtris" ) )\r
1443                 {\r
1444                         if ( !TokenAvailable() )\r
1445                                 Error( "No parameter for -maxtris" );\r
1446                         GetToken( qfalse );\r
1447                         g_data.maxSurfaceTris = atoi( token );\r
1448                 }\r
1449                 else if ( !strcmp( token, "-playerparms" ) )\r
1450                 {\r
1451                         if ( !TokenAvailable() )\r
1452                                 Error( "missing skip start parameter for -playerparms" );\r
1453                         GetToken( qfalse );\r
1454                         g_data.lowerSkipFrameStart = atoi( token );\r
1455 \r
1456 #if 0\r
1457                         if ( !TokenAvailable() )\r
1458                                 Error( "missing skip end parameter for -playerparms" );\r
1459                         GetToken( qfalse );\r
1460                         g_data.lowerSkipFrameEnd = atoi( token );\r
1461 #endif\r
1462 \r
1463                         if ( !TokenAvailable() )\r
1464                                 Error( "missing upper parameter for -playerparms" );\r
1465                         GetToken( qfalse );\r
1466                         g_data.maxUpperFrames = atoi( token );\r
1467 \r
1468                         g_data.lowerSkipFrameEnd = g_data.maxUpperFrames - 1;\r
1469 \r
1470 #if 0\r
1471                         if ( !TokenAvailable() )\r
1472                                 Error( "missing head parameter for -playerparms" );\r
1473                         GetToken( qfalse );\r
1474                         g_data.maxHeadFrames = atoi( token );\r
1475 #endif\r
1476                         g_data.maxHeadFrames = 1;\r
1477 \r
1478                         if ( type != TYPE_ITEM )\r
1479                                 Error( "invalid argument" );\r
1480 \r
1481                         type = TYPE_PLAYER;\r
1482                 }\r
1483                 else if ( !strcmp( token, "-weapon" ) )\r
1484                 {\r
1485                         if ( type != TYPE_ITEM )\r
1486                                 Error( "invalid argument" );\r
1487 \r
1488                         type = TYPE_WEAPON;\r
1489                 }\r
1490         }\r
1491 \r
1492         g_data.type = MD3_TYPE_ASE;\r
1493 \r
1494         if ( type == TYPE_WEAPON && grabAnims )\r
1495         {\r
1496                 Error( "can't grab anims with weapon models" );\r
1497         }\r
1498         if ( type == TYPE_PLAYER && !grabAnims )\r
1499         {\r
1500                 Error( "player models must be converted with $aseanimconvert" );\r
1501         }\r
1502 \r
1503         if ( type == TYPE_WEAPON )\r
1504         {\r
1505                 ConvertASE( filename, type, qfalse );\r
1506                 ConvertASE( filename, TYPE_HAND, qtrue );\r
1507         }\r
1508         else\r
1509         {\r
1510                 ConvertASE( filename, type, grabAnims );\r
1511         }\r
1512 }\r
1513 \r
1514 static int GetSurfaceAnimations( SurfaceAnimation_t sanims[MAX_ANIM_SURFACES], \r
1515                                                                   const char *part,\r
1516                                                                   int skipFrameStart,\r
1517                                                                   int skipFrameEnd,\r
1518                                                                   int maxFrames )\r
1519 \r
1520 {\r
1521         int numSurfaces;\r
1522         int numValidSurfaces;\r
1523         int i;\r
1524         int numFrames = -1;\r
1525 \r
1526         if ( ( numSurfaces = ASE_GetNumSurfaces() ) > MAX_ANIM_SURFACES )\r
1527         {\r
1528                 Error( "Too many surfaces in ASE" );\r
1529         }\r
1530 \r
1531         for ( numValidSurfaces = 0, i = 0; i < numSurfaces; i++ )\r
1532         {\r
1533                 polyset_t *splitSets;\r
1534                 int numNewFrames;\r
1535                 const char *surfaceName = ASE_GetSurfaceName( i );\r
1536 \r
1537                 if ( !surfaceName )\r
1538                 {\r
1539                         continue;\r
1540 //                      Error( "Missing animation frames in model" );\r
1541                 }\r
1542 \r
1543                 if ( strstr( surfaceName, "tag_" ) || \r
1544                          !strcmp( part, "any" ) || \r
1545                          ( strstr( surfaceName, part ) == surfaceName ) )\r
1546                 {\r
1547 \r
1548                         // skip this if it's an inappropriate tag\r
1549                         if ( strcmp( part, "any" ) )\r
1550                         {\r
1551                                 // ignore non-"tag_head" tags if this is the head\r
1552                                 if ( !strcmp( part, "h_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_head" ) )\r
1553                                         continue;\r
1554                                 // ignore "tag_head" if this is the legs\r
1555                                 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_head" ) )\r
1556                                         continue;\r
1557                                 // ignore "tag_weapon" if this is the legs\r
1558                                 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_weapon" ) )\r
1559                                         continue;\r
1560                         }\r
1561 \r
1562                         if ( ( sanims[numValidSurfaces].frames = ASE_GetSurfaceAnimation( i, &sanims[numValidSurfaces].numFrames, skipFrameStart, skipFrameEnd, maxFrames ) ) != 0 )\r
1563                         {\r
1564                                 splitSets = Polyset_SplitSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames, &numNewFrames, g_data.maxSurfaceTris );\r
1565                                 \r
1566                                 if ( numFrames == -1 )\r
1567                                         numFrames = sanims[numValidSurfaces].numFrames;\r
1568                                 else if ( numFrames != sanims[numValidSurfaces].numFrames )\r
1569                                         Error( "Different number of animation frames on surfaces" );\r
1570                                 \r
1571                                 if ( sanims[numValidSurfaces].frames != splitSets )\r
1572                                 {\r
1573                                         int j;\r
1574 \r
1575                                         // free old data if we split the surfaces\r
1576                                         for ( j = 0; j < sanims[numValidSurfaces].numFrames; j++ )\r
1577                                         {\r
1578                                                 free( sanims[numValidSurfaces].frames[j].triangles );\r
1579                                                 free( sanims[numValidSurfaces].frames );\r
1580                                         }\r
1581                                         \r
1582                                         sanims[numValidSurfaces].frames = splitSets;\r
1583                                         sanims[numValidSurfaces].numFrames = numNewFrames;\r
1584                                 }\r
1585                                 Polyset_SnapSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );\r
1586                                 Polyset_ComputeNormals( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );\r
1587 \r
1588                                 numValidSurfaces++;\r
1589                         }\r
1590                 }\r
1591         }\r
1592 \r
1593         return numValidSurfaces;\r
1594 }\r
1595 \r
1596 static int SurfaceOrderToFrameOrder( SurfaceAnimation_t sanims[], ObjectAnimationFrame_t oanims[], int numSurfaces )\r
1597 {\r
1598         int i, s;\r
1599         int numFrames = -1;\r
1600 \r
1601         /*\r
1602         ** we have the data here arranged in surface order, now we need to convert it to \r
1603         ** frame order\r
1604         */\r
1605         for ( i = 0, s = 0; i < numSurfaces; i++ )\r
1606         {\r
1607                 int j;\r
1608                 \r
1609                 if ( sanims[i].frames )\r
1610                 {\r
1611                         if ( numFrames == -1 )\r
1612                                 numFrames = sanims[i].numFrames;\r
1613                         else if ( numFrames != sanims[i].numFrames )\r
1614                                 Error( "numFrames != sanims[i].numFrames (%d != %d)\n", numFrames, sanims[i].numFrames );\r
1615 \r
1616                         for ( j = 0; j < sanims[i].numFrames; j++ )\r
1617                         {\r
1618                                 oanims[j].surfaces[s] = &sanims[i].frames[j];\r
1619                                 oanims[j].numSurfaces = numSurfaces;\r
1620                         }\r
1621                         s++;\r
1622                 }\r
1623         }\r
1624 \r
1625         return numFrames;\r
1626 }\r
1627 \r
1628 static void WriteMD3( const char *_filename, ObjectAnimationFrame_t oanims[], int numFrames )\r
1629 {\r
1630         char filename[1024];\r
1631 \r
1632         strcpy( filename, _filename );\r
1633         if ( strchr( filename, '.' ) )\r
1634                 *strchr( filename, '.' ) = 0;\r
1635         strcat( filename, ".md3" );\r
1636 }\r
1637 \r
1638 static void BuildAnimationFromOAFs( const char *filename, ObjectAnimationFrame_t oanims[], int numFrames, int type )\r
1639 {\r
1640         int f, i, j, tagcount;\r
1641         float *frameXyz;\r
1642         float *frameNormals;\r
1643 \r
1644         g_data.model.numSurfaces = oanims[0].numSurfaces;\r
1645         g_data.model.numFrames = numFrames;\r
1646         if ( g_data.model.numFrames < 0)\r
1647                 Error ("model.numFrames < 0");\r
1648         if ( g_data.model.numFrames >= MD3_MAX_FRAMES)\r
1649                 Error ("model.numFrames >= MD3_MAX_FRAMES");\r
1650 \r
1651         // build base frame\r
1652         BuildBaseFrame( filename, &oanims[0] );\r
1653         \r
1654         // build animation frames\r
1655         for ( f = 0; f < numFrames; f++ )\r
1656         {\r
1657                 ObjectAnimationFrame_t *pOAF = &oanims[f];\r
1658                 qboolean        parentTagExists = qfalse;\r
1659                 md3Tag_t        tagParent;\r
1660                 int                     numtags = 0;\r
1661                 md3Frame_t              *fr;\r
1662                 \r
1663                 fr = &g_data.frames[f];\r
1664                 \r
1665                 strcpy( fr->name, "(from ASE)" );\r
1666                 \r
1667                 // scale and adjust frame\r
1668                 for ( i = 0; i < pOAF->numSurfaces; i++ )\r
1669                 {\r
1670                         triangle_t *pTris = pOAF->surfaces[i]->triangles;\r
1671                         int t;\r
1672                         \r
1673                         for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )\r
1674                         {\r
1675                                 for ( j = 0; j < 3; j++ )\r
1676                                 {\r
1677                                         int k;\r
1678                                         \r
1679                                         // scale and adjust\r
1680                                         for ( k = 0 ; k < 3 ; k++ ) {\r
1681                                                 pTris[t].verts[j][k] = pTris[t].verts[j][k] * g_data.scale_up +\r
1682                                                         g_data.aseAdjust[k];\r
1683                                                 \r
1684                                                 if ( pTris[t].verts[j][k] > 1023 ||\r
1685                                                         pTris[t].verts[j][k] < -1023 )\r
1686                                                 {\r
1687                                                         Error( "Model extents too large" );\r
1688                                                 }\r
1689                                         }\r
1690                                 }\r
1691                         }\r
1692                 }\r
1693                 \r
1694                 //\r
1695                 // find and count tags, locate parent tag\r
1696                 //\r
1697                 for ( i = 0; i < pOAF->numSurfaces; i++ )\r
1698                 {\r
1699                         if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name )\r
1700                         {\r
1701                                 // ignore parent tags when grabbing a weapon model and this is the flash portion\r
1702                                 if ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && strstr( filename, "_flash.md3" ) )\r
1703                                 {\r
1704                                         continue;\r
1705                                 }\r
1706                                 else if ( !strstr( filename, "_hand.md3" ) && (\r
1707                                          ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && !strstr( filename, "_flash.md3" ) ) ||\r
1708                                          ( !strcmp( pOAF->surfaces[i]->name, "tag_torso" ) && ( strstr( filename, "upper_" ) || strstr( filename, "upper.md3" ) ) ) ||\r
1709                                          ( !strcmp( pOAF->surfaces[i]->name, "tag_head" ) && ( strstr( filename, "head.md3" ) || strstr( filename, "head_" ) ) ) ||\r
1710                                          ( !strcmp( pOAF->surfaces[i]->name, "tag_flash" ) && strstr( filename, "_flash.md3" ) )|| \r
1711                                          ( !strcmp( pOAF->surfaces[i]->name, "tag_weapon" ) && type == TYPE_WEAPON ) ) )\r
1712                                 {\r
1713                                         float tri[3][3];\r
1714                                         \r
1715                                         if ( parentTagExists )\r
1716                                                 Error( "Multiple parent tags not allowed" );\r
1717                                         \r
1718                                         memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );\r
1719                                         memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );\r
1720                                         memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );\r
1721                                         \r
1722                                         MD3_ComputeTagFromTri( &tagParent, tri );\r
1723                                         strcpy( tagParent.name, "tag_parent" );\r
1724                                         g_data.tags[f][numtags] = tagParent;\r
1725                                         parentTagExists = qtrue;\r
1726                                 }\r
1727                                 else\r
1728                                 {\r
1729                                         float tri[3][3];\r
1730                                 \r
1731                                         memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );\r
1732                                         memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );\r
1733                                         memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );\r
1734                                         \r
1735                                         MD3_ComputeTagFromTri( &g_data.tags[f][numtags], tri );\r
1736                                         strcpy( g_data.tags[f][numtags].name, pOAF->surfaces[i]->name );\r
1737                                         if ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) )\r
1738                                                 * ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) + strlen( "tag_flash" ) ) = 0;\r
1739                                 }\r
1740 \r
1741                                 numtags++;\r
1742                         }\r
1743                         \r
1744                         if ( strcmp( pOAF->surfaces[i]->name, g_data.surfData[i].header.name ) )\r
1745                         {\r
1746                                 Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, pOAF->surfaces[i]->name, filename );\r
1747                         }\r
1748                 }\r
1749                 \r
1750                 if ( numtags != g_data.model.numTags )\r
1751                 {\r
1752                         Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );\r
1753                 }\r
1754                 \r
1755                 //\r
1756                 // prepare to accumulate bounds and normals\r
1757                 //\r
1758                 ClearBounds( fr->bounds[0], fr->bounds[1] );\r
1759                 \r
1760                 //\r
1761                 // store the frame's vertices in the same order as the base. This assumes the\r
1762                 // triangles and vertices in this frame are in exactly the same order as in the\r
1763                 // base\r
1764                 //\r
1765                 for ( i = 0, tagcount = 0; i < pOAF->numSurfaces; i++ )\r
1766                 {\r
1767                         int t;\r
1768                         triangle_t *pTris = pOAF->surfaces[i]->triangles;\r
1769                         \r
1770                         //\r
1771                         // parent tag adjust\r
1772                         //\r
1773                         if ( parentTagExists ) \r
1774                         {\r
1775                                 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )\r
1776                                 {\r
1777                                         for ( j = 0; j < 3 ; j++ )\r
1778                                         {\r
1779                                                 vec3_t tmp;\r
1780                                                 \r
1781                                                 VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );\r
1782                                                 \r
1783                                                 pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );\r
1784                                                 pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );\r
1785                                                 pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );\r
1786                                                 \r
1787                                                 VectorCopy( pTris[t].normals[j], tmp );\r
1788                                                 pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );\r
1789                                                 pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );\r
1790                                                 pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );\r
1791                                         }\r
1792                                 }\r
1793                         }\r
1794                         \r
1795                         //\r
1796                         // compute tag data\r
1797                         //\r
1798                         if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name )\r
1799                         {\r
1800                                 md3Tag_t *pTag = &g_data.tags[f][tagcount];\r
1801                                 float tri[3][3];\r
1802                                 \r
1803                                 strcpy( pTag->name, pOAF->surfaces[i]->name );\r
1804                                 \r
1805                                 memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );\r
1806                                 memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );\r
1807                                 memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );\r
1808                                 \r
1809                                 MD3_ComputeTagFromTri( pTag, tri );\r
1810                                 tagcount++;\r
1811                         }\r
1812                         else\r
1813                         {\r
1814                                 if ( g_data.surfData[i].verts[f] )\r
1815                                         free( g_data.surfData[i].verts[f] );\r
1816                                 frameXyz = g_data.surfData[i].verts[f] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );\r
1817                                 frameNormals = frameXyz + 3;\r
1818                                 \r
1819                                 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )\r
1820                                 {\r
1821                                         for ( j = 0; j < 3 ; j++ )\r
1822                                         {\r
1823                                                 int index;\r
1824                                                 \r
1825                                                 index = g_data.surfData[i].baseTriangles[t].v[j].index;\r
1826                                                 frameXyz[index*6+0] = pTris[t].verts[j][0];\r
1827                                                 frameXyz[index*6+1] = pTris[t].verts[j][1];\r
1828                                                 frameXyz[index*6+2] = pTris[t].verts[j][2];\r
1829                                                 frameNormals[index*6+0] =  pTris[t].normals[j][0];\r
1830                                                 frameNormals[index*6+1] =  pTris[t].normals[j][1];\r
1831                                                 frameNormals[index*6+2] =  pTris[t].normals[j][2];\r
1832                                                 AddPointToBounds (&frameXyz[index*6], fr->bounds[0], fr->bounds[1] );\r
1833                                         }\r
1834                                 }\r
1835                         }\r
1836                 }\r
1837         }\r
1838 \r
1839         if ( strstr( filename, gamedir + 1 ) )\r
1840         {\r
1841                 strcpy( g_modelname, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );\r
1842         }\r
1843         else\r
1844         {\r
1845                 strcpy( g_modelname, filename );\r
1846         }\r
1847 \r
1848         FinishModel( type );\r
1849         ClearModel();\r
1850 }\r
1851 \r
1852 static void ConvertASE( const char *filename, int type, qboolean grabAnims )\r
1853 {\r
1854         int i, j;\r
1855         int numSurfaces;\r
1856         int numFrames = -1;\r
1857         SurfaceAnimation_t surfaceAnimations[MAX_ANIM_SURFACES];\r
1858         ObjectAnimationFrame_t objectAnimationFrames[MAX_ANIM_FRAMES];\r
1859         char outfilename[1024];\r
1860 \r
1861         /*\r
1862         ** load ASE into memory\r
1863         */\r
1864         ASE_Load( filename, g_verbose, grabAnims );\r
1865 \r
1866         /*\r
1867         ** process parts\r
1868         */\r
1869         if ( type == TYPE_ITEM )\r
1870         {\r
1871                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "any", -1, -1, -1 );\r
1872 \r
1873                 if ( numSurfaces <= 0 )\r
1874                         Error( "numSurfaces <= 0" );\r
1875 \r
1876                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );\r
1877 \r
1878                 if ( numFrames <= 0 )\r
1879                         Error( "numFrames <= 0" );\r
1880 \r
1881                 strcpy( outfilename, filename );\r
1882                 if ( strrchr( outfilename, '.' ) )\r
1883                         *( strrchr( outfilename, '.' ) + 1 ) = 0;\r
1884                 strcat( outfilename, "md3" );\r
1885                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );\r
1886 \r
1887                 // free memory\r
1888                 for ( i = 0; i < numSurfaces; i++ )\r
1889                 {\r
1890                         if ( surfaceAnimations[i].frames )\r
1891                         {\r
1892                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )\r
1893                                 {\r
1894                                         free( surfaceAnimations[i].frames[j].triangles );\r
1895                                 }\r
1896                                 free( surfaceAnimations[i].frames );\r
1897                                 surfaceAnimations[i].frames = 0;\r
1898                         }\r
1899                 }\r
1900         }\r
1901         else if ( type == TYPE_PLAYER )\r
1902         {\r
1903                 qboolean tagTorso = qfalse;\r
1904                 qboolean tagHead = qfalse;\r
1905                 qboolean tagWeapon = qfalse;\r
1906 \r
1907                 //\r
1908                 // verify that all necessary tags exist\r
1909                 //\r
1910                 numSurfaces = ASE_GetNumSurfaces();\r
1911                 for ( i = 0; i < numSurfaces; i++ )\r
1912                 {\r
1913                         if ( !strcmp( ASE_GetSurfaceName( i ), "tag_head" ) )\r
1914                         {\r
1915                                 tagHead = qtrue;\r
1916                         }\r
1917                         if ( !strcmp( ASE_GetSurfaceName( i ), "tag_torso" ) )\r
1918                         {\r
1919                                 tagTorso = qtrue;\r
1920                         }\r
1921                         if ( !strcmp( ASE_GetSurfaceName( i ), "tag_weapon" ) )\r
1922                         {\r
1923                                 tagWeapon = qtrue;\r
1924                         }\r
1925                 }\r
1926 \r
1927                 if ( !tagWeapon )\r
1928                 {\r
1929                         Error( "Missing tag_weapon!" );\r
1930                 }\r
1931                 if ( !tagTorso )\r
1932                 {\r
1933                         Error( "Missing tag_torso!" );\r
1934                 }\r
1935                 if ( !tagWeapon )\r
1936                 {\r
1937                         Error( "Missing tag_weapon!" );\r
1938                 }\r
1939 \r
1940                 // get all upper body surfaces\r
1941                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "u_", -1, -1, g_data.maxUpperFrames );\r
1942                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );\r
1943                 strcpy( outfilename, filename );\r
1944                 if ( strrchr( outfilename, '/' ) )\r
1945                         *( strrchr( outfilename, '/' ) + 1 ) = 0;\r
1946 \r
1947                 if ( g_data.currentLod == 0 )\r
1948                 {\r
1949                         strcat( outfilename, "upper.md3" );\r
1950                 }\r
1951                 else\r
1952                 {\r
1953                         char temp[128];\r
1954 \r
1955                         sprintf( temp, "upper_%d.md3", g_data.currentLod );\r
1956                         strcat( outfilename, temp );\r
1957                 }\r
1958                 \r
1959                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );\r
1960 \r
1961                 // free memory\r
1962                 for ( i = 0; i < numSurfaces; i++ )\r
1963                 {\r
1964                         if ( surfaceAnimations[i].frames )\r
1965                         {\r
1966                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )\r
1967                                 {\r
1968                                         free( surfaceAnimations[i].frames[j].triangles );\r
1969                                 }\r
1970                                 free( surfaceAnimations[i].frames );\r
1971                                 surfaceAnimations[i].frames = 0;\r
1972                         }\r
1973                 }\r
1974 \r
1975                 // get lower body surfaces\r
1976                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "l_", g_data.lowerSkipFrameStart, g_data.lowerSkipFrameEnd, -1 );\r
1977                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );\r
1978                 strcpy( outfilename, filename );\r
1979                 if ( strrchr( outfilename, '/' ) )\r
1980                         *( strrchr( outfilename, '/' ) + 1 ) = 0;\r
1981 \r
1982                 if ( g_data.currentLod == 0 )\r
1983                 {\r
1984                         strcat( outfilename, "lower.md3" );\r
1985                 }\r
1986                 else\r
1987                 {\r
1988                         char temp[128];\r
1989 \r
1990                         sprintf( temp, "lower_%d.md3", g_data.currentLod );\r
1991                         strcat( outfilename, temp );\r
1992                 }\r
1993                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );\r
1994 \r
1995                 // free memory\r
1996                 for ( i = 0; i < numSurfaces; i++ )\r
1997                 {\r
1998                         if ( surfaceAnimations[i].frames )\r
1999                         {\r
2000                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )\r
2001                                 {\r
2002                                         free( surfaceAnimations[i].frames[j].triangles );\r
2003                                 }\r
2004                                 free( surfaceAnimations[i].frames );\r
2005                                 surfaceAnimations[i].frames = 0;\r
2006                         }\r
2007                 }\r
2008 \r
2009                 // get head surfaces\r
2010                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "h_", -1, -1, g_data.maxHeadFrames );\r
2011                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );\r
2012                 strcpy( outfilename, filename );\r
2013                 if ( strrchr( outfilename, '/' ) )\r
2014                         *( strrchr( outfilename, '/' ) + 1 ) = 0;\r
2015 \r
2016                 if ( g_data.currentLod == 0 )\r
2017                 {\r
2018                         strcat( outfilename, "head.md3" );\r
2019                 }\r
2020                 else\r
2021                 {\r
2022                         char temp[128];\r
2023 \r
2024                         sprintf( temp, "head_%d.md3", g_data.currentLod );\r
2025                         strcat( outfilename, temp );\r
2026                 }\r
2027                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );\r
2028 \r
2029                 // free memory\r
2030                 for ( i = 0; i < numSurfaces; i++ )\r
2031                 {\r
2032                         if ( surfaceAnimations[i].frames )\r
2033                         {\r
2034                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )\r
2035                                 {\r
2036                                         free( surfaceAnimations[i].frames[j].triangles );\r
2037                                 }\r
2038                                 free( surfaceAnimations[i].frames );\r
2039                                 surfaceAnimations[i].frames = 0;\r
2040                         }\r
2041                 }\r
2042         }\r
2043         else if ( type == TYPE_WEAPON )\r
2044         {\r
2045                 // get the weapon surfaces\r
2046                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "w_", -1, -1, -1 );\r
2047                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );\r
2048 \r
2049                 strcpy( outfilename, filename );\r
2050                 if ( strrchr( outfilename, '.' ) )\r
2051                         *( strrchr( outfilename, '.' ) + 1 ) = 0;\r
2052                 strcat( outfilename, "md3" );\r
2053                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );\r
2054 \r
2055                 // free memory\r
2056                 for ( i = 0; i < numSurfaces; i++ )\r
2057                 {\r
2058                         if ( surfaceAnimations[i].frames )\r
2059                         {\r
2060                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )\r
2061                                 {\r
2062                                         free( surfaceAnimations[i].frames[j].triangles );\r
2063                                 }\r
2064                                 free( surfaceAnimations[i].frames );\r
2065                                 surfaceAnimations[i].frames = 0;\r
2066                         }\r
2067                 }\r
2068 \r
2069                 // get the flash surfaces\r
2070                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "f_", -1, -1, -1 );\r
2071                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );\r
2072 \r
2073                 strcpy( outfilename, filename );\r
2074                 if ( strrchr( outfilename, '.' ) )\r
2075                         *strrchr( outfilename, '.' ) = 0;\r
2076                 strcat( outfilename, "_flash.md3" );\r
2077                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM );\r
2078 \r
2079                 // free memory\r
2080                 for ( i = 0; i < numSurfaces; i++ )\r
2081                 {\r
2082                         if ( surfaceAnimations[i].frames )\r
2083                         {\r
2084                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )\r
2085                                 {\r
2086                                         free( surfaceAnimations[i].frames[j].triangles );\r
2087                                 }\r
2088                                 free( surfaceAnimations[i].frames );\r
2089                                 surfaceAnimations[i].frames = 0;\r
2090                         }\r
2091                 }\r
2092         }\r
2093         else if ( type == TYPE_HAND )\r
2094         {\r
2095                 // get the hand tags\r
2096                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "tag_", -1, -1, -1 );\r
2097                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );\r
2098 \r
2099                 strcpy( outfilename, filename );\r
2100                 if ( strrchr( outfilename, '.' ) )\r
2101                         *strrchr( outfilename, '.' ) = 0;\r
2102                 strcat( outfilename, "_hand.md3" );\r
2103                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_HAND );\r
2104 \r
2105                 // free memory\r
2106                 for ( i = 0; i < numSurfaces; i++ )\r
2107                 {\r
2108                         if ( surfaceAnimations[i].frames )\r
2109                         {\r
2110                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )\r
2111                                 {\r
2112                                         free( surfaceAnimations[i].frames[j].triangles );\r
2113                                 }\r
2114                                 free( surfaceAnimations[i].frames );\r
2115                                 surfaceAnimations[i].frames = 0;\r
2116                         }\r
2117                 }\r
2118         }\r
2119         else\r
2120         {\r
2121                 Error( "Unknown type passed to ConvertASE()" );\r
2122         }\r
2123 \r
2124         g_data.currentLod = 0;\r
2125         g_data.lodBias = 0;\r
2126         g_data.maxHeadFrames = 0;\r
2127         g_data.maxUpperFrames = 0;\r
2128         g_data.lowerSkipFrameStart = 0;\r
2129         g_data.lowerSkipFrameEnd = 0;\r
2130         VectorCopy( vec3_origin, g_data.aseAdjust );\r
2131 \r
2132         // unload ASE from memory\r
2133         ASE_Free();\r
2134 }\r