2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
22 #include "qd_fmodel.h"
\r
23 #include "animcomp.h"
\r
24 #include "qd_skeletons.h"
\r
25 #include "skeletons.h"
\r
28 #include "reference.h"
\r
33 ========================================================================
\r
35 .FM triangle flexible model file format
\r
37 ========================================================================
\r
40 //=================================================================
\r
42 #define NUMVERTEXNORMALS 162
\r
44 extern float avertexnormals[NUMVERTEXNORMALS][3];
\r
46 #define MAX_GROUPS 128
\r
50 triangle_t triangle;
\r
54 #define TRIVERT_DIST .1
\r
72 //================================================================
\r
75 fmheader_t fmheader;
\r
78 extern char g_skins[MAX_FM_SKINS][64];
\r
81 extern fmstvert_t base_st[MAX_FM_VERTS];
\r
84 extern fmtriangle_t triangles[MAX_FM_TRIANGLES];
\r
87 fmframe_t g_frames[MAX_FM_FRAMES];
\r
88 //fmframe_t *g_FMframes;
\r
91 extern int commands[16384];
\r
92 extern int numcommands;
\r
96 // varibles set by commands
\r
98 extern float scale_up; // set by $scale
\r
99 extern vec3_t adjust; // set by $origin
\r
100 extern int g_fixedwidth, g_fixedheight; // set by $skinsize
\r
101 extern char modelname[64]; // set by $modelname
\r
104 extern char *g_outputDir;
\r
108 mesh_node_t *pmnodes = NULL;
\r
109 fmmeshnode_t mesh_nodes[MAX_FM_MESH_NODES];
\r
111 fmgroup_t groups[MAX_GROUPS];
\r
113 int frame_to_group[MAX_FM_FRAMES];
\r
116 // variables set by command line arguments
\r
118 qboolean g_no_opimizations = false;
\r
124 static int triangle_st[MAX_FM_TRIANGLES][3][2];
\r
127 // number of gl vertices
\r
128 extern int numglverts;
\r
129 // indicates if a triangle has already been used in a glcmd
\r
130 extern int used[MAX_FM_TRIANGLES];
\r
131 // indicates if a triangle has translucency in it or not
\r
132 static qboolean translucent[MAX_FM_TRIANGLES];
\r
134 // main output file handle
\r
135 extern FILE *headerouthandle;
\r
136 // output sizes of buildst()
\r
137 static int skin_width, skin_height;
\r
141 static int total_skin_pixels;
\r
142 static int skin_pixels_used;
\r
144 int ShareVertex( trigroup_t trione, trigroup_t tritwo);
\r
145 float DistBetween(vec3_t point1, vec3_t point2);
\r
146 int GetNumTris( trigroup_t *tris, int group);
\r
147 void GetOneGroup(trigroup_t *tris, int grp, triangle_t* triangles);
\r
148 void ScaleTris( vec3_t min, vec3_t max, int Width, int Height, float* u, float* v, int verts);
\r
149 void NewDrawLine(int x1, int y1, int x2, int y2, unsigned char* picture, int width, int height);
\r
153 void strupr(char *string)
\r
157 for (i=0 ; i<strlen(string); i++)
\r
158 toupper(string[i]);
\r
164 //==============================================================
\r
171 static void ClearModel (void)
\r
173 memset (&fmheader, 0, sizeof(fmheader));
\r
177 VectorCopy (vec3_origin, adjust);
\r
178 g_fixedwidth = g_fixedheight = 0;
\r
179 g_skipmodel = false;
\r
188 ClearSkeletalModel();
\r
192 extern void H_printf(char *fmt, ...);
\r
195 void WriteHeader(FILE *FH, char *Ident, int Version, int Size, void *Data)
\r
198 static long pos = -1;
\r
202 { // Don't write out empty packets
\r
208 CurrentPos = ftell(FH);
\r
209 Size = CurrentPos - pos + sizeof(header_t);
\r
210 fseek(FH, pos, SEEK_SET);
\r
213 else if (Size == -1)
\r
218 memset(&header,0,sizeof(header));
\r
219 strcpy(header.ident,Ident);
\r
220 header.version = Version;
\r
221 header.size = Size;
\r
223 SafeWrite (FH, &header, sizeof(header));
\r
227 SafeWrite (FH, Data, Size);
\r
233 fseek(FH, 0, SEEK_END);
\r
242 static void WriteModelFile (FILE *modelouthandle)
\r
247 fmaliasframe_t *out;
\r
248 byte buffer[MAX_FM_VERTS*4+128];
\r
251 IntListNode_t *current, *toFree;
\r
252 qboolean framesWritten = false;
\r
253 size_t temp ,size = 0;
\r
255 // probably should do this dynamically one of these days
\r
258 float scale[3]; // multiply byte verts by this
\r
259 float translate[3]; // then add this
\r
260 } outFrames[MAX_FM_FRAMES];
\r
262 #define DATA_SIZE 0x60000 // 384K had better be enough, particularly for the reference points
\r
263 byte data[DATA_SIZE];
\r
264 byte data2[DATA_SIZE];
\r
266 fmheader.num_glcmds = numcommands;
\r
267 fmheader.framesize = (int)&((fmaliasframe_t *)0)->verts[fmheader.num_xyz];
\r
269 WriteHeader(modelouthandle, FM_HEADER_NAME, FM_HEADER_VER, sizeof(fmheader), &fmheader);
\r
272 // write out the skin names
\r
275 WriteHeader(modelouthandle, FM_SKIN_NAME, FM_SKIN_VER, fmheader.num_skins * MAX_FM_SKINNAME, g_skins);
\r
278 // write out the texture coordinates
\r
281 for (i=0 ; i<fmheader.num_st ; i++)
\r
283 base_st[i].s = LittleShort (base_st[i].s);
\r
284 base_st[i].t = LittleShort (base_st[i].t);
\r
287 WriteHeader(modelouthandle, FM_ST_NAME, FM_ST_VER, fmheader.num_st * sizeof(base_st[0]), base_st);
\r
290 // write out the triangles
\r
292 WriteHeader(modelouthandle, FM_TRI_NAME, FM_TRI_VER, fmheader.num_tris * sizeof(fmtriangle_t), NULL);
\r
294 for (i=0 ; i<fmheader.num_tris ; i++)
\r
299 for (j=0 ; j<3 ; j++)
\r
301 tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);
\r
302 tri.index_st[j] = LittleShort (triangles[i].index_st[j]);
\r
305 SafeWrite (modelouthandle, &tri, sizeof(tri));
\r
311 // write out the frames
\r
313 WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, fmheader.num_frames * fmheader.framesize, NULL);
\r
314 // WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);
\r
316 for (i=0 ; i<fmheader.num_frames ; i++)
\r
319 out = (fmaliasframe_t *)buffer;
\r
321 strcpy (out->name, in->name);
\r
322 for (j=0 ; j<3 ; j++)
\r
324 out->scale[j] = (in->maxs[j] - in->mins[j])/255;
\r
325 out->translate[j] = in->mins[j];
\r
327 outFrames[i].scale[j] = out->scale[j];
\r
328 outFrames[i].translate[j] = out->translate[j];
\r
331 for (j=0 ; j<fmheader.num_xyz ; j++)
\r
333 // all of these are byte values, so no need to deal with endianness
\r
334 out->verts[j].lightnormalindex = in->v[j].lightnormalindex;
\r
336 for (k=0 ; k<3 ; k++)
\r
338 // scale to byte values & min/max check
\r
339 v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );
\r
341 // clamp, so rounding doesn't wrap from 255.6 to 0
\r
346 out->verts[j].v[k] = v;
\r
350 for (j=0 ; j<3 ; j++)
\r
352 out->scale[j] = LittleFloat (out->scale[j]);
\r
353 out->translate[j] = LittleFloat (out->translate[j]);
\r
356 SafeWrite (modelouthandle, out, fmheader.framesize);
\r
359 // Go back and finish the header
\r
360 // WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);
\r
364 WriteHeader(modelouthandle, FM_SHORT_FRAME_NAME, FM_SHORT_FRAME_VER,FRAME_NAME_LEN*fmheader.num_frames, NULL);
\r
365 for (i=0 ; i<fmheader.num_frames ; i++)
\r
368 SafeWrite (modelouthandle,in->name,FRAME_NAME_LEN);
\r
370 WriteHeader(modelouthandle, FM_NORMAL_NAME, FM_NORMAL_VER,fmheader.num_xyz, NULL);
\r
372 for (j=0 ; j<fmheader.num_xyz ; j++)
\r
373 SafeWrite (modelouthandle,&in->v[j].lightnormalindex,1);
\r
377 // write out glcmds
\r
379 WriteHeader(modelouthandle, FM_GLCMDS_NAME, FM_GLCMDS_VER, numcommands*4, commands);
\r
382 // write out mesh nodes
\r
384 for(i=0;i<fmheader.num_mesh_nodes;i++)
\r
386 memcpy(mesh_nodes[i].tris, pmnodes[i].tris, sizeof(mesh_nodes[i].tris));
\r
387 memcpy(mesh_nodes[i].verts, pmnodes[i].verts, sizeof(mesh_nodes[i].verts));
\r
388 mesh_nodes[i].start_glcmds = LittleShort((short)pmnodes[i].start_glcmds);
\r
389 mesh_nodes[i].num_glcmds = LittleShort((short)pmnodes[i].num_glcmds);
\r
392 WriteHeader(modelouthandle, FM_MESH_NAME, FM_MESH_VER, sizeof(fmmeshnode_t) * fmheader.num_mesh_nodes, mesh_nodes);
\r
403 char *mat; fmheader.num_xyz*3*g->degrees*sizeof(char)
\r
404 char *ccomp; g->num_frames*g->degrees*sizeof(char)
\r
405 char *cbase; fmheader.num_xyz*3*sizeof(unsigned char)
\r
406 float *cscale; g->degrees*sizeof(float)
\r
407 float *coffset; g->degrees*sizeof(float)
\r
408 float trans[3]; 3*sizeof(float)
\r
409 float scale[3]; 3*sizeof(float)
\r
414 size=sizeof(int)+fmheader.num_frames*sizeof(int);
\r
415 for (k=0;k<num_groups;k++)
\r
418 size+=sizeof(int)*3;
\r
419 size+=fmheader.num_xyz*3*g->degrees*sizeof(char);
\r
420 size+=g->num_frames*g->degrees*sizeof(char);
\r
421 size+=fmheader.num_xyz*3*sizeof(unsigned char);
\r
422 size+=g->degrees*sizeof(float);
\r
423 size+=g->degrees*sizeof(float);
\r
424 size+=12*sizeof(float);
\r
426 WriteHeader(modelouthandle, FM_COMP_NAME, FM_COMP_VER,size, NULL);
\r
427 SafeWrite (modelouthandle,&num_groups,sizeof(int));
\r
428 SafeWrite (modelouthandle,frame_to_group,sizeof(int)*fmheader.num_frames);
\r
430 for (k=0;k<num_groups;k++)
\r
433 tmp=LittleLong(g->start_frame);
\r
434 SafeWrite (modelouthandle,&tmp,sizeof(int));
\r
435 tmp=LittleLong(g->num_frames);
\r
436 SafeWrite (modelouthandle,&tmp,sizeof(int));
\r
437 tmp=LittleLong(g->degrees);
\r
438 SafeWrite (modelouthandle,&tmp,sizeof(int));
\r
440 SafeWrite (modelouthandle,g->mat,fmheader.num_xyz*3*g->degrees*sizeof(char));
\r
441 SafeWrite (modelouthandle,g->ccomp,g->num_frames*g->degrees*sizeof(char));
\r
442 SafeWrite (modelouthandle,g->cbase,fmheader.num_xyz*3*sizeof(unsigned char));
\r
443 SafeWrite (modelouthandle,g->cscale,g->degrees*sizeof(float));
\r
444 SafeWrite (modelouthandle,g->coffset,g->degrees*sizeof(float));
\r
445 SafeWrite (modelouthandle,g->trans,3*sizeof(float));
\r
446 SafeWrite (modelouthandle,g->scale,3*sizeof(float));
\r
447 SafeWrite (modelouthandle,g->bmin,3*sizeof(float));
\r
448 SafeWrite (modelouthandle,g->bmax,3*sizeof(float));
\r
457 // write the skeletal info
\r
458 if(g_skelModel.type != SKEL_NULL)
\r
462 temp = sizeof(int); // change this to a byte
\r
463 memcpy(data + size, &g_skelModel.type, temp);
\r
466 // number of joints
\r
467 temp = sizeof(int); // change this to a byte
\r
468 memcpy(data + size, &numJointsInSkeleton[g_skelModel.type], temp);
\r
471 // number of verts in each joint cluster
\r
472 temp = sizeof(int)*numJointsInSkeleton[g_skelModel.type]; // change this to shorts
\r
473 memcpy(data + size, &g_skelModel.new_num_verts[1], temp);
\r
477 for(i = 0; i < numJointsInSkeleton[g_skelModel.type]; ++i)
\r
479 current = g_skelModel.vertLists[i];
\r
482 temp = sizeof(int); // change this to a short
\r
483 memcpy(data + size, ¤t->data, temp);
\r
486 current = current->next;
\r
487 free(toFree); // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
\r
491 if(!num_groups) // joints are stored with regular verts for compressed models
\r
493 framesWritten = true;
\r
495 temp = sizeof(int); // change this to a byte
\r
496 memcpy(data + size, &framesWritten, temp);
\r
499 for (i = 0; i < fmheader.num_frames; ++i)
\r
503 for (j = 0 ; j < numJointsInSkeleton[g_skelModel.type]; ++j)
\r
505 for (k=0 ; k<3 ; k++)
\r
507 // scale to byte values & min/max check
\r
508 v = Q_rint ( (in->joints[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
\r
510 // write out origin as a float since they arn't clamped
\r
511 temp = sizeof(float); // change this to a short
\r
512 assert(size+temp < DATA_SIZE);
\r
513 memcpy(data + size, &v, temp);
\r
517 for (k=0 ; k<3 ; k++)
\r
519 v = Q_rint ( (in->joints[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
\r
521 // write out origin as a float since they arn't clamped
\r
522 temp = sizeof(float); // change this to a short
\r
523 assert(size+temp < DATA_SIZE);
\r
524 memcpy(data + size, &v, temp);
\r
528 for (k=0 ; k<3 ; k++)
\r
530 v = Q_rint ( (in->joints[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
\r
532 // write out origin as a float since they arn't clamped
\r
533 temp = sizeof(float); // change this to a short
\r
534 assert(size+temp < DATA_SIZE);
\r
535 memcpy(data + size, &v, temp);
\r
544 temp = sizeof(int); // change this to a byte
\r
545 memcpy(data + size, &framesWritten, temp);
\r
549 WriteHeader(modelouthandle, FM_SKELETON_NAME, FM_SKELETON_VER, size, data);
\r
552 if(g_skelModel.references != REF_NULL)
\r
557 if (RefPointNum <= 0)
\r
558 { // Hard-coded labels
\r
559 refnum = numReferences[g_skelModel.references];
\r
562 { // Labels indicated in QDT
\r
563 refnum = RefPointNum;
\r
566 temp = sizeof(int); // change this to a byte
\r
567 memcpy(data2 + size, &g_skelModel.references, temp);
\r
572 framesWritten = true;
\r
574 temp = sizeof(int); // change this to a byte
\r
575 memcpy(data2 + size, &framesWritten, temp);
\r
578 for (i = 0; i < fmheader.num_frames; ++i)
\r
582 for (j = 0 ; j < refnum; ++j)
\r
584 for (k=0 ; k<3 ; k++)
\r
586 // scale to byte values & min/max check
\r
587 v = Q_rint ( (in->references[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
\r
589 // write out origin as a float since they arn't clamped
\r
590 temp = sizeof(float); // change this to a short
\r
591 assert(size+temp < DATA_SIZE);
\r
592 memcpy(data2 + size, &v, temp);
\r
596 for (k=0 ; k<3 ; k++)
\r
598 v = Q_rint ( (in->references[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
\r
600 // write out origin as a float since they arn't clamped
\r
601 temp = sizeof(float); // change this to a short
\r
602 assert(size+temp < DATA_SIZE);
\r
603 memcpy(data2 + size, &v, temp);
\r
607 for (k=0 ; k<3 ; k++)
\r
609 v = Q_rint ( (in->references[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
\r
611 // write out origin as a float since they arn't clamped
\r
612 temp = sizeof(float); // change this to a short
\r
613 assert(size+temp < DATA_SIZE);
\r
614 memcpy(data2 + size, &v, temp);
\r
620 else // FINISH ME: references need to be stored with regular verts for compressed models
\r
622 framesWritten = false;
\r
624 temp = sizeof(int); // change this to a byte
\r
625 memcpy(data2 + size, &framesWritten, temp);
\r
629 WriteHeader(modelouthandle, FM_REFERENCES_NAME, FM_REFERENCES_VER, size, data2);
\r
633 static void CompressFrames()
\r
640 for (i=0;i<fmheader.num_frames;i++)
\r
642 while (i>=groups[j].start_frame+groups[j].num_frames&&j<num_groups-1)
\r
644 frame_to_group[i]=j;
\r
647 for (k=0;k<num_groups;k++)
\r
651 printf("\nCompressing Frames for group %i...\n", k);
\r
652 AnimCompressInit(g->num_frames,fmheader.num_xyz,g->degrees);
\r
653 for (i=0;i<g->num_frames;i++)
\r
655 in = &g_frames[i+g->start_frame];
\r
656 for (j=0;j<fmheader.num_xyz;j++)
\r
657 AnimSetFrame(i,j,in->v[j].v[0],in->v[j].v[1],in->v[j].v[2]);
\r
659 AnimCompressDoit();
\r
660 g->mat= (char *) SafeMalloc(fmheader.num_xyz*3*g->degrees*sizeof(char), "CompressFrames");
\r
661 g->ccomp=(char *) SafeMalloc(g->num_frames*g->degrees*sizeof(char), "CompressFrames");
\r
662 g->cbase=(char *) SafeMalloc(fmheader.num_xyz*3*sizeof(unsigned char), "CompressFrames");
\r
663 g->cscale=(float *) SafeMalloc(g->degrees*sizeof(float), "CompressFrames");
\r
664 g->coffset=(float *) SafeMalloc(g->degrees*sizeof(float), "CompressFrames");
\r
665 AnimCompressToBytes(g->trans,g->scale,g->mat,g->ccomp,g->cbase,g->cscale,g->coffset,g->bmin,g->bmax);
\r
670 static void OptimizeVertices(void)
\r
672 qboolean vert_used[MAX_FM_VERTS];
\r
673 short vert_replacement[MAX_FM_VERTS];
\r
674 int i,j,k,l,pos,bit,set_pos,set_bit;
\r
678 static IntListNode_t *newVertLists[NUM_CLUSTERS];
\r
679 static int newNum_verts[NUM_CLUSTERS];
\r
680 IntListNode_t *current, *next;
\r
682 printf("Optimizing vertices...");
\r
684 memset(vert_used, 0, sizeof(vert_used));
\r
686 if(g_skelModel.clustered == true)
\r
688 memset(newNum_verts, 0, sizeof(newNum_verts));
\r
689 memset(newVertLists, 0, sizeof(newVertLists));
\r
694 // search for common points among all the frames
\r
695 for (i=0 ; i<fmheader.num_frames ; i++)
\r
699 for(j=0;j<fmheader.num_xyz;j++)
\r
701 for(k=0,Found=false;k<j;k++)
\r
702 { // starting from the beginning always ensures vert_replacement points to the first point in the array
\r
703 if (in->v[j].v[0] == in->v[k].v[0] &&
\r
704 in->v[j].v[1] == in->v[k].v[1] &&
\r
705 in->v[j].v[2] == in->v[k].v[2])
\r
708 vert_replacement[j] = k;
\r
720 vert_used[j] = true;
\r
725 // recompute the light normals
\r
726 for (i=0 ; i<fmheader.num_frames ; i++)
\r
730 for(j=0;j<fmheader.num_xyz;j++)
\r
734 k = vert_replacement[j];
\r
736 VectorAdd (in->v[j].vnorm.normalsum, in->v[k].vnorm.normalsum, in->v[k].vnorm.normalsum);
\r
737 in->v[k].vnorm.numnormals += in->v[j].vnorm.numnormals++;
\r
741 for (j=0 ; j<fmheader.num_xyz ; j++)
\r
748 c = in->v[j].vnorm.numnormals;
\r
750 Error ("Vertex with no triangles attached");
\r
752 VectorScale (in->v[j].vnorm.normalsum, 1.0/c, v);
\r
753 VectorNormalize (v, v);
\r
755 maxdot = -999999.0;
\r
758 for (k=0 ; k<NUMVERTEXNORMALS ; k++)
\r
762 dot = DotProduct (v, avertexnormals[k]);
\r
770 in->v[j].lightnormalindex = maxdotindex;
\r
774 // create substitution list
\r
776 for(i=0;i<fmheader.num_xyz;i++)
\r
780 vert_replacement[i] = num_unique;
\r
785 vert_replacement[i] = vert_replacement[vert_replacement[i]];
\r
788 // vert_replacement[i] is the new index, i is the old index
\r
789 // need to add the new index to the cluster list if old index was in it
\r
790 if(g_skelModel.clustered == true)
\r
792 for(k = 0; k < numJointsInSkeleton[g_skelModel.type]; ++k)
\r
794 for(l = 0, current = g_skelModel.vertLists[k];
\r
795 l < g_skelModel.new_num_verts[k+1]; ++l, current = current->next)
\r
797 if(current->data == i)
\r
799 IntListNode_t *current2;
\r
801 qboolean added = false;
\r
803 for(m = 0, current2 = newVertLists[k]; m < newNum_verts[k+1];
\r
804 ++m, current2 = current2->next)
\r
806 if(current2->data == vert_replacement[i])
\r
815 ++newNum_verts[k+1];
\r
817 next = newVertLists[k];
\r
819 newVertLists[k] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "OptimizeVertices");
\r
820 // freed after model write out
\r
822 newVertLists[k]->data = vert_replacement[i];
\r
823 newVertLists[k]->next = next;
\r
833 for (i=0 ; i<fmheader.num_frames ; i++)
\r
837 for(j=0;j<fmheader.num_xyz;j++)
\r
839 in->v[vert_replacement[j]] = in->v[j];
\r
844 for(i = 0; i < numJointsInSkeleton[g_skelModel.type]; ++i)
\r
846 IntListNode_t *toFree;
\r
847 current = g_skelModel.vertLists[i];
\r
852 current = current->next;
\r
853 free(toFree); // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
\r
856 g_skelModel.vertLists[i] = newVertLists[i];
\r
857 g_skelModel.new_num_verts[i+1] = newNum_verts[i+1];
\r
861 for(k = 0; k < numJointsInSkeleton[g_skelModel.type]; ++k)
\r
863 for(l = 0, current = g_skelModel.vertLists[k];
\r
864 l < g_skelModel.new_num_verts[k+1]; ++l, current = current->next)
\r
866 IntListNode_t *current2;
\r
869 for(m = l+1, current2 = current->next; m < newNum_verts[k+1];
\r
870 ++m, current2 = current2->next)
\r
872 if(current->data == current2->data)
\r
874 printf("Warning duplicate vertex: %d\n", current->data);
\r
882 for(i=0;i<fmheader.num_mesh_nodes;i++)
\r
883 { // reset the vert bits
\r
884 memset(pmnodes[i].verts,0,sizeof(pmnodes[i].verts));
\r
887 // repleace the master triangle list vertex indexes and update the vert bits for each mesh node
\r
888 for (i=0 ; i<fmheader.num_tris ; i++)
\r
891 bit = 1 << (i & 7 );
\r
893 for (j=0 ; j<3 ; j++)
\r
895 set_bit = set_pos = triangles[i].index_xyz[j] = vert_replacement[triangles[i].index_xyz[j]];
\r
898 set_bit = 1 << (set_bit & 7);
\r
900 for(k=0;k<fmheader.num_mesh_nodes;k++)
\r
902 if (!(pmnodes[k].tris[pos] & bit))
\r
906 pmnodes[k].verts[set_pos] |= set_bit;
\r
911 for (i=0;i<numcommands;i++)
\r
917 for(i++;j;j--,i+=3)
\r
919 commands[i+2] = vert_replacement[commands[i+2]];
\r
924 printf("Reduced by %d\n",fmheader.num_xyz - num_unique);
\r
926 fmheader.num_xyz = num_unique;
\r
929 // tack on the reference verts to the regular verts
\r
930 if(g_skelModel.references != REF_NULL)
\r
936 if (RefPointNum <= 0)
\r
937 { // Hard-coded labels
\r
938 refnum = numReferences[g_skelModel.references];
\r
941 { // Labels indicated in QDT
\r
942 refnum = RefPointNum;
\r
946 for (i = 0; i < fmheader.num_frames; ++i)
\r
949 index = fmheader.num_xyz;
\r
951 for (j = 0 ; j < refnum; ++j)
\r
953 VectorCopy(in->references[j].placement.origin, in->v[index].v);
\r
956 VectorCopy(in->references[j].placement.direction, in->v[index].v);
\r
959 VectorCopy(in->references[j].placement.up, in->v[index].v);
\r
964 fmheader.num_xyz += refnum*3;
\r
967 // tack on the skeletal joint verts to the regular verts
\r
968 if(g_skelModel.type != SKEL_NULL)
\r
973 for (i = 0; i < fmheader.num_frames; ++i)
\r
976 index = fmheader.num_xyz;
\r
978 for (j = 0 ; j < numJointsInSkeleton[g_skelModel.type]; ++j)
\r
980 VectorCopy(in->joints[j].placement.origin, in->v[index].v);
\r
983 VectorCopy(in->joints[j].placement.direction, in->v[index].v);
\r
986 VectorCopy(in->joints[j].placement.up, in->v[index].v);
\r
991 fmheader.num_xyz += numJointsInSkeleton[g_skelModel.type]*3;
\r
1004 void FMFinishModel (void)
\r
1006 FILE *modelouthandle;
\r
1007 int i,j,length,tris,verts,bit,pos,total_tris,total_verts;
\r
1011 if (!fmheader.num_frames)
\r
1015 // copy to release directory tree if doing a release build
\r
1020 sprintf (name, "%s", modelname);
\r
1022 sprintf (name, "%s/tris.fm", cdpartial);
\r
1023 ReleaseFile (name);
\r
1025 for (i=0 ; i<fmheader.num_skins ; i++)
\r
1027 ReleaseFile (g_skins[i]);
\r
1029 fmheader.num_frames = 0;
\r
1036 for(i=0;i<fmheader.num_tris;i++)
\r
1037 if (translucent[i])
\r
1040 if (!g_no_opimizations)
\r
1042 OptimizeVertices();
\r
1046 // write the model output file
\r
1049 sprintf (name, "%s%s", g_outputDir, modelname);
\r
1051 sprintf (name, "%s/tris.fm", g_outputDir);
\r
1052 printf ("saving to %s\n", name);
\r
1053 CreatePath (name);
\r
1054 modelouthandle = SafeOpenWrite (name);
\r
1056 WriteModelFile (modelouthandle);
\r
1058 printf ("%3dx%3d skin\n", fmheader.skinwidth, fmheader.skinheight);
\r
1059 printf ("First frame boundaries:\n");
\r
1060 printf (" minimum x: %3f\n", g_frames[0].mins[0]);
\r
1061 printf (" maximum x: %3f\n", g_frames[0].maxs[0]);
\r
1062 printf (" minimum y: %3f\n", g_frames[0].mins[1]);
\r
1063 printf (" maximum y: %3f\n", g_frames[0].maxs[1]);
\r
1064 printf (" minimum z: %3f\n", g_frames[0].mins[2]);
\r
1065 printf (" maximum z: %3f\n", g_frames[0].maxs[2]);
\r
1066 printf ("%4d vertices\n", fmheader.num_xyz);
\r
1067 printf ("%4d triangles, %4d of them translucent\n", fmheader.num_tris, trans_count);
\r
1068 printf ("%4d frame\n", fmheader.num_frames);
\r
1069 printf ("%4d glverts\n", numglverts);
\r
1070 printf ("%4d glcmd\n", fmheader.num_glcmds);
\r
1071 printf ("%4d skins\n", fmheader.num_skins);
\r
1072 printf ("%4d mesh nodes\n", fmheader.num_mesh_nodes);
\r
1073 printf ("wasted pixels: %d / %d (%5.2f Percent)\n",total_skin_pixels - skin_pixels_used,
\r
1074 total_skin_pixels, (double)(total_skin_pixels - skin_pixels_used) / (double)total_skin_pixels * 100.0);
\r
1076 printf ("file size: %d\n", (int)ftell (modelouthandle) );
\r
1077 printf ("---------------------\n");
\r
1081 if (fmheader.num_mesh_nodes)
\r
1083 total_tris = total_verts = 0;
\r
1084 printf("Node Name Tris Verts\n");
\r
1085 printf("--------------------------------- ---- -----\n");
\r
1086 for(i=0;i<fmheader.num_mesh_nodes;i++)
\r
1090 for(j=0;j<MAXTRIANGLES;j++)
\r
1093 bit = 1 << ((j) & 7 );
\r
1094 if (pmnodes[i].tris[pos] & bit)
\r
1099 for(j=0;j<MAX_FM_VERTS;j++)
\r
1102 bit = 1 << ((j) & 7 );
\r
1103 if (pmnodes[i].verts[pos] & bit)
\r
1109 printf("%-33s %4d %5d\n",pmnodes[i].name,tris,verts);
\r
1111 total_tris += tris;
\r
1112 total_verts += verts;
\r
1114 printf("--------------------------------- ---- -----\n");
\r
1115 printf("%-33s %4d %5d\n","TOTALS",total_tris,total_verts);
\r
1118 fclose (modelouthandle);
\r
1120 // finish writing header file
\r
1123 // scale_up is usefull to allow step distances to be adjusted
\r
1124 H_printf("#define MODEL_SCALE\t\t%f\n", scale_up);
\r
1127 if (fmheader.num_mesh_nodes)
\r
1130 H_printf("#define NUM_MESH_NODES\t\t%d\n\n",fmheader.num_mesh_nodes);
\r
1131 for(i=0;i<fmheader.num_mesh_nodes;i++)
\r
1133 strcpy(name, pmnodes[i].name);
\r
1135 length = strlen(name);
\r
1136 for(j=0;j<length;j++)
\r
1138 if (name[j] == ' ')
\r
1143 H_printf("#define MESH_%s\t\t%d\n", name, i);
\r
1147 fclose (headerouthandle);
\r
1148 headerouthandle = NULL;
\r
1154 =================================================================
\r
1156 ALIAS MODEL DISPLAY LIST GENERATION
\r
1158 =================================================================
\r
1161 extern int strip_xyz[128];
\r
1162 extern int strip_st[128];
\r
1163 extern int strip_tris[128];
\r
1164 extern int stripcount;
\r
1171 static int StripLength (int starttri, int startv, int num_tris, int node)
\r
1176 fmtriangle_t *last, *check;
\r
1180 used[starttri] = 2;
\r
1182 last = &triangles[starttri];
\r
1184 strip_xyz[0] = last->index_xyz[(startv)%3];
\r
1185 strip_xyz[1] = last->index_xyz[(startv+1)%3];
\r
1186 strip_xyz[2] = last->index_xyz[(startv+2)%3];
\r
1187 strip_st[0] = last->index_st[(startv)%3];
\r
1188 strip_st[1] = last->index_st[(startv+1)%3];
\r
1189 strip_st[2] = last->index_st[(startv+2)%3];
\r
1191 strip_tris[0] = starttri;
\r
1194 m1 = last->index_xyz[(startv+2)%3];
\r
1195 st1 = last->index_st[(startv+2)%3];
\r
1196 m2 = last->index_xyz[(startv+1)%3];
\r
1197 st2 = last->index_st[(startv+1)%3];
\r
1199 // look for a matching triangle
\r
1201 for (j=starttri+1, check=&triangles[starttri+1]
\r
1202 ; j<num_tris ; j++, check++)
\r
1205 bit = 1 << (j & 7 );
\r
1206 if (!(pmnodes[node].tris[pos] & bit))
\r
1210 for (k=0 ; k<3 ; k++)
\r
1212 if (check->index_xyz[k] != m1)
\r
1214 if (check->index_st[k] != st1)
\r
1216 if (check->index_xyz[ (k+1)%3 ] != m2)
\r
1218 if (check->index_st[ (k+1)%3 ] != st2)
\r
1221 // this is the next part of the fan
\r
1223 // if we can't use this triangle, this tristrip is done
\r
1224 if (used[j] || translucent[j] != translucent[starttri])
\r
1228 if (stripcount & 1)
\r
1230 m2 = check->index_xyz[ (k+2)%3 ];
\r
1231 st2 = check->index_st[ (k+2)%3 ];
\r
1235 m1 = check->index_xyz[ (k+2)%3 ];
\r
1236 st1 = check->index_st[ (k+2)%3 ];
\r
1239 strip_xyz[stripcount+2] = check->index_xyz[ (k+2)%3 ];
\r
1240 strip_st[stripcount+2] = check->index_st[ (k+2)%3 ];
\r
1241 strip_tris[stripcount] = j;
\r
1250 // clear the temp used flags
\r
1251 for (j=starttri+1 ; j<num_tris ; j++)
\r
1255 return stripcount;
\r
1264 static int FanLength (int starttri, int startv, int num_tris, int node)
\r
1269 fmtriangle_t *last, *check;
\r
1273 used[starttri] = 2;
\r
1275 last = &triangles[starttri];
\r
1277 strip_xyz[0] = last->index_xyz[(startv)%3];
\r
1278 strip_xyz[1] = last->index_xyz[(startv+1)%3];
\r
1279 strip_xyz[2] = last->index_xyz[(startv+2)%3];
\r
1280 strip_st[0] = last->index_st[(startv)%3];
\r
1281 strip_st[1] = last->index_st[(startv+1)%3];
\r
1282 strip_st[2] = last->index_st[(startv+2)%3];
\r
1284 strip_tris[0] = starttri;
\r
1287 m1 = last->index_xyz[(startv+0)%3];
\r
1288 st1 = last->index_st[(startv+0)%3];
\r
1289 m2 = last->index_xyz[(startv+2)%3];
\r
1290 st2 = last->index_st[(startv+2)%3];
\r
1293 // look for a matching triangle
\r
1295 for (j=starttri+1, check=&triangles[starttri+1]
\r
1296 ; j<num_tris ; j++, check++)
\r
1299 bit = 1 << (j & 7 );
\r
1300 if (!(pmnodes[node].tris[pos] & bit))
\r
1304 for (k=0 ; k<3 ; k++)
\r
1306 if (check->index_xyz[k] != m1)
\r
1308 if (check->index_st[k] != st1)
\r
1310 if (check->index_xyz[ (k+1)%3 ] != m2)
\r
1312 if (check->index_st[ (k+1)%3 ] != st2)
\r
1315 // this is the next part of the fan
\r
1317 // if we can't use this triangle, this tristrip is done
\r
1318 if (used[j] || translucent[j] != translucent[starttri])
\r
1322 m2 = check->index_xyz[ (k+2)%3 ];
\r
1323 st2 = check->index_st[ (k+2)%3 ];
\r
1325 strip_xyz[stripcount+2] = m2;
\r
1326 strip_st[stripcount+2] = st2;
\r
1327 strip_tris[stripcount] = j;
\r
1336 // clear the temp used flags
\r
1337 for (j=starttri+1 ; j<num_tris ; j++)
\r
1341 return stripcount;
\r
1350 Generate a list of trifans or strips
\r
1351 for the model, which holds for all frames
\r
1354 static void BuildGlCmds (void)
\r
1359 int len, bestlen, besttype;
\r
1360 int best_xyz[1024];
\r
1361 int best_st[1024];
\r
1362 int best_tris[1024];
\r
1368 // build tristrips
\r
1374 for(l=0;l<fmheader.num_mesh_nodes;l++)
\r
1376 memset (used, 0, sizeof(used));
\r
1378 pmnodes[l].start_glcmds = numcommands;
\r
1380 for(trans_check = 0; trans_check<2; trans_check++)
\r
1382 for (i=0 ; i < fmheader.num_tris ; i++)
\r
1385 bit = 1 << (i & 7 );
\r
1386 if (!(pmnodes[l].tris[pos] & bit))
\r
1391 // pick an unused triangle and start the trifan
\r
1392 if (used[i] || trans_check != translucent[i])
\r
1398 for (type = 0 ; type < 2 ; type++)
\r
1401 for (startv =0 ; startv < 3 ; startv++)
\r
1404 len = StripLength (i, startv, fmheader.num_tris, l);
\r
1406 len = FanLength (i, startv, fmheader.num_tris, l);
\r
1407 if (len > bestlen)
\r
1411 for (j=0 ; j<bestlen+2 ; j++)
\r
1413 best_st[j] = strip_st[j];
\r
1414 best_xyz[j] = strip_xyz[j];
\r
1416 for (j=0 ; j<bestlen ; j++)
\r
1417 best_tris[j] = strip_tris[j];
\r
1422 // mark the tris on the best strip/fan as used
\r
1423 for (j=0 ; j<bestlen ; j++)
\r
1424 used[best_tris[j]] = 1;
\r
1426 if (besttype == 1)
\r
1427 commands[numcommands++] = (bestlen+2);
\r
1429 commands[numcommands++] = -(bestlen+2);
\r
1431 numglverts += bestlen+2;
\r
1433 for (j=0 ; j<bestlen+2 ; j++)
\r
1435 // emit a vertex into the reorder buffer
\r
1438 // emit s/t coords into the commands stream
\r
1442 s = (s ) / fmheader.skinwidth;
\r
1443 t = (t ) / fmheader.skinheight;
\r
1445 *(float *)&commands[numcommands++] = s;
\r
1446 *(float *)&commands[numcommands++] = t;
\r
1447 *(int *)&commands[numcommands++] = best_xyz[j];
\r
1451 commands[numcommands++] = 0; // end of list marker
\r
1452 pmnodes[l].num_glcmds = numcommands - pmnodes[l].start_glcmds;
\r
1458 ===============================================================
\r
1462 ===============================================================
\r
1466 #define LINE_NORMAL 1
\r
1467 #define LINE_FAT 2
\r
1468 #define LINE_DOTTED 3
\r
1471 #define ASCII_SPACE 32
\r
1473 int LineType = LINE_NORMAL;
\r
1474 extern unsigned char pic[SKINPAGE_HEIGHT*SKINPAGE_WIDTH], pic_palette[768];
\r
1475 unsigned char LineColor = 255;
\r
1476 int ScaleWidth, ScaleHeight;
\r
1479 static char *CharDefs[] =
\r
1481 "-------------------------",
\r
1482 "-------------------------", // !
\r
1483 "-------------------------", // "
\r
1484 "-------------------------", // #
\r
1485 "-------------------------", // $
\r
1486 "-------------------------", // %
\r
1487 "-------------------------", // &
\r
1488 "--*----*-----------------", // '
\r
1489 "-*---*----*----*-----*---", // (
\r
1490 "*-----*----*----*---*----", // )
\r
1491 "-----*--*--**---**--*--*-", // *
\r
1492 "-------------------------", // +
\r
1493 "----------------**--**---", // ,
\r
1494 "-------------------------", // -
\r
1495 "----------------**---**--", // .
\r
1496 "-------------------------", // /
\r
1497 " *** * *** * *** * *** ", // 0
\r
1499 "**** * *** * *****",
\r
1500 "**** * *** ***** ",
\r
1501 " ** * * * * ***** * ",
\r
1502 "**** * **** ***** ",
\r
1503 " *** * **** * * *** ",
\r
1505 " *** * * *** * * *** ",
\r
1506 " *** * * **** * *** ", // 9
\r
1507 "-**---**--------**---**--", // :
\r
1508 "-------------------------", // ;
\r
1509 "-------------------------", // <
\r
1510 "-------------------------", // =
\r
1511 "-------------------------", // >
\r
1512 "-------------------------", // ?
\r
1513 "-------------------------", // @
\r
1514 "-***-*---*******---**---*", // A
\r
1515 "****-*---*****-*---*****-",
\r
1516 "-*****----*----*-----****",
\r
1517 "****-*---**---**---*****-",
\r
1518 "******----****-*----*****",
\r
1519 "******----****-*----*----",
\r
1520 "-*****----*--***---*-****",
\r
1521 "*---**---*******---**---*",
\r
1522 "-***---*----*----*---***-",
\r
1523 "----*----*----**---*-***-",
\r
1524 "-*--*-*-*--**---*-*--*--*",
\r
1525 "-*----*----*----*----****",
\r
1526 "*---***-***-*-**---**---*",
\r
1527 "*---***--**-*-**--***---*",
\r
1528 "-***-*---**---**---*-***-",
\r
1529 "****-*---*****-*----*----",
\r
1530 "-***-*---**---*-***----**",
\r
1531 "****-*---*****-*-*--*--**",
\r
1532 "-*****-----***-----*****-",
\r
1533 "*****--*----*----*----*--",
\r
1534 "*---**---**---**---******",
\r
1535 "*---**---**---*-*-*---*--",
\r
1536 "*---**---**-*-***-***---*",
\r
1537 "*---*-*-*---*---*-*-*---*",
\r
1538 "*---**---*-*-*---*----*--",
\r
1539 "*****---*---*---*---*****" // Z
\r
1542 void DrawLine(int x1, int y1, int x2, int y2)
\r
1547 float xfrac, yfrac, xstep, ystep;
\r
1556 count = adx > ady ? adx : ady;
\r
1561 printf("Bad count\n");
\r
1562 return; // don't ever hang up on bad data
\r
1568 xstep = (float)dx/count;
\r
1569 ystep = (float)dy/count;
\r
1576 if(xfrac < SKINPAGE_WIDTH && yfrac < SKINPAGE_HEIGHT)
\r
1578 pic[(int)yfrac*SKINPAGE_WIDTH+(int)xfrac] = LineColor;
\r
1583 } while (count > 0);
\r
1588 for (u=-0.1 ; u<=0.9 ; u+=0.999)
\r
1590 for (v=-0.1 ; v<=0.9 ; v+=0.999)
\r
1594 if(sx < SKINPAGE_WIDTH && sy < SKINPAGE_HEIGHT)
\r
1596 pic[sy*SKINPAGE_WIDTH+sx] = LineColor;
\r
1603 } while (count > 0);
\r
1608 if(count&1 && xfrac < SKINPAGE_WIDTH &&
\r
1609 yfrac < SKINPAGE_HEIGHT)
\r
1611 pic[(int)yfrac*SKINPAGE_WIDTH+(int)xfrac] = LineColor;
\r
1616 } while (count > 0);
\r
1619 Error("Unknown <linetype> %d.\n", LineType);
\r
1623 //==========================================================================
\r
1627 //==========================================================================
\r
1629 static void DrawCharacter(int x, int y, int character)
\r
1634 character = toupper(character);
\r
1635 if(character < ASCII_SPACE || character > 'Z')
\r
1637 character = ASCII_SPACE;
\r
1639 character -= ASCII_SPACE;
\r
1640 for(def = CharDefs[character], r = 0; r < 5; r++)
\r
1642 for(c = 0; c < 5; c++)
\r
1644 pic[(y+r)*SKINPAGE_WIDTH+x+c] = *def++ == '*' ? 255 : 0;
\r
1649 //==========================================================================
\r
1653 //==========================================================================
\r
1655 void DrawTextChar(int x, int y, char *text)
\r
1659 while((c = *text++) != '\0')
\r
1661 DrawCharacter(x, y, c);
\r
1667 extern void DrawScreen(float s_scale, float t_scale, float iwidth, float iheight);
\r
1669 //==========================================================================
\r
1672 static int ExtractDigit(byte *pic, int x, int y)
\r
1676 char digString[32];
\r
1681 backColor = pic[(SKINPAGE_HEIGHT - 1) * SKINPAGE_WIDTH];
\r
1682 DigitDefs = &CharDefs['0' - ASCII_SPACE];
\r
1684 buffer = digString;
\r
1685 for(r = 0; r < 5; r++)
\r
1687 for(c = 0; c < 5; c++)
\r
1689 *buffer++ = (pic[(y + r) * SKINPAGE_WIDTH + x + c] == backColor) ? ' ' : '*';
\r
1693 for(i = 0; i < 10; i++)
\r
1695 if(strcmp(DigitDefs[i], digString) == 0)
\r
1701 Error("Unable to extract scaling info from skin PCX.");
\r
1705 //==========================================================================
\r
1708 int ExtractNumber(byte *pic, int x, int y)
\r
1710 return ExtractDigit(pic, x, y) * 100 + ExtractDigit(pic, x + 6, y) * 10 + ExtractDigit(pic, x + 12, y);
\r
1721 Builds the triangle_st array for the base frame and
\r
1722 fmheader.skinwidth / fmheader.skinheight
\r
1724 FIXME: allow this to be loaded from a file for
\r
1725 arbitrary mappings
\r
1728 static void BuildST (triangle_t *ptri, int numtri, qboolean DrawSkin)
\r
1730 int backface_flag;
\r
1732 int width, height, iwidth, iheight, swidth;
\r
1733 float basex, basey;
\r
1735 vec3_t mins, maxs;
\r
1737 vec3_t vtemp1, vtemp2, normal;
\r
1738 float s_scale, t_scale;
\r
1745 // find bounds of all the verts on the base frame
\r
1747 ClearBounds (mins, maxs);
\r
1748 backface_flag = false;
\r
1750 if (ptri[0].HasUV) // if we have the uv already, we don't want to double up or scale
\r
1752 iwidth = ScaleWidth;
\r
1753 iheight = ScaleHeight;
\r
1755 t_scale = s_scale = 1.0;
\r
1759 for (i=0 ; i<numtri ; i++)
\r
1760 for (j=0 ; j<3 ; j++)
\r
1761 AddPointToBounds (ptri[i].verts[j], mins, maxs);
\r
1763 for (i=0 ; i<3 ; i++)
\r
1765 mins[i] = floor(mins[i]);
\r
1766 maxs[i] = ceil(maxs[i]);
\r
1769 width = maxs[0] - mins[0];
\r
1770 height = maxs[2] - mins[2];
\r
1772 for (i=0 ; i<numtri ; i++)
\r
1774 VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
\r
1775 VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
\r
1776 CrossProduct (vtemp1, vtemp2, normal);
\r
1778 if (normal[1] > 0)
\r
1780 backface_flag = true;
\r
1784 scWidth = ScaleWidth*SCALE_ADJUST_FACTOR;
\r
1785 if (backface_flag) //we are doubling
\r
1788 scHeight = ScaleHeight*SCALE_ADJUST_FACTOR;
\r
1790 scale = scWidth/width;
\r
1792 if(height*scale >= scHeight)
\r
1794 scale = scHeight/height;
\r
1797 iwidth = ceil(width*scale)+4;
\r
1798 iheight = ceil(height*scale)+4;
\r
1800 s_scale = (float)(iwidth-4) / width;
\r
1801 t_scale = (float)(iheight-4) / height;
\r
1802 t_scale = s_scale;
\r
1807 DrawScreen(s_scale, t_scale, iwidth*2, iheight);
\r
1809 DrawScreen(s_scale, t_scale, iwidth, iheight);
\r
1811 if (backface_flag)
\r
1812 skinwidth=iwidth*2;
\r
1815 skinheight=iheight;
\r
1818 /* if (!g_fixedwidth)
\r
1821 if (width*scale >= 150)
\r
1822 scale = 150.0 / width;
\r
1823 if (height*scale >= 190)
\r
1824 scale = 190.0 / height;
\r
1826 s_scale = t_scale = scale;
\r
1828 iwidth = ceil(width*s_scale);
\r
1829 iheight = ceil(height*t_scale);
\r
1836 iwidth = g_fixedwidth / 2;
\r
1837 iheight = g_fixedheight;
\r
1839 s_scale = (float)(iwidth-4) / width;
\r
1840 t_scale = (float)(iheight-4) / height;
\r
1844 // determine which side of each triangle to map the texture to
\r
1847 for (i=0 ; i<numtri ; i++)
\r
1849 if (ptri[i].HasUV)
\r
1851 for (j=0 ; j<3 ; j++)
\r
1853 triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*skinwidth);
\r
1854 triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*skinheight);
\r
1859 VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
\r
1860 VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
\r
1861 CrossProduct (vtemp1, vtemp2, normal);
\r
1863 if (normal[1] > 0)
\r
1865 basex = iwidth + 2;
\r
1872 for (j=0 ; j<3 ; j++)
\r
1874 pbasevert = ptri[i].verts[j];
\r
1876 triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);
\r
1877 triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);
\r
1883 DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],
\r
1884 triangle_st[i][1][0], triangle_st[i][1][1]);
\r
1885 DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],
\r
1886 triangle_st[i][2][0], triangle_st[i][2][1]);
\r
1887 DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],
\r
1888 triangle_st[i][0][0], triangle_st[i][0][1]);
\r
1892 // make the width a multiple of 4; some hardware requires this, and it ensures
\r
1893 // dword alignment for each scan
\r
1898 fmheader.skinwidth = (swidth + 3) & ~3;
\r
1899 fmheader.skinheight = iheight;
\r
1901 skin_width = iwidth;
\r
1902 skin_height = iheight;
\r
1906 static void BuildNewST (triangle_t *ptri, int numtri, qboolean DrawSkin)
\r
1910 for (i=0 ; i<numtri ; i++)
\r
1912 if (ptri[i].HasUV)
\r
1914 for (j=0 ; j<3 ; j++)
\r
1916 triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*(ScaleWidth-1));
\r
1917 triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*(ScaleHeight-1));
\r
1923 DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],
\r
1924 triangle_st[i][1][0], triangle_st[i][1][1]);
\r
1925 DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],
\r
1926 triangle_st[i][2][0], triangle_st[i][2][1]);
\r
1927 DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],
\r
1928 triangle_st[i][0][0], triangle_st[i][0][1]);
\r
1932 // make the width a multiple of 4; some hardware requires this, and it ensures
\r
1933 // dword alignment for each scan
\r
1935 fmheader.skinwidth = (ScaleWidth + 3) & ~3;
\r
1936 fmheader.skinheight = ScaleHeight;
\r
1938 skin_width = ScaleWidth;
\r
1939 skin_height = ScaleHeight;
\r
1945 byte *BasePalette;
\r
1946 byte *BasePixels,*TransPixels;
\r
1947 int BaseWidth, BaseHeight, TransWidth, TransHeight;
\r
1948 qboolean BaseTrueColor;
\r
1949 static qboolean SetPixel = false;
\r
1951 int CheckTransRecursiveTri (int *lp1, int *lp2, int *lp3)
\r
1957 d = lp2[0] - lp1[0];
\r
1958 if (d < -1 || d > 1)
\r
1960 d = lp2[1] - lp1[1];
\r
1961 if (d < -1 || d > 1)
\r
1964 d = lp3[0] - lp2[0];
\r
1965 if (d < -1 || d > 1)
\r
1967 d = lp3[1] - lp2[1];
\r
1968 if (d < -1 || d > 1)
\r
1971 d = lp1[0] - lp3[0];
\r
1972 if (d < -1 || d > 1)
\r
1974 d = lp1[1] - lp3[1];
\r
1975 if (d < -1 || d > 1)
\r
1986 return 0; // entire tri is filled
\r
1995 // split this edge
\r
1996 new[0] = (lp1[0] + lp2[0]) >> 1;
\r
1997 new[1] = (lp1[1] + lp2[1]) >> 1;
\r
1999 // draw the point if splitting a leading edge
\r
2000 if (lp2[1] > lp1[1])
\r
2002 if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0]))
\r
2007 assert ((new[1]*BaseWidth) + new[0] < BaseWidth*BaseHeight);
\r
2009 if (BaseTrueColor)
\r
2011 BasePixels[((new[1]*BaseWidth) + new[0]) * 4] = 1;
\r
2015 BasePixels[(new[1]*BaseWidth) + new[0]] = 1;
\r
2022 if (TransPixels[(new[1]*TransWidth) + new[0]] != 255)
\r
2025 else if (BaseTrueColor)
\r
2027 if (BasePixels[(((new[1]*BaseWidth) + new[0]) * 4) + 3] != 255)
\r
2032 // pixel = BasePixels[(new[1]*BaseWidth) + new[0]];
\r
2037 // recursively continue
\r
2038 if (CheckTransRecursiveTri(lp3, lp1, new))
\r
2041 return CheckTransRecursiveTri(lp3, new, lp2);
\r
2044 static void ReplaceClusterIndex(int newIndex, int oldindex, int **clusters,
\r
2045 IntListNode_t **vertLists, int *num_verts, int *new_num_verts)
\r
2048 IntListNode_t *next;
\r
2050 for(j = 0; j < numJointsInSkeleton[g_skelModel.type]; ++j)
\r
2057 for(i = 0; i < num_verts[j+1]; ++i)
\r
2059 if(clusters[j][i] == oldindex)
\r
2061 ++new_num_verts[j+1];
\r
2063 next = vertLists[j];
\r
2065 vertLists[j] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "ReplaceClusterIndex");
\r
2066 // Currently freed in WriteJointedModelFile only
\r
2068 vertLists[j]->data = newIndex;
\r
2069 vertLists[j]->next = next;
\r
2075 #define FUDGE_EPSILON 0.002
\r
2077 qboolean VectorFudgeCompare (vec3_t v1, vec3_t v2)
\r
2081 for (i=0 ; i<3 ; i++)
\r
2082 if (fabs(v1[i]-v2[i]) > FUDGE_EPSILON)
\r
2093 void Cmd_FMBase (qboolean GetST)
\r
2095 triangle_t *ptri, *st_tri;
\r
2100 char file1[1024],file2[1024],trans_file[1024], stfile[1024], extension[256];
\r
2101 vec3_t base_xyz[MAX_FM_VERTS];
\r
2106 GetScriptToken (false);
\r
2108 if (g_skipmodel || g_release || g_archive)
\r
2111 printf ("---------------------\n");
\r
2112 sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext);
\r
2113 printf ("%s ", file1);
\r
2115 ExpandPathAndArchive (file1);
\r
2117 // Use the input filepath for this one.
\r
2118 sprintf (file1, "%s/%s", cddir, token);
\r
2120 // time1 = FileTime (file1);
\r
2121 // if (time1 == -1)
\r
2122 // Error ("%s doesn't exist", file1);
\r
2125 // load the base triangles
\r
2128 Load3DSTriangleList (file1, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);
\r
2130 LoadTriangleList (file1, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);
\r
2132 if (g_ignoreTriUV)
\r
2134 for (i=0;i<fmheader.num_tris;i++)
\r
2140 GetScriptToken (false);
\r
2141 sprintf (file2, "%s/%s", cddir, token);
\r
2142 sprintf (trans_file, "%s/!%s_a.pcx", cddir, token);
\r
2144 ExtractFileExtension (file2, extension);
\r
2145 if (extension[0] == 0)
\r
2147 strcat(file2, ".pcx");
\r
2149 printf ("skin: %s\n", file2);
\r
2151 BaseTrueColor = LoadAnyImage (file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight);
\r
2154 if (BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT)
\r
2156 if (g_allow_newskin)
\r
2158 ScaleWidth = BaseWidth;
\r
2159 ScaleHeight = BaseHeight;
\r
2164 Error("Invalid skin page size: (%d,%d) should be (%d,%d)",
\r
2165 BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT);
\r
2168 else if (!BaseTrueColor)
\r
2170 ScaleWidth = (float)ExtractNumber(BasePixels, ENCODED_WIDTH_X,
\r
2172 ScaleHeight = (float)ExtractNumber(BasePixels, ENCODED_HEIGHT_X,
\r
2173 ENCODED_HEIGHT_Y);
\r
2177 Error("Texture coordinates not supported on true color image");
\r
2182 GetScriptToken (false);
\r
2184 sprintf (stfile, "%s/%s.%s", cdarchive, token, trifileext);
\r
2185 printf ("ST: %s ", stfile);
\r
2187 sprintf (stfile, "%s/%s", cddir, token);
\r
2190 Load3DSTriangleList (stfile, &st_tri, &num_st_tris, NULL, NULL);
\r
2192 LoadTriangleList (stfile, &st_tri, &num_st_tris, NULL, NULL);
\r
2194 if (num_st_tris != fmheader.num_tris)
\r
2196 Error ("num st tris mismatch: st %d / base %d", num_st_tris, fmheader.num_tris);
\r
2199 printf(" matching triangles...\n");
\r
2200 for(i=0;i<fmheader.num_tris;i++)
\r
2203 for(j=0;j<num_st_tris;j++)
\r
2215 if (z == x || z == y)
\r
2220 if (VectorFudgeCompare (ptri[i].verts[0], st_tri[j].verts[x]) &&
\r
2221 VectorFudgeCompare (ptri[i].verts[1], st_tri[j].verts[y]) &&
\r
2222 VectorFudgeCompare (ptri[i].verts[2], st_tri[j].verts[z]))
\r
2227 ptri[i].HasUV = st_tri[k].HasUV;
\r
2228 ptri[i].uv[0][0] = st_tri[k].uv[x][0];
\r
2229 ptri[i].uv[0][1] = st_tri[k].uv[x][1];
\r
2230 ptri[i].uv[1][0] = st_tri[k].uv[y][0];
\r
2231 ptri[i].uv[1][1] = st_tri[k].uv[y][1];
\r
2232 ptri[i].uv[2][0] = st_tri[k].uv[z][0];
\r
2233 ptri[i].uv[2][1] = st_tri[k].uv[z][1];
\r
2238 printf("Duplicate triangle %d found in st file: %d and %d\n",i,k,j);
\r
2239 printf(" (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",
\r
2240 ptri[i].verts[0][0],ptri[i].verts[0][1],ptri[i].verts[0][2],
\r
2241 ptri[i].verts[1][0],ptri[i].verts[1][1],ptri[i].verts[1][2],
\r
2242 ptri[i].verts[2][0],ptri[i].verts[2][1],ptri[i].verts[2][2]);
\r
2243 printf(" (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",
\r
2244 st_tri[k].verts[0][0],st_tri[k].verts[0][1],st_tri[k].verts[0][2],
\r
2245 st_tri[k].verts[1][0],st_tri[k].verts[1][1],st_tri[k].verts[1][2],
\r
2246 st_tri[k].verts[2][0],st_tri[k].verts[2][1],st_tri[k].verts[2][2]);
\r
2247 printf(" (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",
\r
2248 st_tri[j].verts[0][0],st_tri[j].verts[0][1],st_tri[j].verts[0][2],
\r
2249 st_tri[j].verts[1][0],st_tri[j].verts[1][1],st_tri[j].verts[1][2],
\r
2250 st_tri[j].verts[2][0],st_tri[j].verts[2][1],st_tri[j].verts[2][2]);
\r
2259 printf("No matching triangle %d\n",i);
\r
2266 // get the ST values
\r
2268 if (ptri && ptri[0].HasUV)
\r
2272 Error("Base has UVs with old style skin page\nMaybe you want to use -ignoreUV");
\r
2276 BuildNewST (ptri, fmheader.num_tris, false);
\r
2283 Error("Base has new style skin without UVs");
\r
2287 BuildST (ptri, fmheader.num_tris, false);
\r
2291 TransPixels = NULL;
\r
2292 if (!BaseTrueColor)
\r
2294 FH = fopen(trans_file,"rb");
\r
2298 Load256Image (trans_file, &TransPixels, NULL, &TransWidth, &TransHeight);
\r
2299 if (TransWidth != fmheader.skinwidth || TransHeight != fmheader.skinheight)
\r
2301 Error ("source image %s dimensions (%d,%d) are not the same as alpha image (%d,%d)\n",file2,fmheader.skinwidth,fmheader.skinheight,TransWidth,TransHeight);
\r
2307 // run through all the base triangles, storing each unique vertex in the
\r
2308 // base vertex list and setting the indirect triangles to point to the base
\r
2311 for(l=0;l<fmheader.num_mesh_nodes;l++)
\r
2313 for (i=0 ; i < fmheader.num_tris ; i++)
\r
2316 bit = 1 << (i & 7 );
\r
2317 if (!(pmnodes[l].tris[pos] & bit))
\r
2322 for (j=0 ; j<3 ; j++)
\r
2324 // get the xyz index
\r
2325 for (k=0 ; k<fmheader.num_xyz ; k++)
\r
2327 if (VectorCompare (ptri[i].verts[j], base_xyz[k]))
\r
2329 break; // this vertex is already in the base vertex list
\r
2333 if (k == fmheader.num_xyz)
\r
2335 VectorCopy (ptri[i].verts[j], base_xyz[fmheader.num_xyz]);
\r
2337 if(pmnodes[l].clustered == true)
\r
2339 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);
\r
2342 fmheader.num_xyz++;
\r
2346 bit = 1 << (k & 7);
\r
2347 pmnodes[l].verts[pos] |= bit;
\r
2349 triangles[i].index_xyz[j] = k;
\r
2351 // get the st index
\r
2352 for (k=0 ; k<fmheader.num_st ; k++)
\r
2354 if (triangle_st[i][j][0] == base_st[k].s
\r
2355 && triangle_st[i][j][1] == base_st[k].t)
\r
2357 break; // this vertex is already in the base vertex list
\r
2361 if (k == fmheader.num_st)
\r
2363 base_st[fmheader.num_st].s = triangle_st[i][j][0];
\r
2364 base_st[fmheader.num_st].t = triangle_st[i][j][1];
\r
2365 fmheader.num_st++;
\r
2368 triangles[i].index_st[j] = k;
\r
2371 if (TransPixels || BaseTrueColor)
\r
2373 translucent[i] = CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);
\r
2377 translucent[i] = false;
\r
2382 if (!BaseTrueColor)
\r
2385 memset(BasePixels,0,BaseWidth*BaseHeight);
\r
2386 for (i=0 ; i < fmheader.num_tris ; i++)
\r
2388 CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);
\r
2392 skin_pixels_used = 0;
\r
2393 for(i=0;i<fmheader.skinheight;i++)
\r
2395 for(j=0;j<fmheader.skinwidth;j++)
\r
2397 skin_pixels_used += BasePixels[(i*BaseWidth) + j];
\r
2400 total_skin_pixels = fmheader.skinheight*fmheader.skinwidth;
\r
2405 memset(BasePixels,0,BaseWidth*BaseHeight*4);
\r
2406 for (i=0 ; i < fmheader.num_tris ; i++)
\r
2408 CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);
\r
2412 skin_pixels_used = 0;
\r
2413 for(i=0;i<fmheader.skinheight;i++)
\r
2415 for(j=0;j<fmheader.skinwidth;j++)
\r
2417 skin_pixels_used += BasePixels[((i*BaseWidth) + j)*4];
\r
2420 total_skin_pixels = fmheader.skinheight*fmheader.skinwidth;
\r
2423 // build triangle strips / fans
\r
2428 free(TransPixels);
\r
2430 free (BasePixels);
\r
2433 free (BasePalette);
\r
2438 void Cmd_FMNodeOrder(void)
\r
2440 mesh_node_t *newnodes, *pos;
\r
2445 Error ("Base has not been established yet");
\r
2448 pos = newnodes = malloc(sizeof(mesh_node_t) * fmheader.num_mesh_nodes);
\r
2450 for(i=0;i<fmheader.num_mesh_nodes;i++)
\r
2452 GetScriptToken (false);
\r
2454 for(j=0;j<fmheader.num_mesh_nodes;j++)
\r
2456 if (strcmpi(pmnodes[j].name, token) == 0)
\r
2458 *pos = pmnodes[j];
\r
2463 if (j >= fmheader.num_mesh_nodes)
\r
2465 Error("Node '%s' not in base list!\n", token);
\r
2470 pmnodes = newnodes;
\r
2473 //===============================================================
\r
2475 extern char *FindFrameFile (char *frame);
\r
2483 void GrabFrame (char *frame)
\r
2487 fmtrivert_t *ptrivert;
\r
2494 // the frame 'run1' will be looked for as either
\r
2495 // run.1 or run1.tri, so the new alias sequence save
\r
2496 // feature an be used
\r
2497 framefile = FindFrameFile (frame);
\r
2499 sprintf (file1, "%s/%s", cdarchive, framefile);
\r
2500 ExpandPathAndArchive (file1);
\r
2502 sprintf (file1, "%s/%s",cddir, framefile);
\r
2504 printf ("grabbing %s ", file1);
\r
2506 if (fmheader.num_frames >= MAX_FM_FRAMES)
\r
2507 Error ("fmheader.num_frames >= MAX_FM_FRAMES");
\r
2508 fr = &g_frames[fmheader.num_frames];
\r
2509 fmheader.num_frames++;
\r
2511 strcpy (fr->name, frame);
\r
2517 Load3DSTriangleList (file1, &ptri, &num_tris, NULL, NULL);
\r
2519 LoadTriangleList (file1, &ptri, &num_tris, NULL, NULL);
\r
2521 if (num_tris != fmheader.num_tris)
\r
2522 Error ("%s: number of triangles (%d) doesn't match base frame (%d)\n", file1, num_tris, fmheader.num_tris);
\r
2525 // allocate storage for the frame's vertices
\r
2529 for (i=0 ; i<fmheader.num_xyz ; i++)
\r
2531 ptrivert[i].vnorm.numnormals = 0;
\r
2532 VectorClear (ptrivert[i].vnorm.normalsum);
\r
2534 ClearBounds (fr->mins, fr->maxs);
\r
2537 // store the frame's vertices in the same order as the base. This assumes the
\r
2538 // triangles and vertices in this frame are in exactly the same order as in the
\r
2541 for (i=0 ; i<num_tris ; i++)
\r
2543 vec3_t vtemp1, vtemp2, normal;
\r
2546 VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
\r
2547 VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
\r
2548 CrossProduct (vtemp1, vtemp2, normal);
\r
2550 VectorNormalize (normal, normal);
\r
2552 // rotate the normal so the model faces down the positive x axis
\r
2553 ftemp = normal[0];
\r
2554 normal[0] = -normal[1];
\r
2555 normal[1] = ftemp;
\r
2557 for (j=0 ; j<3 ; j++)
\r
2559 index_xyz = triangles[i].index_xyz[j];
\r
2561 // rotate the vertices so the model faces down the positive x axis
\r
2562 // also adjust the vertices to the desired origin
\r
2563 ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +
\r
2565 ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) +
\r
2567 ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) +
\r
2570 AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);
\r
2572 VectorAdd (ptrivert[index_xyz].vnorm.normalsum, normal, ptrivert[index_xyz].vnorm.normalsum);
\r
2573 ptrivert[index_xyz].vnorm.numnormals++;
\r
2578 // calculate the vertex normals, match them to the template list, and store the
\r
2579 // index of the best match
\r
2581 for (i=0 ; i<fmheader.num_xyz ; i++)
\r
2589 c = ptrivert[i].vnorm.numnormals;
\r
2591 Error ("Vertex with no triangles attached");
\r
2593 VectorScale (ptrivert[i].vnorm.normalsum, 1.0/c, v);
\r
2594 VectorNormalize (v, v);
\r
2596 maxdot = -999999.0;
\r
2599 for (j=0 ; j<NUMVERTEXNORMALS ; j++)
\r
2603 dot = DotProduct (v, avertexnormals[j]);
\r
2611 ptrivert[i].lightnormalindex = maxdotindex;
\r
2622 void Cmd_FMFrame (void)
\r
2624 while (ScriptTokenAvailable())
\r
2626 GetScriptToken (false);
\r
2629 if (g_release || g_archive)
\r
2631 fmheader.num_frames = 1; // don't skip the writeout
\r
2635 H_printf("#define FRAME_%-16s\t%i\n", token, fmheader.num_frames);
\r
2637 if((g_skelModel.type != SKEL_NULL) || (g_skelModel.references != REF_NULL))
\r
2639 GrabModelTransform(token);
\r
2642 GrabFrame (token);
\r
2644 if(g_skelModel.type != SKEL_NULL)
\r
2646 GrabSkeletalFrame(token);
\r
2649 if(g_skelModel.references != REF_NULL)
\r
2651 GrabReferencedFrame(token);
\r
2654 // need to add the up and dir points to the frame bounds here
\r
2655 // using AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);
\r
2656 // then remove fudge in determining scale on frame write out
\r
2664 Skins aren't actually stored in the file, only a reference
\r
2665 is saved out to the header file.
\r
2668 void Cmd_FMSkin (void)
\r
2672 int width, height;
\r
2675 char name[1024], savename[1024], transname[1024], extension[256];
\r
2676 miptex32_t *qtex32;
\r
2679 qboolean TrueColor;
\r
2681 GetScriptToken (false);
\r
2683 if (fmheader.num_skins == MAX_FM_SKINS)
\r
2684 Error ("fmheader.num_skins == MAX_FM_SKINS");
\r
2689 sprintf (name, "%s/%s", cdarchive, token);
\r
2690 strcpy (name, ExpandPathAndArchive( name ) );
\r
2691 // sprintf (name, "%s/%s.lbm", cddir, token);
\r
2693 if (ScriptTokenAvailable())
\r
2695 GetScriptToken (false);
\r
2696 sprintf (g_skins[fmheader.num_skins], "!%s", token);
\r
2697 sprintf (savename, "%s!%s", g_outputDir, token);
\r
2698 sprintf (transname, "%s!%s_a.pcx", gamedir, token);
\r
2702 sprintf (g_skins[fmheader.num_skins], "%s/!%s", cdpartial, token);
\r
2703 sprintf (savename, "%s/!%s", g_outputDir, token);
\r
2704 sprintf (transname, "%s/!%s_a.pcx", cddir, token);
\r
2707 fmheader.num_skins++;
\r
2709 if (g_skipmodel || g_release || g_archive)
\r
2713 printf ("loading %s\n", name);
\r
2714 ExtractFileExtension (name, extension);
\r
2715 if (extension[0] == 0)
\r
2717 strcat(name, ".pcx");
\r
2721 TrueColor = LoadAnyImage (name, &pixels, &palette, &width, &height);
\r
2722 // RemapZero (pixels, palette, width, height);
\r
2724 // crop it to the proper size
\r
2728 cropped = (byte *) SafeMalloc (fmheader.skinwidth*fmheader.skinheight, "Cmd_FMSkin");
\r
2729 for (y=0 ; y<fmheader.skinheight ; y++)
\r
2731 memcpy (cropped+y*fmheader.skinwidth,
\r
2732 pixels+y*width, fmheader.skinwidth);
\r
2735 TransPixels = NULL;
\r
2736 FH = fopen(transname,"rb");
\r
2741 strcat(g_skins[fmheader.num_skins-1],".pcx");
\r
2742 strcat(savename,".pcx");
\r
2744 // save off the new image
\r
2745 printf ("saving %s\n", savename);
\r
2746 CreatePath (savename);
\r
2747 WritePCXfile (savename, cropped, fmheader.skinwidth, fmheader.skinheight, palette);
\r
2753 qtex = CreateMip(cropped, fmheader.skinwidth, fmheader.skinheight, palette, &size, true);
\r
2755 strcat(g_skins[fmheader.num_skins-1],".m8");
\r
2756 strcat(savename,".m8");
\r
2758 printf ("saving %s\n", savename);
\r
2759 CreatePath (savename);
\r
2760 SaveFile (savename, (byte *)qtex, size);
\r
2763 strcat(g_skins[fmheader.num_skins-1],".pcx");
\r
2764 strcat(savename,".pcx");
\r
2766 // save off the new image
\r
2767 printf ("saving %s\n", savename);
\r
2768 CreatePath (savename);
\r
2769 WritePCXfile (savename, cropped, fmheader.skinwidth, fmheader.skinheight, palette);
\r
2775 cropped = (byte *) SafeMalloc (fmheader.skinwidth*fmheader.skinheight*4, "Cmd_FMSkin");
\r
2776 for (y=0 ; y<fmheader.skinheight ; y++)
\r
2778 memcpy (cropped+((y*fmheader.skinwidth)*4), pixels+(y*width*4), fmheader.skinwidth*4);
\r
2781 qtex32 = CreateMip32((unsigned *)cropped, fmheader.skinwidth, fmheader.skinheight, &size, true);
\r
2783 StripExtension(g_skins[fmheader.num_skins-1]);
\r
2784 strcat(g_skins[fmheader.num_skins-1],".m32");
\r
2785 StripExtension(savename);
\r
2786 strcat(savename,".m32");
\r
2788 printf ("saving %s\n", savename);
\r
2789 CreatePath (savename);
\r
2790 SaveFile (savename, (byte *)qtex32, size);
\r
2807 void Cmd_FMCd (void)
\r
2814 GetScriptToken (false);
\r
2816 // this is a silly mess...
\r
2817 sprintf(cdpartial, "models/%s", token);
\r
2818 sprintf(cdarchive, "%smodels/%s", gamedir+strlen(qdir), token);
\r
2819 sprintf(cddir, "%s%s", gamedir, cdpartial);
\r
2821 // Since we also changed directories on the output side (for mirror) make sure the outputdir is set properly too.
\r
2822 sprintf(temp, "%s%s", g_outputDir, cdpartial);
\r
2823 strcpy(g_outputDir, temp);
\r
2825 // if -only was specified and this cd doesn't match,
\r
2826 // skip the model (you only need to match leading chars,
\r
2827 // so you could regrab all monsters with -only monsters)
\r
2830 if (strncmp(token, g_only, strlen(g_only)))
\r
2832 g_skipmodel = true;
\r
2833 printf ("skipping %s\n", cdpartial);
\r
2840 //=======================
\r
2842 //=======================
\r
2844 void NewGen (char *ModelFile, char *OutputName, int width, int height)
\r
2846 trigroup_t *triangles;
\r
2848 triangle_t *grouptris;
\r
2849 mesh_node_t *pmnodes;
\r
2853 vec3_t aveNorm, crossvect;
\r
2854 vec3_t diffvect1, diffvect2;
\r
2855 vec3_t v0, v1, v2;
\r
2857 vec3_t base, zaxis, yaxis;
\r
2858 vec3_t uvwMin, uvwMax;
\r
2859 vec3_t groupMin, groupMax;
\r
2862 float *uFinal, *vFinal;
\r
2863 unsigned char *newpic;
\r
2865 int finalstart = 0, finalcount = 0;
\r
2866 int xbase = 0, xwidth = 0, ywidth = 0;
\r
2867 int *todo, *done, finished;
\r
2868 int i, j, k, l; //counters
\r
2869 int groupnum, numtris, numverts, num;
\r
2874 for ( i = 0; i<3; i++)
\r
2877 uvwMin[i] = 1e30f;
\r
2878 uvwMax[i] = -1e30f;
\r
2893 LoadTriangleList (ModelFile, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);
\r
2895 todo = (int*)SafeMalloc(fmheader.num_tris*sizeof(int), "NewGen");
\r
2896 done = (int*)SafeMalloc(fmheader.num_tris*sizeof(int), "NewGen");
\r
2897 triangles = (trigroup_t*)SafeMalloc(fmheader.num_tris*sizeof(trigroup_t), "NewGen");
\r
2899 for ( i=0; i < fmheader.num_tris; i++)
\r
2903 triangles[i].triangle = ptri[i];
\r
2904 triangles[i].group = 0;
\r
2909 // transitive closure algorithm follows
\r
2910 // put all triangles who transitively share vertices into separate groups
\r
2914 for ( i = 0; i < fmheader.num_tris; i++)
\r
2921 if ( i == fmheader.num_tris)
\r
2930 for ( i = 0; i < fmheader.num_tris; i++)
\r
2935 triangles[i].group = groupnum;
\r
2937 for ( j = 0; j < fmheader.num_tris; j++)
\r
2939 if ((!done[j]) && (ShareVertex(triangles[i],triangles[j])))
\r
2950 uFinal = (float*)SafeMalloc(3*fmheader.num_tris*sizeof(float), "NewGen");
\r
2951 vFinal = (float*)SafeMalloc(3*fmheader.num_tris*sizeof(float), "NewGen");
\r
2953 grpfile = fopen("grpdebug.txt","w");
\r
2956 for (i = 0; i < groupnum; i++)
\r
2959 fprintf(grpfile,"Group Number: %d\n", i);
\r
2961 numtris = GetNumTris(triangles, i); // number of triangles in group i
\r
2962 numverts = numtris * 3;
\r
2964 fprintf(grpfile,"%d triangles.\n", numtris);
\r
2966 vertices = (vec3_t*)SafeMalloc(numverts*sizeof(vec3_t), "NewGen");
\r
2967 uvs = (vec3_t*)SafeMalloc(numverts*sizeof(vec3_t), "NewGen");
\r
2968 grouptris = (triangle_t*)SafeMalloc(numtris*sizeof(triangle_t), "NewGen");
\r
2970 for (count = 0; count < fmheader.num_tris; count++)
\r
2972 if (triangles[count].group == i)
\r
2974 fprintf(grpfile,"Triangle %d\n", count);
\r
2977 fprintf(grpfile,"\n");
\r
2982 GetOneGroup(triangles, i, grouptris);
\r
2985 for (j = 0; j < numtris; j++)
\r
2987 VectorCopy(grouptris[j].verts[0], v0);
\r
2988 VectorCopy(grouptris[j].verts[1], v1);
\r
2989 VectorCopy(grouptris[j].verts[2], v2);
\r
2990 VectorSubtract(v1, v0, diffvect1);
\r
2991 VectorSubtract(v2, v1, diffvect2);
\r
2992 CrossProduct( diffvect1, diffvect2, crossvect);
\r
2993 VectorAdd(aveNorm, crossvect, aveNorm);
\r
2994 VectorCopy(v0,vertices[num]);
\r
2996 VectorCopy(v1,vertices[num]);
\r
2997 num++; // add routine to add only verts that
\r
2998 VectorCopy(v2,vertices[num]);
\r
2999 num++; // have not already been added
\r
3002 assert (num >= 3);
\r
3003 // figure out the best plane projections
\r
3004 DOsvdPlane ((float*)vertices, num, (float *)&n, (float *)&base);
\r
3006 if (DotProduct(aveNorm,n) < 0.0f)
\r
3008 VectorScale(n, -1.0f, n);
\r
3010 VectorNormalize(n,n);
\r
3011 if (fabs(n[2]) < .57)
\r
3013 CrossProduct( zaxis, n, crossvect);
\r
3014 VectorCopy(crossvect, u);
\r
3018 CrossProduct( yaxis, n, crossvect);
\r
3019 VectorCopy(crossvect, u);
\r
3021 VectorNormalize(u,u);
\r
3022 CrossProduct( n, u, crossvect);
\r
3023 VectorCopy(crossvect, v);
\r
3024 VectorNormalize(v,v);
\r
3028 for ( j = 0; j < 3; j++)
\r
3030 groupMin[j] = 1e30f;
\r
3031 groupMax[j] = -1e30f;
\r
3034 for ( j = 0; j < numtris; j++)
\r
3036 for ( k = 0; k < 3; k++)
\r
3038 VectorCopy(grouptris[j].verts[k],v0);
\r
3039 VectorSubtract(v0, base, v0);
\r
3040 uvw[0] = DotProduct(v0, u);
\r
3041 uvw[1] = DotProduct(v0, v);
\r
3042 uvw[2] = DotProduct(v0, n);
\r
3043 VectorCopy(uvw,uvs[num]);
\r
3045 for ( l = 0; l < 3; l++)
\r
3047 if (uvw[l] < groupMin[l])
\r
3049 groupMin[l] = uvw[l];
\r
3051 if (uvw[l] > groupMax[l])
\r
3053 groupMax[l] = uvw[l];
\r
3059 xwidth = ceil(0 - groupMin[0]) + 2; // move right of origin and avoid overlap
\r
3060 ywidth = ceil(0 - groupMin[1]) + 2; // move "above" origin
\r
3062 for ( j=0; j < numverts; j++)
\r
3064 uFinal[finalcount] = uvs[j][0] + xwidth + xbase;
\r
3065 vFinal[finalcount] = uvs[j][1] + ywidth;
\r
3066 if (uFinal[finalcount] < uvwMin[0])
\r
3068 uvwMin[0] = uFinal[finalcount];
\r
3070 if (uFinal[finalcount] > uvwMax[0])
\r
3072 uvwMax[0] = uFinal[finalcount];
\r
3074 if (vFinal[finalcount] < uvwMin[1])
\r
3076 uvwMin[1] = vFinal[finalcount];
\r
3078 if (vFinal[finalcount] > uvwMax[1])
\r
3080 uvwMax[1] = vFinal[finalcount];
\r
3085 fprintf(grpfile,"svdPlaned Group min: ( %f , %f )\n",groupMin[0] + xwidth + xbase, groupMin[1] + ywidth);
\r
3086 fprintf(grpfile,"svdPlaned Group max: ( %f , %f )\n",groupMax[0] + xwidth + xbase, groupMax[1] + ywidth);
\r
3088 finalcount = finalstart;
\r
3090 for ( count = 0; count < numverts; count++)
\r
3092 fprintf(grpfile,"Vertex %d: ( %f , %f , %f )\n",count,vertices[count][0],vertices[count][1],vertices[count][2]);
\r
3093 fprintf(grpfile,"svdPlaned: ( %f , %f )\n",uFinal[finalcount],vFinal[finalcount++]);
\r
3096 finalstart = finalcount;
\r
3098 fprintf(grpfile,"\n");
\r
3104 xbase += ceil(groupMax[0] - groupMin[0]) + 2;
\r
3108 fprintf(grpfile,"Global Min ( %f , %f )\n",uvwMin[0],uvwMin[1]);
\r
3109 fprintf(grpfile,"Global Max ( %f , %f )\n",uvwMax[0],uvwMax[1]);
\r
3112 ScaleTris(uvwMin, uvwMax, width, height, uFinal, vFinal, finalcount);
\r
3114 for (k = 0; k < finalcount; k++)
\r
3116 fprintf(grpfile, "scaled vertex %d: ( %f , %f )\n",k,uFinal[k],vFinal[k]);
\r
3119 // i've got the array of vertices in uFinal and vFinal. Now I need to write them and draw lines
\r
3121 datasize = width * height*sizeof(unsigned char);
\r
3122 newpic = (unsigned char*)SafeMalloc(datasize, "NewGen");
\r
3123 memset(newpic,0,datasize);
\r
3124 memset(pic_palette,0,sizeof(pic_palette));
\r
3125 pic_palette[767] = pic_palette[766] = pic_palette[765] = 255;
\r
3128 while (k < finalcount)
\r
3130 NewDrawLine(uFinal[k], vFinal[k], uFinal[k+1], vFinal[k+1], newpic, width, height);
\r
3132 NewDrawLine(uFinal[k], vFinal[k], uFinal[k+1], vFinal[k+1], newpic, width, height);
\r
3134 NewDrawLine(uFinal[k], vFinal[k], uFinal[k-2], vFinal[k-2], newpic, width, height);
\r
3136 fprintf(grpfile, "output tri with verts %d, %d, %d", k-2, k-1, k);
\r
3139 WritePCXfile (OutputName, newpic, width, height, pic_palette);
\r
3149 void NewDrawLine(int x1, int y1, int x2, int y2, unsigned char* picture, int width, int height)
\r
3154 float xfrac, yfrac, xstep, ystep;
\r
3155 unsigned long sx, sy;
\r
3163 count = adx > ady ? adx : ady;
\r
3168 printf("Bad count\n");
\r
3169 return; // don't ever hang up on bad data
\r
3175 xstep = (float)dx/count;
\r
3176 ystep = (float)dy/count;
\r
3183 if(xfrac < width && yfrac < height)
\r
3185 picture[(long)yfrac*width+(long)xfrac] = LineColor;
\r
3190 } while (count > 0);
\r
3195 for (u=-0.1 ; u<=0.9 ; u+=0.999)
\r
3197 for (v=-0.1 ; v<=0.9 ; v+=0.999)
\r
3201 if(sx < width && sy < height)
\r
3203 picture[sy*width+sx] = LineColor;
\r
3210 } while (count > 0);
\r
3215 if(count&1 && xfrac < width &&
\r
3218 picture[(long)yfrac*width+(long)xfrac] = LineColor;
\r
3223 } while (count > 0);
\r
3226 Error("Unknown <linetype> %d.\n", LineType);
\r
3230 void ScaleTris( vec3_t min, vec3_t max, int Width, int Height, float* u, float* v, int verts)
\r
3234 float hscale, vscale;
\r
3240 hscale = (Width-2) / max[0];
\r
3241 vscale = (Height-2) / max[1];
\r
3244 if (scale > vscale)
\r
3248 for ( i = 0; i<verts; i++)
\r
3257 void GetOneGroup(trigroup_t *tris, int grp, triangle_t* triangles)
\r
3263 for (i = 0; i < fmheader.num_tris; i++)
\r
3265 if (tris[i].group == grp)
\r
3267 triangles[j++] = tris[i].triangle;
\r
3274 int GetNumTris( trigroup_t *tris, int grp)
\r
3280 for (i = 0; i < fmheader.num_tris; i++)
\r
3282 if (tris[i].group == grp)
\r
3291 int ShareVertex( trigroup_t trione, trigroup_t tritwo)
\r
3298 for ( i = 0; i < 3; i++)
\r
3300 for ( j = 0; j < 3; j++)
\r
3302 if (DistBetween(trione.triangle.verts[i],tritwo.triangle.verts[j]) < TRIVERT_DIST)
\r
3312 float DistBetween(vec3_t point1, vec3_t point2)
\r
3316 dist = (point1[0] - point2[0]);
\r
3318 dist += (point1[1] - point2[1])*(point1[1]-point2[1]);
\r
3319 dist += (point1[2] - point2[2])*(point1[2]-point2[2]);
\r
3320 dist = sqrt(dist);
\r
3325 void GenSkin(char *ModelFile, char *OutputName, int Width, int Height)
\r
3328 mesh_node_t *pmnodes;
\r
3334 LoadTriangleList (ModelFile, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);
\r
3335 if (g_ignoreTriUV)
\r
3337 for (i=0;i<fmheader.num_tris;i++)
\r
3343 memset(pic,0,sizeof(pic));
\r
3344 memset(pic_palette,0,sizeof(pic_palette));
\r
3345 pic_palette[767] = pic_palette[766] = pic_palette[765] = 255;
\r
3347 ScaleWidth = Width;
\r
3348 ScaleHeight = Height;
\r
3350 BuildST (ptri, fmheader.num_tris, true);
\r
3352 WritePCXfile (OutputName, pic, SKINPAGE_WIDTH, SKINPAGE_HEIGHT, pic_palette);
\r
3354 printf("Gen Skin Stats:\n");
\r
3355 printf(" Input Base: %s\n",ModelFile);
\r
3356 printf(" Input Dimensions: %d,%d\n",Width,Height);
\r
3358 printf(" Output File: %s\n",OutputName);
\r
3359 printf(" Output Dimensions: %d,%d\n",ScaleWidth,ScaleHeight);
\r
3361 if (fmheader.num_mesh_nodes)
\r
3363 printf("\nNodes:\n");
\r
3364 for(i=0;i<fmheader.num_mesh_nodes;i++)
\r
3366 printf(" %s\n",pmnodes[i].name);
\r
3375 void Cmd_FMBeginGroup (void)
\r
3377 GetScriptToken (false);
\r
3379 g_no_opimizations = false;
\r
3381 groups[num_groups].start_frame = fmheader.num_frames;
\r
3382 groups[num_groups].num_frames = 0;
\r
3384 groups[num_groups].degrees = atol(token);
\r
3385 if (groups[num_groups].degrees < 1 || groups[num_groups].degrees > 32)
\r
3387 Error ("Degrees of freedom out of range: %d",groups[num_groups].degrees);
\r
3391 void Cmd_FMEndGroup (void)
\r
3393 groups[num_groups].num_frames = fmheader.num_frames - groups[num_groups].start_frame;
\r
3395 if(num_groups < MAX_GROUPS - 1)
\r
3401 Error("Number of compression groups exceded: %i\n", MAX_GROUPS);
\r