2 Copyright (C) 1999-2006 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;
566 // find bounds of all the verts on the base frame
568 ClearBounds (mins, maxs);
570 for (i=0 ; i<numtri ; i++)
571 for (j=0 ; j<3 ; j++)
572 AddPointToBounds (ptri[i].verts[j], mins, maxs);
574 for (i=0 ; i<3 ; i++)
576 mins[i] = floor(mins[i]);
577 maxs[i] = ceil(maxs[i]);
580 width = maxs[0] - mins[0];
581 height = maxs[2] - mins[2];
583 if (!g_data.fixedwidth)
586 if (width*scale >= 150)
587 scale = 150.0 / width;
588 if (height*scale >= 190)
589 scale = 190.0 / height;
591 s_scale = t_scale = scale;
593 iwidth = ceil(width*s_scale);
594 iheight = ceil(height*t_scale);
601 iwidth = g_data.fixedwidth / 2;
602 iheight = g_data.fixedheight;
604 s_scale = (float)(iwidth-4) / width;
605 t_scale = (float)(iheight-4) / height;
608 // make the width a multiple of 4; some hardware requires this, and it ensures
609 // dword alignment for each scan
611 g_data.skinwidth = (swidth + 3) & ~3;
612 g_data.skinheight = iheight;
615 for ( i = 0; i < numtri ; i++, ptri++, bTri++ )
619 for ( j = 0 ; j < 3 ; j++ )
621 pbasevert = ptri->verts[j];
623 VectorCopy( ptri->verts[j], bTri->v[j].xyz );
624 VectorCopy( ptri->normals[j], bTri->v[j].normal );
626 bTri->v[j].st[0] = ptri->texcoords[j][0];
627 bTri->v[j].st[1] = ptri->texcoords[j][1];
632 static void BuildBaseFrame( const char *filename, ObjectAnimationFrame_t *pOAF ){
633 baseTriangle_t *bTri;
637 // calculate the base triangles
638 for ( i = 0; i < g_data.model.numSurfaces; i++ )
640 CopyTrianglesToBaseTriangles( pOAF->surfaces[i]->triangles,
641 pOAF->surfaces[i]->numtriangles,
642 g_data.surfData[i].baseTriangles );
644 strcpy( g_data.surfData[i].header.name, pOAF->surfaces[i]->name );
646 g_data.surfData[i].header.numTriangles = pOAF->surfaces[i]->numtriangles;
647 g_data.surfData[i].header.numVerts = 0;
650 if ( strstr( filename, gamedir + 1 ) )
652 strcpy( shaderName, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
656 strcpy( shaderName, filename );
659 if ( strrchr( shaderName, '/' ) )
660 *( strrchr( shaderName, '/' ) + 1 ) = 0;
663 strcpy( shaderName, pOAF->surfaces[i]->materialname );
665 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, pOAF->surfaces[i]->materialname );
667 g_data.surfData[i].header.numShaders++;
671 // compute unique vertices for each polyset
673 for ( i = 0; i < g_data.model.numSurfaces; i++ )
677 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
679 bTri = &g_data.surfData[i].baseTriangles[t];
681 for ( j = 0 ; j < 3 ; j++ )
688 for ( k = 0; k < g_data.surfData[i].header.numVerts; k++ )
690 if ( ( g_data.surfData[i].baseVertexes[k].st[0] == bVert->st[0] ) &&
691 ( g_data.surfData[i].baseVertexes[k].st[1] == bVert->st[1] ) &&
692 ( VectorCompare( bVert->xyz, g_data.surfData[i].baseVertexes[k].xyz ) ) &&
693 ( VectorCompare( bVert->normal, g_data.surfData[i].baseVertexes[k].normal ) ) ) {
694 break; // this vertex is already in the base vertex list
698 if ( k == g_data.surfData[i].header.numVerts ) { // new index
699 g_data.surfData[i].baseVertexes[g_data.surfData[i].header.numVerts] = *bVert;
700 g_data.surfData[i].header.numVerts++;
705 g_data.surfData[i].lodTriangles[t][j] = k;
713 for ( i = 0; i < g_data.model.numSurfaces; i++ )
715 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) {
716 if ( pOAF->surfaces[i]->numtriangles != 1 ) {
717 Error( "tag polysets must consist of only one triangle" );
719 if ( strstr( filename, "_flash.md3" ) && !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) ) {
722 printf( "found tag '%s'\n", pOAF->surfaces[i]->name );
723 g_data.model.numTags++;
729 static int LoadModelFile( const char *filename, polyset_t **psets, int *numpolysets ){
732 const char *frameFile;
734 printf( "---------------------\n" );
735 if ( filename[1] != ':' ) {
736 frameFile = filename;
737 sprintf( file1, "%s/%s", g_cddir, frameFile );
741 strcpy( file1, filename );
744 time1 = FileTime( file1 );
746 Error( "%s doesn't exist", file1 );
750 // load the base triangles
752 *psets = Polyset_LoadSets( file1, numpolysets, g_data.maxSurfaceTris );
757 Polyset_SnapSets( *psets, *numpolysets );
759 if ( strstr( file1, ".3ds" ) || strstr( file1, ".3DS" ) ) {
760 return MD3_TYPE_BASE3DS;
763 Error( "Unknown model file type" );
765 return MD3_TYPE_UNKNOWN;
773 void Cmd_Base( void ){
777 sprintf( filename, "%s/%s", g_cddir, token );
778 LoadBase( filename );
781 static void LoadBase( const char *filename ){
785 ObjectAnimationFrame_t oaf;
787 // determine polyset splitting threshold
788 if ( TokenAvailable() ) {
790 g_data.maxSurfaceTris = atoi( token );
794 g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;
797 g_data.type = LoadModelFile( filename, &psets, &numpolysets );
799 Polyset_ComputeNormals( psets, numpolysets );
801 g_data.model.numSurfaces = numpolysets;
803 memset( &oaf, 0, sizeof( oaf ) );
805 for ( i = 0; i < numpolysets; i++ )
807 oaf.surfaces[i] = &psets[i];
808 oaf.numSurfaces = numpolysets;
811 BuildBaseFrame( filename, &oaf );
813 free( psets[0].triangles );
821 $spritebase xorg yorg width height
823 Generate a single square for the model
826 void Cmd_SpriteBase( void ){
827 float xl, yl, width, height;
829 g_data.type = MD3_TYPE_SPRITE;
836 width = atof( token );
838 height = atof( token );
840 // if (g_skipmodel || g_release || g_archive)
843 printf( "---------------------\n" );
845 g_data.surfData[0].verts[0] = ( float * ) calloc( 1, sizeof( float ) * 6 * 4 );
847 g_data.surfData[0].header.numVerts = 4;
849 g_data.surfData[0].verts[0][0 + 0] = 0;
850 g_data.surfData[0].verts[0][0 + 1] = -xl;
851 g_data.surfData[0].verts[0][0 + 2] = yl + height;
853 g_data.surfData[0].verts[0][0 + 3] = -1;
854 g_data.surfData[0].verts[0][0 + 4] = 0;
855 g_data.surfData[0].verts[0][0 + 5] = 0;
856 g_data.surfData[0].baseVertexes[0].st[0] = 0;
857 g_data.surfData[0].baseVertexes[0].st[1] = 0;
860 g_data.surfData[0].verts[0][6 + 0] = 0;
861 g_data.surfData[0].verts[0][6 + 1] = -xl - width;
862 g_data.surfData[0].verts[0][6 + 2] = yl + height;
864 g_data.surfData[0].verts[0][6 + 3] = -1;
865 g_data.surfData[0].verts[0][6 + 4] = 0;
866 g_data.surfData[0].verts[0][6 + 5] = 0;
867 g_data.surfData[0].baseVertexes[1].st[0] = 1;
868 g_data.surfData[0].baseVertexes[1].st[1] = 0;
871 g_data.surfData[0].verts[0][12 + 0] = 0;
872 g_data.surfData[0].verts[0][12 + 1] = -xl - width;
873 g_data.surfData[0].verts[0][12 + 2] = yl;
875 g_data.surfData[0].verts[0][12 + 3] = -1;
876 g_data.surfData[0].verts[0][12 + 4] = 0;
877 g_data.surfData[0].verts[0][12 + 5] = 0;
878 g_data.surfData[0].baseVertexes[2].st[0] = 1;
879 g_data.surfData[0].baseVertexes[2].st[1] = 1;
882 g_data.surfData[0].verts[0][18 + 0] = 0;
883 g_data.surfData[0].verts[0][18 + 1] = -xl;
884 g_data.surfData[0].verts[0][18 + 2] = yl;
886 g_data.surfData[0].verts[0][18 + 3] = -1;
887 g_data.surfData[0].verts[0][18 + 4] = 0;
888 g_data.surfData[0].verts[0][18 + 5] = 0;
889 g_data.surfData[0].baseVertexes[3].st[0] = 0;
890 g_data.surfData[0].baseVertexes[3].st[1] = 1;
892 g_data.surfData[0].lodTriangles[0][0] = 0;
893 g_data.surfData[0].lodTriangles[0][1] = 1;
894 g_data.surfData[0].lodTriangles[0][2] = 2;
896 g_data.surfData[0].lodTriangles[1][0] = 2;
897 g_data.surfData[0].lodTriangles[1][1] = 3;
898 g_data.surfData[0].lodTriangles[1][2] = 0;
900 g_data.model.numSurfaces = 1;
902 g_data.surfData[0].header.numTriangles = 2;
903 g_data.surfData[0].header.numVerts = 4;
905 g_data.model.numFrames = 1;
909 ===========================================================================
913 ===========================================================================
921 void GrabFrame( const char *frame ){
928 const char *framefile;
930 qboolean parentTagExists = qfalse;
935 // the frame 'run1' will be looked for as either
936 // run.1 or run1.tri, so the new alias sequence save
937 // feature an be used
938 if ( frame[1] != ':' ) {
939 // framefile = FindFrameFile (frame);
941 sprintf( file1, "%s/%s",g_cddir, framefile );
945 strcpy( file1, frame );
947 printf( "grabbing %s\n", file1 );
949 if ( g_data.model.numFrames >= MD3_MAX_FRAMES ) {
950 Error( "model.numFrames >= MD3_MAX_FRAMES" );
952 fr = &g_data.frames[g_data.model.numFrames];
954 strcpy( fr->name, frame );
956 psets = Polyset_LoadSets( file1, &numpolysets, g_data.maxSurfaceTris );
961 Polyset_SnapSets( psets, numpolysets );
964 // compute vertex normals
966 Polyset_ComputeNormals( psets, numpolysets );
969 // flip everything to compensate for the alias coordinate system
970 // and perform global scale and adjust
972 for ( i = 0; i < g_data.model.numSurfaces; i++ )
974 triangle_t *ptri = psets[i].triangles;
977 for ( t = 0; t < psets[i].numtriangles; t++ )
980 for ( j = 0; j < 3; j++ )
984 for ( k = 0 ; k < 3 ; k++ ) {
985 ptri[t].verts[j][k] = ptri[t].verts[j][k] * g_data.scale_up +
988 if ( ptri[t].verts[j][k] > 1023 ||
989 ptri[t].verts[j][k] < -1023 ) {
990 Error( "Model extents too large" );
998 // find and count tags, locate parent tag
1000 for ( i = 0; i < numpolysets; i++ )
1002 if ( strstr( psets[i].name, "tag_" ) == psets[i].name ) {
1003 if ( strstr( psets[i].name, "tag_parent" ) == psets[i].name ) {
1004 if ( strstr( psets[i].name, "tag_parent" ) ) {
1007 if ( parentTagExists ) {
1008 Error( "Multiple parent tags not allowed" );
1011 memcpy( tri[0], psets[i].triangles[0].verts[0], sizeof( float ) * 3 );
1012 memcpy( tri[1], psets[i].triangles[0].verts[1], sizeof( float ) * 3 );
1013 memcpy( tri[2], psets[i].triangles[0].verts[2], sizeof( float ) * 3 );
1015 MD3_ComputeTagFromTri( &tagParent, tri );
1016 strcpy( tagParent.name, psets[i].name );
1017 g_data.tags[g_data.model.numFrames][numtags] = tagParent;
1018 parentTagExists = qtrue;
1025 if ( strcmp( psets[i].name, g_data.surfData[i].header.name ) ) {
1026 Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, psets[i].name, g_modelname );
1030 if ( numtags != g_data.model.numTags ) {
1031 Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
1034 if ( numpolysets != g_data.model.numSurfaces ) {
1035 Error( "mismatched number of surfaces in frame(%d) vs. base(%d)", numpolysets - numtags, g_data.model.numSurfaces );
1039 // prepare to accumulate bounds and normals
1041 ClearBounds( fr->bounds[0], fr->bounds[1] );
1044 // store the frame's vertices in the same order as the base. This assumes the
1045 // triangles and vertices in this frame are in exactly the same order as in the
1048 for ( i = 0, tagcount = 0; i < numpolysets; i++ )
1051 triangle_t *pTris = psets[i].triangles;
1053 strcpy( g_data.surfData[i].header.name, psets[i].name );
1056 // parent tag adjust
1058 if ( parentTagExists ) {
1059 for ( t = 0; t < psets[i].numtriangles; t++ )
1061 for ( j = 0; j < 3 ; j++ )
1065 VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );
1067 pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );
1068 pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );
1069 pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );
1071 VectorCopy( pTris[t].normals[j], tmp );
1072 pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );
1073 pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );
1074 pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );
1082 if ( strstr( psets[i].name, "tag_" ) == psets[i].name ) {
1083 md3Tag_t *pTag = &g_data.tags[g_data.model.numFrames][tagcount];
1086 strcpy( pTag->name, psets[i].name );
1088 memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );
1089 memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );
1090 memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );
1092 MD3_ComputeTagFromTri( pTag, tri );
1097 if ( g_data.surfData[i].verts[g_data.model.numFrames] ) {
1098 free( g_data.surfData[i].verts[g_data.model.numFrames] );
1100 frameXyz = g_data.surfData[i].verts[g_data.model.numFrames] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
1101 frameNormals = frameXyz + 3;
1103 for ( t = 0; t < psets[i].numtriangles; t++ )
1105 for ( j = 0; j < 3 ; j++ )
1109 index = g_data.surfData[i].baseTriangles[t].v[j].index;
1110 frameXyz[index * 6 + 0] = pTris[t].verts[j][0];
1111 frameXyz[index * 6 + 1] = pTris[t].verts[j][1];
1112 frameXyz[index * 6 + 2] = pTris[t].verts[j][2];
1113 frameNormals[index * 6 + 0] = pTris[t].normals[j][0];
1114 frameNormals[index * 6 + 1] = pTris[t].normals[j][1];
1115 frameNormals[index * 6 + 2] = pTris[t].normals[j][2];
1116 AddPointToBounds( &frameXyz[index * 6], fr->bounds[0], fr->bounds[1] );
1122 g_data.model.numFrames++;
1124 // only free the first triangle array, all of the psets in this array share the
1125 // same triangle pool!!!
1126 // free( psets[0].triangles );
1130 //===========================================================================
1139 void Cmd_Frame( void ){
1140 while ( TokenAvailable() )
1143 if ( g_skipmodel ) {
1146 if ( g_release || g_archive ) {
1147 g_data.model.numFrames = 1; // don't skip the writeout
1162 void SkinFrom3DS( const char *filename ){
1168 _3DS_LoadPolysets( filename, &psets, &numPolysets, g_verbose );
1170 for ( i = 0; i < numPolysets; i++ )
1173 if ( strstr( filename, gamedir + 1 ) )
1175 strcpy( name, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
1179 strcpy( name, filename );
1182 if ( strrchr( name, '/' ) )
1183 *( strrchr( name, '/' ) + 1 ) = 0;
1185 strcpy( name, psets[i].materialname );
1186 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, name );
1188 g_data.surfData[i].header.numShaders++;
1191 free( psets[0].triangles );
1195 void Cmd_Skin( void ){
1196 char skinfile[1024];
1198 if ( g_data.type == MD3_TYPE_BASE3DS ) {
1201 sprintf( skinfile, "%s/%s", g_cddir, token );
1203 if ( strstr( token, ".3ds" ) || strstr( token, ".3DS" ) ) {
1204 SkinFrom3DS( skinfile );
1208 Error( "Unknown file format for $skin '%s'\n", skinfile );
1213 Error( "invalid model type while processing $skin" );
1216 g_data.model.numSkins++;
1224 This routine is also called for $oldskin
1227 void Cmd_SpriteShader(){
1229 strcpy( g_data.surfData[0].shaders[g_data.surfData[0].header.numShaders].name, token );
1230 g_data.surfData[0].header.numShaders++;
1231 g_data.model.numSkins++;
1239 void Cmd_Origin( void ){
1240 // rotate points into frame of reference so model points down the
1242 // FIXME: use alias native coordinate system
1244 g_data.adjust[1] = -atof( token );
1247 g_data.adjust[0] = atof( token );
1250 g_data.adjust[2] = -atof( token );
1259 void Cmd_ScaleUp( void ){
1261 g_data.scale_up = atof( token );
1262 if ( g_skipmodel || g_release || g_archive ) {
1266 printf( "Scale up: %f\n", g_data.scale_up );
1274 Set a skin size other than the default
1278 void Cmd_Skinsize( void ){
1280 g_data.fixedwidth = atoi( token );
1282 g_data.fixedheight = atoi( token );
1289 Begin creating a model of the given name
1292 void Cmd_Modelname( void ){
1293 FinishModel( TYPE_UNKNOWN );
1297 strcpy( g_modelname, token );
1298 StripExtension( g_modelname );
1299 strcat( g_modelname, ".md3" );
1300 strcpy( g_data.model.name, g_modelname );
1308 void Cmd_Cd( void ){
1310 Error( "$cd command without a $modelname" );
1315 sprintf( g_cddir, "%s%s", gamedir, token );
1317 // if -only was specified and this cd doesn't match,
1318 // skip the model (you only need to match leading chars,
1319 // so you could regrab all monsters with -only models/monsters)
1323 if ( strncmp( token, g_only, strlen( g_only ) ) ) {
1324 g_skipmodel = qtrue;
1325 printf( "skipping %s\n", token );
1329 void Convert3DStoMD3( const char *file ){
1332 SkinFrom3DS( file );
1334 strcpy( g_data.model.name, g_modelname );
1336 FinishModel( TYPE_UNKNOWN );
1343 void Cmd_3DSConvert(){
1346 FinishModel( TYPE_UNKNOWN );
1351 sprintf( file, "%s%s", gamedir, token );
1352 strcpy( g_modelname, token );
1353 if ( strrchr( g_modelname, '.' ) ) {
1354 *strrchr( g_modelname, '.' ) = 0;
1356 strcat( g_modelname, ".md3" );
1358 if ( FileTime( file ) == -1 ) {
1359 Error( "%s doesn't exist", file );
1362 if ( TokenAvailable() ) {
1364 g_data.scale_up = atof( token );
1367 Convert3DStoMD3( file );
1370 static void ConvertASE( const char *filename, int type, qboolean grabAnims );
1375 void Cmd_ASEConvert( qboolean grabAnims ){
1376 char filename[1024];
1377 int type = TYPE_ITEM;
1379 FinishModel( TYPE_UNKNOWN );
1383 sprintf( filename, "%s%s", gamedir, token );
1385 strcpy( g_modelname, token );
1386 StripExtension( g_modelname );
1387 strcat( g_modelname, ".md3" );
1388 strcpy( g_data.model.name, g_modelname );
1390 if ( !strstr( filename, ".ase" ) && !strstr( filename, ".ASE" ) ) {
1391 strcat( filename, ".ASE" );
1394 g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;
1396 while ( TokenAvailable() )
1399 if ( !strcmp( token, "-origin" ) ) {
1400 if ( !TokenAvailable() ) {
1401 Error( "missing parameter for -origin" );
1404 g_data.aseAdjust[1] = -atof( token );
1406 if ( !TokenAvailable() ) {
1407 Error( "missing parameter for -origin" );
1410 g_data.aseAdjust[0] = atof( token );
1412 if ( !TokenAvailable() ) {
1413 Error( "missing parameter for -origin" );
1416 g_data.aseAdjust[2] = -atof( token );
1418 else if ( !strcmp( token, "-lod" ) ) {
1419 if ( !TokenAvailable() ) {
1420 Error( "No parameter for -lod" );
1423 g_data.currentLod = atoi( token );
1424 if ( g_data.currentLod > MD3_MAX_LODS - 1 ) {
1425 Error( "-lod parameter too large! (%d)\n", g_data.currentLod );
1428 if ( !TokenAvailable() ) {
1429 Error( "No second parameter for -lod" );
1432 g_data.lodBias = atof( token );
1434 else if ( !strcmp( token, "-maxtris" ) ) {
1435 if ( !TokenAvailable() ) {
1436 Error( "No parameter for -maxtris" );
1439 g_data.maxSurfaceTris = atoi( token );
1441 else if ( !strcmp( token, "-playerparms" ) ) {
1442 if ( !TokenAvailable() ) {
1443 Error( "missing skip start parameter for -playerparms" );
1446 g_data.lowerSkipFrameStart = atoi( token );
1449 if ( !TokenAvailable() ) {
1450 Error( "missing skip end parameter for -playerparms" );
1453 g_data.lowerSkipFrameEnd = atoi( token );
1456 if ( !TokenAvailable() ) {
1457 Error( "missing upper parameter for -playerparms" );
1460 g_data.maxUpperFrames = atoi( token );
1462 g_data.lowerSkipFrameEnd = g_data.maxUpperFrames - 1;
1465 if ( !TokenAvailable() ) {
1466 Error( "missing head parameter for -playerparms" );
1469 g_data.maxHeadFrames = atoi( token );
1471 g_data.maxHeadFrames = 1;
1473 if ( type != TYPE_ITEM ) {
1474 Error( "invalid argument" );
1479 else if ( !strcmp( token, "-weapon" ) ) {
1480 if ( type != TYPE_ITEM ) {
1481 Error( "invalid argument" );
1488 g_data.type = MD3_TYPE_ASE;
1490 if ( type == TYPE_WEAPON && grabAnims ) {
1491 Error( "can't grab anims with weapon models" );
1493 if ( type == TYPE_PLAYER && !grabAnims ) {
1494 Error( "player models must be converted with $aseanimconvert" );
1497 if ( type == TYPE_WEAPON ) {
1498 ConvertASE( filename, type, qfalse );
1499 ConvertASE( filename, TYPE_HAND, qtrue );
1503 ConvertASE( filename, type, grabAnims );
1507 static int GetSurfaceAnimations( SurfaceAnimation_t sanims[MAX_ANIM_SURFACES],
1513 int numValidSurfaces;
1517 if ( ( numSurfaces = ASE_GetNumSurfaces() ) > MAX_ANIM_SURFACES ) {
1518 Error( "Too many surfaces in ASE" );
1521 for ( numValidSurfaces = 0, i = 0; i < numSurfaces; i++ )
1523 polyset_t *splitSets;
1525 const char *surfaceName = ASE_GetSurfaceName( i );
1527 if ( !surfaceName ) {
1529 // Error( "Missing animation frames in model" );
1532 if ( strstr( surfaceName, "tag_" ) ||
1533 !strcmp( part, "any" ) ||
1534 ( strstr( surfaceName, part ) == surfaceName ) ) {
1536 // skip this if it's an inappropriate tag
1537 if ( strcmp( part, "any" ) ) {
1538 // ignore non-"tag_head" tags if this is the head
1539 if ( !strcmp( part, "h_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_head" ) ) {
1542 // ignore "tag_head" if this is the legs
1543 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_head" ) ) {
1546 // ignore "tag_weapon" if this is the legs
1547 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_weapon" ) ) {
1552 if ( ( sanims[numValidSurfaces].frames = ASE_GetSurfaceAnimation( i, &sanims[numValidSurfaces].numFrames, skipFrameStart, skipFrameEnd, maxFrames ) ) != 0 ) {
1553 splitSets = Polyset_SplitSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames, &numNewFrames, g_data.maxSurfaceTris );
1555 if ( numFrames == -1 ) {
1556 numFrames = sanims[numValidSurfaces].numFrames;
1558 else if ( numFrames != sanims[numValidSurfaces].numFrames ) {
1559 Error( "Different number of animation frames on surfaces" );
1562 if ( sanims[numValidSurfaces].frames != splitSets ) {
1565 // free old data if we split the surfaces
1566 for ( j = 0; j < sanims[numValidSurfaces].numFrames; j++ )
1568 free( sanims[numValidSurfaces].frames[j].triangles );
1569 free( sanims[numValidSurfaces].frames );
1572 sanims[numValidSurfaces].frames = splitSets;
1573 sanims[numValidSurfaces].numFrames = numNewFrames;
1575 Polyset_SnapSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );
1576 Polyset_ComputeNormals( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );
1583 return numValidSurfaces;
1586 static int SurfaceOrderToFrameOrder( SurfaceAnimation_t sanims[], ObjectAnimationFrame_t oanims[], int numSurfaces ){
1591 ** we have the data here arranged in surface order, now we need to convert it to
1594 for ( i = 0, s = 0; i < numSurfaces; i++ )
1598 if ( sanims[i].frames ) {
1599 if ( numFrames == -1 ) {
1600 numFrames = sanims[i].numFrames;
1602 else if ( numFrames != sanims[i].numFrames ) {
1603 Error( "numFrames != sanims[i].numFrames (%d != %d)\n", numFrames, sanims[i].numFrames );
1606 for ( j = 0; j < sanims[i].numFrames; j++ )
1608 oanims[j].surfaces[s] = &sanims[i].frames[j];
1609 oanims[j].numSurfaces = numSurfaces;
1618 static void WriteMD3( const char *_filename, ObjectAnimationFrame_t oanims[], int numFrames ){
1619 char filename[1024];
1621 strcpy( filename, _filename );
1622 if ( strchr( filename, '.' ) ) {
1623 *strchr( filename, '.' ) = 0;
1625 strcat( filename, ".md3" );
1628 static void BuildAnimationFromOAFs( const char *filename, ObjectAnimationFrame_t oanims[], int numFrames, int type ){
1629 int f, i, j, tagcount;
1631 float *frameNormals;
1633 g_data.model.numSurfaces = oanims[0].numSurfaces;
1634 g_data.model.numFrames = numFrames;
1635 if ( g_data.model.numFrames < 0 ) {
1636 Error( "model.numFrames < 0" );
1638 if ( g_data.model.numFrames >= MD3_MAX_FRAMES ) {
1639 Error( "model.numFrames >= MD3_MAX_FRAMES" );
1643 BuildBaseFrame( filename, &oanims[0] );
1645 // build animation frames
1646 for ( f = 0; f < numFrames; f++ )
1648 ObjectAnimationFrame_t *pOAF = &oanims[f];
1649 qboolean parentTagExists = qfalse;
1654 fr = &g_data.frames[f];
1656 strcpy( fr->name, "(from ASE)" );
1658 // scale and adjust frame
1659 for ( i = 0; i < pOAF->numSurfaces; i++ )
1661 triangle_t *pTris = pOAF->surfaces[i]->triangles;
1664 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1666 for ( j = 0; j < 3; j++ )
1671 for ( k = 0 ; k < 3 ; k++ ) {
1672 pTris[t].verts[j][k] = pTris[t].verts[j][k] * g_data.scale_up +
1673 g_data.aseAdjust[k];
1675 if ( pTris[t].verts[j][k] > 1023 ||
1676 pTris[t].verts[j][k] < -1023 ) {
1677 Error( "Model extents too large" );
1685 // find and count tags, locate parent tag
1687 for ( i = 0; i < pOAF->numSurfaces; i++ )
1689 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) {
1690 // ignore parent tags when grabbing a weapon model and this is the flash portion
1691 if ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && strstr( filename, "_flash.md3" ) ) {
1694 else if ( !strstr( filename, "_hand.md3" ) && (
1695 ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && !strstr( filename, "_flash.md3" ) ) ||
1696 ( !strcmp( pOAF->surfaces[i]->name, "tag_torso" ) && ( strstr( filename, "upper_" ) || strstr( filename, "upper.md3" ) ) ) ||
1697 ( !strcmp( pOAF->surfaces[i]->name, "tag_head" ) && ( strstr( filename, "head.md3" ) || strstr( filename, "head_" ) ) ) ||
1698 ( !strcmp( pOAF->surfaces[i]->name, "tag_flash" ) && strstr( filename, "_flash.md3" ) ) ||
1699 ( !strcmp( pOAF->surfaces[i]->name, "tag_weapon" ) && type == TYPE_WEAPON ) ) ) {
1702 if ( parentTagExists ) {
1703 Error( "Multiple parent tags not allowed" );
1706 memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );
1707 memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );
1708 memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );
1710 MD3_ComputeTagFromTri( &tagParent, tri );
1711 strcpy( tagParent.name, "tag_parent" );
1712 g_data.tags[f][numtags] = tagParent;
1713 parentTagExists = qtrue;
1719 memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );
1720 memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );
1721 memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );
1723 MD3_ComputeTagFromTri( &g_data.tags[f][numtags], tri );
1724 strcpy( g_data.tags[f][numtags].name, pOAF->surfaces[i]->name );
1725 if ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) ) {
1726 *( strstr( g_data.tags[f][numtags].name, "tag_flash" ) + strlen( "tag_flash" ) ) = 0;
1733 if ( strcmp( pOAF->surfaces[i]->name, g_data.surfData[i].header.name ) ) {
1734 Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, pOAF->surfaces[i]->name, filename );
1738 if ( numtags != g_data.model.numTags ) {
1739 Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
1743 // prepare to accumulate bounds and normals
1745 ClearBounds( fr->bounds[0], fr->bounds[1] );
1748 // store the frame's vertices in the same order as the base. This assumes the
1749 // triangles and vertices in this frame are in exactly the same order as in the
1752 for ( i = 0, tagcount = 0; i < pOAF->numSurfaces; i++ )
1755 triangle_t *pTris = pOAF->surfaces[i]->triangles;
1758 // parent tag adjust
1760 if ( parentTagExists ) {
1761 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1763 for ( j = 0; j < 3 ; j++ )
1767 VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );
1769 pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );
1770 pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );
1771 pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );
1773 VectorCopy( pTris[t].normals[j], tmp );
1774 pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );
1775 pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );
1776 pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );
1784 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) {
1785 md3Tag_t *pTag = &g_data.tags[f][tagcount];
1788 strcpy( pTag->name, pOAF->surfaces[i]->name );
1790 memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );
1791 memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );
1792 memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );
1794 MD3_ComputeTagFromTri( pTag, tri );
1799 if ( g_data.surfData[i].verts[f] ) {
1800 free( g_data.surfData[i].verts[f] );
1802 frameXyz = g_data.surfData[i].verts[f] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
1803 frameNormals = frameXyz + 3;
1805 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1807 for ( j = 0; j < 3 ; j++ )
1811 index = g_data.surfData[i].baseTriangles[t].v[j].index;
1812 frameXyz[index * 6 + 0] = pTris[t].verts[j][0];
1813 frameXyz[index * 6 + 1] = pTris[t].verts[j][1];
1814 frameXyz[index * 6 + 2] = pTris[t].verts[j][2];
1815 frameNormals[index * 6 + 0] = pTris[t].normals[j][0];
1816 frameNormals[index * 6 + 1] = pTris[t].normals[j][1];
1817 frameNormals[index * 6 + 2] = pTris[t].normals[j][2];
1818 AddPointToBounds( &frameXyz[index * 6], fr->bounds[0], fr->bounds[1] );
1825 if ( strstr( filename, gamedir + 1 ) ) {
1826 strcpy( g_modelname, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
1830 strcpy( g_modelname, filename );
1833 FinishModel( type );
1837 static void ConvertASE( const char *filename, int type, qboolean grabAnims ){
1841 SurfaceAnimation_t surfaceAnimations[MAX_ANIM_SURFACES];
1842 ObjectAnimationFrame_t objectAnimationFrames[MAX_ANIM_FRAMES];
1843 char outfilename[1024];
1846 ** load ASE into memory
1848 ASE_Load( filename, g_verbose, grabAnims );
1853 if ( type == TYPE_ITEM ) {
1854 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "any", -1, -1, -1 );
1856 if ( numSurfaces <= 0 ) {
1857 Error( "numSurfaces <= 0" );
1860 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1862 if ( numFrames <= 0 ) {
1863 Error( "numFrames <= 0" );
1866 strcpy( outfilename, filename );
1867 if ( strrchr( outfilename, '.' ) ) {
1868 *( strrchr( outfilename, '.' ) + 1 ) = 0;
1870 strcat( outfilename, "md3" );
1871 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1874 for ( i = 0; i < numSurfaces; i++ )
1876 if ( surfaceAnimations[i].frames ) {
1877 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1879 free( surfaceAnimations[i].frames[j].triangles );
1881 free( surfaceAnimations[i].frames );
1882 surfaceAnimations[i].frames = 0;
1886 else if ( type == TYPE_PLAYER ) {
1887 qboolean tagTorso = qfalse;
1888 qboolean tagHead = qfalse;
1889 qboolean tagWeapon = qfalse;
1892 // verify that all necessary tags exist
1894 numSurfaces = ASE_GetNumSurfaces();
1895 for ( i = 0; i < numSurfaces; i++ )
1897 if ( !strcmp( ASE_GetSurfaceName( i ), "tag_head" ) ) {
1900 if ( !strcmp( ASE_GetSurfaceName( i ), "tag_torso" ) ) {
1903 if ( !strcmp( ASE_GetSurfaceName( i ), "tag_weapon" ) ) {
1909 Error( "Missing tag_weapon!" );
1912 Error( "Missing tag_torso!" );
1915 Error( "Missing tag_weapon!" );
1918 // get all upper body surfaces
1919 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "u_", -1, -1, g_data.maxUpperFrames );
1920 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1921 strcpy( outfilename, filename );
1922 if ( strrchr( outfilename, '/' ) ) {
1923 *( strrchr( outfilename, '/' ) + 1 ) = 0;
1926 if ( g_data.currentLod == 0 ) {
1927 strcat( outfilename, "upper.md3" );
1933 sprintf( temp, "upper_%d.md3", g_data.currentLod );
1934 strcat( outfilename, temp );
1937 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1940 for ( i = 0; i < numSurfaces; i++ )
1942 if ( surfaceAnimations[i].frames ) {
1943 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1945 free( surfaceAnimations[i].frames[j].triangles );
1947 free( surfaceAnimations[i].frames );
1948 surfaceAnimations[i].frames = 0;
1952 // get lower body surfaces
1953 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "l_", g_data.lowerSkipFrameStart, g_data.lowerSkipFrameEnd, -1 );
1954 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1955 strcpy( outfilename, filename );
1956 if ( strrchr( outfilename, '/' ) ) {
1957 *( strrchr( outfilename, '/' ) + 1 ) = 0;
1960 if ( g_data.currentLod == 0 ) {
1961 strcat( outfilename, "lower.md3" );
1967 sprintf( temp, "lower_%d.md3", g_data.currentLod );
1968 strcat( outfilename, temp );
1970 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1973 for ( i = 0; i < numSurfaces; i++ )
1975 if ( surfaceAnimations[i].frames ) {
1976 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1978 free( surfaceAnimations[i].frames[j].triangles );
1980 free( surfaceAnimations[i].frames );
1981 surfaceAnimations[i].frames = 0;
1985 // get head surfaces
1986 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "h_", -1, -1, g_data.maxHeadFrames );
1987 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1988 strcpy( outfilename, filename );
1989 if ( strrchr( outfilename, '/' ) ) {
1990 *( strrchr( outfilename, '/' ) + 1 ) = 0;
1993 if ( g_data.currentLod == 0 ) {
1994 strcat( outfilename, "head.md3" );
2000 sprintf( temp, "head_%d.md3", g_data.currentLod );
2001 strcat( outfilename, temp );
2003 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
2006 for ( i = 0; i < numSurfaces; i++ )
2008 if ( surfaceAnimations[i].frames ) {
2009 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2011 free( surfaceAnimations[i].frames[j].triangles );
2013 free( surfaceAnimations[i].frames );
2014 surfaceAnimations[i].frames = 0;
2018 else if ( type == TYPE_WEAPON ) {
2019 // get the weapon surfaces
2020 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "w_", -1, -1, -1 );
2021 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2023 strcpy( outfilename, filename );
2024 if ( strrchr( outfilename, '.' ) ) {
2025 *( strrchr( outfilename, '.' ) + 1 ) = 0;
2027 strcat( outfilename, "md3" );
2028 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
2031 for ( i = 0; i < numSurfaces; i++ )
2033 if ( surfaceAnimations[i].frames ) {
2034 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2036 free( surfaceAnimations[i].frames[j].triangles );
2038 free( surfaceAnimations[i].frames );
2039 surfaceAnimations[i].frames = 0;
2043 // get the flash surfaces
2044 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "f_", -1, -1, -1 );
2045 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2047 strcpy( outfilename, filename );
2048 if ( strrchr( outfilename, '.' ) ) {
2049 *strrchr( outfilename, '.' ) = 0;
2051 strcat( outfilename, "_flash.md3" );
2052 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM );
2055 for ( i = 0; i < numSurfaces; i++ )
2057 if ( surfaceAnimations[i].frames ) {
2058 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2060 free( surfaceAnimations[i].frames[j].triangles );
2062 free( surfaceAnimations[i].frames );
2063 surfaceAnimations[i].frames = 0;
2067 else if ( type == TYPE_HAND ) {
2068 // get the hand tags
2069 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "tag_", -1, -1, -1 );
2070 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2072 strcpy( outfilename, filename );
2073 if ( strrchr( outfilename, '.' ) ) {
2074 *strrchr( outfilename, '.' ) = 0;
2076 strcat( outfilename, "_hand.md3" );
2077 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_HAND );
2080 for ( i = 0; i < numSurfaces; i++ )
2082 if ( surfaceAnimations[i].frames ) {
2083 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2085 free( surfaceAnimations[i].frames[j].triangles );
2087 free( surfaceAnimations[i].frames );
2088 surfaceAnimations[i].frames = 0;
2094 Error( "Unknown type passed to ConvertASE()" );
2097 g_data.currentLod = 0;
2099 g_data.maxHeadFrames = 0;
2100 g_data.maxUpperFrames = 0;
2101 g_data.lowerSkipFrameStart = 0;
2102 g_data.lowerSkipFrameEnd = 0;
2103 VectorCopy( vec3_origin, g_data.aseAdjust );
2105 // unload ASE from memory