2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "qd_fmodel.h"
24 #include "qd_skeletons.h"
25 #include "skeletons.h"
28 #include "reference.h"
33 ========================================================================
35 .FM triangle flexible model file format
37 ========================================================================
40 //=================================================================
42 #define NUMVERTEXNORMALS 162
44 extern float avertexnormals[NUMVERTEXNORMALS][3];
46 #define MAX_GROUPS 128
54 #define TRIVERT_DIST .1
72 //================================================================
78 extern char g_skins[MAX_FM_SKINS][64];
81 extern fmstvert_t base_st[MAX_FM_VERTS];
84 extern fmtriangle_t triangles[MAX_FM_TRIANGLES];
87 fmframe_t g_frames[MAX_FM_FRAMES];
88 //fmframe_t *g_FMframes;
91 extern int commands[16384];
92 extern int numcommands;
96 // varibles set by commands
98 extern float scale_up; // set by $scale
99 extern vec3_t adjust; // set by $origin
100 extern int g_fixedwidth, g_fixedheight; // set by $skinsize
101 extern char modelname[64]; // set by $modelname
104 extern char *g_outputDir;
108 mesh_node_t *pmnodes = NULL;
109 fmmeshnode_t mesh_nodes[MAX_FM_MESH_NODES];
111 fmgroup_t groups[MAX_GROUPS];
113 int frame_to_group[MAX_FM_FRAMES];
116 // variables set by command line arguments
118 qboolean g_no_opimizations = false;
124 static int triangle_st[MAX_FM_TRIANGLES][3][2];
127 // number of gl vertices
128 extern int numglverts;
129 // indicates if a triangle has already been used in a glcmd
130 extern int used[MAX_FM_TRIANGLES];
131 // indicates if a triangle has translucency in it or not
132 static qboolean translucent[MAX_FM_TRIANGLES];
134 // main output file handle
135 extern FILE *headerouthandle;
136 // output sizes of buildst()
137 static int skin_width, skin_height;
141 static int total_skin_pixels;
142 static int skin_pixels_used;
144 int ShareVertex( trigroup_t trione, trigroup_t tritwo );
145 float DistBetween( vec3_t point1, vec3_t point2 );
146 int GetNumTris( trigroup_t *tris, int group );
147 void GetOneGroup( trigroup_t *tris, int grp, triangle_t* triangles );
148 void ScaleTris( vec3_t min, vec3_t max, int Width, int Height, float* u, float* v, int verts );
149 void NewDrawLine( int x1, int y1, int x2, int y2, unsigned char* picture, int width, int height );
153 void strupr( char *string ){
156 for ( i = 0 ; i < strlen( string ); i++ )
157 toupper( string[i] );
163 //==============================================================
170 static void ClearModel( void ){
171 memset( &fmheader, 0, sizeof( fmheader ) );
175 VectorCopy( vec3_origin, adjust );
176 g_fixedwidth = g_fixedheight = 0;
185 ClearSkeletalModel();
189 extern void H_printf( char *fmt, ... );
192 void WriteHeader( FILE *FH, char *Ident, int Version, int Size, void *Data ){
194 static long pos = -1;
197 if ( Size == 0 ) { // Don't write out empty packets
202 CurrentPos = ftell( FH );
203 Size = CurrentPos - pos + sizeof( header_t );
204 fseek( FH, pos, SEEK_SET );
207 else if ( Size == -1 ) {
211 memset( &header,0,sizeof( header ) );
212 strcpy( header.ident,Ident );
213 header.version = Version;
216 SafeWrite( FH, &header, sizeof( header ) );
219 SafeWrite( FH, Data, Size );
224 fseek( FH, 0, SEEK_END );
233 static void WriteModelFile( FILE *modelouthandle ){
238 byte buffer[MAX_FM_VERTS * 4 + 128];
241 IntListNode_t *current, *toFree;
242 qboolean framesWritten = false;
243 size_t temp,size = 0;
245 // probably should do this dynamically one of these days
248 float scale[3]; // multiply byte verts by this
249 float translate[3]; // then add this
250 } outFrames[MAX_FM_FRAMES];
252 #define DATA_SIZE 0x60000 // 384K had better be enough, particularly for the reference points
253 byte data[DATA_SIZE];
254 byte data2[DATA_SIZE];
256 fmheader.num_glcmds = numcommands;
257 fmheader.framesize = (int)&( (fmaliasframe_t *)0 )->verts[fmheader.num_xyz];
259 WriteHeader( modelouthandle, FM_HEADER_NAME, FM_HEADER_VER, sizeof( fmheader ), &fmheader );
262 // write out the skin names
265 WriteHeader( modelouthandle, FM_SKIN_NAME, FM_SKIN_VER, fmheader.num_skins * MAX_FM_SKINNAME, g_skins );
268 // write out the texture coordinates
271 for ( i = 0 ; i < fmheader.num_st ; i++ )
273 base_st[i].s = LittleShort( base_st[i].s );
274 base_st[i].t = LittleShort( base_st[i].t );
277 WriteHeader( modelouthandle, FM_ST_NAME, FM_ST_VER, fmheader.num_st * sizeof( base_st[0] ), base_st );
280 // write out the triangles
282 WriteHeader( modelouthandle, FM_TRI_NAME, FM_TRI_VER, fmheader.num_tris * sizeof( fmtriangle_t ), NULL );
284 for ( i = 0 ; i < fmheader.num_tris ; i++ )
289 for ( j = 0 ; j < 3 ; j++ )
291 tri.index_xyz[j] = LittleShort( triangles[i].index_xyz[j] );
292 tri.index_st[j] = LittleShort( triangles[i].index_st[j] );
295 SafeWrite( modelouthandle, &tri, sizeof( tri ) );
300 // write out the frames
302 WriteHeader( modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, fmheader.num_frames * fmheader.framesize, NULL );
303 // WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);
305 for ( i = 0 ; i < fmheader.num_frames ; i++ )
308 out = (fmaliasframe_t *)buffer;
310 strcpy( out->name, in->name );
311 for ( j = 0 ; j < 3 ; j++ )
313 out->scale[j] = ( in->maxs[j] - in->mins[j] ) / 255;
314 out->translate[j] = in->mins[j];
316 outFrames[i].scale[j] = out->scale[j];
317 outFrames[i].translate[j] = out->translate[j];
320 for ( j = 0 ; j < fmheader.num_xyz ; j++ )
322 // all of these are byte values, so no need to deal with endianness
323 out->verts[j].lightnormalindex = in->v[j].lightnormalindex;
325 for ( k = 0 ; k < 3 ; k++ )
327 // scale to byte values & min/max check
328 v = Q_rint( ( in->v[j].v[k] - out->translate[k] ) / out->scale[k] );
330 // clamp, so rounding doesn't wrap from 255.6 to 0
337 out->verts[j].v[k] = v;
341 for ( j = 0 ; j < 3 ; j++ )
343 out->scale[j] = LittleFloat( out->scale[j] );
344 out->translate[j] = LittleFloat( out->translate[j] );
347 SafeWrite( modelouthandle, out, fmheader.framesize );
350 // Go back and finish the header
351 // WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);
355 WriteHeader( modelouthandle, FM_SHORT_FRAME_NAME, FM_SHORT_FRAME_VER,FRAME_NAME_LEN * fmheader.num_frames, NULL );
356 for ( i = 0 ; i < fmheader.num_frames ; i++ )
359 SafeWrite( modelouthandle,in->name,FRAME_NAME_LEN );
361 WriteHeader( modelouthandle, FM_NORMAL_NAME, FM_NORMAL_VER,fmheader.num_xyz, NULL );
363 for ( j = 0 ; j < fmheader.num_xyz ; j++ )
364 SafeWrite( modelouthandle,&in->v[j].lightnormalindex,1 );
370 WriteHeader( modelouthandle, FM_GLCMDS_NAME, FM_GLCMDS_VER, numcommands * 4, commands );
373 // write out mesh nodes
375 for ( i = 0; i < fmheader.num_mesh_nodes; i++ )
377 memcpy( mesh_nodes[i].tris, pmnodes[i].tris, sizeof( mesh_nodes[i].tris ) );
378 memcpy( mesh_nodes[i].verts, pmnodes[i].verts, sizeof( mesh_nodes[i].verts ) );
379 mesh_nodes[i].start_glcmds = LittleShort( (short)pmnodes[i].start_glcmds );
380 mesh_nodes[i].num_glcmds = LittleShort( (short)pmnodes[i].num_glcmds );
383 WriteHeader( modelouthandle, FM_MESH_NAME, FM_MESH_VER, sizeof( fmmeshnode_t ) * fmheader.num_mesh_nodes, mesh_nodes );
393 char *mat; fmheader.num_xyz*3*g->degrees*sizeof(char)
394 char *ccomp; g->num_frames*g->degrees*sizeof(char)
395 char *cbase; fmheader.num_xyz*3*sizeof(unsigned char)
396 float *cscale; g->degrees*sizeof(float)
397 float *coffset; g->degrees*sizeof(float)
398 float trans[3]; 3*sizeof(float)
399 float scale[3]; 3*sizeof(float)
404 size = sizeof( int ) + fmheader.num_frames * sizeof( int );
405 for ( k = 0; k < num_groups; k++ )
408 size += sizeof( int ) * 3;
409 size += fmheader.num_xyz * 3 * g->degrees * sizeof( char );
410 size += g->num_frames * g->degrees * sizeof( char );
411 size += fmheader.num_xyz * 3 * sizeof( unsigned char );
412 size += g->degrees * sizeof( float );
413 size += g->degrees * sizeof( float );
414 size += 12 * sizeof( float );
416 WriteHeader( modelouthandle, FM_COMP_NAME, FM_COMP_VER,size, NULL );
417 SafeWrite( modelouthandle,&num_groups,sizeof( int ) );
418 SafeWrite( modelouthandle,frame_to_group,sizeof( int ) * fmheader.num_frames );
420 for ( k = 0; k < num_groups; k++ )
423 tmp = LittleLong( g->start_frame );
424 SafeWrite( modelouthandle,&tmp,sizeof( int ) );
425 tmp = LittleLong( g->num_frames );
426 SafeWrite( modelouthandle,&tmp,sizeof( int ) );
427 tmp = LittleLong( g->degrees );
428 SafeWrite( modelouthandle,&tmp,sizeof( int ) );
430 SafeWrite( modelouthandle,g->mat,fmheader.num_xyz * 3 * g->degrees * sizeof( char ) );
431 SafeWrite( modelouthandle,g->ccomp,g->num_frames * g->degrees * sizeof( char ) );
432 SafeWrite( modelouthandle,g->cbase,fmheader.num_xyz * 3 * sizeof( unsigned char ) );
433 SafeWrite( modelouthandle,g->cscale,g->degrees * sizeof( float ) );
434 SafeWrite( modelouthandle,g->coffset,g->degrees * sizeof( float ) );
435 SafeWrite( modelouthandle,g->trans,3 * sizeof( float ) );
436 SafeWrite( modelouthandle,g->scale,3 * sizeof( float ) );
437 SafeWrite( modelouthandle,g->bmin,3 * sizeof( float ) );
438 SafeWrite( modelouthandle,g->bmax,3 * sizeof( float ) );
447 // write the skeletal info
448 if ( g_skelModel.type != SKEL_NULL ) {
451 temp = sizeof( int ); // change this to a byte
452 memcpy( data + size, &g_skelModel.type, temp );
456 temp = sizeof( int ); // change this to a byte
457 memcpy( data + size, &numJointsInSkeleton[g_skelModel.type], temp );
460 // number of verts in each joint cluster
461 temp = sizeof( int ) * numJointsInSkeleton[g_skelModel.type]; // change this to shorts
462 memcpy( data + size, &g_skelModel.new_num_verts[1], temp );
466 for ( i = 0; i < numJointsInSkeleton[g_skelModel.type]; ++i )
468 current = g_skelModel.vertLists[i];
471 temp = sizeof( int ); // change this to a short
472 memcpy( data + size, ¤t->data, temp );
475 current = current->next;
476 free( toFree ); // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
480 if ( !num_groups ) { // joints are stored with regular verts for compressed models
481 framesWritten = true;
483 temp = sizeof( int ); // change this to a byte
484 memcpy( data + size, &framesWritten, temp );
487 for ( i = 0; i < fmheader.num_frames; ++i )
491 for ( j = 0 ; j < numJointsInSkeleton[g_skelModel.type]; ++j )
493 for ( k = 0 ; k < 3 ; k++ )
495 // scale to byte values & min/max check
496 v = Q_rint( ( in->joints[j].placement.origin[k] - outFrames[i].translate[k] ) / outFrames[i].scale[k] );
498 // write out origin as a float since they arn't clamped
499 temp = sizeof( float ); // change this to a short
500 assert( size + temp < DATA_SIZE );
501 memcpy( data + size, &v, temp );
505 for ( k = 0 ; k < 3 ; k++ )
507 v = Q_rint( ( in->joints[j].placement.direction[k] - outFrames[i].translate[k] ) / outFrames[i].scale[k] );
509 // write out origin as a float since they arn't clamped
510 temp = sizeof( float ); // change this to a short
511 assert( size + temp < DATA_SIZE );
512 memcpy( data + size, &v, temp );
516 for ( k = 0 ; k < 3 ; k++ )
518 v = Q_rint( ( in->joints[j].placement.up[k] - outFrames[i].translate[k] ) / outFrames[i].scale[k] );
520 // write out origin as a float since they arn't clamped
521 temp = sizeof( float ); // change this to a short
522 assert( size + temp < DATA_SIZE );
523 memcpy( data + size, &v, temp );
532 temp = sizeof( int ); // change this to a byte
533 memcpy( data + size, &framesWritten, temp );
537 WriteHeader( modelouthandle, FM_SKELETON_NAME, FM_SKELETON_VER, size, data );
540 if ( g_skelModel.references != REF_NULL ) {
544 if ( RefPointNum <= 0 ) { // Hard-coded labels
545 refnum = numReferences[g_skelModel.references];
548 { // Labels indicated in QDT
549 refnum = RefPointNum;
552 temp = sizeof( int ); // change this to a byte
553 memcpy( data2 + size, &g_skelModel.references, temp );
557 framesWritten = true;
559 temp = sizeof( int ); // change this to a byte
560 memcpy( data2 + size, &framesWritten, temp );
563 for ( i = 0; i < fmheader.num_frames; ++i )
567 for ( j = 0 ; j < refnum; ++j )
569 for ( k = 0 ; k < 3 ; k++ )
571 // scale to byte values & min/max check
572 v = Q_rint( ( in->references[j].placement.origin[k] - outFrames[i].translate[k] ) / outFrames[i].scale[k] );
574 // write out origin as a float since they arn't clamped
575 temp = sizeof( float ); // change this to a short
576 assert( size + temp < DATA_SIZE );
577 memcpy( data2 + size, &v, temp );
581 for ( k = 0 ; k < 3 ; k++ )
583 v = Q_rint( ( in->references[j].placement.direction[k] - outFrames[i].translate[k] ) / outFrames[i].scale[k] );
585 // write out origin as a float since they arn't clamped
586 temp = sizeof( float ); // change this to a short
587 assert( size + temp < DATA_SIZE );
588 memcpy( data2 + size, &v, temp );
592 for ( k = 0 ; k < 3 ; k++ )
594 v = Q_rint( ( in->references[j].placement.up[k] - outFrames[i].translate[k] ) / outFrames[i].scale[k] );
596 // write out origin as a float since they arn't clamped
597 temp = sizeof( float ); // change this to a short
598 assert( size + temp < DATA_SIZE );
599 memcpy( data2 + size, &v, temp );
605 else // FINISH ME: references need to be stored with regular verts for compressed models
607 framesWritten = false;
609 temp = sizeof( int ); // change this to a byte
610 memcpy( data2 + size, &framesWritten, temp );
614 WriteHeader( modelouthandle, FM_REFERENCES_NAME, FM_REFERENCES_VER, size, data2 );
618 static void CompressFrames(){
624 for ( i = 0; i < fmheader.num_frames; i++ )
626 while ( i >= groups[j].start_frame + groups[j].num_frames && j < num_groups - 1 )
628 frame_to_group[i] = j;
631 for ( k = 0; k < num_groups; k++ )
635 printf( "\nCompressing Frames for group %i...\n", k );
636 AnimCompressInit( g->num_frames,fmheader.num_xyz,g->degrees );
637 for ( i = 0; i < g->num_frames; i++ )
639 in = &g_frames[i + g->start_frame];
640 for ( j = 0; j < fmheader.num_xyz; j++ )
641 AnimSetFrame( i,j,in->v[j].v[0],in->v[j].v[1],in->v[j].v[2] );
644 g->mat = (char *) SafeMalloc( fmheader.num_xyz * 3 * g->degrees * sizeof( char ), "CompressFrames" );
645 g->ccomp = (char *) SafeMalloc( g->num_frames * g->degrees * sizeof( char ), "CompressFrames" );
646 g->cbase = (char *) SafeMalloc( fmheader.num_xyz * 3 * sizeof( unsigned char ), "CompressFrames" );
647 g->cscale = (float *) SafeMalloc( g->degrees * sizeof( float ), "CompressFrames" );
648 g->coffset = (float *) SafeMalloc( g->degrees * sizeof( float ), "CompressFrames" );
649 AnimCompressToBytes( g->trans,g->scale,g->mat,g->ccomp,g->cbase,g->cscale,g->coffset,g->bmin,g->bmax );
654 static void OptimizeVertices( void ){
655 qboolean vert_used[MAX_FM_VERTS];
656 short vert_replacement[MAX_FM_VERTS];
657 int i,j,k,l,pos,bit,set_pos,set_bit;
661 static IntListNode_t *newVertLists[NUM_CLUSTERS];
662 static int newNum_verts[NUM_CLUSTERS];
663 IntListNode_t *current, *next;
665 printf( "Optimizing vertices..." );
667 memset( vert_used, 0, sizeof( vert_used ) );
669 if ( g_skelModel.clustered == true ) {
670 memset( newNum_verts, 0, sizeof( newNum_verts ) );
671 memset( newVertLists, 0, sizeof( newVertLists ) );
676 // search for common points among all the frames
677 for ( i = 0 ; i < fmheader.num_frames ; i++ )
681 for ( j = 0; j < fmheader.num_xyz; j++ )
683 for ( k = 0,Found = false; k < j; k++ )
684 { // starting from the beginning always ensures vert_replacement points to the first point in the array
685 if ( in->v[j].v[0] == in->v[k].v[0] &&
686 in->v[j].v[1] == in->v[k].v[1] &&
687 in->v[j].v[2] == in->v[k].v[2] ) {
689 vert_replacement[j] = k;
696 if ( !vert_used[j] ) {
704 // recompute the light normals
705 for ( i = 0 ; i < fmheader.num_frames ; i++ )
709 for ( j = 0; j < fmheader.num_xyz; j++ )
711 if ( !vert_used[j] ) {
712 k = vert_replacement[j];
714 VectorAdd( in->v[j].vnorm.normalsum, in->v[k].vnorm.normalsum, in->v[k].vnorm.normalsum );
715 in->v[k].vnorm.numnormals += in->v[j].vnorm.numnormals++;
719 for ( j = 0 ; j < fmheader.num_xyz ; j++ )
726 c = in->v[j].vnorm.numnormals;
728 Error( "Vertex with no triangles attached" );
731 VectorScale( in->v[j].vnorm.normalsum, 1.0 / c, v );
732 VectorNormalize( v, v );
737 for ( k = 0 ; k < NUMVERTEXNORMALS ; k++ )
741 dot = DotProduct( v, avertexnormals[k] );
742 if ( dot > maxdot ) {
748 in->v[j].lightnormalindex = maxdotindex;
752 // create substitution list
754 for ( i = 0; i < fmheader.num_xyz; i++ )
756 if ( vert_used[i] ) {
757 vert_replacement[i] = num_unique;
762 vert_replacement[i] = vert_replacement[vert_replacement[i]];
765 // vert_replacement[i] is the new index, i is the old index
766 // need to add the new index to the cluster list if old index was in it
767 if ( g_skelModel.clustered == true ) {
768 for ( k = 0; k < numJointsInSkeleton[g_skelModel.type]; ++k )
770 for ( l = 0, current = g_skelModel.vertLists[k];
771 l < g_skelModel.new_num_verts[k + 1]; ++l, current = current->next )
773 if ( current->data == i ) {
774 IntListNode_t *current2;
776 qboolean added = false;
778 for ( m = 0, current2 = newVertLists[k]; m < newNum_verts[k + 1];
779 ++m, current2 = current2->next )
781 if ( current2->data == vert_replacement[i] ) {
788 ++newNum_verts[k + 1];
790 next = newVertLists[k];
792 newVertLists[k] = (IntListNode_t *) SafeMalloc( sizeof( IntListNode_t ), "OptimizeVertices" );
793 // freed after model write out
795 newVertLists[k]->data = vert_replacement[i];
796 newVertLists[k]->next = next;
806 for ( i = 0 ; i < fmheader.num_frames ; i++ )
810 for ( j = 0; j < fmheader.num_xyz; j++ )
812 in->v[vert_replacement[j]] = in->v[j];
817 for ( i = 0; i < numJointsInSkeleton[g_skelModel.type]; ++i )
819 IntListNode_t *toFree;
820 current = g_skelModel.vertLists[i];
825 current = current->next;
826 free( toFree ); // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
829 g_skelModel.vertLists[i] = newVertLists[i];
830 g_skelModel.new_num_verts[i + 1] = newNum_verts[i + 1];
834 for ( k = 0; k < numJointsInSkeleton[g_skelModel.type]; ++k )
836 for ( l = 0, current = g_skelModel.vertLists[k];
837 l < g_skelModel.new_num_verts[k + 1]; ++l, current = current->next )
839 IntListNode_t *current2;
842 for ( m = l + 1, current2 = current->next; m < newNum_verts[k + 1];
843 ++m, current2 = current2->next )
845 if ( current->data == current2->data ) {
846 printf( "Warning duplicate vertex: %d\n", current->data );
854 for ( i = 0; i < fmheader.num_mesh_nodes; i++ )
855 { // reset the vert bits
856 memset( pmnodes[i].verts,0,sizeof( pmnodes[i].verts ) );
859 // repleace the master triangle list vertex indexes and update the vert bits for each mesh node
860 for ( i = 0 ; i < fmheader.num_tris ; i++ )
863 bit = 1 << ( i & 7 );
865 for ( j = 0 ; j < 3 ; j++ )
867 set_bit = set_pos = triangles[i].index_xyz[j] = vert_replacement[triangles[i].index_xyz[j]];
870 set_bit = 1 << ( set_bit & 7 );
872 for ( k = 0; k < fmheader.num_mesh_nodes; k++ )
874 if ( !( pmnodes[k].tris[pos] & bit ) ) {
877 pmnodes[k].verts[set_pos] |= set_bit;
882 for ( i = 0; i < numcommands; i++ )
890 for ( i++; j; j--,i += 3 )
892 commands[i + 2] = vert_replacement[commands[i + 2]];
897 printf( "Reduced by %d\n",fmheader.num_xyz - num_unique );
899 fmheader.num_xyz = num_unique;
901 // tack on the reference verts to the regular verts
902 if ( g_skelModel.references != REF_NULL ) {
907 if ( RefPointNum <= 0 ) { // Hard-coded labels
908 refnum = numReferences[g_skelModel.references];
911 { // Labels indicated in QDT
912 refnum = RefPointNum;
916 for ( i = 0; i < fmheader.num_frames; ++i )
919 index = fmheader.num_xyz;
921 for ( j = 0 ; j < refnum; ++j )
923 VectorCopy( in->references[j].placement.origin, in->v[index].v );
926 VectorCopy( in->references[j].placement.direction, in->v[index].v );
929 VectorCopy( in->references[j].placement.up, in->v[index].v );
934 fmheader.num_xyz += refnum * 3;
937 // tack on the skeletal joint verts to the regular verts
938 if ( g_skelModel.type != SKEL_NULL ) {
942 for ( i = 0; i < fmheader.num_frames; ++i )
945 index = fmheader.num_xyz;
947 for ( j = 0 ; j < numJointsInSkeleton[g_skelModel.type]; ++j )
949 VectorCopy( in->joints[j].placement.origin, in->v[index].v );
952 VectorCopy( in->joints[j].placement.direction, in->v[index].v );
955 VectorCopy( in->joints[j].placement.up, in->v[index].v );
960 fmheader.num_xyz += numJointsInSkeleton[g_skelModel.type] * 3;
973 void FMFinishModel( void ){
974 FILE *modelouthandle;
975 int i,j,length,tris,verts,bit,pos,total_tris,total_verts;
979 if ( !fmheader.num_frames ) {
984 // copy to release directory tree if doing a release build
987 if ( modelname[0] ) {
988 sprintf( name, "%s", modelname );
991 sprintf( name, "%s/tris.fm", cdpartial );
995 for ( i = 0 ; i < fmheader.num_skins ; i++ )
997 ReleaseFile( g_skins[i] );
999 fmheader.num_frames = 0;
1006 for ( i = 0; i < fmheader.num_tris; i++ )
1007 if ( translucent[i] ) {
1011 if ( !g_no_opimizations ) {
1016 // write the model output file
1018 if ( modelname[0] ) {
1019 sprintf( name, "%s%s", g_outputDir, modelname );
1022 sprintf( name, "%s/tris.fm", g_outputDir );
1024 printf( "saving to %s\n", name );
1026 modelouthandle = SafeOpenWrite( name );
1028 WriteModelFile( modelouthandle );
1030 printf( "%3dx%3d skin\n", fmheader.skinwidth, fmheader.skinheight );
1031 printf( "First frame boundaries:\n" );
1032 printf( " minimum x: %3f\n", g_frames[0].mins[0] );
1033 printf( " maximum x: %3f\n", g_frames[0].maxs[0] );
1034 printf( " minimum y: %3f\n", g_frames[0].mins[1] );
1035 printf( " maximum y: %3f\n", g_frames[0].maxs[1] );
1036 printf( " minimum z: %3f\n", g_frames[0].mins[2] );
1037 printf( " maximum z: %3f\n", g_frames[0].maxs[2] );
1038 printf( "%4d vertices\n", fmheader.num_xyz );
1039 printf( "%4d triangles, %4d of them translucent\n", fmheader.num_tris, trans_count );
1040 printf( "%4d frame\n", fmheader.num_frames );
1041 printf( "%4d glverts\n", numglverts );
1042 printf( "%4d glcmd\n", fmheader.num_glcmds );
1043 printf( "%4d skins\n", fmheader.num_skins );
1044 printf( "%4d mesh nodes\n", fmheader.num_mesh_nodes );
1045 printf( "wasted pixels: %d / %d (%5.2f Percent)\n",total_skin_pixels - skin_pixels_used,
1046 total_skin_pixels, (double)( total_skin_pixels - skin_pixels_used ) / (double)total_skin_pixels * 100.0 );
1048 printf( "file size: %d\n", (int)ftell( modelouthandle ) );
1049 printf( "---------------------\n" );
1052 if ( fmheader.num_mesh_nodes ) {
1053 total_tris = total_verts = 0;
1054 printf( "Node Name Tris Verts\n" );
1055 printf( "--------------------------------- ---- -----\n" );
1056 for ( i = 0; i < fmheader.num_mesh_nodes; i++ )
1060 for ( j = 0; j < MAXTRIANGLES; j++ )
1063 bit = 1 << ( ( j ) & 7 );
1064 if ( pmnodes[i].tris[pos] & bit ) {
1068 for ( j = 0; j < MAX_FM_VERTS; j++ )
1071 bit = 1 << ( ( j ) & 7 );
1072 if ( pmnodes[i].verts[pos] & bit ) {
1077 printf( "%-33s %4d %5d\n",pmnodes[i].name,tris,verts );
1080 total_verts += verts;
1082 printf( "--------------------------------- ---- -----\n" );
1083 printf( "%-33s %4d %5d\n","TOTALS",total_tris,total_verts );
1086 fclose( modelouthandle );
1088 // finish writing header file
1091 // scale_up is usefull to allow step distances to be adjusted
1092 H_printf( "#define MODEL_SCALE\t\t%f\n", scale_up );
1095 if ( fmheader.num_mesh_nodes ) {
1097 H_printf( "#define NUM_MESH_NODES\t\t%d\n\n",fmheader.num_mesh_nodes );
1098 for ( i = 0; i < fmheader.num_mesh_nodes; i++ )
1100 strcpy( name, pmnodes[i].name );
1102 length = strlen( name );
1103 for ( j = 0; j < length; j++ )
1105 if ( name[j] == ' ' ) {
1109 H_printf( "#define MESH_%s\t\t%d\n", name, i );
1113 fclose( headerouthandle );
1114 headerouthandle = NULL;
1120 =================================================================
1122 ALIAS MODEL DISPLAY LIST GENERATION
1124 =================================================================
1127 extern int strip_xyz[128];
1128 extern int strip_st[128];
1129 extern int strip_tris[128];
1130 extern int stripcount;
1137 static int StripLength( int starttri, int startv, int num_tris, int node ){
1141 fmtriangle_t *last, *check;
1147 last = &triangles[starttri];
1149 strip_xyz[0] = last->index_xyz[( startv ) % 3];
1150 strip_xyz[1] = last->index_xyz[( startv + 1 ) % 3];
1151 strip_xyz[2] = last->index_xyz[( startv + 2 ) % 3];
1152 strip_st[0] = last->index_st[( startv ) % 3];
1153 strip_st[1] = last->index_st[( startv + 1 ) % 3];
1154 strip_st[2] = last->index_st[( startv + 2 ) % 3];
1156 strip_tris[0] = starttri;
1159 m1 = last->index_xyz[( startv + 2 ) % 3];
1160 st1 = last->index_st[( startv + 2 ) % 3];
1161 m2 = last->index_xyz[( startv + 1 ) % 3];
1162 st2 = last->index_st[( startv + 1 ) % 3];
1164 // look for a matching triangle
1166 for ( j = starttri + 1, check = &triangles[starttri + 1]
1167 ; j < num_tris ; j++, check++ )
1170 bit = 1 << ( j & 7 );
1171 if ( !( pmnodes[node].tris[pos] & bit ) ) {
1174 for ( k = 0 ; k < 3 ; k++ )
1176 if ( check->index_xyz[k] != m1 ) {
1179 if ( check->index_st[k] != st1 ) {
1182 if ( check->index_xyz[ ( k + 1 ) % 3 ] != m2 ) {
1185 if ( check->index_st[ ( k + 1 ) % 3 ] != st2 ) {
1189 // this is the next part of the fan
1191 // if we can't use this triangle, this tristrip is done
1192 if ( used[j] || translucent[j] != translucent[starttri] ) {
1197 if ( stripcount & 1 ) {
1198 m2 = check->index_xyz[ ( k + 2 ) % 3 ];
1199 st2 = check->index_st[ ( k + 2 ) % 3 ];
1203 m1 = check->index_xyz[ ( k + 2 ) % 3 ];
1204 st1 = check->index_st[ ( k + 2 ) % 3 ];
1207 strip_xyz[stripcount + 2] = check->index_xyz[ ( k + 2 ) % 3 ];
1208 strip_st[stripcount + 2] = check->index_st[ ( k + 2 ) % 3 ];
1209 strip_tris[stripcount] = j;
1218 // clear the temp used flags
1219 for ( j = starttri + 1 ; j < num_tris ; j++ )
1220 if ( used[j] == 2 ) {
1233 static int FanLength( int starttri, int startv, int num_tris, int node ){
1237 fmtriangle_t *last, *check;
1243 last = &triangles[starttri];
1245 strip_xyz[0] = last->index_xyz[( startv ) % 3];
1246 strip_xyz[1] = last->index_xyz[( startv + 1 ) % 3];
1247 strip_xyz[2] = last->index_xyz[( startv + 2 ) % 3];
1248 strip_st[0] = last->index_st[( startv ) % 3];
1249 strip_st[1] = last->index_st[( startv + 1 ) % 3];
1250 strip_st[2] = last->index_st[( startv + 2 ) % 3];
1252 strip_tris[0] = starttri;
1255 m1 = last->index_xyz[( startv + 0 ) % 3];
1256 st1 = last->index_st[( startv + 0 ) % 3];
1257 m2 = last->index_xyz[( startv + 2 ) % 3];
1258 st2 = last->index_st[( startv + 2 ) % 3];
1261 // look for a matching triangle
1263 for ( j = starttri + 1, check = &triangles[starttri + 1]
1264 ; j < num_tris ; j++, check++ )
1267 bit = 1 << ( j & 7 );
1268 if ( !( pmnodes[node].tris[pos] & bit ) ) {
1271 for ( k = 0 ; k < 3 ; k++ )
1273 if ( check->index_xyz[k] != m1 ) {
1276 if ( check->index_st[k] != st1 ) {
1279 if ( check->index_xyz[ ( k + 1 ) % 3 ] != m2 ) {
1282 if ( check->index_st[ ( k + 1 ) % 3 ] != st2 ) {
1286 // this is the next part of the fan
1288 // if we can't use this triangle, this tristrip is done
1289 if ( used[j] || translucent[j] != translucent[starttri] ) {
1294 m2 = check->index_xyz[ ( k + 2 ) % 3 ];
1295 st2 = check->index_st[ ( k + 2 ) % 3 ];
1297 strip_xyz[stripcount + 2] = m2;
1298 strip_st[stripcount + 2] = st2;
1299 strip_tris[stripcount] = j;
1308 // clear the temp used flags
1309 for ( j = starttri + 1 ; j < num_tris ; j++ )
1310 if ( used[j] == 2 ) {
1323 Generate a list of trifans or strips
1324 for the model, which holds for all frames
1327 static void BuildGlCmds( void ){
1331 int len, bestlen, besttype;
1334 int best_tris[1024];
1346 for ( l = 0; l < fmheader.num_mesh_nodes; l++ )
1348 memset( used, 0, sizeof( used ) );
1350 pmnodes[l].start_glcmds = numcommands;
1352 for ( trans_check = 0; trans_check < 2; trans_check++ )
1354 for ( i = 0 ; i < fmheader.num_tris ; i++ )
1357 bit = 1 << ( i & 7 );
1358 if ( !( pmnodes[l].tris[pos] & bit ) ) {
1362 // pick an unused triangle and start the trifan
1363 if ( used[i] || trans_check != translucent[i] ) {
1368 for ( type = 0 ; type < 2 ; type++ )
1371 for ( startv = 0 ; startv < 3 ; startv++ )
1374 len = StripLength( i, startv, fmheader.num_tris, l );
1377 len = FanLength( i, startv, fmheader.num_tris, l );
1379 if ( len > bestlen ) {
1382 for ( j = 0 ; j < bestlen + 2 ; j++ )
1384 best_st[j] = strip_st[j];
1385 best_xyz[j] = strip_xyz[j];
1387 for ( j = 0 ; j < bestlen ; j++ )
1388 best_tris[j] = strip_tris[j];
1393 // mark the tris on the best strip/fan as used
1394 for ( j = 0 ; j < bestlen ; j++ )
1395 used[best_tris[j]] = 1;
1397 if ( besttype == 1 ) {
1398 commands[numcommands++] = ( bestlen + 2 );
1401 commands[numcommands++] = -( bestlen + 2 );
1404 numglverts += bestlen + 2;
1406 for ( j = 0 ; j < bestlen + 2 ; j++ )
1408 // emit a vertex into the reorder buffer
1411 // emit s/t coords into the commands stream
1415 s = ( s ) / fmheader.skinwidth;
1416 t = ( t ) / fmheader.skinheight;
1418 *(float *)&commands[numcommands++] = s;
1419 *(float *)&commands[numcommands++] = t;
1420 *(int *)&commands[numcommands++] = best_xyz[j];
1424 commands[numcommands++] = 0; // end of list marker
1425 pmnodes[l].num_glcmds = numcommands - pmnodes[l].start_glcmds;
1431 ===============================================================
1435 ===============================================================
1439 #define LINE_NORMAL 1
1441 #define LINE_DOTTED 3
1444 #define ASCII_SPACE 32
1446 int LineType = LINE_NORMAL;
1447 extern unsigned char pic[SKINPAGE_HEIGHT * SKINPAGE_WIDTH], pic_palette[768];
1448 unsigned char LineColor = 255;
1449 int ScaleWidth, ScaleHeight;
1452 static char *CharDefs[] =
1454 "-------------------------",
1455 "-------------------------", // !
1456 "-------------------------", // "
1457 "-------------------------", // #
1458 "-------------------------", // $
1459 "-------------------------", // %
1460 "-------------------------", // &
1461 "--*----*-----------------", // '
1462 "-*---*----*----*-----*---", // (
1463 "*-----*----*----*---*----", // )
1464 "-----*--*--**---**--*--*-", // *
1465 "-------------------------", // +
1466 "----------------**--**---", // ,
1467 "-------------------------", // -
1468 "----------------**---**--", // .
1469 "-------------------------", // /
1470 " *** * *** * *** * *** ", // 0
1472 "**** * *** * *****",
1473 "**** * *** ***** ",
1474 " ** * * * * ***** * ",
1475 "**** * **** ***** ",
1476 " *** * **** * * *** ",
1478 " *** * * *** * * *** ",
1479 " *** * * **** * *** ", // 9
1480 "-**---**--------**---**--", // :
1481 "-------------------------", // ;
1482 "-------------------------", // <
1483 "-------------------------", // =
1484 "-------------------------", // >
1485 "-------------------------", // ?
1486 "-------------------------", // @
1487 "-***-*---*******---**---*", // A
1488 "****-*---*****-*---*****-",
1489 "-*****----*----*-----****",
1490 "****-*---**---**---*****-",
1491 "******----****-*----*****",
1492 "******----****-*----*----",
1493 "-*****----*--***---*-****",
1494 "*---**---*******---**---*",
1495 "-***---*----*----*---***-",
1496 "----*----*----**---*-***-",
1497 "-*--*-*-*--**---*-*--*--*",
1498 "-*----*----*----*----****",
1499 "*---***-***-*-**---**---*",
1500 "*---***--**-*-**--***---*",
1501 "-***-*---**---**---*-***-",
1502 "****-*---*****-*----*----",
1503 "-***-*---**---*-***----**",
1504 "****-*---*****-*-*--*--**",
1505 "-*****-----***-----*****-",
1506 "*****--*----*----*----*--",
1507 "*---**---**---**---******",
1508 "*---**---**---*-*-*---*--",
1509 "*---**---**-*-***-***---*",
1510 "*---*-*-*---*---*-*-*---*",
1511 "*---**---*-*-*---*----*--",
1512 "*****---*---*---*---*****" // Z
1515 void DrawLine( int x1, int y1, int x2, int y2 ){
1519 float xfrac, yfrac, xstep, ystep;
1528 count = adx > ady ? adx : ady;
1531 if ( count > 300 ) {
1532 printf( "Bad count\n" );
1533 return; // don't ever hang up on bad data
1539 xstep = (float)dx / count;
1540 ystep = (float)dy / count;
1547 if ( xfrac < SKINPAGE_WIDTH && yfrac < SKINPAGE_HEIGHT ) {
1548 pic[(int)yfrac * SKINPAGE_WIDTH + (int)xfrac] = LineColor;
1553 } while ( count > 0 );
1558 for ( u = -0.1 ; u <= 0.9 ; u += 0.999 )
1560 for ( v = -0.1 ; v <= 0.9 ; v += 0.999 )
1564 if ( sx < SKINPAGE_WIDTH && sy < SKINPAGE_HEIGHT ) {
1565 pic[sy * SKINPAGE_WIDTH + sx] = LineColor;
1572 } while ( count > 0 );
1577 if ( count & 1 && xfrac < SKINPAGE_WIDTH &&
1578 yfrac < SKINPAGE_HEIGHT ) {
1579 pic[(int)yfrac * SKINPAGE_WIDTH + (int)xfrac] = LineColor;
1584 } while ( count > 0 );
1587 Error( "Unknown <linetype> %d.\n", LineType );
1591 //==========================================================================
1595 //==========================================================================
1597 static void DrawCharacter( int x, int y, int character ){
1601 character = toupper( character );
1602 if ( character < ASCII_SPACE || character > 'Z' ) {
1603 character = ASCII_SPACE;
1605 character -= ASCII_SPACE;
1606 for ( def = CharDefs[character], r = 0; r < 5; r++ )
1608 for ( c = 0; c < 5; c++ )
1610 pic[( y + r ) * SKINPAGE_WIDTH + x + c] = *def++ == '*' ? 255 : 0;
1615 //==========================================================================
1619 //==========================================================================
1621 void DrawTextChar( int x, int y, char *text ){
1624 while ( ( c = *text++ ) != '\0' )
1626 DrawCharacter( x, y, c );
1632 extern void DrawScreen( float s_scale, float t_scale, float iwidth, float iheight );
1634 //==========================================================================
1637 static int ExtractDigit( byte *pic, int x, int y ){
1645 backColor = pic[( SKINPAGE_HEIGHT - 1 ) * SKINPAGE_WIDTH];
1646 DigitDefs = &CharDefs['0' - ASCII_SPACE];
1649 for ( r = 0; r < 5; r++ )
1651 for ( c = 0; c < 5; c++ )
1653 *buffer++ = ( pic[( y + r ) * SKINPAGE_WIDTH + x + c] == backColor ) ? ' ' : '*';
1657 for ( i = 0; i < 10; i++ )
1659 if ( strcmp( DigitDefs[i], digString ) == 0 ) {
1664 Error( "Unable to extract scaling info from skin PCX." );
1668 //==========================================================================
1671 int ExtractNumber( byte *pic, int x, int y ){
1672 return ExtractDigit( pic, x, y ) * 100 + ExtractDigit( pic, x + 6, y ) * 10 + ExtractDigit( pic, x + 12, y );
1683 Builds the triangle_st array for the base frame and
1684 fmheader.skinwidth / fmheader.skinheight
1686 FIXME: allow this to be loaded from a file for
1690 static void BuildST( triangle_t *ptri, int numtri, qboolean DrawSkin ){
1693 int width, height, iwidth, iheight, swidth;
1698 vec3_t vtemp1, vtemp2, normal;
1699 float s_scale, t_scale;
1706 // find bounds of all the verts on the base frame
1708 ClearBounds( mins, maxs );
1709 backface_flag = false;
1711 if ( ptri[0].HasUV ) { // if we have the uv already, we don't want to double up or scale
1712 iwidth = ScaleWidth;
1713 iheight = ScaleHeight;
1715 t_scale = s_scale = 1.0;
1719 for ( i = 0 ; i < numtri ; i++ )
1720 for ( j = 0 ; j < 3 ; j++ )
1721 AddPointToBounds( ptri[i].verts[j], mins, maxs );
1723 for ( i = 0 ; i < 3 ; i++ )
1725 mins[i] = floor( mins[i] );
1726 maxs[i] = ceil( maxs[i] );
1729 width = maxs[0] - mins[0];
1730 height = maxs[2] - mins[2];
1732 for ( i = 0 ; i < numtri ; i++ )
1734 VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 );
1735 VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 );
1736 CrossProduct( vtemp1, vtemp2, normal );
1738 if ( normal[1] > 0 ) {
1739 backface_flag = true;
1743 scWidth = ScaleWidth * SCALE_ADJUST_FACTOR;
1744 if ( backface_flag ) { //we are doubling
1748 scHeight = ScaleHeight * SCALE_ADJUST_FACTOR;
1750 scale = scWidth / width;
1752 if ( height * scale >= scHeight ) {
1753 scale = scHeight / height;
1756 iwidth = ceil( width * scale ) + 4;
1757 iheight = ceil( height * scale ) + 4;
1759 s_scale = (float)( iwidth - 4 ) / width;
1760 t_scale = (float)( iheight - 4 ) / height;
1764 if ( backface_flag ) {
1765 DrawScreen( s_scale, t_scale, iwidth * 2, iheight );
1768 DrawScreen( s_scale, t_scale, iwidth, iheight );
1771 if ( backface_flag ) {
1772 skinwidth = iwidth * 2;
1777 skinheight = iheight;
1780 /* if (!g_fixedwidth)
1783 if (width*scale >= 150)
1784 scale = 150.0 / width;
1785 if (height*scale >= 190)
1786 scale = 190.0 / height;
1788 s_scale = t_scale = scale;
1790 iwidth = ceil(width*s_scale);
1791 iheight = ceil(height*t_scale);
1798 iwidth = g_fixedwidth / 2;
1799 iheight = g_fixedheight;
1801 s_scale = (float)(iwidth-4) / width;
1802 t_scale = (float)(iheight-4) / height;
1806 // determine which side of each triangle to map the texture to
1809 for ( i = 0 ; i < numtri ; i++ )
1811 if ( ptri[i].HasUV ) {
1812 for ( j = 0 ; j < 3 ; j++ )
1814 triangle_st[i][j][0] = Q_rint( ptri[i].uv[j][0] * skinwidth );
1815 triangle_st[i][j][1] = Q_rint( ( 1.0f - ptri[i].uv[j][1] ) * skinheight );
1820 VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 );
1821 VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 );
1822 CrossProduct( vtemp1, vtemp2, normal );
1824 if ( normal[1] > 0 ) {
1832 for ( j = 0 ; j < 3 ; j++ )
1834 pbasevert = ptri[i].verts[j];
1836 triangle_st[i][j][0] = Q_rint( ( pbasevert[0] - mins[0] ) * s_scale + basex );
1837 triangle_st[i][j][1] = Q_rint( ( maxs[2] - pbasevert[2] ) * t_scale + basey );
1842 DrawLine( triangle_st[i][0][0], triangle_st[i][0][1],
1843 triangle_st[i][1][0], triangle_st[i][1][1] );
1844 DrawLine( triangle_st[i][1][0], triangle_st[i][1][1],
1845 triangle_st[i][2][0], triangle_st[i][2][1] );
1846 DrawLine( triangle_st[i][2][0], triangle_st[i][2][1],
1847 triangle_st[i][0][0], triangle_st[i][0][1] );
1851 // make the width a multiple of 4; some hardware requires this, and it ensures
1852 // dword alignment for each scan
1855 if ( backface_flag ) {
1858 fmheader.skinwidth = ( swidth + 3 ) & ~3;
1859 fmheader.skinheight = iheight;
1861 skin_width = iwidth;
1862 skin_height = iheight;
1866 static void BuildNewST( triangle_t *ptri, int numtri, qboolean DrawSkin ){
1869 for ( i = 0 ; i < numtri ; i++ )
1871 if ( ptri[i].HasUV ) {
1872 for ( j = 0 ; j < 3 ; j++ )
1874 triangle_st[i][j][0] = Q_rint( ptri[i].uv[j][0] * ( ScaleWidth - 1 ) );
1875 triangle_st[i][j][1] = Q_rint( ( 1.0f - ptri[i].uv[j][1] ) * ( ScaleHeight - 1 ) );
1880 DrawLine( triangle_st[i][0][0], triangle_st[i][0][1],
1881 triangle_st[i][1][0], triangle_st[i][1][1] );
1882 DrawLine( triangle_st[i][1][0], triangle_st[i][1][1],
1883 triangle_st[i][2][0], triangle_st[i][2][1] );
1884 DrawLine( triangle_st[i][2][0], triangle_st[i][2][1],
1885 triangle_st[i][0][0], triangle_st[i][0][1] );
1889 // make the width a multiple of 4; some hardware requires this, and it ensures
1890 // dword alignment for each scan
1892 fmheader.skinwidth = ( ScaleWidth + 3 ) & ~3;
1893 fmheader.skinheight = ScaleHeight;
1895 skin_width = ScaleWidth;
1896 skin_height = ScaleHeight;
1903 byte *BasePixels,*TransPixels;
1904 int BaseWidth, BaseHeight, TransWidth, TransHeight;
1905 qboolean BaseTrueColor;
1906 static qboolean SetPixel = false;
1908 int CheckTransRecursiveTri( int *lp1, int *lp2, int *lp3 ){
1913 d = lp2[0] - lp1[0];
1914 if ( d < -1 || d > 1 ) {
1917 d = lp2[1] - lp1[1];
1918 if ( d < -1 || d > 1 ) {
1922 d = lp3[0] - lp2[0];
1923 if ( d < -1 || d > 1 ) {
1926 d = lp3[1] - lp2[1];
1927 if ( d < -1 || d > 1 ) {
1931 d = lp1[0] - lp3[0];
1932 if ( d < -1 || d > 1 ) {
1935 d = lp1[1] - lp3[1];
1936 if ( d < -1 || d > 1 ) {
1946 return 0; // entire tri is filled
1956 new[0] = ( lp1[0] + lp2[0] ) >> 1;
1957 new[1] = ( lp1[1] + lp2[1] ) >> 1;
1959 // draw the point if splitting a leading edge
1960 if ( lp2[1] > lp1[1] ) {
1963 if ( ( lp2[1] == lp1[1] ) && ( lp2[0] < lp1[0] ) ) {
1968 assert( ( new[1] * BaseWidth ) + new[0] < BaseWidth * BaseHeight );
1970 if ( BaseTrueColor ) {
1971 BasePixels[( ( new[1] * BaseWidth ) + new[0] ) * 4] = 1;
1975 BasePixels[( new[1] * BaseWidth ) + new[0]] = 1;
1980 if ( TransPixels ) {
1981 if ( TransPixels[( new[1] * TransWidth ) + new[0]] != 255 ) {
1985 else if ( BaseTrueColor ) {
1986 if ( BasePixels[( ( ( new[1] * BaseWidth ) + new[0] ) * 4 ) + 3] != 255 ) {
1992 // pixel = BasePixels[(new[1]*BaseWidth) + new[0]];
1997 // recursively continue
1998 if ( CheckTransRecursiveTri( lp3, lp1, new ) ) {
2002 return CheckTransRecursiveTri( lp3, new, lp2 );
2005 static void ReplaceClusterIndex( int newIndex, int oldindex, int **clusters,
2006 IntListNode_t **vertLists, int *num_verts, int *new_num_verts ){
2008 IntListNode_t *next;
2010 for ( j = 0; j < numJointsInSkeleton[g_skelModel.type]; ++j )
2012 if ( !clusters[j] ) {
2016 for ( i = 0; i < num_verts[j + 1]; ++i )
2018 if ( clusters[j][i] == oldindex ) {
2019 ++new_num_verts[j + 1];
2021 next = vertLists[j];
2023 vertLists[j] = (IntListNode_t *) SafeMalloc( sizeof( IntListNode_t ), "ReplaceClusterIndex" );
2024 // Currently freed in WriteJointedModelFile only
2026 vertLists[j]->data = newIndex;
2027 vertLists[j]->next = next;
2033 #define FUDGE_EPSILON 0.002
2035 qboolean VectorFudgeCompare( vec3_t v1, vec3_t v2 ){
2038 for ( i = 0 ; i < 3 ; i++ )
2039 if ( fabs( v1[i] - v2[i] ) > FUDGE_EPSILON ) {
2051 void Cmd_FMBase( qboolean GetST ){
2052 triangle_t *ptri, *st_tri;
2057 char file1[1024],file2[1024],trans_file[1024], stfile[1024], extension[256];
2058 vec3_t base_xyz[MAX_FM_VERTS];
2063 GetScriptToken( false );
2065 if ( g_skipmodel || g_release || g_archive ) {
2069 printf( "---------------------\n" );
2070 sprintf( file1, "%s/%s.%s", cdarchive, token, trifileext );
2071 printf( "%s ", file1 );
2073 ExpandPathAndArchive( file1 );
2075 // Use the input filepath for this one.
2076 sprintf( file1, "%s/%s", cddir, token );
2078 // time1 = FileTime (file1);
2080 // Error ("%s doesn't exist", file1);
2083 // load the base triangles
2086 Load3DSTriangleList( file1, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes );
2089 LoadTriangleList( file1, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes );
2092 if ( g_ignoreTriUV ) {
2093 for ( i = 0; i < fmheader.num_tris; i++ )
2099 GetScriptToken( false );
2100 sprintf( file2, "%s/%s", cddir, token );
2101 sprintf( trans_file, "%s/!%s_a.pcx", cddir, token );
2103 ExtractFileExtension( file2, extension );
2104 if ( extension[0] == 0 ) {
2105 strcat( file2, ".pcx" );
2107 printf( "skin: %s\n", file2 );
2109 BaseTrueColor = LoadAnyImage( file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight );
2112 if ( BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT ) {
2113 if ( g_allow_newskin ) {
2114 ScaleWidth = BaseWidth;
2115 ScaleHeight = BaseHeight;
2120 Error( "Invalid skin page size: (%d,%d) should be (%d,%d)",
2121 BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT );
2124 else if ( !BaseTrueColor ) {
2125 ScaleWidth = (float)ExtractNumber( BasePixels, ENCODED_WIDTH_X,
2127 ScaleHeight = (float)ExtractNumber( BasePixels, ENCODED_HEIGHT_X,
2132 Error( "Texture coordinates not supported on true color image" );
2136 GetScriptToken( false );
2138 sprintf( stfile, "%s/%s.%s", cdarchive, token, trifileext );
2139 printf( "ST: %s ", stfile );
2141 sprintf( stfile, "%s/%s", cddir, token );
2144 Load3DSTriangleList( stfile, &st_tri, &num_st_tris, NULL, NULL );
2147 LoadTriangleList( stfile, &st_tri, &num_st_tris, NULL, NULL );
2150 if ( num_st_tris != fmheader.num_tris ) {
2151 Error( "num st tris mismatch: st %d / base %d", num_st_tris, fmheader.num_tris );
2154 printf( " matching triangles...\n" );
2155 for ( i = 0; i < fmheader.num_tris; i++ )
2158 for ( j = 0; j < num_st_tris; j++ )
2160 for ( x = 0; x < 3; x++ )
2162 for ( y = 0; y < 3; y++ )
2167 for ( z = 0; z < 3; z++ )
2169 if ( z == x || z == y ) {
2173 if ( VectorFudgeCompare( ptri[i].verts[0], st_tri[j].verts[x] ) &&
2174 VectorFudgeCompare( ptri[i].verts[1], st_tri[j].verts[y] ) &&
2175 VectorFudgeCompare( ptri[i].verts[2], st_tri[j].verts[z] ) ) {
2178 ptri[i].HasUV = st_tri[k].HasUV;
2179 ptri[i].uv[0][0] = st_tri[k].uv[x][0];
2180 ptri[i].uv[0][1] = st_tri[k].uv[x][1];
2181 ptri[i].uv[1][0] = st_tri[k].uv[y][0];
2182 ptri[i].uv[1][1] = st_tri[k].uv[y][1];
2183 ptri[i].uv[2][0] = st_tri[k].uv[z][0];
2184 ptri[i].uv[2][1] = st_tri[k].uv[z][1];
2187 else if ( k != j ) {
2188 printf( "Duplicate triangle %d found in st file: %d and %d\n",i,k,j );
2189 printf( " (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",
2190 ptri[i].verts[0][0],ptri[i].verts[0][1],ptri[i].verts[0][2],
2191 ptri[i].verts[1][0],ptri[i].verts[1][1],ptri[i].verts[1][2],
2192 ptri[i].verts[2][0],ptri[i].verts[2][1],ptri[i].verts[2][2] );
2193 printf( " (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",
2194 st_tri[k].verts[0][0],st_tri[k].verts[0][1],st_tri[k].verts[0][2],
2195 st_tri[k].verts[1][0],st_tri[k].verts[1][1],st_tri[k].verts[1][2],
2196 st_tri[k].verts[2][0],st_tri[k].verts[2][1],st_tri[k].verts[2][2] );
2197 printf( " (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",
2198 st_tri[j].verts[0][0],st_tri[j].verts[0][1],st_tri[j].verts[0][2],
2199 st_tri[j].verts[1][0],st_tri[j].verts[1][1],st_tri[j].verts[1][2],
2200 st_tri[j].verts[2][0],st_tri[j].verts[2][1],st_tri[j].verts[2][2] );
2208 printf( "No matching triangle %d\n",i );
2215 // get the ST values
2217 if ( ptri && ptri[0].HasUV ) {
2219 Error( "Base has UVs with old style skin page\nMaybe you want to use -ignoreUV" );
2223 BuildNewST( ptri, fmheader.num_tris, false );
2229 Error( "Base has new style skin without UVs" );
2233 BuildST( ptri, fmheader.num_tris, false );
2238 if ( !BaseTrueColor ) {
2239 FH = fopen( trans_file,"rb" );
2242 Load256Image( trans_file, &TransPixels, NULL, &TransWidth, &TransHeight );
2243 if ( TransWidth != fmheader.skinwidth || TransHeight != fmheader.skinheight ) {
2244 Error( "source image %s dimensions (%d,%d) are not the same as alpha image (%d,%d)\n",file2,fmheader.skinwidth,fmheader.skinheight,TransWidth,TransHeight );
2250 // run through all the base triangles, storing each unique vertex in the
2251 // base vertex list and setting the indirect triangles to point to the base
2254 for ( l = 0; l < fmheader.num_mesh_nodes; l++ )
2256 for ( i = 0 ; i < fmheader.num_tris ; i++ )
2259 bit = 1 << ( i & 7 );
2260 if ( !( pmnodes[l].tris[pos] & bit ) ) {
2264 for ( j = 0 ; j < 3 ; j++ )
2266 // get the xyz index
2267 for ( k = 0 ; k < fmheader.num_xyz ; k++ )
2269 if ( VectorCompare( ptri[i].verts[j], base_xyz[k] ) ) {
2270 break; // this vertex is already in the base vertex list
2274 if ( k == fmheader.num_xyz ) { // new index
2275 VectorCopy( ptri[i].verts[j], base_xyz[fmheader.num_xyz] );
2277 if ( pmnodes[l].clustered == true ) {
2278 ReplaceClusterIndex( k, ptri[i].indicies[j], (int **)&pmnodes[l].clusters, (IntListNode_t **)&g_skelModel.vertLists, (int *)&pmnodes[l].num_verts, (int *)&g_skelModel.new_num_verts );
2285 bit = 1 << ( k & 7 );
2286 pmnodes[l].verts[pos] |= bit;
2288 triangles[i].index_xyz[j] = k;
2291 for ( k = 0 ; k < fmheader.num_st ; k++ )
2293 if ( triangle_st[i][j][0] == base_st[k].s
2294 && triangle_st[i][j][1] == base_st[k].t ) {
2295 break; // this vertex is already in the base vertex list
2299 if ( k == fmheader.num_st ) { // new index
2300 base_st[fmheader.num_st].s = triangle_st[i][j][0];
2301 base_st[fmheader.num_st].t = triangle_st[i][j][1];
2305 triangles[i].index_st[j] = k;
2308 if ( TransPixels || BaseTrueColor ) {
2309 translucent[i] = CheckTransRecursiveTri( triangle_st[i][0], triangle_st[i][1], triangle_st[i][2] );
2313 translucent[i] = false;
2318 if ( !BaseTrueColor ) {
2320 memset( BasePixels,0,BaseWidth * BaseHeight );
2321 for ( i = 0 ; i < fmheader.num_tris ; i++ )
2323 CheckTransRecursiveTri( triangle_st[i][0], triangle_st[i][1], triangle_st[i][2] );
2327 skin_pixels_used = 0;
2328 for ( i = 0; i < fmheader.skinheight; i++ )
2330 for ( j = 0; j < fmheader.skinwidth; j++ )
2332 skin_pixels_used += BasePixels[( i * BaseWidth ) + j];
2335 total_skin_pixels = fmheader.skinheight * fmheader.skinwidth;
2340 memset( BasePixels,0,BaseWidth * BaseHeight * 4 );
2341 for ( i = 0 ; i < fmheader.num_tris ; i++ )
2343 CheckTransRecursiveTri( triangle_st[i][0], triangle_st[i][1], triangle_st[i][2] );
2347 skin_pixels_used = 0;
2348 for ( i = 0; i < fmheader.skinheight; i++ )
2350 for ( j = 0; j < fmheader.skinwidth; j++ )
2352 skin_pixels_used += BasePixels[( ( i * BaseWidth ) + j ) * 4];
2355 total_skin_pixels = fmheader.skinheight * fmheader.skinwidth;
2358 // build triangle strips / fans
2361 if ( TransPixels ) {
2362 free( TransPixels );
2365 if ( BasePalette ) {
2366 free( BasePalette );
2371 void Cmd_FMNodeOrder( void ){
2372 mesh_node_t *newnodes, *pos;
2376 Error( "Base has not been established yet" );
2379 pos = newnodes = malloc( sizeof( mesh_node_t ) * fmheader.num_mesh_nodes );
2381 for ( i = 0; i < fmheader.num_mesh_nodes; i++ )
2383 GetScriptToken( false );
2385 for ( j = 0; j < fmheader.num_mesh_nodes; j++ )
2387 if ( strcmpi( pmnodes[j].name, token ) == 0 ) {
2393 if ( j >= fmheader.num_mesh_nodes ) {
2394 Error( "Node '%s' not in base list!\n", token );
2402 //===============================================================
2404 extern char *FindFrameFile( char *frame );
2412 void GrabFrame( char *frame ){
2415 fmtrivert_t *ptrivert;
2422 // the frame 'run1' will be looked for as either
2423 // run.1 or run1.tri, so the new alias sequence save
2424 // feature an be used
2425 framefile = FindFrameFile( frame );
2427 sprintf( file1, "%s/%s", cdarchive, framefile );
2428 ExpandPathAndArchive( file1 );
2430 sprintf( file1, "%s/%s",cddir, framefile );
2432 printf( "grabbing %s ", file1 );
2434 if ( fmheader.num_frames >= MAX_FM_FRAMES ) {
2435 Error( "fmheader.num_frames >= MAX_FM_FRAMES" );
2437 fr = &g_frames[fmheader.num_frames];
2438 fmheader.num_frames++;
2440 strcpy( fr->name, frame );
2446 Load3DSTriangleList( file1, &ptri, &num_tris, NULL, NULL );
2449 LoadTriangleList( file1, &ptri, &num_tris, NULL, NULL );
2452 if ( num_tris != fmheader.num_tris ) {
2453 Error( "%s: number of triangles (%d) doesn't match base frame (%d)\n", file1, num_tris, fmheader.num_tris );
2457 // allocate storage for the frame's vertices
2461 for ( i = 0 ; i < fmheader.num_xyz ; i++ )
2463 ptrivert[i].vnorm.numnormals = 0;
2464 VectorClear( ptrivert[i].vnorm.normalsum );
2466 ClearBounds( fr->mins, fr->maxs );
2469 // store the frame's vertices in the same order as the base. This assumes the
2470 // triangles and vertices in this frame are in exactly the same order as in the
2473 for ( i = 0 ; i < num_tris ; i++ )
2475 vec3_t vtemp1, vtemp2, normal;
2478 VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 );
2479 VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 );
2480 CrossProduct( vtemp1, vtemp2, normal );
2482 VectorNormalize( normal, normal );
2484 // rotate the normal so the model faces down the positive x axis
2486 normal[0] = -normal[1];
2489 for ( j = 0 ; j < 3 ; j++ )
2491 index_xyz = triangles[i].index_xyz[j];
2493 // rotate the vertices so the model faces down the positive x axis
2494 // also adjust the vertices to the desired origin
2495 ptrivert[index_xyz].v[0] = ( ( -ptri[i].verts[j][1] ) * scale_up ) +
2497 ptrivert[index_xyz].v[1] = ( ptri[i].verts[j][0] * scale_up ) +
2499 ptrivert[index_xyz].v[2] = ( ptri[i].verts[j][2] * scale_up ) +
2502 AddPointToBounds( ptrivert[index_xyz].v, fr->mins, fr->maxs );
2504 VectorAdd( ptrivert[index_xyz].vnorm.normalsum, normal, ptrivert[index_xyz].vnorm.normalsum );
2505 ptrivert[index_xyz].vnorm.numnormals++;
2510 // calculate the vertex normals, match them to the template list, and store the
2511 // index of the best match
2513 for ( i = 0 ; i < fmheader.num_xyz ; i++ )
2521 c = ptrivert[i].vnorm.numnormals;
2523 Error( "Vertex with no triangles attached" );
2526 VectorScale( ptrivert[i].vnorm.normalsum, 1.0 / c, v );
2527 VectorNormalize( v, v );
2532 for ( j = 0 ; j < NUMVERTEXNORMALS ; j++ )
2536 dot = DotProduct( v, avertexnormals[j] );
2537 if ( dot > maxdot ) {
2543 ptrivert[i].lightnormalindex = maxdotindex;
2554 void Cmd_FMFrame( void ){
2555 while ( ScriptTokenAvailable() )
2557 GetScriptToken( false );
2558 if ( g_skipmodel ) {
2561 if ( g_release || g_archive ) {
2562 fmheader.num_frames = 1; // don't skip the writeout
2566 H_printf( "#define FRAME_%-16s\t%i\n", token, fmheader.num_frames );
2568 if ( ( g_skelModel.type != SKEL_NULL ) || ( g_skelModel.references != REF_NULL ) ) {
2569 GrabModelTransform( token );
2574 if ( g_skelModel.type != SKEL_NULL ) {
2575 GrabSkeletalFrame( token );
2578 if ( g_skelModel.references != REF_NULL ) {
2579 GrabReferencedFrame( token );
2582 // need to add the up and dir points to the frame bounds here
2583 // using AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);
2584 // then remove fudge in determining scale on frame write out
2592 Skins aren't actually stored in the file, only a reference
2593 is saved out to the header file.
2596 void Cmd_FMSkin( void ){
2602 char name[1024], savename[1024], transname[1024], extension[256];
2608 GetScriptToken( false );
2610 if ( fmheader.num_skins == MAX_FM_SKINS ) {
2611 Error( "fmheader.num_skins == MAX_FM_SKINS" );
2614 if ( g_skipmodel ) {
2618 sprintf( name, "%s/%s", cdarchive, token );
2619 strcpy( name, ExpandPathAndArchive( name ) );
2620 // sprintf (name, "%s/%s.lbm", cddir, token);
2622 if ( ScriptTokenAvailable() ) {
2623 GetScriptToken( false );
2624 sprintf( g_skins[fmheader.num_skins], "!%s", token );
2625 sprintf( savename, "%s!%s", g_outputDir, token );
2626 sprintf( transname, "%s!%s_a.pcx", gamedir, token );
2630 sprintf( g_skins[fmheader.num_skins], "%s/!%s", cdpartial, token );
2631 sprintf( savename, "%s/!%s", g_outputDir, token );
2632 sprintf( transname, "%s/!%s_a.pcx", cddir, token );
2635 fmheader.num_skins++;
2637 if ( g_skipmodel || g_release || g_archive ) {
2642 printf( "loading %s\n", name );
2643 ExtractFileExtension( name, extension );
2644 if ( extension[0] == 0 ) {
2645 strcat( name, ".pcx" );
2649 TrueColor = LoadAnyImage( name, &pixels, &palette, &width, &height );
2650 // RemapZero (pixels, palette, width, height);
2652 // crop it to the proper size
2655 cropped = (byte *) SafeMalloc( fmheader.skinwidth * fmheader.skinheight, "Cmd_FMSkin" );
2656 for ( y = 0 ; y < fmheader.skinheight ; y++ )
2658 memcpy( cropped + y * fmheader.skinwidth,
2659 pixels + y * width, fmheader.skinwidth );
2663 FH = fopen( transname,"rb" );
2667 strcat( g_skins[fmheader.num_skins - 1],".pcx" );
2668 strcat( savename,".pcx" );
2670 // save off the new image
2671 printf( "saving %s\n", savename );
2672 CreatePath( savename );
2673 WritePCXfile( savename, cropped, fmheader.skinwidth, fmheader.skinheight, palette );
2679 qtex = CreateMip( cropped, fmheader.skinwidth, fmheader.skinheight, palette, &size, true );
2681 strcat( g_skins[fmheader.num_skins - 1],".m8" );
2682 strcat( savename,".m8" );
2684 printf( "saving %s\n", savename );
2685 CreatePath( savename );
2686 SaveFile( savename, (byte *)qtex, size );
2689 strcat( g_skins[fmheader.num_skins - 1],".pcx" );
2690 strcat( savename,".pcx" );
2692 // save off the new image
2693 printf( "saving %s\n", savename );
2694 CreatePath( savename );
2695 WritePCXfile( savename, cropped, fmheader.skinwidth, fmheader.skinheight, palette );
2701 cropped = (byte *) SafeMalloc( fmheader.skinwidth * fmheader.skinheight * 4, "Cmd_FMSkin" );
2702 for ( y = 0 ; y < fmheader.skinheight ; y++ )
2704 memcpy( cropped + ( ( y * fmheader.skinwidth ) * 4 ), pixels + ( y * width * 4 ), fmheader.skinwidth * 4 );
2707 qtex32 = CreateMip32( (unsigned *)cropped, fmheader.skinwidth, fmheader.skinheight, &size, true );
2709 StripExtension( g_skins[fmheader.num_skins - 1] );
2710 strcat( g_skins[fmheader.num_skins - 1],".m32" );
2711 StripExtension( savename );
2712 strcat( savename,".m32" );
2714 printf( "saving %s\n", savename );
2715 CreatePath( savename );
2716 SaveFile( savename, (byte *)qtex32, size );
2732 void Cmd_FMCd( void ){
2738 GetScriptToken( false );
2740 // this is a silly mess...
2741 sprintf( cdpartial, "models/%s", token );
2742 sprintf( cdarchive, "%smodels/%s", gamedir + strlen( qdir ), token );
2743 sprintf( cddir, "%s%s", gamedir, cdpartial );
2745 // Since we also changed directories on the output side (for mirror) make sure the outputdir is set properly too.
2746 sprintf( temp, "%s%s", g_outputDir, cdpartial );
2747 strcpy( g_outputDir, temp );
2749 // if -only was specified and this cd doesn't match,
2750 // skip the model (you only need to match leading chars,
2751 // so you could regrab all monsters with -only monsters)
2755 if ( strncmp( token, g_only, strlen( g_only ) ) ) {
2757 printf( "skipping %s\n", cdpartial );
2764 //=======================
2766 //=======================
2768 void NewGen (char *ModelFile, char *OutputName, int width, int height)
2770 trigroup_t *triangles;
2772 triangle_t *grouptris;
2773 mesh_node_t *pmnodes;
2777 vec3_t aveNorm, crossvect;
2778 vec3_t diffvect1, diffvect2;
2781 vec3_t base, zaxis, yaxis;
2782 vec3_t uvwMin, uvwMax;
2783 vec3_t groupMin, groupMax;
2786 float *uFinal, *vFinal;
2787 unsigned char *newpic;
2789 int finalstart = 0, finalcount = 0;
2790 int xbase = 0, xwidth = 0, ywidth = 0;
2791 int *todo, *done, finished;
2792 int i, j, k, l; //counters
2793 int groupnum, numtris, numverts, num;
2798 for ( i = 0; i<3; i++)
2817 LoadTriangleList (ModelFile, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);
2819 todo = (int*)SafeMalloc(fmheader.num_tris*sizeof(int), "NewGen");
2820 done = (int*)SafeMalloc(fmheader.num_tris*sizeof(int), "NewGen");
2821 triangles = (trigroup_t*)SafeMalloc(fmheader.num_tris*sizeof(trigroup_t), "NewGen");
2823 for ( i=0; i < fmheader.num_tris; i++)
2827 triangles[i].triangle = ptri[i];
2828 triangles[i].group = 0;
2833 // transitive closure algorithm follows
2834 // put all triangles who transitively share vertices into separate groups
2838 for ( i = 0; i < fmheader.num_tris; i++)
2845 if ( i == fmheader.num_tris)
2854 for ( i = 0; i < fmheader.num_tris; i++)
2859 triangles[i].group = groupnum;
2861 for ( j = 0; j < fmheader.num_tris; j++)
2863 if ((!done[j]) && (ShareVertex(triangles[i],triangles[j])))
2874 uFinal = (float*)SafeMalloc(3*fmheader.num_tris*sizeof(float), "NewGen");
2875 vFinal = (float*)SafeMalloc(3*fmheader.num_tris*sizeof(float), "NewGen");
2877 grpfile = fopen("grpdebug.txt","w");
2880 for (i = 0; i < groupnum; i++)
2883 fprintf(grpfile,"Group Number: %d\n", i);
2885 numtris = GetNumTris(triangles, i); // number of triangles in group i
2886 numverts = numtris * 3;
2888 fprintf(grpfile,"%d triangles.\n", numtris);
2890 vertices = (vec3_t*)SafeMalloc(numverts*sizeof(vec3_t), "NewGen");
2891 uvs = (vec3_t*)SafeMalloc(numverts*sizeof(vec3_t), "NewGen");
2892 grouptris = (triangle_t*)SafeMalloc(numtris*sizeof(triangle_t), "NewGen");
2894 for (count = 0; count < fmheader.num_tris; count++)
2896 if (triangles[count].group == i)
2898 fprintf(grpfile,"Triangle %d\n", count);
2901 fprintf(grpfile,"\n");
2906 GetOneGroup(triangles, i, grouptris);
2909 for (j = 0; j < numtris; j++)
2911 VectorCopy(grouptris[j].verts[0], v0);
2912 VectorCopy(grouptris[j].verts[1], v1);
2913 VectorCopy(grouptris[j].verts[2], v2);
2914 VectorSubtract(v1, v0, diffvect1);
2915 VectorSubtract(v2, v1, diffvect2);
2916 CrossProduct( diffvect1, diffvect2, crossvect);
2917 VectorAdd(aveNorm, crossvect, aveNorm);
2918 VectorCopy(v0,vertices[num]);
2920 VectorCopy(v1,vertices[num]);
2921 num++; // add routine to add only verts that
2922 VectorCopy(v2,vertices[num]);
2923 num++; // have not already been added
2927 // figure out the best plane projections
2928 DOsvdPlane ((float*)vertices, num, (float *)&n, (float *)&base);
2930 if (DotProduct(aveNorm,n) < 0.0f)
2932 VectorScale(n, -1.0f, n);
2934 VectorNormalize(n,n);
2935 if (fabs(n[2]) < .57)
2937 CrossProduct( zaxis, n, crossvect);
2938 VectorCopy(crossvect, u);
2942 CrossProduct( yaxis, n, crossvect);
2943 VectorCopy(crossvect, u);
2945 VectorNormalize(u,u);
2946 CrossProduct( n, u, crossvect);
2947 VectorCopy(crossvect, v);
2948 VectorNormalize(v,v);
2952 for ( j = 0; j < 3; j++)
2954 groupMin[j] = 1e30f;
2955 groupMax[j] = -1e30f;
2958 for ( j = 0; j < numtris; j++)
2960 for ( k = 0; k < 3; k++)
2962 VectorCopy(grouptris[j].verts[k],v0);
2963 VectorSubtract(v0, base, v0);
2964 uvw[0] = DotProduct(v0, u);
2965 uvw[1] = DotProduct(v0, v);
2966 uvw[2] = DotProduct(v0, n);
2967 VectorCopy(uvw,uvs[num]);
2969 for ( l = 0; l < 3; l++)
2971 if (uvw[l] < groupMin[l])
2973 groupMin[l] = uvw[l];
2975 if (uvw[l] > groupMax[l])
2977 groupMax[l] = uvw[l];
2983 xwidth = ceil(0 - groupMin[0]) + 2; // move right of origin and avoid overlap
2984 ywidth = ceil(0 - groupMin[1]) + 2; // move "above" origin
2986 for ( j=0; j < numverts; j++)
2988 uFinal[finalcount] = uvs[j][0] + xwidth + xbase;
2989 vFinal[finalcount] = uvs[j][1] + ywidth;
2990 if (uFinal[finalcount] < uvwMin[0])
2992 uvwMin[0] = uFinal[finalcount];
2994 if (uFinal[finalcount] > uvwMax[0])
2996 uvwMax[0] = uFinal[finalcount];
2998 if (vFinal[finalcount] < uvwMin[1])
3000 uvwMin[1] = vFinal[finalcount];
3002 if (vFinal[finalcount] > uvwMax[1])
3004 uvwMax[1] = vFinal[finalcount];
3009 fprintf(grpfile,"svdPlaned Group min: ( %f , %f )\n",groupMin[0] + xwidth + xbase, groupMin[1] + ywidth);
3010 fprintf(grpfile,"svdPlaned Group max: ( %f , %f )\n",groupMax[0] + xwidth + xbase, groupMax[1] + ywidth);
3012 finalcount = finalstart;
3014 for ( count = 0; count < numverts; count++)
3016 fprintf(grpfile,"Vertex %d: ( %f , %f , %f )\n",count,vertices[count][0],vertices[count][1],vertices[count][2]);
3017 fprintf(grpfile,"svdPlaned: ( %f , %f )\n",uFinal[finalcount],vFinal[finalcount++]);
3020 finalstart = finalcount;
3022 fprintf(grpfile,"\n");
3028 xbase += ceil(groupMax[0] - groupMin[0]) + 2;
3032 fprintf(grpfile,"Global Min ( %f , %f )\n",uvwMin[0],uvwMin[1]);
3033 fprintf(grpfile,"Global Max ( %f , %f )\n",uvwMax[0],uvwMax[1]);
3036 ScaleTris(uvwMin, uvwMax, width, height, uFinal, vFinal, finalcount);
3038 for (k = 0; k < finalcount; k++)
3040 fprintf(grpfile, "scaled vertex %d: ( %f , %f )\n",k,uFinal[k],vFinal[k]);
3043 // i've got the array of vertices in uFinal and vFinal. Now I need to write them and draw lines
3045 datasize = width * height*sizeof(unsigned char);
3046 newpic = (unsigned char*)SafeMalloc(datasize, "NewGen");
3047 memset(newpic,0,datasize);
3048 memset(pic_palette,0,sizeof(pic_palette));
3049 pic_palette[767] = pic_palette[766] = pic_palette[765] = 255;
3052 while (k < finalcount)
3054 NewDrawLine(uFinal[k], vFinal[k], uFinal[k+1], vFinal[k+1], newpic, width, height);
3056 NewDrawLine(uFinal[k], vFinal[k], uFinal[k+1], vFinal[k+1], newpic, width, height);
3058 NewDrawLine(uFinal[k], vFinal[k], uFinal[k-2], vFinal[k-2], newpic, width, height);
3060 fprintf(grpfile, "output tri with verts %d, %d, %d", k-2, k-1, k);
3063 WritePCXfile (OutputName, newpic, width, height, pic_palette);
3073 void NewDrawLine(int x1, int y1, int x2, int y2, unsigned char* picture, int width, int height)
3078 float xfrac, yfrac, xstep, ystep;
3079 unsigned long sx, sy;
3087 count = adx > ady ? adx : ady;
3092 printf("Bad count\n");
3093 return; // don't ever hang up on bad data
3099 xstep = (float)dx/count;
3100 ystep = (float)dy/count;
3107 if(xfrac < width && yfrac < height)
3109 picture[(long)yfrac*width+(long)xfrac] = LineColor;
3114 } while (count > 0);
3119 for (u=-0.1 ; u<=0.9 ; u+=0.999)
3121 for (v=-0.1 ; v<=0.9 ; v+=0.999)
3125 if(sx < width && sy < height)
3127 picture[sy*width+sx] = LineColor;
3134 } while (count > 0);
3139 if(count&1 && xfrac < width &&
3142 picture[(long)yfrac*width+(long)xfrac] = LineColor;
3147 } while (count > 0);
3150 Error("Unknown <linetype> %d.\n", LineType);
3154 void ScaleTris( vec3_t min, vec3_t max, int Width, int Height, float* u, float* v, int verts ){
3157 float hscale, vscale;
3163 hscale = ( Width - 2 ) / max[0];
3164 vscale = ( Height - 2 ) / max[1];
3167 if ( scale > vscale ) {
3170 for ( i = 0; i < verts; i++ )
3179 void GetOneGroup( trigroup_t *tris, int grp, triangle_t* triangles ){
3184 for ( i = 0; i < fmheader.num_tris; i++ )
3186 if ( tris[i].group == grp ) {
3187 triangles[j++] = tris[i].triangle;
3194 int GetNumTris( trigroup_t *tris, int grp ){
3199 for ( i = 0; i < fmheader.num_tris; i++ )
3201 if ( tris[i].group == grp ) {
3209 int ShareVertex( trigroup_t trione, trigroup_t tritwo ){
3215 for ( i = 0; i < 3; i++ )
3217 for ( j = 0; j < 3; j++ )
3219 if ( DistBetween( trione.triangle.verts[i],tritwo.triangle.verts[j] ) < TRIVERT_DIST ) {
3228 float DistBetween( vec3_t point1, vec3_t point2 ){
3231 dist = ( point1[0] - point2[0] );
3233 dist += ( point1[1] - point2[1] ) * ( point1[1] - point2[1] );
3234 dist += ( point1[2] - point2[2] ) * ( point1[2] - point2[2] );
3235 dist = sqrt( dist );
3240 void GenSkin( char *ModelFile, char *OutputName, int Width, int Height ){
3242 mesh_node_t *pmnodes;
3248 LoadTriangleList( ModelFile, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes );
3249 if ( g_ignoreTriUV ) {
3250 for ( i = 0; i < fmheader.num_tris; i++ )
3256 memset( pic,0,sizeof( pic ) );
3257 memset( pic_palette,0,sizeof( pic_palette ) );
3258 pic_palette[767] = pic_palette[766] = pic_palette[765] = 255;
3261 ScaleHeight = Height;
3263 BuildST( ptri, fmheader.num_tris, true );
3265 WritePCXfile( OutputName, pic, SKINPAGE_WIDTH, SKINPAGE_HEIGHT, pic_palette );
3267 printf( "Gen Skin Stats:\n" );
3268 printf( " Input Base: %s\n",ModelFile );
3269 printf( " Input Dimensions: %d,%d\n",Width,Height );
3271 printf( " Output File: %s\n",OutputName );
3272 printf( " Output Dimensions: %d,%d\n",ScaleWidth,ScaleHeight );
3274 if ( fmheader.num_mesh_nodes ) {
3275 printf( "\nNodes:\n" );
3276 for ( i = 0; i < fmheader.num_mesh_nodes; i++ )
3278 printf( " %s\n",pmnodes[i].name );
3287 void Cmd_FMBeginGroup( void ){
3288 GetScriptToken( false );
3290 g_no_opimizations = false;
3292 groups[num_groups].start_frame = fmheader.num_frames;
3293 groups[num_groups].num_frames = 0;
3295 groups[num_groups].degrees = atol( token );
3296 if ( groups[num_groups].degrees < 1 || groups[num_groups].degrees > 32 ) {
3297 Error( "Degrees of freedom out of range: %d",groups[num_groups].degrees );
3301 void Cmd_FMEndGroup( void ){
3302 groups[num_groups].num_frames = fmheader.num_frames - groups[num_groups].start_frame;
3304 if ( num_groups < MAX_GROUPS - 1 ) {
3309 Error( "Number of compression groups exceded: %i\n", MAX_GROUPS );