2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 //=================================================================
27 static void OrderSurfaces( void );
28 static void LoadBase( const char *filename );
29 static int LoadModelFile( const char *filename, polyset_t **ppsets, int *pnumpolysets );
31 #define MAX_SURFACE_TRIS ( SHADER_MAX_INDEXES / 3 )
32 #define MAX_SURFACE_VERTS SHADER_MAX_VERTEXES
34 #define MD3_TYPE_UNKNOWN 0
35 #define MD3_TYPE_BASE3DS 1
36 #define MD3_TYPE_SPRITE 2
37 #define MD3_TYPE_ASE 3
39 #define MAX_ANIM_FRAMES 512
40 #define MAX_ANIM_SURFACES 32
50 polyset_t *surfaces[MAX_ANIM_SURFACES];
52 } ObjectAnimationFrame_t;
66 //================================================================
71 md3Shader_t shaders[MD3_MAX_SHADERS];
72 // all verts (xyz_normal)
73 float *verts[MD3_MAX_FRAMES];
75 baseTriangle_t baseTriangles[MD3_MAX_TRIANGLES];
77 // the triangles will be sorted so that they form long generalized tristrips
78 int orderedTriangles[MD3_MAX_TRIANGLES][3];
79 int lodTriangles[MD3_MAX_TRIANGLES][3];
80 baseVertex_t baseVertexes[MD3_MAX_VERTS];
86 int skinwidth, skinheight;
88 md3SurfaceData_t surfData[MD3_MAX_SURFACES];
90 md3Tag_t tags[MD3_MAX_FRAMES][MD3_MAX_TAGS];
91 md3Frame_t frames[MD3_MAX_FRAMES];
94 float scale_up; // set by $scale
95 vec3_t adjust; // set by $origin
97 int fixedwidth, fixedheight; // set by $skinsize
101 int lowerSkipFrameStart, lowerSkipFrameEnd;
107 int type; // MD3_TYPE_BASE, MD3_TYPE_OLDBASE, MD3_TYPE_ASE, or MD3_TYPE_SPRITE
113 // the command list holds counts, the count * 3 xyz, st, normal indexes
114 // that are valid for every frame
116 char g_modelname[1024];
118 //==============================================================
125 void ClearModel( void ){
128 g_data.type = MD3_TYPE_UNKNOWN;
130 for ( i = 0; i < MD3_MAX_SURFACES; i++ )
132 memset( &g_data.surfData[i].header, 0, sizeof( g_data.surfData[i].header ) );
133 memset( &g_data.surfData[i].shaders, 0, sizeof( g_data.surfData[i].shaders ) );
134 memset( &g_data.surfData[i].verts, 0, sizeof( g_data.surfData[i].verts ) );
137 memset( g_data.tags, 0, sizeof( g_data.tags ) );
139 for ( i = 0; i < g_data.model.numSurfaces; i++ )
143 for ( j = 0; j < g_data.surfData[i].header.numShaders; j++ )
145 memset( &g_data.surfData[i].shaders[j], 0, sizeof( g_data.surfData[i].shaders[j] ) );
148 memset( &g_data.model, 0, sizeof( g_data.model ) );
149 memset( g_cddir, 0, sizeof( g_cddir ) );
152 g_data.scale_up = 1.0;
153 memset( &g_data.model, 0, sizeof( g_data.model ) );
154 VectorCopy( vec3_origin, g_data.adjust );
155 g_data.fixedwidth = g_data.fixedheight = 0;
156 g_skipmodel = qfalse;
160 ** void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData )
162 ** This routine assumes that the file position has been adjusted
163 ** properly prior to entry to point at the beginning of the surface.
165 ** Since surface header information is completely relative, we can't
166 ** just randomly seek to an arbitrary surface location right now. Is
167 ** this something we should add?
169 void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData ){
170 md3Surface_t *pSurf = &pSurfData->header;
171 md3Shader_t *pShader = pSurfData->shaders;
172 baseVertex_t *pBaseVertex = pSurfData->baseVertexes;
173 float **verts = pSurfData->verts;
175 short xyznormals[MD3_MAX_VERTS][4];
177 float base_st[MD3_MAX_VERTS][2];
178 md3Surface_t surftemp;
182 if ( strstr( pSurf->name, "tag_" ) == pSurf->name ) {
187 // write out the header
190 surftemp.ident = LittleLong( MD3_IDENT );
191 surftemp.flags = LittleLong( pSurf->flags );
192 surftemp.numFrames = LittleLong( pSurf->numFrames );
193 surftemp.numShaders = LittleLong( pSurf->numShaders );
195 surftemp.ofsShaders = LittleLong( pSurf->ofsShaders );
197 surftemp.ofsTriangles = LittleLong( pSurf->ofsTriangles );
198 surftemp.numTriangles = LittleLong( pSurf->numTriangles );
200 surftemp.ofsSt = LittleLong( pSurf->ofsSt );
201 surftemp.ofsXyzNormals = LittleLong( pSurf->ofsXyzNormals );
202 surftemp.ofsEnd = LittleLong( pSurf->ofsEnd );
204 SafeWrite( modelouthandle, &surftemp, sizeof( surftemp ) );
207 printf( "surface '%s'\n", pSurf->name );
208 printf( "...num shaders: %d\n", pSurf->numShaders );
214 for ( i = 0; i < pSurf->numShaders; i++ )
216 md3Shader_t shadertemp;
219 printf( "......'%s'\n", pShader[i].name );
222 shadertemp = pShader[i];
223 shadertemp.shaderIndex = LittleLong( shadertemp.shaderIndex );
224 SafeWrite( modelouthandle, &shadertemp, sizeof( shadertemp ) );
228 // write out the triangles
230 for ( i = 0 ; i < pSurf->numTriangles ; i++ )
232 for ( j = 0 ; j < 3 ; j++ )
234 int ivalue = LittleLong( pSurfData->orderedTriangles[i][j] );
235 pSurfData->orderedTriangles[i][j] = ivalue;
239 SafeWrite( modelouthandle, pSurfData->orderedTriangles, pSurf->numTriangles * sizeof( g_data.surfData[0].orderedTriangles[0] ) );
242 printf( "\n...num verts: %d\n", pSurf->numVerts );
243 printf( "...TEX COORDINATES\n" );
247 // write out the texture coordinates
249 for ( i = 0; i < pSurf->numVerts ; i++ ) {
250 base_st[i][0] = LittleFloat( pBaseVertex[i].st[0] );
251 base_st[i][1] = LittleFloat( pBaseVertex[i].st[1] );
253 printf( "......%d: %f,%f\n", i, base_st[i][0], base_st[i][1] );
256 SafeWrite( modelouthandle, base_st, pSurf->numVerts * sizeof( base_st[0] ) );
259 // write the xyz_normal
262 printf( "...XYZNORMALS\n" );
264 for ( f = 0; f < g_data.model.numFrames; f++ )
266 for ( j = 0 ; j < pSurf->numVerts; j++ )
270 for ( k = 0 ; k < 3 ; k++ )
272 value = ( short ) ( verts[f][j * 6 + k] / MD3_XYZ_SCALE );
273 xyznormals[j][k] = LittleShort( value );
275 NormalToLatLong( &verts[f][j * 6 + 3], (byte *)&xyznormals[j][3] );
277 SafeWrite( modelouthandle, xyznormals, pSurf->numVerts * sizeof( short ) * 4 );
282 ** void WriteModelFile( FILE *modelouthandle )
285 ** header sizeof( md3Header_t )
286 ** frames sizeof( md3Frame_t ) * numFrames
287 ** tags sizeof( md3Tag_t ) * numFrames * numTags
288 ** surfaces surfaceSum
290 void WriteModelFile( FILE *modelouthandle ){
293 md3Header_t modeltemp;
295 int numRealSurfaces = 0;
296 int numFrames = g_data.model.numFrames;
298 // compute offsets for all surfaces, sum their total size
299 for ( i = 0; i < g_data.model.numSurfaces; i++ )
301 if ( strstr( g_data.surfData[i].header.name, "tag_" ) != g_data.surfData[i].header.name ) {
302 md3Surface_t *psurf = &g_data.surfData[i].header;
304 if ( psurf->numTriangles == 0 || psurf->numVerts == 0 ) {
309 // the triangle and vertex split threshold is controlled by a parameter
310 // to $base, a la $base blah.3ds 1900, where "1900" determines the number
311 // of triangles to split on
313 else if ( psurf->numVerts > MAX_SURFACE_VERTS ) {
314 Error( "too many vertices\n" );
317 psurf->numFrames = numFrames;
319 psurf->ofsShaders = sizeof( md3Surface_t );
321 if ( psurf->numTriangles > MAX_SURFACE_TRIS ) {
322 Error( "too many faces\n" );
325 psurf->ofsTriangles = psurf->ofsShaders + psurf->numShaders * sizeof( md3Shader_t );
327 psurf->ofsSt = psurf->ofsTriangles + psurf->numTriangles * sizeof( md3Triangle_t );
328 psurf->ofsXyzNormals = psurf->ofsSt + psurf->numVerts * sizeof( md3St_t );
329 psurf->ofsEnd = psurf->ofsXyzNormals + psurf->numFrames * psurf->numVerts * ( sizeof( short ) * 4 );
331 surfaceSum += psurf->ofsEnd;
337 g_data.model.ident = MD3_IDENT;
338 g_data.model.version = MD3_VERSION;
340 g_data.model.ofsFrames = sizeof( md3Header_t );
341 g_data.model.ofsTags = g_data.model.ofsFrames + numFrames * sizeof( md3Frame_t );
342 g_data.model.ofsSurfaces = g_data.model.ofsTags + numFrames * g_data.model.numTags * sizeof( md3Tag_t );
343 g_data.model.ofsEnd = g_data.model.ofsSurfaces + surfaceSum;
346 // write out the model header
348 modeltemp = g_data.model;
349 modeltemp.ident = LittleLong( modeltemp.ident );
350 modeltemp.version = LittleLong( modeltemp.version );
351 modeltemp.numFrames = LittleLong( modeltemp.numFrames );
352 modeltemp.numTags = LittleLong( modeltemp.numTags );
353 modeltemp.numSurfaces = LittleLong( numRealSurfaces );
354 modeltemp.ofsFrames = LittleLong( modeltemp.ofsFrames );
355 modeltemp.ofsTags = LittleLong( modeltemp.ofsTags );
356 modeltemp.ofsSurfaces = LittleLong( modeltemp.ofsSurfaces );
357 modeltemp.ofsEnd = LittleLong( modeltemp.ofsEnd );
359 SafeWrite( modelouthandle, &modeltemp, sizeof( modeltemp ) );
362 // write out the frames
364 for ( i = 0 ; i < numFrames ; i++ )
370 // compute localOrigin and radius
372 g_data.frames[i].localOrigin[0] =
373 g_data.frames[i].localOrigin[1] =
374 g_data.frames[i].localOrigin[2] = 0;
376 for ( j = 0; j < 8; j++ )
378 tmpVec[0] = g_data.frames[i].bounds[( j & 1 ) != 0][0];
379 tmpVec[1] = g_data.frames[i].bounds[( j & 2 ) != 0][1];
380 tmpVec[2] = g_data.frames[i].bounds[( j & 4 ) != 0][2];
382 if ( VectorLength( tmpVec ) > maxRadius ) {
383 maxRadius = VectorLength( tmpVec );
387 g_data.frames[i].radius = LittleFloat( maxRadius );
390 for ( j = 0 ; j < 3 ; j++ ) {
391 g_data.frames[i].bounds[0][j] = LittleFloat( g_data.frames[i].bounds[0][j] );
392 g_data.frames[i].bounds[1][j] = LittleFloat( g_data.frames[i].bounds[1][j] );
393 g_data.frames[i].localOrigin[j] = LittleFloat( g_data.frames[i].localOrigin[j] );
396 fseek( modelouthandle, g_data.model.ofsFrames, SEEK_SET );
397 SafeWrite( modelouthandle, g_data.frames, numFrames * sizeof( g_data.frames[0] ) );
400 // write out the tags
402 fseek( modelouthandle, g_data.model.ofsTags, SEEK_SET );
403 for ( f = 0 ; f < g_data.model.numFrames; f++ )
407 for ( t = 0; t < g_data.model.numTags; t++ )
409 g_data.tags[f][t].origin[0] = LittleFloat( g_data.tags[f][t].origin[0] );
410 g_data.tags[f][t].origin[1] = LittleFloat( g_data.tags[f][t].origin[1] );
411 g_data.tags[f][t].origin[2] = LittleFloat( g_data.tags[f][t].origin[2] );
413 for ( j = 0 ; j < 3 ; j++ )
415 g_data.tags[f][t].axis[0][j] = LittleFloat( g_data.tags[f][t].axis[0][j] );
416 g_data.tags[f][t].axis[1][j] = LittleFloat( g_data.tags[f][t].axis[1][j] );
417 g_data.tags[f][t].axis[2][j] = LittleFloat( g_data.tags[f][t].axis[2][j] );
420 SafeWrite( modelouthandle, g_data.tags[f], g_data.model.numTags * sizeof( md3Tag_t ) );
424 // write out the surfaces
426 fseek( modelouthandle, g_data.model.ofsSurfaces, SEEK_SET );
427 for ( i = 0; i < g_data.model.numSurfaces; i++ )
429 WriteModelSurface( modelouthandle, &g_data.surfData[i] );
439 void FinishModel( int type ){
440 FILE *modelouthandle;
441 FILE *defaultSkinHandle;
445 if ( !g_data.model.numFrames ) {
450 // build generalized triangle strips
454 if ( type == TYPE_PLAYER ) {
455 sprintf( name, "%s%s", writedir, g_modelname );
456 *strrchr( name, '.' ) = 0;
457 strcat( name, "_default.skin" );
459 defaultSkinHandle = fopen( name, "wt" );
460 for ( i = 0; i < g_data.model.numSurfaces; i++ )
462 fprintf( defaultSkinHandle, "%s,%s\n", g_data.surfData[i].header.name, g_data.surfData[i].shaders[0].name );
464 fclose( defaultSkinHandle );
467 sprintf( name, "%s%s", writedir, g_modelname );
470 // copy the model and its shaders to release directory tree
471 // if doing a release build
475 md3SurfaceData_t *pSurf;
477 ReleaseFile( g_modelname );
479 for ( i = 0; i < g_data.model.numSurfaces; i++ ) {
480 pSurf = &g_data.surfData[i];
481 for ( j = 0; j < g_data.model.numSkins; j++ ) {
482 ReleaseShader( pSurf->shaders[j].name );
489 // write the model output file
491 printf( "saving to %s\n", name );
493 modelouthandle = SafeOpenWrite( name );
495 WriteModelFile( modelouthandle );
497 printf( "%4d surfaces\n", g_data.model.numSurfaces );
498 printf( "%4d frames\n", g_data.model.numFrames );
499 printf( "%4d tags\n", g_data.model.numTags );
500 printf( "file size: %d\n", (int)ftell( modelouthandle ) );
501 printf( "---------------------\n" );
503 fclose( modelouthandle );
509 ** Reorders triangles in all the surfaces.
511 static void OrderSurfaces( void ){
513 extern qboolean g_stripify;
515 // go through each surface and find best strip/fans possible
516 for ( s = 0; s < g_data.model.numSurfaces; s++ )
518 int mesh[MD3_MAX_TRIANGLES][3];
521 printf( "stripifying surface %d/%d with %d tris\n", s, g_data.model.numSurfaces, g_data.surfData[s].header.numTriangles );
523 for ( i = 0; i < g_data.surfData[s].header.numTriangles; i++ )
525 mesh[i][0] = g_data.surfData[s].lodTriangles[i][0];
526 mesh[i][1] = g_data.surfData[s].lodTriangles[i][1];
527 mesh[i][2] = g_data.surfData[s].lodTriangles[i][2];
531 OrderMesh( mesh, // input
532 g_data.surfData[s].orderedTriangles, // output
533 g_data.surfData[s].header.numTriangles );
537 memcpy( g_data.surfData[s].orderedTriangles, mesh, sizeof( int ) * 3 * g_data.surfData[s].header.numTriangles );
544 ===============================================================
548 ===============================================================
552 CopyTrianglesToBaseTriangles
556 static void CopyTrianglesToBaseTriangles( triangle_t *ptri, int numtri, baseTriangle_t *bTri ){
558 // int width, height, iwidth, iheight, swidth;
559 // float s_scale, t_scale;
561 // vec3_t mins, maxs;
565 // find bounds of all the verts on the base frame
567 ClearBounds (mins, maxs);
569 for (i=0 ; i<numtri ; i++)
570 for (j=0 ; j<3 ; j++)
571 AddPointToBounds (ptri[i].verts[j], mins, maxs);
573 for (i=0 ; i<3 ; i++)
575 mins[i] = floor(mins[i]);
576 maxs[i] = ceil(maxs[i]);
579 width = maxs[0] - mins[0];
580 height = maxs[2] - mins[2];
582 if (!g_data.fixedwidth)
585 if (width*scale >= 150)
586 scale = 150.0 / width;
587 if (height*scale >= 190)
588 scale = 190.0 / height;
590 s_scale = t_scale = scale;
592 iwidth = ceil(width*s_scale);
593 iheight = ceil(height*t_scale);
600 iwidth = g_data.fixedwidth / 2;
601 iheight = g_data.fixedheight;
603 s_scale = (float)(iwidth-4) / width;
604 t_scale = (float)(iheight-4) / height;
607 // make the width a multiple of 4; some hardware requires this, and it ensures
608 // dword alignment for each scan
610 g_data.skinwidth = (swidth + 3) & ~3;
611 g_data.skinheight = iheight;
614 for ( i = 0; i < numtri ; i++, ptri++, bTri++ )
618 for ( j = 0 ; j < 3 ; j++ )
620 VectorCopy( ptri->verts[j], bTri->v[j].xyz );
621 VectorCopy( ptri->normals[j], bTri->v[j].normal );
623 bTri->v[j].st[0] = ptri->texcoords[j][0];
624 bTri->v[j].st[1] = ptri->texcoords[j][1];
629 static void BuildBaseFrame( const char *filename, ObjectAnimationFrame_t *pOAF ){
630 baseTriangle_t *bTri;
634 // calculate the base triangles
635 for ( i = 0; i < g_data.model.numSurfaces; i++ )
637 CopyTrianglesToBaseTriangles( pOAF->surfaces[i]->triangles,
638 pOAF->surfaces[i]->numtriangles,
639 g_data.surfData[i].baseTriangles );
641 strcpy( g_data.surfData[i].header.name, pOAF->surfaces[i]->name );
643 g_data.surfData[i].header.numTriangles = pOAF->surfaces[i]->numtriangles;
644 g_data.surfData[i].header.numVerts = 0;
647 if ( strstr( filename, gamedir + 1 ) )
649 strcpy( shaderName, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
653 strcpy( shaderName, filename );
656 if ( strrchr( shaderName, '/' ) )
657 *( strrchr( shaderName, '/' ) + 1 ) = 0;
660 strcpy( shaderName, pOAF->surfaces[i]->materialname );
662 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, pOAF->surfaces[i]->materialname );
664 g_data.surfData[i].header.numShaders++;
668 // compute unique vertices for each polyset
670 for ( i = 0; i < g_data.model.numSurfaces; i++ )
674 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
676 bTri = &g_data.surfData[i].baseTriangles[t];
678 for ( j = 0 ; j < 3 ; j++ )
685 for ( k = 0; k < g_data.surfData[i].header.numVerts; k++ )
687 if ( ( g_data.surfData[i].baseVertexes[k].st[0] == bVert->st[0] ) &&
688 ( g_data.surfData[i].baseVertexes[k].st[1] == bVert->st[1] ) &&
689 ( VectorCompare( bVert->xyz, g_data.surfData[i].baseVertexes[k].xyz ) ) &&
690 ( VectorCompare( bVert->normal, g_data.surfData[i].baseVertexes[k].normal ) ) ) {
691 break; // this vertex is already in the base vertex list
695 if ( k == g_data.surfData[i].header.numVerts ) { // new index
696 g_data.surfData[i].baseVertexes[g_data.surfData[i].header.numVerts] = *bVert;
697 g_data.surfData[i].header.numVerts++;
702 g_data.surfData[i].lodTriangles[t][j] = k;
710 for ( i = 0; i < g_data.model.numSurfaces; i++ )
712 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) {
713 if ( pOAF->surfaces[i]->numtriangles != 1 ) {
714 Error( "tag polysets must consist of only one triangle" );
716 if ( strstr( filename, "_flash.md3" ) && !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) ) {
719 printf( "found tag '%s'\n", pOAF->surfaces[i]->name );
720 g_data.model.numTags++;
726 static int LoadModelFile( const char *filename, polyset_t **psets, int *numpolysets ){
729 const char *frameFile;
731 printf( "---------------------\n" );
732 if ( filename[1] != ':' ) {
733 frameFile = filename;
734 sprintf( file1, "%s/%s", g_cddir, frameFile );
738 strcpy( file1, filename );
741 time1 = FileTime( file1 );
743 Error( "%s doesn't exist", file1 );
747 // load the base triangles
749 *psets = Polyset_LoadSets( file1, numpolysets, g_data.maxSurfaceTris );
754 Polyset_SnapSets( *psets, *numpolysets );
756 if ( strstr( file1, ".3ds" ) || strstr( file1, ".3DS" ) ) {
757 return MD3_TYPE_BASE3DS;
760 Error( "Unknown model file type" );
762 return MD3_TYPE_UNKNOWN;
770 void Cmd_Base( void ){
774 sprintf( filename, "%s/%s", g_cddir, token );
775 LoadBase( filename );
778 static void LoadBase( const char *filename ){
782 ObjectAnimationFrame_t oaf;
784 // determine polyset splitting threshold
785 if ( TokenAvailable() ) {
787 g_data.maxSurfaceTris = atoi( token );
791 g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;
794 g_data.type = LoadModelFile( filename, &psets, &numpolysets );
796 Polyset_ComputeNormals( psets, numpolysets );
798 g_data.model.numSurfaces = numpolysets;
800 memset( &oaf, 0, sizeof( oaf ) );
802 for ( i = 0; i < numpolysets; i++ )
804 oaf.surfaces[i] = &psets[i];
805 oaf.numSurfaces = numpolysets;
808 BuildBaseFrame( filename, &oaf );
810 free( psets[0].triangles );
818 $spritebase xorg yorg width height
820 Generate a single square for the model
823 void Cmd_SpriteBase( void ){
824 float xl, yl, width, height;
826 g_data.type = MD3_TYPE_SPRITE;
833 width = atof( token );
835 height = atof( token );
837 // if (g_skipmodel || g_release || g_archive)
840 printf( "---------------------\n" );
842 g_data.surfData[0].verts[0] = ( float * ) calloc( 1, sizeof( float ) * 6 * 4 );
844 g_data.surfData[0].header.numVerts = 4;
846 g_data.surfData[0].verts[0][0 + 0] = 0;
847 g_data.surfData[0].verts[0][0 + 1] = -xl;
848 g_data.surfData[0].verts[0][0 + 2] = yl + height;
850 g_data.surfData[0].verts[0][0 + 3] = -1;
851 g_data.surfData[0].verts[0][0 + 4] = 0;
852 g_data.surfData[0].verts[0][0 + 5] = 0;
853 g_data.surfData[0].baseVertexes[0].st[0] = 0;
854 g_data.surfData[0].baseVertexes[0].st[1] = 0;
857 g_data.surfData[0].verts[0][6 + 0] = 0;
858 g_data.surfData[0].verts[0][6 + 1] = -xl - width;
859 g_data.surfData[0].verts[0][6 + 2] = yl + height;
861 g_data.surfData[0].verts[0][6 + 3] = -1;
862 g_data.surfData[0].verts[0][6 + 4] = 0;
863 g_data.surfData[0].verts[0][6 + 5] = 0;
864 g_data.surfData[0].baseVertexes[1].st[0] = 1;
865 g_data.surfData[0].baseVertexes[1].st[1] = 0;
868 g_data.surfData[0].verts[0][12 + 0] = 0;
869 g_data.surfData[0].verts[0][12 + 1] = -xl - width;
870 g_data.surfData[0].verts[0][12 + 2] = yl;
872 g_data.surfData[0].verts[0][12 + 3] = -1;
873 g_data.surfData[0].verts[0][12 + 4] = 0;
874 g_data.surfData[0].verts[0][12 + 5] = 0;
875 g_data.surfData[0].baseVertexes[2].st[0] = 1;
876 g_data.surfData[0].baseVertexes[2].st[1] = 1;
879 g_data.surfData[0].verts[0][18 + 0] = 0;
880 g_data.surfData[0].verts[0][18 + 1] = -xl;
881 g_data.surfData[0].verts[0][18 + 2] = yl;
883 g_data.surfData[0].verts[0][18 + 3] = -1;
884 g_data.surfData[0].verts[0][18 + 4] = 0;
885 g_data.surfData[0].verts[0][18 + 5] = 0;
886 g_data.surfData[0].baseVertexes[3].st[0] = 0;
887 g_data.surfData[0].baseVertexes[3].st[1] = 1;
889 g_data.surfData[0].lodTriangles[0][0] = 0;
890 g_data.surfData[0].lodTriangles[0][1] = 1;
891 g_data.surfData[0].lodTriangles[0][2] = 2;
893 g_data.surfData[0].lodTriangles[1][0] = 2;
894 g_data.surfData[0].lodTriangles[1][1] = 3;
895 g_data.surfData[0].lodTriangles[1][2] = 0;
897 g_data.model.numSurfaces = 1;
899 g_data.surfData[0].header.numTriangles = 2;
900 g_data.surfData[0].header.numVerts = 4;
902 g_data.model.numFrames = 1;
906 ===========================================================================
910 ===========================================================================
918 void GrabFrame( const char *frame ){
925 const char *framefile;
927 qboolean parentTagExists = qfalse;
932 // the frame 'run1' will be looked for as either
933 // run.1 or run1.tri, so the new alias sequence save
934 // feature an be used
935 if ( frame[1] != ':' ) {
936 // framefile = FindFrameFile (frame);
938 sprintf( file1, "%s/%s",g_cddir, framefile );
942 strcpy( file1, frame );
944 printf( "grabbing %s\n", file1 );
946 if ( g_data.model.numFrames >= MD3_MAX_FRAMES ) {
947 Error( "model.numFrames >= MD3_MAX_FRAMES" );
949 fr = &g_data.frames[g_data.model.numFrames];
951 strcpy( fr->name, frame );
953 psets = Polyset_LoadSets( file1, &numpolysets, g_data.maxSurfaceTris );
958 Polyset_SnapSets( psets, numpolysets );
961 // compute vertex normals
963 Polyset_ComputeNormals( psets, numpolysets );
966 // flip everything to compensate for the alias coordinate system
967 // and perform global scale and adjust
969 for ( i = 0; i < g_data.model.numSurfaces; i++ )
971 triangle_t *ptri = psets[i].triangles;
974 for ( t = 0; t < psets[i].numtriangles; t++ )
977 for ( j = 0; j < 3; j++ )
981 for ( k = 0 ; k < 3 ; k++ ) {
982 ptri[t].verts[j][k] = ptri[t].verts[j][k] * g_data.scale_up +
985 if ( ptri[t].verts[j][k] > 1023 ||
986 ptri[t].verts[j][k] < -1023 ) {
987 Error( "Model extents too large" );
995 // find and count tags, locate parent tag
997 for ( i = 0; i < numpolysets; i++ )
999 if ( strstr( psets[i].name, "tag_" ) == psets[i].name ) {
1000 if ( strstr( psets[i].name, "tag_parent" ) == psets[i].name ) {
1001 if ( strstr( psets[i].name, "tag_parent" ) ) {
1004 if ( parentTagExists ) {
1005 Error( "Multiple parent tags not allowed" );
1008 memcpy( tri[0], psets[i].triangles[0].verts[0], sizeof( float ) * 3 );
1009 memcpy( tri[1], psets[i].triangles[0].verts[1], sizeof( float ) * 3 );
1010 memcpy( tri[2], psets[i].triangles[0].verts[2], sizeof( float ) * 3 );
1012 MD3_ComputeTagFromTri( &tagParent, tri );
1013 strcpy( tagParent.name, psets[i].name );
1014 g_data.tags[g_data.model.numFrames][numtags] = tagParent;
1015 parentTagExists = qtrue;
1022 if ( strcmp( psets[i].name, g_data.surfData[i].header.name ) ) {
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 );
1027 if ( numtags != g_data.model.numTags ) {
1028 Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
1031 if ( numpolysets != g_data.model.numSurfaces ) {
1032 Error( "mismatched number of surfaces in frame(%d) vs. base(%d)", numpolysets - numtags, g_data.model.numSurfaces );
1036 // prepare to accumulate bounds and normals
1038 ClearBounds( fr->bounds[0], fr->bounds[1] );
1041 // store the frame's vertices in the same order as the base. This assumes the
1042 // triangles and vertices in this frame are in exactly the same order as in the
1045 for ( i = 0, tagcount = 0; i < numpolysets; i++ )
1048 triangle_t *pTris = psets[i].triangles;
1050 strcpy( g_data.surfData[i].header.name, psets[i].name );
1053 // parent tag adjust
1055 if ( parentTagExists ) {
1056 for ( t = 0; t < psets[i].numtriangles; t++ )
1058 for ( j = 0; j < 3 ; j++ )
1062 VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );
1064 pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );
1065 pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );
1066 pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );
1068 VectorCopy( pTris[t].normals[j], tmp );
1069 pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );
1070 pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );
1071 pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );
1079 if ( strstr( psets[i].name, "tag_" ) == psets[i].name ) {
1080 md3Tag_t *pTag = &g_data.tags[g_data.model.numFrames][tagcount];
1083 strcpy( pTag->name, psets[i].name );
1085 memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );
1086 memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );
1087 memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );
1089 MD3_ComputeTagFromTri( pTag, tri );
1094 if ( g_data.surfData[i].verts[g_data.model.numFrames] ) {
1095 free( g_data.surfData[i].verts[g_data.model.numFrames] );
1097 frameXyz = g_data.surfData[i].verts[g_data.model.numFrames] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
1098 frameNormals = frameXyz + 3;
1100 for ( t = 0; t < psets[i].numtriangles; t++ )
1102 for ( j = 0; j < 3 ; j++ )
1106 index = g_data.surfData[i].baseTriangles[t].v[j].index;
1107 frameXyz[index * 6 + 0] = pTris[t].verts[j][0];
1108 frameXyz[index * 6 + 1] = pTris[t].verts[j][1];
1109 frameXyz[index * 6 + 2] = pTris[t].verts[j][2];
1110 frameNormals[index * 6 + 0] = pTris[t].normals[j][0];
1111 frameNormals[index * 6 + 1] = pTris[t].normals[j][1];
1112 frameNormals[index * 6 + 2] = pTris[t].normals[j][2];
1113 AddPointToBounds( &frameXyz[index * 6], fr->bounds[0], fr->bounds[1] );
1119 g_data.model.numFrames++;
1121 // only free the first triangle array, all of the psets in this array share the
1122 // same triangle pool!!!
1123 // free( psets[0].triangles );
1127 //===========================================================================
1136 void Cmd_Frame( void ){
1137 while ( TokenAvailable() )
1140 if ( g_skipmodel ) {
1143 if ( g_release || g_archive ) {
1144 g_data.model.numFrames = 1; // don't skip the writeout
1159 void SkinFrom3DS( const char *filename ){
1165 _3DS_LoadPolysets( filename, &psets, &numPolysets, g_verbose );
1167 for ( i = 0; i < numPolysets; i++ )
1170 if ( strstr( filename, gamedir + 1 ) )
1172 strcpy( name, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
1176 strcpy( name, filename );
1179 if ( strrchr( name, '/' ) )
1180 *( strrchr( name, '/' ) + 1 ) = 0;
1182 strcpy( name, psets[i].materialname );
1183 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, name );
1185 g_data.surfData[i].header.numShaders++;
1188 free( psets[0].triangles );
1192 void Cmd_Skin( void ){
1193 char skinfile[1024];
1195 if ( g_data.type == MD3_TYPE_BASE3DS ) {
1198 sprintf( skinfile, "%s/%s", g_cddir, token );
1200 if ( strstr( token, ".3ds" ) || strstr( token, ".3DS" ) ) {
1201 SkinFrom3DS( skinfile );
1205 Error( "Unknown file format for $skin '%s'\n", skinfile );
1210 Error( "invalid model type while processing $skin" );
1213 g_data.model.numSkins++;
1221 This routine is also called for $oldskin
1224 void Cmd_SpriteShader(){
1226 strcpy( g_data.surfData[0].shaders[g_data.surfData[0].header.numShaders].name, token );
1227 g_data.surfData[0].header.numShaders++;
1228 g_data.model.numSkins++;
1236 void Cmd_Origin( void ){
1237 // rotate points into frame of reference so model points down the
1239 // FIXME: use alias native coordinate system
1241 g_data.adjust[1] = -atof( token );
1244 g_data.adjust[0] = atof( token );
1247 g_data.adjust[2] = -atof( token );
1256 void Cmd_ScaleUp( void ){
1258 g_data.scale_up = atof( token );
1259 if ( g_skipmodel || g_release || g_archive ) {
1263 printf( "Scale up: %f\n", g_data.scale_up );
1271 Set a skin size other than the default
1275 void Cmd_Skinsize( void ){
1277 g_data.fixedwidth = atoi( token );
1279 g_data.fixedheight = atoi( token );
1286 Begin creating a model of the given name
1289 void Cmd_Modelname( void ){
1290 FinishModel( TYPE_UNKNOWN );
1294 strcpy( g_modelname, token );
1295 StripExtension( g_modelname );
1296 strcat( g_modelname, ".md3" );
1297 strcpy( g_data.model.name, g_modelname );
1305 void Cmd_Cd( void ){
1307 Error( "$cd command without a $modelname" );
1312 sprintf( g_cddir, "%s%s", gamedir, token );
1314 // if -only was specified and this cd doesn't match,
1315 // skip the model (you only need to match leading chars,
1316 // so you could regrab all monsters with -only models/monsters)
1320 if ( strncmp( token, g_only, strlen( g_only ) ) ) {
1321 g_skipmodel = qtrue;
1322 printf( "skipping %s\n", token );
1326 void Convert3DStoMD3( const char *file ){
1329 SkinFrom3DS( file );
1331 strcpy( g_data.model.name, g_modelname );
1333 FinishModel( TYPE_UNKNOWN );
1340 void Cmd_3DSConvert(){
1343 FinishModel( TYPE_UNKNOWN );
1348 sprintf( file, "%s%s", gamedir, token );
1349 strcpy( g_modelname, token );
1350 if ( strrchr( g_modelname, '.' ) ) {
1351 *strrchr( g_modelname, '.' ) = 0;
1353 strcat( g_modelname, ".md3" );
1355 if ( FileTime( file ) == -1 ) {
1356 Error( "%s doesn't exist", file );
1359 if ( TokenAvailable() ) {
1361 g_data.scale_up = atof( token );
1364 Convert3DStoMD3( file );
1367 static void ConvertASE( const char *filename, int type, qboolean grabAnims );
1372 void Cmd_ASEConvert( qboolean grabAnims ){
1373 char filename[1024];
1374 int type = TYPE_ITEM;
1376 FinishModel( TYPE_UNKNOWN );
1380 sprintf( filename, "%s%s", gamedir, token );
1382 strcpy( g_modelname, token );
1383 StripExtension( g_modelname );
1384 strcat( g_modelname, ".md3" );
1385 strcpy( g_data.model.name, g_modelname );
1387 if ( !strstr( filename, ".ase" ) && !strstr( filename, ".ASE" ) ) {
1388 strcat( filename, ".ASE" );
1391 g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;
1393 while ( TokenAvailable() )
1396 if ( !strcmp( token, "-origin" ) ) {
1397 if ( !TokenAvailable() ) {
1398 Error( "missing parameter for -origin" );
1401 g_data.aseAdjust[1] = -atof( token );
1403 if ( !TokenAvailable() ) {
1404 Error( "missing parameter for -origin" );
1407 g_data.aseAdjust[0] = atof( token );
1409 if ( !TokenAvailable() ) {
1410 Error( "missing parameter for -origin" );
1413 g_data.aseAdjust[2] = -atof( token );
1415 else if ( !strcmp( token, "-lod" ) ) {
1416 if ( !TokenAvailable() ) {
1417 Error( "No parameter for -lod" );
1420 g_data.currentLod = atoi( token );
1421 if ( g_data.currentLod > MD3_MAX_LODS - 1 ) {
1422 Error( "-lod parameter too large! (%d)\n", g_data.currentLod );
1425 if ( !TokenAvailable() ) {
1426 Error( "No second parameter for -lod" );
1429 g_data.lodBias = atof( token );
1431 else if ( !strcmp( token, "-maxtris" ) ) {
1432 if ( !TokenAvailable() ) {
1433 Error( "No parameter for -maxtris" );
1436 g_data.maxSurfaceTris = atoi( token );
1438 else if ( !strcmp( token, "-playerparms" ) ) {
1439 if ( !TokenAvailable() ) {
1440 Error( "missing skip start parameter for -playerparms" );
1443 g_data.lowerSkipFrameStart = atoi( token );
1446 if ( !TokenAvailable() ) {
1447 Error( "missing skip end parameter for -playerparms" );
1450 g_data.lowerSkipFrameEnd = atoi( token );
1453 if ( !TokenAvailable() ) {
1454 Error( "missing upper parameter for -playerparms" );
1457 g_data.maxUpperFrames = atoi( token );
1459 g_data.lowerSkipFrameEnd = g_data.maxUpperFrames - 1;
1462 if ( !TokenAvailable() ) {
1463 Error( "missing head parameter for -playerparms" );
1466 g_data.maxHeadFrames = atoi( token );
1468 g_data.maxHeadFrames = 1;
1470 if ( type != TYPE_ITEM ) {
1471 Error( "invalid argument" );
1476 else if ( !strcmp( token, "-weapon" ) ) {
1477 if ( type != TYPE_ITEM ) {
1478 Error( "invalid argument" );
1485 g_data.type = MD3_TYPE_ASE;
1487 if ( type == TYPE_WEAPON && grabAnims ) {
1488 Error( "can't grab anims with weapon models" );
1490 if ( type == TYPE_PLAYER && !grabAnims ) {
1491 Error( "player models must be converted with $aseanimconvert" );
1494 if ( type == TYPE_WEAPON ) {
1495 ConvertASE( filename, type, qfalse );
1496 ConvertASE( filename, TYPE_HAND, qtrue );
1500 ConvertASE( filename, type, grabAnims );
1504 static int GetSurfaceAnimations( SurfaceAnimation_t sanims[MAX_ANIM_SURFACES],
1510 int numValidSurfaces;
1514 if ( ( numSurfaces = ASE_GetNumSurfaces() ) > MAX_ANIM_SURFACES ) {
1515 Error( "Too many surfaces in ASE" );
1518 for ( numValidSurfaces = 0, i = 0; i < numSurfaces; i++ )
1520 polyset_t *splitSets;
1522 const char *surfaceName = ASE_GetSurfaceName( i );
1524 if ( !surfaceName ) {
1526 // Error( "Missing animation frames in model" );
1529 if ( strstr( surfaceName, "tag_" ) ||
1530 !strcmp( part, "any" ) ||
1531 ( strstr( surfaceName, part ) == surfaceName ) ) {
1533 // skip this if it's an inappropriate tag
1534 if ( strcmp( part, "any" ) ) {
1535 // ignore non-"tag_head" tags if this is the head
1536 if ( !strcmp( part, "h_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_head" ) ) {
1539 // ignore "tag_head" if this is the legs
1540 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_head" ) ) {
1543 // ignore "tag_weapon" if this is the legs
1544 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_weapon" ) ) {
1549 if ( ( sanims[numValidSurfaces].frames = ASE_GetSurfaceAnimation( i, &sanims[numValidSurfaces].numFrames, skipFrameStart, skipFrameEnd, maxFrames ) ) != 0 ) {
1550 splitSets = Polyset_SplitSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames, &numNewFrames, g_data.maxSurfaceTris );
1552 if ( numFrames == -1 ) {
1553 numFrames = sanims[numValidSurfaces].numFrames;
1555 else if ( numFrames != sanims[numValidSurfaces].numFrames ) {
1556 Error( "Different number of animation frames on surfaces" );
1559 if ( sanims[numValidSurfaces].frames != splitSets ) {
1562 // free old data if we split the surfaces
1563 for ( j = 0; j < sanims[numValidSurfaces].numFrames; j++ )
1565 free( sanims[numValidSurfaces].frames[j].triangles );
1566 free( sanims[numValidSurfaces].frames );
1569 sanims[numValidSurfaces].frames = splitSets;
1570 sanims[numValidSurfaces].numFrames = numNewFrames;
1572 Polyset_SnapSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );
1573 Polyset_ComputeNormals( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );
1580 return numValidSurfaces;
1583 static int SurfaceOrderToFrameOrder( SurfaceAnimation_t sanims[], ObjectAnimationFrame_t oanims[], int numSurfaces ){
1588 ** we have the data here arranged in surface order, now we need to convert it to
1591 for ( i = 0, s = 0; i < numSurfaces; i++ )
1595 if ( sanims[i].frames ) {
1596 if ( numFrames == -1 ) {
1597 numFrames = sanims[i].numFrames;
1599 else if ( numFrames != sanims[i].numFrames ) {
1600 Error( "numFrames != sanims[i].numFrames (%d != %d)\n", numFrames, sanims[i].numFrames );
1603 for ( j = 0; j < sanims[i].numFrames; j++ )
1605 oanims[j].surfaces[s] = &sanims[i].frames[j];
1606 oanims[j].numSurfaces = numSurfaces;
1615 static void WriteMD3( const char *_filename, ObjectAnimationFrame_t oanims[], int numFrames ){
1616 char filename[1024];
1618 strcpy( filename, _filename );
1619 if ( strchr( filename, '.' ) ) {
1620 *strchr( filename, '.' ) = 0;
1622 strcat( filename, ".md3" );
1625 static void BuildAnimationFromOAFs( const char *filename, ObjectAnimationFrame_t oanims[], int numFrames, int type ){
1626 int f, i, j, tagcount;
1628 float *frameNormals;
1630 g_data.model.numSurfaces = oanims[0].numSurfaces;
1631 g_data.model.numFrames = numFrames;
1632 if ( g_data.model.numFrames < 0 ) {
1633 Error( "model.numFrames < 0" );
1635 if ( g_data.model.numFrames >= MD3_MAX_FRAMES ) {
1636 Error( "model.numFrames >= MD3_MAX_FRAMES" );
1640 BuildBaseFrame( filename, &oanims[0] );
1642 // build animation frames
1643 for ( f = 0; f < numFrames; f++ )
1645 ObjectAnimationFrame_t *pOAF = &oanims[f];
1646 qboolean parentTagExists = qfalse;
1651 fr = &g_data.frames[f];
1653 strcpy( fr->name, "(from ASE)" );
1655 // scale and adjust frame
1656 for ( i = 0; i < pOAF->numSurfaces; i++ )
1658 triangle_t *pTris = pOAF->surfaces[i]->triangles;
1661 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1663 for ( j = 0; j < 3; j++ )
1668 for ( k = 0 ; k < 3 ; k++ ) {
1669 pTris[t].verts[j][k] = pTris[t].verts[j][k] * g_data.scale_up +
1670 g_data.aseAdjust[k];
1672 if ( pTris[t].verts[j][k] > 1023 ||
1673 pTris[t].verts[j][k] < -1023 ) {
1674 Error( "Model extents too large" );
1682 // find and count tags, locate parent tag
1684 for ( i = 0; i < pOAF->numSurfaces; i++ )
1686 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) {
1687 // ignore parent tags when grabbing a weapon model and this is the flash portion
1688 if ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && strstr( filename, "_flash.md3" ) ) {
1691 else if ( !strstr( filename, "_hand.md3" ) && (
1692 ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && !strstr( filename, "_flash.md3" ) ) ||
1693 ( !strcmp( pOAF->surfaces[i]->name, "tag_torso" ) && ( strstr( filename, "upper_" ) || strstr( filename, "upper.md3" ) ) ) ||
1694 ( !strcmp( pOAF->surfaces[i]->name, "tag_head" ) && ( strstr( filename, "head.md3" ) || strstr( filename, "head_" ) ) ) ||
1695 ( !strcmp( pOAF->surfaces[i]->name, "tag_flash" ) && strstr( filename, "_flash.md3" ) ) ||
1696 ( !strcmp( pOAF->surfaces[i]->name, "tag_weapon" ) && type == TYPE_WEAPON ) ) ) {
1699 if ( parentTagExists ) {
1700 Error( "Multiple parent tags not allowed" );
1703 memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );
1704 memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );
1705 memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );
1707 MD3_ComputeTagFromTri( &tagParent, tri );
1708 strcpy( tagParent.name, "tag_parent" );
1709 g_data.tags[f][numtags] = tagParent;
1710 parentTagExists = qtrue;
1716 memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );
1717 memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );
1718 memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );
1720 MD3_ComputeTagFromTri( &g_data.tags[f][numtags], tri );
1721 strcpy( g_data.tags[f][numtags].name, pOAF->surfaces[i]->name );
1722 if ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) ) {
1723 *( strstr( g_data.tags[f][numtags].name, "tag_flash" ) + strlen( "tag_flash" ) ) = 0;
1730 if ( strcmp( pOAF->surfaces[i]->name, g_data.surfData[i].header.name ) ) {
1731 Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, pOAF->surfaces[i]->name, filename );
1735 if ( numtags != g_data.model.numTags ) {
1736 Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
1740 // prepare to accumulate bounds and normals
1742 ClearBounds( fr->bounds[0], fr->bounds[1] );
1745 // store the frame's vertices in the same order as the base. This assumes the
1746 // triangles and vertices in this frame are in exactly the same order as in the
1749 for ( i = 0, tagcount = 0; i < pOAF->numSurfaces; i++ )
1752 triangle_t *pTris = pOAF->surfaces[i]->triangles;
1755 // parent tag adjust
1757 if ( parentTagExists ) {
1758 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1760 for ( j = 0; j < 3 ; j++ )
1764 VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );
1766 pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );
1767 pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );
1768 pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );
1770 VectorCopy( pTris[t].normals[j], tmp );
1771 pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );
1772 pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );
1773 pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );
1781 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) {
1782 md3Tag_t *pTag = &g_data.tags[f][tagcount];
1785 strcpy( pTag->name, pOAF->surfaces[i]->name );
1787 memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );
1788 memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );
1789 memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );
1791 MD3_ComputeTagFromTri( pTag, tri );
1796 if ( g_data.surfData[i].verts[f] ) {
1797 free( g_data.surfData[i].verts[f] );
1799 frameXyz = g_data.surfData[i].verts[f] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
1800 frameNormals = frameXyz + 3;
1802 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1804 for ( j = 0; j < 3 ; j++ )
1808 index = g_data.surfData[i].baseTriangles[t].v[j].index;
1809 frameXyz[index * 6 + 0] = pTris[t].verts[j][0];
1810 frameXyz[index * 6 + 1] = pTris[t].verts[j][1];
1811 frameXyz[index * 6 + 2] = pTris[t].verts[j][2];
1812 frameNormals[index * 6 + 0] = pTris[t].normals[j][0];
1813 frameNormals[index * 6 + 1] = pTris[t].normals[j][1];
1814 frameNormals[index * 6 + 2] = pTris[t].normals[j][2];
1815 AddPointToBounds( &frameXyz[index * 6], fr->bounds[0], fr->bounds[1] );
1822 if ( strstr( filename, gamedir + 1 ) ) {
1823 strcpy( g_modelname, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
1827 strcpy( g_modelname, filename );
1830 FinishModel( type );
1834 static void ConvertASE( const char *filename, int type, qboolean grabAnims ){
1838 SurfaceAnimation_t surfaceAnimations[MAX_ANIM_SURFACES];
1839 ObjectAnimationFrame_t objectAnimationFrames[MAX_ANIM_FRAMES];
1840 char outfilename[1024];
1843 ** load ASE into memory
1845 ASE_Load( filename, g_verbose, grabAnims );
1850 if ( type == TYPE_ITEM ) {
1851 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "any", -1, -1, -1 );
1853 if ( numSurfaces <= 0 ) {
1854 Error( "numSurfaces <= 0" );
1857 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1859 if ( numFrames <= 0 ) {
1860 Error( "numFrames <= 0" );
1863 strcpy( outfilename, filename );
1864 if ( strrchr( outfilename, '.' ) ) {
1865 *( strrchr( outfilename, '.' ) + 1 ) = 0;
1867 strcat( outfilename, "md3" );
1868 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1871 for ( i = 0; i < numSurfaces; i++ )
1873 if ( surfaceAnimations[i].frames ) {
1874 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1876 free( surfaceAnimations[i].frames[j].triangles );
1878 free( surfaceAnimations[i].frames );
1879 surfaceAnimations[i].frames = 0;
1883 else if ( type == TYPE_PLAYER ) {
1884 qboolean tagTorso = qfalse;
1885 qboolean tagHead = qfalse;
1886 qboolean tagWeapon = qfalse;
1889 // verify that all necessary tags exist
1891 numSurfaces = ASE_GetNumSurfaces();
1892 for ( i = 0; i < numSurfaces; i++ )
1894 if ( !strcmp( ASE_GetSurfaceName( i ), "tag_head" ) ) {
1897 if ( !strcmp( ASE_GetSurfaceName( i ), "tag_torso" ) ) {
1900 if ( !strcmp( ASE_GetSurfaceName( i ), "tag_weapon" ) ) {
1906 // todo: was never being checked; should we error now that it is?
1907 // Error( "Missing tag_head!" );
1910 Error( "Missing tag_torso!" );
1913 Error( "Missing tag_weapon!" );
1916 // get all upper body surfaces
1917 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "u_", -1, -1, g_data.maxUpperFrames );
1918 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1919 strcpy( outfilename, filename );
1920 if ( strrchr( outfilename, '/' ) ) {
1921 *( strrchr( outfilename, '/' ) + 1 ) = 0;
1924 if ( g_data.currentLod == 0 ) {
1925 strcat( outfilename, "upper.md3" );
1931 sprintf( temp, "upper_%d.md3", g_data.currentLod );
1932 strcat( outfilename, temp );
1935 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1938 for ( i = 0; i < numSurfaces; i++ )
1940 if ( surfaceAnimations[i].frames ) {
1941 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1943 free( surfaceAnimations[i].frames[j].triangles );
1945 free( surfaceAnimations[i].frames );
1946 surfaceAnimations[i].frames = 0;
1950 // get lower body surfaces
1951 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "l_", g_data.lowerSkipFrameStart, g_data.lowerSkipFrameEnd, -1 );
1952 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1953 strcpy( outfilename, filename );
1954 if ( strrchr( outfilename, '/' ) ) {
1955 *( strrchr( outfilename, '/' ) + 1 ) = 0;
1958 if ( g_data.currentLod == 0 ) {
1959 strcat( outfilename, "lower.md3" );
1965 sprintf( temp, "lower_%d.md3", g_data.currentLod );
1966 strcat( outfilename, temp );
1968 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1971 for ( i = 0; i < numSurfaces; i++ )
1973 if ( surfaceAnimations[i].frames ) {
1974 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1976 free( surfaceAnimations[i].frames[j].triangles );
1978 free( surfaceAnimations[i].frames );
1979 surfaceAnimations[i].frames = 0;
1983 // get head surfaces
1984 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "h_", -1, -1, g_data.maxHeadFrames );
1985 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1986 strcpy( outfilename, filename );
1987 if ( strrchr( outfilename, '/' ) ) {
1988 *( strrchr( outfilename, '/' ) + 1 ) = 0;
1991 if ( g_data.currentLod == 0 ) {
1992 strcat( outfilename, "head.md3" );
1998 sprintf( temp, "head_%d.md3", g_data.currentLod );
1999 strcat( outfilename, temp );
2001 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
2004 for ( i = 0; i < numSurfaces; i++ )
2006 if ( surfaceAnimations[i].frames ) {
2007 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2009 free( surfaceAnimations[i].frames[j].triangles );
2011 free( surfaceAnimations[i].frames );
2012 surfaceAnimations[i].frames = 0;
2016 else if ( type == TYPE_WEAPON ) {
2017 // get the weapon surfaces
2018 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "w_", -1, -1, -1 );
2019 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2021 strcpy( outfilename, filename );
2022 if ( strrchr( outfilename, '.' ) ) {
2023 *( strrchr( outfilename, '.' ) + 1 ) = 0;
2025 strcat( outfilename, "md3" );
2026 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
2029 for ( i = 0; i < numSurfaces; i++ )
2031 if ( surfaceAnimations[i].frames ) {
2032 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2034 free( surfaceAnimations[i].frames[j].triangles );
2036 free( surfaceAnimations[i].frames );
2037 surfaceAnimations[i].frames = 0;
2041 // get the flash surfaces
2042 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "f_", -1, -1, -1 );
2043 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2045 strcpy( outfilename, filename );
2046 if ( strrchr( outfilename, '.' ) ) {
2047 *strrchr( outfilename, '.' ) = 0;
2049 strcat( outfilename, "_flash.md3" );
2050 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM );
2053 for ( i = 0; i < numSurfaces; i++ )
2055 if ( surfaceAnimations[i].frames ) {
2056 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2058 free( surfaceAnimations[i].frames[j].triangles );
2060 free( surfaceAnimations[i].frames );
2061 surfaceAnimations[i].frames = 0;
2065 else if ( type == TYPE_HAND ) {
2066 // get the hand tags
2067 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "tag_", -1, -1, -1 );
2068 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2070 strcpy( outfilename, filename );
2071 if ( strrchr( outfilename, '.' ) ) {
2072 *strrchr( outfilename, '.' ) = 0;
2074 strcat( outfilename, "_hand.md3" );
2075 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_HAND );
2078 for ( i = 0; i < numSurfaces; i++ )
2080 if ( surfaceAnimations[i].frames ) {
2081 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2083 free( surfaceAnimations[i].frames[j].triangles );
2085 free( surfaceAnimations[i].frames );
2086 surfaceAnimations[i].frames = 0;
2092 Error( "Unknown type passed to ConvertASE()" );
2095 g_data.currentLod = 0;
2097 g_data.maxHeadFrames = 0;
2098 g_data.maxUpperFrames = 0;
2099 g_data.lowerSkipFrameStart = 0;
2100 g_data.lowerSkipFrameEnd = 0;
2101 VectorCopy( vec3_origin, g_data.aseAdjust );
2103 // unload ASE from memory