4 //=================================================================
\r
6 static void OrderSurfaces( void );
\r
7 static void LoadBase( const char *filename );
\r
8 static int LoadModelFile( const char *filename, polyset_t **ppsets, int *pnumpolysets );
\r
10 #define MAX_SURFACE_TRIS (SHADER_MAX_INDEXES / 3)
\r
11 #define MAX_SURFACE_VERTS SHADER_MAX_VERTEXES
\r
13 #define MD3_TYPE_UNKNOWN 0
\r
14 #define MD3_TYPE_BASE3DS 1
\r
15 #define MD3_TYPE_SPRITE 2
\r
16 #define MD3_TYPE_ASE 3
\r
18 #define MAX_ANIM_FRAMES 512
\r
19 #define MAX_ANIM_SURFACES 32
\r
25 } SurfaceAnimation_t;
\r
29 polyset_t *surfaces[MAX_ANIM_SURFACES];
\r
31 } ObjectAnimationFrame_t;
\r
45 //================================================================
\r
49 md3Surface_t header;
\r
50 md3Shader_t shaders[MD3_MAX_SHADERS];
\r
51 // all verts (xyz_normal)
\r
52 float *verts[MD3_MAX_FRAMES];
\r
54 baseTriangle_t baseTriangles[MD3_MAX_TRIANGLES];
\r
56 // the triangles will be sorted so that they form long generalized tristrips
\r
57 int orderedTriangles[MD3_MAX_TRIANGLES][3];
\r
58 int lodTriangles[MD3_MAX_TRIANGLES][3];
\r
59 baseVertex_t baseVertexes[MD3_MAX_VERTS];
\r
65 int skinwidth, skinheight;
\r
67 md3SurfaceData_t surfData[MD3_MAX_SURFACES];
\r
69 md3Tag_t tags[MD3_MAX_FRAMES][MD3_MAX_TAGS];
\r
70 md3Frame_t frames[MD3_MAX_FRAMES];
\r
73 float scale_up; // set by $scale
\r
74 vec3_t adjust; // set by $origin
\r
76 int fixedwidth, fixedheight; // set by $skinsize
\r
80 int lowerSkipFrameStart, lowerSkipFrameEnd;
\r
86 int type; // MD3_TYPE_BASE, MD3_TYPE_OLDBASE, MD3_TYPE_ASE, or MD3_TYPE_SPRITE
\r
92 // the command list holds counts, the count * 3 xyz, st, normal indexes
\r
93 // that are valid for every frame
\r
95 char g_modelname[1024];
\r
97 //==============================================================
\r
104 void ClearModel (void)
\r
108 g_data.type = MD3_TYPE_UNKNOWN;
\r
110 for ( i = 0; i < MD3_MAX_SURFACES; i++ )
\r
112 memset( &g_data.surfData[i].header, 0, sizeof( g_data.surfData[i].header ) );
\r
113 memset( &g_data.surfData[i].shaders, 0, sizeof( g_data.surfData[i].shaders ) );
\r
114 memset( &g_data.surfData[i].verts, 0, sizeof( g_data.surfData[i].verts ) );
\r
117 memset( g_data.tags, 0, sizeof( g_data.tags ) );
\r
119 for ( i = 0; i < g_data.model.numSurfaces; i++ )
\r
123 for ( j = 0; j < g_data.surfData[i].header.numShaders; j++ )
\r
125 memset( &g_data.surfData[i].shaders[j], 0, sizeof( g_data.surfData[i].shaders[j] ) );
\r
128 memset (&g_data.model, 0, sizeof(g_data.model));
\r
129 memset (g_cddir, 0, sizeof(g_cddir));
\r
131 g_modelname[0] = 0;
\r
132 g_data.scale_up = 1.0;
\r
133 memset( &g_data.model, 0, sizeof( g_data.model ) );
\r
134 VectorCopy (vec3_origin, g_data.adjust);
\r
135 g_data.fixedwidth = g_data.fixedheight = 0;
\r
136 g_skipmodel = qfalse;
\r
140 ** void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData )
\r
142 ** This routine assumes that the file position has been adjusted
\r
143 ** properly prior to entry to point at the beginning of the surface.
\r
145 ** Since surface header information is completely relative, we can't
\r
146 ** just randomly seek to an arbitrary surface location right now. Is
\r
147 ** this something we should add?
\r
149 void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData )
\r
151 md3Surface_t *pSurf = &pSurfData->header;
\r
152 md3Shader_t *pShader = pSurfData->shaders;
\r
153 baseVertex_t *pBaseVertex = pSurfData->baseVertexes;
\r
154 float **verts = pSurfData->verts;
\r
156 short xyznormals[MD3_MAX_VERTS][4];
\r
158 float base_st[MD3_MAX_VERTS][2];
\r
159 md3Surface_t surftemp;
\r
163 if ( strstr( pSurf->name, "tag_" ) == pSurf->name )
\r
167 // write out the header
\r
170 surftemp.ident = LittleLong( MD3_IDENT );
\r
171 surftemp.flags = LittleLong( pSurf->flags );
\r
172 surftemp.numFrames = LittleLong( pSurf->numFrames );
\r
173 surftemp.numShaders = LittleLong( pSurf->numShaders );
\r
175 surftemp.ofsShaders = LittleLong( pSurf->ofsShaders );
\r
177 surftemp.ofsTriangles = LittleLong( pSurf->ofsTriangles );
\r
178 surftemp.numTriangles = LittleLong( pSurf->numTriangles );
\r
180 surftemp.ofsSt = LittleLong( pSurf->ofsSt );
\r
181 surftemp.ofsXyzNormals = LittleLong( pSurf->ofsXyzNormals );
\r
182 surftemp.ofsEnd = LittleLong( pSurf->ofsEnd );
\r
184 SafeWrite( modelouthandle, &surftemp, sizeof( surftemp ) );
\r
188 printf( "surface '%s'\n", pSurf->name );
\r
189 printf( "...num shaders: %d\n", pSurf->numShaders );
\r
193 // write out shaders
\r
195 for ( i = 0; i < pSurf->numShaders; i++ )
\r
197 md3Shader_t shadertemp;
\r
200 printf( "......'%s'\n", pShader[i].name );
\r
202 shadertemp = pShader[i];
\r
203 shadertemp.shaderIndex = LittleLong( shadertemp.shaderIndex );
\r
204 SafeWrite( modelouthandle, &shadertemp, sizeof( shadertemp ) );
\r
208 // write out the triangles
\r
210 for ( i = 0 ; i < pSurf->numTriangles ; i++ )
\r
212 for (j = 0 ; j < 3 ; j++)
\r
214 int ivalue = LittleLong( pSurfData->orderedTriangles[i][j] );
\r
215 pSurfData->orderedTriangles[i][j] = ivalue;
\r
219 SafeWrite( modelouthandle, pSurfData->orderedTriangles, pSurf->numTriangles * sizeof( g_data.surfData[0].orderedTriangles[0] ) );
\r
223 printf( "\n...num verts: %d\n", pSurf->numVerts );
\r
224 printf( "...TEX COORDINATES\n" );
\r
228 // write out the texture coordinates
\r
230 for ( i = 0; i < pSurf->numVerts ; i++) {
\r
231 base_st[i][0] = LittleFloat( pBaseVertex[i].st[0] );
\r
232 base_st[i][1] = LittleFloat( pBaseVertex[i].st[1] );
\r
234 printf( "......%d: %f,%f\n", i, base_st[i][0], base_st[i][1] );
\r
236 SafeWrite( modelouthandle, base_st, pSurf->numVerts * sizeof(base_st[0]));
\r
239 // write the xyz_normal
\r
242 printf( "...XYZNORMALS\n" );
\r
243 for ( f = 0; f < g_data.model.numFrames; f++ )
\r
245 for (j=0 ; j< pSurf->numVerts; j++)
\r
249 for (k=0 ; k < 3 ; k++)
\r
251 value = ( short ) ( verts[f][j*6+k] / MD3_XYZ_SCALE );
\r
252 xyznormals[j][k] = LittleShort( value );
\r
254 NormalToLatLong( &verts[f][j*6+3], (byte *)&xyznormals[j][3] );
\r
256 SafeWrite( modelouthandle, xyznormals, pSurf->numVerts * sizeof( short ) * 4 );
\r
261 ** void WriteModelFile( FILE *modelouthandle )
\r
264 ** header sizeof( md3Header_t )
\r
265 ** frames sizeof( md3Frame_t ) * numFrames
\r
266 ** tags sizeof( md3Tag_t ) * numFrames * numTags
\r
267 ** surfaces surfaceSum
\r
269 void WriteModelFile( FILE *modelouthandle )
\r
273 md3Header_t modeltemp;
\r
274 long surfaceSum = 0;
\r
275 int numRealSurfaces = 0;
\r
276 int numFrames = g_data.model.numFrames;
\r
278 // compute offsets for all surfaces, sum their total size
\r
279 for ( i = 0; i < g_data.model.numSurfaces; i++ )
\r
281 if ( strstr( g_data.surfData[i].header.name, "tag_" ) != g_data.surfData[i].header.name )
\r
283 md3Surface_t *psurf = &g_data.surfData[i].header;
\r
285 if ( psurf->numTriangles == 0 || psurf->numVerts == 0 )
\r
289 // the triangle and vertex split threshold is controlled by a parameter
\r
290 // to $base, a la $base blah.3ds 1900, where "1900" determines the number
\r
291 // of triangles to split on
\r
293 else if ( psurf->numVerts > MAX_SURFACE_VERTS )
\r
295 Error( "too many vertices\n" );
\r
298 psurf->numFrames = numFrames;
\r
300 psurf->ofsShaders = sizeof( md3Surface_t );
\r
302 if ( psurf->numTriangles > MAX_SURFACE_TRIS )
\r
304 Error( "too many faces\n" );
\r
307 psurf->ofsTriangles = psurf->ofsShaders + psurf->numShaders * sizeof( md3Shader_t );
\r
309 psurf->ofsSt = psurf->ofsTriangles + psurf->numTriangles * sizeof( md3Triangle_t );
\r
310 psurf->ofsXyzNormals = psurf->ofsSt + psurf->numVerts * sizeof( md3St_t );
\r
311 psurf->ofsEnd = psurf->ofsXyzNormals + psurf->numFrames * psurf->numVerts * ( sizeof( short ) * 4 );
\r
313 surfaceSum += psurf->ofsEnd;
\r
319 g_data.model.ident = MD3_IDENT;
\r
320 g_data.model.version = MD3_VERSION;
\r
322 g_data.model.ofsFrames = sizeof(md3Header_t);
\r
323 g_data.model.ofsTags = g_data.model.ofsFrames + numFrames*sizeof(md3Frame_t);
\r
324 g_data.model.ofsSurfaces = g_data.model.ofsTags + numFrames*g_data.model.numTags*sizeof(md3Tag_t);
\r
325 g_data.model.ofsEnd = g_data.model.ofsSurfaces + surfaceSum;
\r
328 // write out the model header
\r
330 modeltemp = g_data.model;
\r
331 modeltemp.ident = LittleLong( modeltemp.ident );
\r
332 modeltemp.version = LittleLong( modeltemp.version );
\r
333 modeltemp.numFrames = LittleLong( modeltemp.numFrames );
\r
334 modeltemp.numTags = LittleLong( modeltemp.numTags );
\r
335 modeltemp.numSurfaces = LittleLong( numRealSurfaces );
\r
336 modeltemp.ofsFrames = LittleLong( modeltemp.ofsFrames );
\r
337 modeltemp.ofsTags = LittleLong( modeltemp.ofsTags );
\r
338 modeltemp.ofsSurfaces = LittleLong( modeltemp.ofsSurfaces );
\r
339 modeltemp.ofsEnd = LittleLong( modeltemp.ofsEnd );
\r
341 SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));
\r
344 // write out the frames
\r
346 for (i=0 ; i < numFrames ; i++)
\r
349 float maxRadius = 0;
\r
352 // compute localOrigin and radius
\r
354 g_data.frames[i].localOrigin[0] =
\r
355 g_data.frames[i].localOrigin[1] =
\r
356 g_data.frames[i].localOrigin[2] = 0;
\r
358 for ( j = 0; j < 8; j++ )
\r
360 tmpVec[0] = g_data.frames[i].bounds[(j&1)!=0][0];
\r
361 tmpVec[1] = g_data.frames[i].bounds[(j&2)!=0][1];
\r
362 tmpVec[2] = g_data.frames[i].bounds[(j&4)!=0][2];
\r
364 if ( VectorLength( tmpVec ) > maxRadius )
\r
365 maxRadius = VectorLength( tmpVec );
\r
368 g_data.frames[i].radius = LittleFloat( maxRadius );
\r
371 for (j=0 ; j<3 ; j++) {
\r
372 g_data.frames[i].bounds[0][j] = LittleFloat( g_data.frames[i].bounds[0][j] );
\r
373 g_data.frames[i].bounds[1][j] = LittleFloat( g_data.frames[i].bounds[1][j] );
\r
374 g_data.frames[i].localOrigin[j] = LittleFloat( g_data.frames[i].localOrigin[j] );
\r
377 fseek (modelouthandle, g_data.model.ofsFrames, SEEK_SET);
\r
378 SafeWrite( modelouthandle, g_data.frames, numFrames * sizeof(g_data.frames[0]) );
\r
381 // write out the tags
\r
383 fseek( modelouthandle, g_data.model.ofsTags, SEEK_SET );
\r
384 for (f=0 ; f<g_data.model.numFrames; f++)
\r
388 for ( t = 0; t < g_data.model.numTags; t++ )
\r
390 g_data.tags[f][t].origin[0] = LittleFloat(g_data.tags[f][t].origin[0]);
\r
391 g_data.tags[f][t].origin[1] = LittleFloat(g_data.tags[f][t].origin[1]);
\r
392 g_data.tags[f][t].origin[2] = LittleFloat(g_data.tags[f][t].origin[2]);
\r
394 for (j=0 ; j<3 ; j++)
\r
396 g_data.tags[f][t].axis[0][j] = LittleFloat(g_data.tags[f][t].axis[0][j]);
\r
397 g_data.tags[f][t].axis[1][j] = LittleFloat(g_data.tags[f][t].axis[1][j]);
\r
398 g_data.tags[f][t].axis[2][j] = LittleFloat(g_data.tags[f][t].axis[2][j]);
\r
401 SafeWrite( modelouthandle, g_data.tags[f], g_data.model.numTags * sizeof(md3Tag_t) );
\r
405 // write out the surfaces
\r
407 fseek( modelouthandle, g_data.model.ofsSurfaces, SEEK_SET );
\r
408 for ( i = 0; i < g_data.model.numSurfaces; i++ )
\r
410 WriteModelSurface( modelouthandle, &g_data.surfData[i] );
\r
420 void FinishModel ( int type )
\r
422 FILE *modelouthandle;
\r
423 FILE *defaultSkinHandle;
\r
427 if (!g_data.model.numFrames)
\r
431 // build generalized triangle strips
\r
435 if ( type == TYPE_PLAYER )
\r
437 sprintf( name, "%s%s", writedir, g_modelname );
\r
438 *strrchr( name, '.' ) = 0;
\r
439 strcat( name, "_default.skin" );
\r
441 defaultSkinHandle = fopen( name, "wt" );
\r
442 for ( i = 0; i < g_data.model.numSurfaces; i++ )
\r
444 fprintf( defaultSkinHandle, "%s,%s\n", g_data.surfData[i].header.name, g_data.surfData[i].shaders[0].name );
\r
446 fclose( defaultSkinHandle );
\r
449 sprintf (name, "%s%s", writedir, g_modelname);
\r
452 // copy the model and its shaders to release directory tree
\r
453 // if doing a release build
\r
457 md3SurfaceData_t *pSurf;
\r
459 ReleaseFile( g_modelname );
\r
461 for ( i = 0; i < g_data.model.numSurfaces; i++ ) {
\r
462 pSurf = &g_data.surfData[i];
\r
463 for ( j = 0; j < g_data.model.numSkins; j++ ) {
\r
464 ReleaseShader( pSurf->shaders[j].name );
\r
471 // write the model output file
\r
473 printf ("saving to %s\n", name);
\r
475 modelouthandle = SafeOpenWrite (name);
\r
477 WriteModelFile (modelouthandle);
\r
479 printf ("%4d surfaces\n", g_data.model.numSurfaces);
\r
480 printf ("%4d frames\n", g_data.model.numFrames);
\r
481 printf ("%4d tags\n", g_data.model.numTags);
\r
482 printf ("file size: %d\n", (int)ftell (modelouthandle) );
\r
483 printf ("---------------------\n");
\r
485 fclose (modelouthandle);
\r
491 ** Reorders triangles in all the surfaces.
\r
493 static void OrderSurfaces( void )
\r
496 extern qboolean g_stripify;
\r
498 // go through each surface and find best strip/fans possible
\r
499 for ( s = 0; s < g_data.model.numSurfaces; s++ )
\r
501 int mesh[MD3_MAX_TRIANGLES][3];
\r
504 printf( "stripifying surface %d/%d with %d tris\n", s, g_data.model.numSurfaces, g_data.surfData[s].header.numTriangles );
\r
506 for ( i = 0; i < g_data.surfData[s].header.numTriangles; i++ )
\r
508 mesh[i][0] = g_data.surfData[s].lodTriangles[i][0];
\r
509 mesh[i][1] = g_data.surfData[s].lodTriangles[i][1];
\r
510 mesh[i][2] = g_data.surfData[s].lodTriangles[i][2];
\r
515 OrderMesh( mesh, // input
\r
516 g_data.surfData[s].orderedTriangles, // output
\r
517 g_data.surfData[s].header.numTriangles );
\r
521 memcpy( g_data.surfData[s].orderedTriangles, mesh, sizeof( int ) * 3 * g_data.surfData[s].header.numTriangles );
\r
528 ===============================================================
\r
532 ===============================================================
\r
536 CopyTrianglesToBaseTriangles
\r
540 static void CopyTrianglesToBaseTriangles(triangle_t *ptri, int numtri, baseTriangle_t *bTri )
\r
543 // int width, height, iwidth, iheight, swidth;
\r
544 // float s_scale, t_scale;
\r
546 // vec3_t mins, maxs;
\r
551 // find bounds of all the verts on the base frame
\r
553 ClearBounds (mins, maxs);
\r
555 for (i=0 ; i<numtri ; i++)
\r
556 for (j=0 ; j<3 ; j++)
\r
557 AddPointToBounds (ptri[i].verts[j], mins, maxs);
\r
559 for (i=0 ; i<3 ; i++)
\r
561 mins[i] = floor(mins[i]);
\r
562 maxs[i] = ceil(maxs[i]);
\r
565 width = maxs[0] - mins[0];
\r
566 height = maxs[2] - mins[2];
\r
568 if (!g_data.fixedwidth)
\r
571 if (width*scale >= 150)
\r
572 scale = 150.0 / width;
\r
573 if (height*scale >= 190)
\r
574 scale = 190.0 / height;
\r
576 s_scale = t_scale = scale;
\r
578 iwidth = ceil(width*s_scale);
\r
579 iheight = ceil(height*t_scale);
\r
586 iwidth = g_data.fixedwidth / 2;
\r
587 iheight = g_data.fixedheight;
\r
589 s_scale = (float)(iwidth-4) / width;
\r
590 t_scale = (float)(iheight-4) / height;
\r
593 // make the width a multiple of 4; some hardware requires this, and it ensures
\r
594 // dword alignment for each scan
\r
596 g_data.skinwidth = (swidth + 3) & ~3;
\r
597 g_data.skinheight = iheight;
\r
600 for (i=0; i<numtri ; i++, ptri++, bTri++)
\r
604 for (j=0 ; j<3 ; j++)
\r
606 pbasevert = ptri->verts[j];
\r
608 VectorCopy( ptri->verts[j], bTri->v[j].xyz);
\r
609 VectorCopy( ptri->normals[j], bTri->v[j].normal );
\r
611 bTri->v[j].st[0] = ptri->texcoords[j][0];
\r
612 bTri->v[j].st[1] = ptri->texcoords[j][1];
\r
617 static void BuildBaseFrame( const char *filename, ObjectAnimationFrame_t *pOAF )
\r
619 baseTriangle_t *bTri;
\r
620 baseVertex_t *bVert;
\r
623 // calculate the base triangles
\r
624 for ( i = 0; i < g_data.model.numSurfaces; i++ )
\r
626 CopyTrianglesToBaseTriangles( pOAF->surfaces[i]->triangles,
\r
627 pOAF->surfaces[i]->numtriangles,
\r
628 g_data.surfData[i].baseTriangles );
\r
630 strcpy( g_data.surfData[i].header.name, pOAF->surfaces[i]->name );
\r
632 g_data.surfData[i].header.numTriangles = pOAF->surfaces[i]->numtriangles;
\r
633 g_data.surfData[i].header.numVerts = 0;
\r
636 if ( strstr( filename, gamedir + 1 ) )
\r
638 strcpy( shaderName, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
\r
642 strcpy( shaderName, filename );
\r
645 if ( strrchr( shaderName, '/' ) )
\r
646 *( strrchr( shaderName, '/' ) + 1 ) = 0;
\r
649 strcpy( shaderName, pOAF->surfaces[i]->materialname );
\r
651 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, pOAF->surfaces[i]->materialname );
\r
653 g_data.surfData[i].header.numShaders++;
\r
657 // compute unique vertices for each polyset
\r
659 for ( i = 0; i < g_data.model.numSurfaces; i++ )
\r
663 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
\r
665 bTri = &g_data.surfData[i].baseTriangles[t];
\r
667 for (j=0 ; j<3 ; j++)
\r
671 bVert = &bTri->v[j];
\r
673 // get the xyz index
\r
674 for ( k = 0; k < g_data.surfData[i].header.numVerts; k++ )
\r
676 if ( ( g_data.surfData[i].baseVertexes[k].st[0] == bVert->st[0] ) &&
\r
677 ( g_data.surfData[i].baseVertexes[k].st[1] == bVert->st[1] ) &&
\r
678 ( VectorCompare (bVert->xyz, g_data.surfData[i].baseVertexes[k].xyz) ) &&
\r
679 ( VectorCompare (bVert->normal, g_data.surfData[i].baseVertexes[k].normal) ) )
\r
681 break; // this vertex is already in the base vertex list
\r
685 if (k == g_data.surfData[i].header.numVerts) { // new index
\r
686 g_data.surfData[i].baseVertexes[g_data.surfData[i].header.numVerts] = *bVert;
\r
687 g_data.surfData[i].header.numVerts++;
\r
692 g_data.surfData[i].lodTriangles[t][j] = k;
\r
700 for ( i = 0; i < g_data.model.numSurfaces; i++ )
\r
702 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name )
\r
704 if ( pOAF->surfaces[i]->numtriangles != 1 )
\r
706 Error( "tag polysets must consist of only one triangle" );
\r
708 if ( strstr( filename, "_flash.md3" ) && !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) )
\r
710 printf( "found tag '%s'\n", pOAF->surfaces[i]->name );
\r
711 g_data.model.numTags++;
\r
717 static int LoadModelFile( const char *filename, polyset_t **psets, int *numpolysets )
\r
721 const char *frameFile;
\r
723 printf ("---------------------\n");
\r
724 if ( filename[1] != ':' )
\r
726 frameFile = filename;
\r
727 sprintf( file1, "%s/%s", g_cddir, frameFile );
\r
731 strcpy( file1, filename );
\r
734 time1 = FileTime (file1);
\r
736 Error ("%s doesn't exist", file1);
\r
739 // load the base triangles
\r
741 *psets = Polyset_LoadSets( file1, numpolysets, g_data.maxSurfaceTris );
\r
746 Polyset_SnapSets( *psets, *numpolysets );
\r
748 if ( strstr( file1, ".3ds" ) || strstr( file1, ".3DS" ) )
\r
749 return MD3_TYPE_BASE3DS;
\r
751 Error( "Unknown model file type" );
\r
753 return MD3_TYPE_UNKNOWN;
\r
761 void Cmd_Base( void )
\r
763 char filename[1024];
\r
765 GetToken( qfalse );
\r
766 sprintf( filename, "%s/%s", g_cddir, token );
\r
767 LoadBase( filename );
\r
770 static void LoadBase( const char *filename )
\r
775 ObjectAnimationFrame_t oaf;
\r
777 // determine polyset splitting threshold
\r
778 if ( TokenAvailable() )
\r
780 GetToken( qfalse );
\r
781 g_data.maxSurfaceTris = atoi( token );
\r
785 g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;
\r
788 g_data.type = LoadModelFile( filename, &psets, &numpolysets );
\r
790 Polyset_ComputeNormals( psets, numpolysets );
\r
792 g_data.model.numSurfaces = numpolysets;
\r
794 memset( &oaf, 0, sizeof( oaf ) );
\r
796 for ( i = 0; i < numpolysets; i++ )
\r
798 oaf.surfaces[i] = &psets[i];
\r
799 oaf.numSurfaces = numpolysets;
\r
802 BuildBaseFrame( filename, &oaf );
\r
804 free( psets[0].triangles );
\r
812 $spritebase xorg yorg width height
\r
814 Generate a single square for the model
\r
817 void Cmd_SpriteBase (void)
\r
819 float xl, yl, width, height;
\r
821 g_data.type = MD3_TYPE_SPRITE;
\r
828 width = atof(token);
\r
830 height = atof(token);
\r
832 // if (g_skipmodel || g_release || g_archive)
\r
835 printf ("---------------------\n");
\r
837 g_data.surfData[0].verts[0] = ( float * ) calloc( 1, sizeof( float ) * 6 * 4 );
\r
839 g_data.surfData[0].header.numVerts = 4;
\r
841 g_data.surfData[0].verts[0][0+0] = 0;
\r
842 g_data.surfData[0].verts[0][0+1] = -xl;
\r
843 g_data.surfData[0].verts[0][0+2] = yl + height;
\r
845 g_data.surfData[0].verts[0][0+3] = -1;
\r
846 g_data.surfData[0].verts[0][0+4] = 0;
\r
847 g_data.surfData[0].verts[0][0+5] = 0;
\r
848 g_data.surfData[0].baseVertexes[0].st[0] = 0;
\r
849 g_data.surfData[0].baseVertexes[0].st[1] = 0;
\r
852 g_data.surfData[0].verts[0][6+0] = 0;
\r
853 g_data.surfData[0].verts[0][6+1] = -xl - width;
\r
854 g_data.surfData[0].verts[0][6+2] = yl + height;
\r
856 g_data.surfData[0].verts[0][6+3] = -1;
\r
857 g_data.surfData[0].verts[0][6+4] = 0;
\r
858 g_data.surfData[0].verts[0][6+5] = 0;
\r
859 g_data.surfData[0].baseVertexes[1].st[0] = 1;
\r
860 g_data.surfData[0].baseVertexes[1].st[1] = 0;
\r
863 g_data.surfData[0].verts[0][12+0] = 0;
\r
864 g_data.surfData[0].verts[0][12+1] = -xl - width;
\r
865 g_data.surfData[0].verts[0][12+2] = yl;
\r
867 g_data.surfData[0].verts[0][12+3] = -1;
\r
868 g_data.surfData[0].verts[0][12+4] = 0;
\r
869 g_data.surfData[0].verts[0][12+5] = 0;
\r
870 g_data.surfData[0].baseVertexes[2].st[0] = 1;
\r
871 g_data.surfData[0].baseVertexes[2].st[1] = 1;
\r
874 g_data.surfData[0].verts[0][18+0] = 0;
\r
875 g_data.surfData[0].verts[0][18+1] = -xl;
\r
876 g_data.surfData[0].verts[0][18+2] = yl;
\r
878 g_data.surfData[0].verts[0][18+3] = -1;
\r
879 g_data.surfData[0].verts[0][18+4] = 0;
\r
880 g_data.surfData[0].verts[0][18+5] = 0;
\r
881 g_data.surfData[0].baseVertexes[3].st[0] = 0;
\r
882 g_data.surfData[0].baseVertexes[3].st[1] = 1;
\r
884 g_data.surfData[0].lodTriangles[0][0] = 0;
\r
885 g_data.surfData[0].lodTriangles[0][1] = 1;
\r
886 g_data.surfData[0].lodTriangles[0][2] = 2;
\r
888 g_data.surfData[0].lodTriangles[1][0] = 2;
\r
889 g_data.surfData[0].lodTriangles[1][1] = 3;
\r
890 g_data.surfData[0].lodTriangles[1][2] = 0;
\r
892 g_data.model.numSurfaces = 1;
\r
894 g_data.surfData[0].header.numTriangles = 2;
\r
895 g_data.surfData[0].header.numVerts = 4;
\r
897 g_data.model.numFrames = 1;
\r
901 ===========================================================================
\r
905 ===========================================================================
\r
913 void GrabFrame (const char *frame)
\r
918 md3Tag_t tagParent;
\r
920 float *frameNormals;
\r
921 const char *framefile;
\r
923 qboolean parentTagExists = qfalse;
\r
928 // the frame 'run1' will be looked for as either
\r
929 // run.1 or run1.tri, so the new alias sequence save
\r
930 // feature an be used
\r
931 if ( frame[1] != ':' )
\r
933 // framefile = FindFrameFile (frame);
\r
935 sprintf (file1, "%s/%s",g_cddir, framefile);
\r
939 strcpy( file1, frame );
\r
941 printf ("grabbing %s\n", file1);
\r
943 if (g_data.model.numFrames >= MD3_MAX_FRAMES)
\r
944 Error ("model.numFrames >= MD3_MAX_FRAMES");
\r
945 fr = &g_data.frames[g_data.model.numFrames];
\r
947 strcpy (fr->name, frame);
\r
949 psets = Polyset_LoadSets( file1, &numpolysets, g_data.maxSurfaceTris );
\r
954 Polyset_SnapSets( psets, numpolysets );
\r
957 // compute vertex normals
\r
959 Polyset_ComputeNormals( psets, numpolysets );
\r
962 // flip everything to compensate for the alias coordinate system
\r
963 // and perform global scale and adjust
\r
965 for ( i = 0; i < g_data.model.numSurfaces; i++ )
\r
967 triangle_t *ptri = psets[i].triangles;
\r
970 for ( t = 0; t < psets[i].numtriangles; t++ )
\r
973 for ( j = 0; j < 3; j++ )
\r
976 // scale and adjust
\r
977 for ( k = 0 ; k < 3 ; k++ ) {
\r
978 ptri[t].verts[j][k] = ptri[t].verts[j][k] * g_data.scale_up +
\r
981 if ( ptri[t].verts[j][k] > 1023 ||
\r
982 ptri[t].verts[j][k] < -1023 )
\r
984 Error( "Model extents too large" );
\r
992 // find and count tags, locate parent tag
\r
994 for ( i = 0; i < numpolysets; i++ )
\r
996 if ( strstr( psets[i].name, "tag_" ) == psets[i].name )
\r
998 if ( strstr( psets[i].name, "tag_parent" ) == psets[i].name )
\r
1000 if ( strstr( psets[i].name, "tag_parent" ) )
\r
1004 if ( parentTagExists )
\r
1005 Error( "Multiple parent tags not allowed" );
\r
1007 memcpy( tri[0], psets[i].triangles[0].verts[0], sizeof( float ) * 3 );
\r
1008 memcpy( tri[1], psets[i].triangles[0].verts[1], sizeof( float ) * 3 );
\r
1009 memcpy( tri[2], psets[i].triangles[0].verts[2], sizeof( float ) * 3 );
\r
1011 MD3_ComputeTagFromTri( &tagParent, tri );
\r
1012 strcpy( tagParent.name, psets[i].name );
\r
1013 g_data.tags[g_data.model.numFrames][numtags] = tagParent;
\r
1014 parentTagExists = qtrue;
\r
1021 if ( strcmp( psets[i].name, g_data.surfData[i].header.name ) )
\r
1023 Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, psets[i].name, g_modelname );
\r
1027 if ( numtags != g_data.model.numTags )
\r
1029 Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
\r
1032 if ( numpolysets != g_data.model.numSurfaces )
\r
1034 Error( "mismatched number of surfaces in frame(%d) vs. base(%d)", numpolysets-numtags, g_data.model.numSurfaces );
\r
1038 // prepare to accumulate bounds and normals
\r
1040 ClearBounds( fr->bounds[0], fr->bounds[1] );
\r
1043 // store the frame's vertices in the same order as the base. This assumes the
\r
1044 // triangles and vertices in this frame are in exactly the same order as in the
\r
1047 for ( i = 0, tagcount = 0; i < numpolysets; i++ )
\r
1050 triangle_t *pTris = psets[i].triangles;
\r
1052 strcpy( g_data.surfData[i].header.name, psets[i].name );
\r
1055 // parent tag adjust
\r
1057 if ( parentTagExists ) {
\r
1058 for ( t = 0; t < psets[i].numtriangles; t++ )
\r
1060 for ( j = 0; j < 3 ; j++ )
\r
1064 VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );
\r
1066 pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );
\r
1067 pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );
\r
1068 pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );
\r
1070 VectorCopy( pTris[t].normals[j], tmp );
\r
1071 pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );
\r
1072 pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );
\r
1073 pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );
\r
1079 // compute tag data
\r
1081 if ( strstr( psets[i].name, "tag_" ) == psets[i].name )
\r
1083 md3Tag_t *pTag = &g_data.tags[g_data.model.numFrames][tagcount];
\r
1086 strcpy( pTag->name, psets[i].name );
\r
1088 memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );
\r
1089 memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );
\r
1090 memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );
\r
1092 MD3_ComputeTagFromTri( pTag, tri );
\r
1097 if ( g_data.surfData[i].verts[g_data.model.numFrames] )
\r
1098 free( g_data.surfData[i].verts[g_data.model.numFrames] );
\r
1099 frameXyz = g_data.surfData[i].verts[g_data.model.numFrames] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
\r
1100 frameNormals = frameXyz + 3;
\r
1102 for ( t = 0; t < psets[i].numtriangles; t++ )
\r
1104 for ( j = 0; j < 3 ; j++ )
\r
1108 index = g_data.surfData[i].baseTriangles[t].v[j].index;
\r
1109 frameXyz[index*6+0] = pTris[t].verts[j][0];
\r
1110 frameXyz[index*6+1] = pTris[t].verts[j][1];
\r
1111 frameXyz[index*6+2] = pTris[t].verts[j][2];
\r
1112 frameNormals[index*6+0] = pTris[t].normals[j][0];
\r
1113 frameNormals[index*6+1] = pTris[t].normals[j][1];
\r
1114 frameNormals[index*6+2] = pTris[t].normals[j][2];
\r
1115 AddPointToBounds (&frameXyz[index*6], fr->bounds[0], fr->bounds[1] );
\r
1121 g_data.model.numFrames++;
\r
1123 // only free the first triangle array, all of the psets in this array share the
\r
1124 // same triangle pool!!!
\r
1125 // free( psets[0].triangles );
\r
1129 //===========================================================================
\r
1138 void Cmd_Frame (void)
\r
1140 while (TokenAvailable())
\r
1142 GetToken (qfalse);
\r
1145 if (g_release || g_archive)
\r
1147 g_data.model.numFrames = 1; // don't skip the writeout
\r
1151 GrabFrame( token );
\r
1162 void SkinFrom3DS( const char *filename )
\r
1169 _3DS_LoadPolysets( filename, &psets, &numPolysets, g_verbose );
\r
1171 for ( i = 0; i < numPolysets; i++ )
\r
1174 if ( strstr( filename, gamedir + 1 ) )
\r
1176 strcpy( name, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
\r
1180 strcpy( name, filename );
\r
1183 if ( strrchr( name, '/' ) )
\r
1184 *( strrchr( name, '/' ) + 1 ) = 0;
\r
1186 strcpy( name, psets[i].materialname );
\r
1187 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, name );
\r
1189 g_data.surfData[i].header.numShaders++;
\r
1192 free( psets[0].triangles );
\r
1196 void Cmd_Skin (void)
\r
1198 char skinfile[1024];
\r
1200 if ( g_data.type == MD3_TYPE_BASE3DS )
\r
1202 GetToken( qfalse );
\r
1204 sprintf( skinfile, "%s/%s", g_cddir, token );
\r
1206 if ( strstr( token, ".3ds" ) || strstr( token, ".3DS" ) )
\r
1208 SkinFrom3DS( skinfile );
\r
1212 Error( "Unknown file format for $skin '%s'\n", skinfile );
\r
1217 Error( "invalid model type while processing $skin" );
\r
1220 g_data.model.numSkins++;
\r
1228 This routine is also called for $oldskin
\r
1231 void Cmd_SpriteShader()
\r
1233 GetToken( qfalse );
\r
1234 strcpy( g_data.surfData[0].shaders[g_data.surfData[0].header.numShaders].name, token );
\r
1235 g_data.surfData[0].header.numShaders++;
\r
1236 g_data.model.numSkins++;
\r
1244 void Cmd_Origin (void)
\r
1246 // rotate points into frame of reference so model points down the
\r
1247 // positive x axis
\r
1248 // FIXME: use alias native coordinate system
\r
1249 GetToken (qfalse);
\r
1250 g_data.adjust[1] = -atof (token);
\r
1252 GetToken (qfalse);
\r
1253 g_data.adjust[0] = atof (token);
\r
1255 GetToken (qfalse);
\r
1256 g_data.adjust[2] = -atof (token);
\r
1265 void Cmd_ScaleUp (void)
\r
1267 GetToken (qfalse);
\r
1268 g_data.scale_up = atof (token);
\r
1269 if (g_skipmodel || g_release || g_archive)
\r
1272 printf ("Scale up: %f\n", g_data.scale_up);
\r
1280 Set a skin size other than the default
\r
1281 QUAKE3: not needed
\r
1284 void Cmd_Skinsize (void)
\r
1286 GetToken (qfalse);
\r
1287 g_data.fixedwidth = atoi(token);
\r
1288 GetToken (qfalse);
\r
1289 g_data.fixedheight = atoi(token);
\r
1296 Begin creating a model of the given name
\r
1299 void Cmd_Modelname (void)
\r
1301 FinishModel ( TYPE_UNKNOWN );
\r
1304 GetToken (qfalse);
\r
1305 strcpy (g_modelname, token);
\r
1306 StripExtension (g_modelname);
\r
1307 strcat (g_modelname, ".md3");
\r
1308 strcpy (g_data.model.name, g_modelname);
\r
1316 void Cmd_Cd (void)
\r
1318 if ( g_cddir[0]) {
\r
1319 Error ("$cd command without a $modelname");
\r
1322 GetToken (qfalse);
\r
1324 sprintf ( g_cddir, "%s%s", gamedir, token);
\r
1326 // if -only was specified and this cd doesn't match,
\r
1327 // skip the model (you only need to match leading chars,
\r
1328 // so you could regrab all monsters with -only models/monsters)
\r
1331 if (strncmp(token, g_only, strlen(g_only)))
\r
1333 g_skipmodel = qtrue;
\r
1334 printf ("skipping %s\n", token);
\r
1338 void Convert3DStoMD3( const char *file )
\r
1341 GrabFrame( file );
\r
1342 SkinFrom3DS( file );
\r
1344 strcpy( g_data.model.name, g_modelname );
\r
1346 FinishModel( TYPE_UNKNOWN );
\r
1353 void Cmd_3DSConvert()
\r
1357 FinishModel( TYPE_UNKNOWN );
\r
1360 GetToken( qfalse );
\r
1362 sprintf( file, "%s%s", gamedir, token );
\r
1363 strcpy( g_modelname, token );
\r
1364 if ( strrchr( g_modelname, '.' ) )
\r
1365 *strrchr( g_modelname, '.' ) = 0;
\r
1366 strcat( g_modelname, ".md3" );
\r
1368 if ( FileTime( file ) == -1 )
\r
1369 Error( "%s doesn't exist", file );
\r
1371 if ( TokenAvailable() )
\r
1373 GetToken( qfalse );
\r
1374 g_data.scale_up = atof( token );
\r
1377 Convert3DStoMD3( file );
\r
1380 static void ConvertASE( const char *filename, int type, qboolean grabAnims );
\r
1385 void Cmd_ASEConvert( qboolean grabAnims )
\r
1387 char filename[1024];
\r
1388 int type = TYPE_ITEM;
\r
1390 FinishModel( TYPE_UNKNOWN );
\r
1393 GetToken( qfalse );
\r
1394 sprintf( filename, "%s%s", gamedir, token );
\r
1396 strcpy (g_modelname, token);
\r
1397 StripExtension (g_modelname);
\r
1398 strcat (g_modelname, ".md3");
\r
1399 strcpy (g_data.model.name, g_modelname);
\r
1401 if ( !strstr( filename, ".ase" ) && !strstr( filename, ".ASE" ) )
\r
1402 strcat( filename, ".ASE" );
\r
1404 g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;
\r
1406 while ( TokenAvailable() )
\r
1408 GetToken( qfalse );
\r
1409 if ( !strcmp( token, "-origin" ) )
\r
1411 if ( !TokenAvailable() )
\r
1412 Error( "missing parameter for -origin" );
\r
1413 GetToken( qfalse );
\r
1414 g_data.aseAdjust[1] = -atof( token );
\r
1416 if ( !TokenAvailable() )
\r
1417 Error( "missing parameter for -origin" );
\r
1418 GetToken( qfalse );
\r
1419 g_data.aseAdjust[0] = atof (token);
\r
1421 if ( !TokenAvailable() )
\r
1422 Error( "missing parameter for -origin" );
\r
1423 GetToken( qfalse );
\r
1424 g_data.aseAdjust[2] = -atof (token);
\r
1426 else if ( !strcmp( token, "-lod" ) )
\r
1428 if ( !TokenAvailable() )
\r
1429 Error( "No parameter for -lod" );
\r
1430 GetToken( qfalse );
\r
1431 g_data.currentLod = atoi( token );
\r
1432 if ( g_data.currentLod > MD3_MAX_LODS - 1 )
\r
1434 Error( "-lod parameter too large! (%d)\n", g_data.currentLod );
\r
1437 if ( !TokenAvailable() )
\r
1438 Error( "No second parameter for -lod" );
\r
1439 GetToken( qfalse );
\r
1440 g_data.lodBias = atof( token );
\r
1442 else if ( !strcmp( token, "-maxtris" ) )
\r
1444 if ( !TokenAvailable() )
\r
1445 Error( "No parameter for -maxtris" );
\r
1446 GetToken( qfalse );
\r
1447 g_data.maxSurfaceTris = atoi( token );
\r
1449 else if ( !strcmp( token, "-playerparms" ) )
\r
1451 if ( !TokenAvailable() )
\r
1452 Error( "missing skip start parameter for -playerparms" );
\r
1453 GetToken( qfalse );
\r
1454 g_data.lowerSkipFrameStart = atoi( token );
\r
1457 if ( !TokenAvailable() )
\r
1458 Error( "missing skip end parameter for -playerparms" );
\r
1459 GetToken( qfalse );
\r
1460 g_data.lowerSkipFrameEnd = atoi( token );
\r
1463 if ( !TokenAvailable() )
\r
1464 Error( "missing upper parameter for -playerparms" );
\r
1465 GetToken( qfalse );
\r
1466 g_data.maxUpperFrames = atoi( token );
\r
1468 g_data.lowerSkipFrameEnd = g_data.maxUpperFrames - 1;
\r
1471 if ( !TokenAvailable() )
\r
1472 Error( "missing head parameter for -playerparms" );
\r
1473 GetToken( qfalse );
\r
1474 g_data.maxHeadFrames = atoi( token );
\r
1476 g_data.maxHeadFrames = 1;
\r
1478 if ( type != TYPE_ITEM )
\r
1479 Error( "invalid argument" );
\r
1481 type = TYPE_PLAYER;
\r
1483 else if ( !strcmp( token, "-weapon" ) )
\r
1485 if ( type != TYPE_ITEM )
\r
1486 Error( "invalid argument" );
\r
1488 type = TYPE_WEAPON;
\r
1492 g_data.type = MD3_TYPE_ASE;
\r
1494 if ( type == TYPE_WEAPON && grabAnims )
\r
1496 Error( "can't grab anims with weapon models" );
\r
1498 if ( type == TYPE_PLAYER && !grabAnims )
\r
1500 Error( "player models must be converted with $aseanimconvert" );
\r
1503 if ( type == TYPE_WEAPON )
\r
1505 ConvertASE( filename, type, qfalse );
\r
1506 ConvertASE( filename, TYPE_HAND, qtrue );
\r
1510 ConvertASE( filename, type, grabAnims );
\r
1514 static int GetSurfaceAnimations( SurfaceAnimation_t sanims[MAX_ANIM_SURFACES],
\r
1516 int skipFrameStart,
\r
1522 int numValidSurfaces;
\r
1524 int numFrames = -1;
\r
1526 if ( ( numSurfaces = ASE_GetNumSurfaces() ) > MAX_ANIM_SURFACES )
\r
1528 Error( "Too many surfaces in ASE" );
\r
1531 for ( numValidSurfaces = 0, i = 0; i < numSurfaces; i++ )
\r
1533 polyset_t *splitSets;
\r
1535 const char *surfaceName = ASE_GetSurfaceName( i );
\r
1537 if ( !surfaceName )
\r
1540 // Error( "Missing animation frames in model" );
\r
1543 if ( strstr( surfaceName, "tag_" ) ||
\r
1544 !strcmp( part, "any" ) ||
\r
1545 ( strstr( surfaceName, part ) == surfaceName ) )
\r
1548 // skip this if it's an inappropriate tag
\r
1549 if ( strcmp( part, "any" ) )
\r
1551 // ignore non-"tag_head" tags if this is the head
\r
1552 if ( !strcmp( part, "h_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_head" ) )
\r
1554 // ignore "tag_head" if this is the legs
\r
1555 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_head" ) )
\r
1557 // ignore "tag_weapon" if this is the legs
\r
1558 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_weapon" ) )
\r
1562 if ( ( sanims[numValidSurfaces].frames = ASE_GetSurfaceAnimation( i, &sanims[numValidSurfaces].numFrames, skipFrameStart, skipFrameEnd, maxFrames ) ) != 0 )
\r
1564 splitSets = Polyset_SplitSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames, &numNewFrames, g_data.maxSurfaceTris );
\r
1566 if ( numFrames == -1 )
\r
1567 numFrames = sanims[numValidSurfaces].numFrames;
\r
1568 else if ( numFrames != sanims[numValidSurfaces].numFrames )
\r
1569 Error( "Different number of animation frames on surfaces" );
\r
1571 if ( sanims[numValidSurfaces].frames != splitSets )
\r
1575 // free old data if we split the surfaces
\r
1576 for ( j = 0; j < sanims[numValidSurfaces].numFrames; j++ )
\r
1578 free( sanims[numValidSurfaces].frames[j].triangles );
\r
1579 free( sanims[numValidSurfaces].frames );
\r
1582 sanims[numValidSurfaces].frames = splitSets;
\r
1583 sanims[numValidSurfaces].numFrames = numNewFrames;
\r
1585 Polyset_SnapSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );
\r
1586 Polyset_ComputeNormals( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );
\r
1588 numValidSurfaces++;
\r
1593 return numValidSurfaces;
\r
1596 static int SurfaceOrderToFrameOrder( SurfaceAnimation_t sanims[], ObjectAnimationFrame_t oanims[], int numSurfaces )
\r
1599 int numFrames = -1;
\r
1602 ** we have the data here arranged in surface order, now we need to convert it to
\r
1605 for ( i = 0, s = 0; i < numSurfaces; i++ )
\r
1609 if ( sanims[i].frames )
\r
1611 if ( numFrames == -1 )
\r
1612 numFrames = sanims[i].numFrames;
\r
1613 else if ( numFrames != sanims[i].numFrames )
\r
1614 Error( "numFrames != sanims[i].numFrames (%d != %d)\n", numFrames, sanims[i].numFrames );
\r
1616 for ( j = 0; j < sanims[i].numFrames; j++ )
\r
1618 oanims[j].surfaces[s] = &sanims[i].frames[j];
\r
1619 oanims[j].numSurfaces = numSurfaces;
\r
1628 static void WriteMD3( const char *_filename, ObjectAnimationFrame_t oanims[], int numFrames )
\r
1630 char filename[1024];
\r
1632 strcpy( filename, _filename );
\r
1633 if ( strchr( filename, '.' ) )
\r
1634 *strchr( filename, '.' ) = 0;
\r
1635 strcat( filename, ".md3" );
\r
1638 static void BuildAnimationFromOAFs( const char *filename, ObjectAnimationFrame_t oanims[], int numFrames, int type )
\r
1640 int f, i, j, tagcount;
\r
1642 float *frameNormals;
\r
1644 g_data.model.numSurfaces = oanims[0].numSurfaces;
\r
1645 g_data.model.numFrames = numFrames;
\r
1646 if ( g_data.model.numFrames < 0)
\r
1647 Error ("model.numFrames < 0");
\r
1648 if ( g_data.model.numFrames >= MD3_MAX_FRAMES)
\r
1649 Error ("model.numFrames >= MD3_MAX_FRAMES");
\r
1651 // build base frame
\r
1652 BuildBaseFrame( filename, &oanims[0] );
\r
1654 // build animation frames
\r
1655 for ( f = 0; f < numFrames; f++ )
\r
1657 ObjectAnimationFrame_t *pOAF = &oanims[f];
\r
1658 qboolean parentTagExists = qfalse;
\r
1659 md3Tag_t tagParent;
\r
1663 fr = &g_data.frames[f];
\r
1665 strcpy( fr->name, "(from ASE)" );
\r
1667 // scale and adjust frame
\r
1668 for ( i = 0; i < pOAF->numSurfaces; i++ )
\r
1670 triangle_t *pTris = pOAF->surfaces[i]->triangles;
\r
1673 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
\r
1675 for ( j = 0; j < 3; j++ )
\r
1679 // scale and adjust
\r
1680 for ( k = 0 ; k < 3 ; k++ ) {
\r
1681 pTris[t].verts[j][k] = pTris[t].verts[j][k] * g_data.scale_up +
\r
1682 g_data.aseAdjust[k];
\r
1684 if ( pTris[t].verts[j][k] > 1023 ||
\r
1685 pTris[t].verts[j][k] < -1023 )
\r
1687 Error( "Model extents too large" );
\r
1695 // find and count tags, locate parent tag
\r
1697 for ( i = 0; i < pOAF->numSurfaces; i++ )
\r
1699 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name )
\r
1701 // ignore parent tags when grabbing a weapon model and this is the flash portion
\r
1702 if ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && strstr( filename, "_flash.md3" ) )
\r
1706 else if ( !strstr( filename, "_hand.md3" ) && (
\r
1707 ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && !strstr( filename, "_flash.md3" ) ) ||
\r
1708 ( !strcmp( pOAF->surfaces[i]->name, "tag_torso" ) && ( strstr( filename, "upper_" ) || strstr( filename, "upper.md3" ) ) ) ||
\r
1709 ( !strcmp( pOAF->surfaces[i]->name, "tag_head" ) && ( strstr( filename, "head.md3" ) || strstr( filename, "head_" ) ) ) ||
\r
1710 ( !strcmp( pOAF->surfaces[i]->name, "tag_flash" ) && strstr( filename, "_flash.md3" ) )||
\r
1711 ( !strcmp( pOAF->surfaces[i]->name, "tag_weapon" ) && type == TYPE_WEAPON ) ) )
\r
1715 if ( parentTagExists )
\r
1716 Error( "Multiple parent tags not allowed" );
\r
1718 memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );
\r
1719 memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );
\r
1720 memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );
\r
1722 MD3_ComputeTagFromTri( &tagParent, tri );
\r
1723 strcpy( tagParent.name, "tag_parent" );
\r
1724 g_data.tags[f][numtags] = tagParent;
\r
1725 parentTagExists = qtrue;
\r
1731 memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );
\r
1732 memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );
\r
1733 memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );
\r
1735 MD3_ComputeTagFromTri( &g_data.tags[f][numtags], tri );
\r
1736 strcpy( g_data.tags[f][numtags].name, pOAF->surfaces[i]->name );
\r
1737 if ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) )
\r
1738 * ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) + strlen( "tag_flash" ) ) = 0;
\r
1744 if ( strcmp( pOAF->surfaces[i]->name, g_data.surfData[i].header.name ) )
\r
1746 Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, pOAF->surfaces[i]->name, filename );
\r
1750 if ( numtags != g_data.model.numTags )
\r
1752 Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
\r
1756 // prepare to accumulate bounds and normals
\r
1758 ClearBounds( fr->bounds[0], fr->bounds[1] );
\r
1761 // store the frame's vertices in the same order as the base. This assumes the
\r
1762 // triangles and vertices in this frame are in exactly the same order as in the
\r
1765 for ( i = 0, tagcount = 0; i < pOAF->numSurfaces; i++ )
\r
1768 triangle_t *pTris = pOAF->surfaces[i]->triangles;
\r
1771 // parent tag adjust
\r
1773 if ( parentTagExists )
\r
1775 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
\r
1777 for ( j = 0; j < 3 ; j++ )
\r
1781 VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );
\r
1783 pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );
\r
1784 pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );
\r
1785 pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );
\r
1787 VectorCopy( pTris[t].normals[j], tmp );
\r
1788 pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );
\r
1789 pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );
\r
1790 pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );
\r
1796 // compute tag data
\r
1798 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name )
\r
1800 md3Tag_t *pTag = &g_data.tags[f][tagcount];
\r
1803 strcpy( pTag->name, pOAF->surfaces[i]->name );
\r
1805 memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );
\r
1806 memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );
\r
1807 memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );
\r
1809 MD3_ComputeTagFromTri( pTag, tri );
\r
1814 if ( g_data.surfData[i].verts[f] )
\r
1815 free( g_data.surfData[i].verts[f] );
\r
1816 frameXyz = g_data.surfData[i].verts[f] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
\r
1817 frameNormals = frameXyz + 3;
\r
1819 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
\r
1821 for ( j = 0; j < 3 ; j++ )
\r
1825 index = g_data.surfData[i].baseTriangles[t].v[j].index;
\r
1826 frameXyz[index*6+0] = pTris[t].verts[j][0];
\r
1827 frameXyz[index*6+1] = pTris[t].verts[j][1];
\r
1828 frameXyz[index*6+2] = pTris[t].verts[j][2];
\r
1829 frameNormals[index*6+0] = pTris[t].normals[j][0];
\r
1830 frameNormals[index*6+1] = pTris[t].normals[j][1];
\r
1831 frameNormals[index*6+2] = pTris[t].normals[j][2];
\r
1832 AddPointToBounds (&frameXyz[index*6], fr->bounds[0], fr->bounds[1] );
\r
1839 if ( strstr( filename, gamedir + 1 ) )
\r
1841 strcpy( g_modelname, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
\r
1845 strcpy( g_modelname, filename );
\r
1848 FinishModel( type );
\r
1852 static void ConvertASE( const char *filename, int type, qboolean grabAnims )
\r
1856 int numFrames = -1;
\r
1857 SurfaceAnimation_t surfaceAnimations[MAX_ANIM_SURFACES];
\r
1858 ObjectAnimationFrame_t objectAnimationFrames[MAX_ANIM_FRAMES];
\r
1859 char outfilename[1024];
\r
1862 ** load ASE into memory
\r
1864 ASE_Load( filename, g_verbose, grabAnims );
\r
1869 if ( type == TYPE_ITEM )
\r
1871 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "any", -1, -1, -1 );
\r
1873 if ( numSurfaces <= 0 )
\r
1874 Error( "numSurfaces <= 0" );
\r
1876 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
\r
1878 if ( numFrames <= 0 )
\r
1879 Error( "numFrames <= 0" );
\r
1881 strcpy( outfilename, filename );
\r
1882 if ( strrchr( outfilename, '.' ) )
\r
1883 *( strrchr( outfilename, '.' ) + 1 ) = 0;
\r
1884 strcat( outfilename, "md3" );
\r
1885 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
\r
1888 for ( i = 0; i < numSurfaces; i++ )
\r
1890 if ( surfaceAnimations[i].frames )
\r
1892 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
\r
1894 free( surfaceAnimations[i].frames[j].triangles );
\r
1896 free( surfaceAnimations[i].frames );
\r
1897 surfaceAnimations[i].frames = 0;
\r
1901 else if ( type == TYPE_PLAYER )
\r
1903 qboolean tagTorso = qfalse;
\r
1904 qboolean tagHead = qfalse;
\r
1905 qboolean tagWeapon = qfalse;
\r
1908 // verify that all necessary tags exist
\r
1910 numSurfaces = ASE_GetNumSurfaces();
\r
1911 for ( i = 0; i < numSurfaces; i++ )
\r
1913 if ( !strcmp( ASE_GetSurfaceName( i ), "tag_head" ) )
\r
1917 if ( !strcmp( ASE_GetSurfaceName( i ), "tag_torso" ) )
\r
1921 if ( !strcmp( ASE_GetSurfaceName( i ), "tag_weapon" ) )
\r
1923 tagWeapon = qtrue;
\r
1929 Error( "Missing tag_weapon!" );
\r
1933 Error( "Missing tag_torso!" );
\r
1937 Error( "Missing tag_weapon!" );
\r
1940 // get all upper body surfaces
\r
1941 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "u_", -1, -1, g_data.maxUpperFrames );
\r
1942 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
\r
1943 strcpy( outfilename, filename );
\r
1944 if ( strrchr( outfilename, '/' ) )
\r
1945 *( strrchr( outfilename, '/' ) + 1 ) = 0;
\r
1947 if ( g_data.currentLod == 0 )
\r
1949 strcat( outfilename, "upper.md3" );
\r
1955 sprintf( temp, "upper_%d.md3", g_data.currentLod );
\r
1956 strcat( outfilename, temp );
\r
1959 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
\r
1962 for ( i = 0; i < numSurfaces; i++ )
\r
1964 if ( surfaceAnimations[i].frames )
\r
1966 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
\r
1968 free( surfaceAnimations[i].frames[j].triangles );
\r
1970 free( surfaceAnimations[i].frames );
\r
1971 surfaceAnimations[i].frames = 0;
\r
1975 // get lower body surfaces
\r
1976 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "l_", g_data.lowerSkipFrameStart, g_data.lowerSkipFrameEnd, -1 );
\r
1977 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
\r
1978 strcpy( outfilename, filename );
\r
1979 if ( strrchr( outfilename, '/' ) )
\r
1980 *( strrchr( outfilename, '/' ) + 1 ) = 0;
\r
1982 if ( g_data.currentLod == 0 )
\r
1984 strcat( outfilename, "lower.md3" );
\r
1990 sprintf( temp, "lower_%d.md3", g_data.currentLod );
\r
1991 strcat( outfilename, temp );
\r
1993 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
\r
1996 for ( i = 0; i < numSurfaces; i++ )
\r
1998 if ( surfaceAnimations[i].frames )
\r
2000 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
\r
2002 free( surfaceAnimations[i].frames[j].triangles );
\r
2004 free( surfaceAnimations[i].frames );
\r
2005 surfaceAnimations[i].frames = 0;
\r
2009 // get head surfaces
\r
2010 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "h_", -1, -1, g_data.maxHeadFrames );
\r
2011 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
\r
2012 strcpy( outfilename, filename );
\r
2013 if ( strrchr( outfilename, '/' ) )
\r
2014 *( strrchr( outfilename, '/' ) + 1 ) = 0;
\r
2016 if ( g_data.currentLod == 0 )
\r
2018 strcat( outfilename, "head.md3" );
\r
2024 sprintf( temp, "head_%d.md3", g_data.currentLod );
\r
2025 strcat( outfilename, temp );
\r
2027 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
\r
2030 for ( i = 0; i < numSurfaces; i++ )
\r
2032 if ( surfaceAnimations[i].frames )
\r
2034 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
\r
2036 free( surfaceAnimations[i].frames[j].triangles );
\r
2038 free( surfaceAnimations[i].frames );
\r
2039 surfaceAnimations[i].frames = 0;
\r
2043 else if ( type == TYPE_WEAPON )
\r
2045 // get the weapon surfaces
\r
2046 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "w_", -1, -1, -1 );
\r
2047 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
\r
2049 strcpy( outfilename, filename );
\r
2050 if ( strrchr( outfilename, '.' ) )
\r
2051 *( strrchr( outfilename, '.' ) + 1 ) = 0;
\r
2052 strcat( outfilename, "md3" );
\r
2053 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
\r
2056 for ( i = 0; i < numSurfaces; i++ )
\r
2058 if ( surfaceAnimations[i].frames )
\r
2060 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
\r
2062 free( surfaceAnimations[i].frames[j].triangles );
\r
2064 free( surfaceAnimations[i].frames );
\r
2065 surfaceAnimations[i].frames = 0;
\r
2069 // get the flash surfaces
\r
2070 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "f_", -1, -1, -1 );
\r
2071 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
\r
2073 strcpy( outfilename, filename );
\r
2074 if ( strrchr( outfilename, '.' ) )
\r
2075 *strrchr( outfilename, '.' ) = 0;
\r
2076 strcat( outfilename, "_flash.md3" );
\r
2077 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM );
\r
2080 for ( i = 0; i < numSurfaces; i++ )
\r
2082 if ( surfaceAnimations[i].frames )
\r
2084 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
\r
2086 free( surfaceAnimations[i].frames[j].triangles );
\r
2088 free( surfaceAnimations[i].frames );
\r
2089 surfaceAnimations[i].frames = 0;
\r
2093 else if ( type == TYPE_HAND )
\r
2095 // get the hand tags
\r
2096 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "tag_", -1, -1, -1 );
\r
2097 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
\r
2099 strcpy( outfilename, filename );
\r
2100 if ( strrchr( outfilename, '.' ) )
\r
2101 *strrchr( outfilename, '.' ) = 0;
\r
2102 strcat( outfilename, "_hand.md3" );
\r
2103 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_HAND );
\r
2106 for ( i = 0; i < numSurfaces; i++ )
\r
2108 if ( surfaceAnimations[i].frames )
\r
2110 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
\r
2112 free( surfaceAnimations[i].frames[j].triangles );
\r
2114 free( surfaceAnimations[i].frames );
\r
2115 surfaceAnimations[i].frames = 0;
\r
2121 Error( "Unknown type passed to ConvertASE()" );
\r
2124 g_data.currentLod = 0;
\r
2125 g_data.lodBias = 0;
\r
2126 g_data.maxHeadFrames = 0;
\r
2127 g_data.maxUpperFrames = 0;
\r
2128 g_data.lowerSkipFrameStart = 0;
\r
2129 g_data.lowerSkipFrameEnd = 0;
\r
2130 VectorCopy( vec3_origin, g_data.aseAdjust );
\r
2132 // unload ASE from memory
\r