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
25 #include "jointed.h"
\r
28 //=================================================================
\r
39 int lightnormalindex;
\r
46 trivert_t v[MAX_VERTS];
\r
47 QDataJoint_t joints[NUM_CLUSTERS]; // ,this
\r
50 // ,and all of this should get out of here, need to use new stuff in fmodels instead
\r
52 typedef struct IntListNode_s
\r
55 struct IntListNode_s *next;
\r
56 } IntListNode_t; // gaak
\r
60 float scale[3]; // multiply byte verts by this
\r
61 float translate[3]; // then add this
\r
62 } PartialAliasFrame_t;
\r
67 int *clusters[NUM_CLUSTERS];
\r
68 IntListNode_t *vertLists[NUM_CLUSTERS];
\r
69 int num_verts[NUM_CLUSTERS + 1];
\r
70 int new_num_verts[NUM_CLUSTERS + 1];
\r
74 //================================================================
\r
76 frame_t g_frames[MAX_FRAMES];
\r
77 //frame_t *g_frames;
\r
79 static dmdl_t model;
\r
82 float scale_up; // set by $scale
\r
83 vec3_t adjust; // set by $origin
\r
84 int g_fixedwidth, g_fixedheight; // set by $skinsize
\r
90 dstvert_t base_st[MAX_VERTS];
\r
91 dtriangle_t triangles[MAX_TRIANGLES];
\r
93 static int triangle_st[MAX_TRIANGLES][3][2];
\r
95 // the command list holds counts, s/t values, and xyz indexes
\r
96 // that are valid for every frame
\r
97 int commands[16384];
\r
100 int used[MAX_TRIANGLES];
\r
102 char g_skins[MAX_MD2SKINS][64];
\r
104 char cdarchive[1024];
\r
105 char cdpartial[1024];
\r
108 char modelname[64]; // empty unless $modelname issued (players)
\r
110 extern char *g_outputDir;
\r
112 #define NUMVERTEXNORMALS 162
\r
114 float avertexnormals[NUMVERTEXNORMALS][3] =
\r
116 #include "anorms.h"
\r
119 unsigned char pic[SKINPAGE_HEIGHT*SKINPAGE_WIDTH], pic_palette[768];
\r
121 FILE *headerouthandle = NULL;
\r
123 //==============================================================
\r
130 static void ClearModel (void)
\r
132 memset (&model, 0, sizeof(model));
\r
135 jointed = NOT_JOINTED;
\r
138 VectorCopy (vec3_origin, adjust);
\r
139 g_fixedwidth = g_fixedheight = 0;
\r
140 g_skipmodel = false;
\r
144 void H_printf(char *fmt, ...)
\r
149 if (!headerouthandle)
\r
151 sprintf (name, "%s/tris.h", cddir);
\r
152 headerouthandle = SafeOpenWrite (name);
\r
153 fprintf(headerouthandle, "// %s\n\n", cddir);
\r
154 fprintf(headerouthandle, "// This file generated by qdata - Do NOT Modify\n\n");
\r
157 va_start (argptr, fmt);
\r
158 vfprintf (headerouthandle, fmt, argptr);
\r
168 void WriteCommonModelFile (FILE *modelouthandle, PartialAliasFrame_t *outFrames)
\r
174 daliasframe_t *out;
\r
175 byte buffer[MAX_VERTS*4+128];
\r
179 model.version = ALIAS_VERSION;
\r
180 model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz];
\r
181 model.num_glcmds = numcommands;
\r
182 model.ofs_skins = sizeof(dmdl_t);
\r
183 model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;
\r
184 model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t);
\r
185 model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t);
\r
186 model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize;
\r
187 model.ofs_end = model.ofs_glcmds + model.num_glcmds*sizeof(int);
\r
189 // write out the model header
\r
191 for (i=0 ; i<sizeof(dmdl_t)/4 ; i++)
\r
192 ((int *)&modeltemp)[i] = LittleLong (((int *)&model)[i]);
\r
194 SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));
\r
197 // write out the skin names
\r
199 SafeWrite (modelouthandle, g_skins, model.num_skins * MAX_SKINNAME);
\r
202 // write out the texture coordinates
\r
205 for (i=0 ; i<model.num_st ; i++)
\r
207 base_st[i].s = LittleShort (base_st[i].s);
\r
208 base_st[i].t = LittleShort (base_st[i].t);
\r
211 SafeWrite (modelouthandle, base_st, model.num_st * sizeof(base_st[0]));
\r
214 // write out the triangles
\r
216 for (i=0 ; i<model.num_tris ; i++)
\r
221 for (j=0 ; j<3 ; j++)
\r
223 tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);
\r
224 tri.index_st[j] = LittleShort (triangles[i].index_st[j]);
\r
227 SafeWrite (modelouthandle, &tri, sizeof(tri));
\r
231 // write out the frames
\r
233 for (i=0 ; i<model.num_frames ; i++)
\r
236 out = (daliasframe_t *)buffer;
\r
238 strcpy (out->name, in->name);
\r
239 for (j=0 ; j<3 ; j++)
\r
241 out->scale[j] = (in->maxs[j] - in->mins[j])/255;
\r
242 out->translate[j] = in->mins[j];
\r
246 outFrames[i].scale[j] = out->scale[j];
\r
247 outFrames[i].translate[j] = out->translate[j];
\r
251 for (j=0 ; j<model.num_xyz ; j++)
\r
253 // all of these are byte values, so no need to deal with endianness
\r
254 out->verts[j].lightnormalindex = in->v[j].lightnormalindex;
\r
256 for (k=0 ; k<3 ; k++)
\r
258 // scale to byte values & min/max check
\r
259 v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );
\r
261 // clamp, so rounding doesn't wrap from 255.6 to 0
\r
266 out->verts[j].v[k] = v;
\r
270 for (j=0 ; j<3 ; j++)
\r
272 out->scale[j] = LittleFloat (out->scale[j]);
\r
273 out->translate[j] = LittleFloat (out->translate[j]);
\r
276 SafeWrite (modelouthandle, out, model.framesize);
\r
280 // write out glcmds
\r
282 SafeWrite (modelouthandle, commands, numcommands*4);
\r
290 void WriteModelFile (FILE *modelouthandle)
\r
292 model.ident = IDALIASHEADER;
\r
294 WriteCommonModelFile(modelouthandle, NULL);
\r
299 WriteJointedModelFile
\r
302 void WriteJointedModelFile (FILE *modelouthandle)
\r
308 IntListNode_t *current, *toFree;
\r
309 PartialAliasFrame_t outFrames[MAX_FRAMES];
\r
311 model.ident = IDJOINTEDALIASHEADER;
\r
313 WriteCommonModelFile(modelouthandle, outFrames);
\r
316 SafeWrite(modelouthandle, &jointed, sizeof(int));
\r
318 // number of joints
\r
319 SafeWrite(modelouthandle, &numJointsForSkeleton[jointed], sizeof(int));
\r
321 // number of verts in each cluster
\r
322 SafeWrite(modelouthandle, &new_num_verts[1], sizeof(int)*numJointsForSkeleton[jointed]);
\r
325 for(i = 0; i < new_num_verts[0]; ++i)
\r
327 current = vertLists[i];
\r
330 SafeWrite (modelouthandle, ¤t->data, sizeof(int));
\r
332 current = current->next;
\r
333 free(toFree); // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
\r
337 for (i=0 ; i<model.num_frames ; i++)
\r
341 for (j = 0 ; j < new_num_verts[0]; ++j)
\r
343 for (k=0 ; k<3 ; k++)
\r
345 // scale to byte values & min/max check
\r
346 v = Q_rint ( (in->joints[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
\r
348 // clamp, so rounding doesn't wrap from 255.6 to 0
\r
359 // write out origin as a float (there's only a few per model, so it's not really
\r
361 SafeWrite (modelouthandle, &v, sizeof(float));
\r
364 for (k=0 ; k<3 ; k++)
\r
366 v = Q_rint ( (in->joints[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
\r
368 // clamp, so rounding doesn't wrap from 255.6 to 0
\r
379 // write out origin as a float (there's only a few per model, so it's not really
\r
381 SafeWrite (modelouthandle, &v, sizeof(float));
\r
384 for (k=0 ; k<3 ; k++)
\r
386 v = Q_rint ( (in->joints[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
\r
388 // clamp, so rounding doesn't wrap from 255.6 to 0
\r
399 // write out origin as a float (there's only a few per model, so it's not really
\r
401 SafeWrite (modelouthandle, &v, sizeof(float));
\r
412 static void WriteModelFile (FILE *modelouthandle)
\r
418 daliasframe_t *out;
\r
419 byte buffer[MAX_VERTS*4+128];
\r
423 model.ident = IDALIASHEADER;
\r
424 model.version = ALIAS_VERSION;
\r
425 model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz];
\r
426 model.num_glcmds = numcommands;
\r
427 model.ofs_skins = sizeof(dmdl_t);
\r
428 model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;
\r
429 model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t);
\r
430 model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t);
\r
431 model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize;
\r
432 model.ofs_end = model.ofs_glcmds + model.num_glcmds*4;
\r
435 // write out the model header
\r
437 for (i=0 ; i<sizeof(dmdl_t)/4 ; i++)
\r
438 ((int *)&modeltemp)[i] = LittleLong (((int *)&model)[i]);
\r
440 SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));
\r
443 // write out the skin names
\r
445 SafeWrite (modelouthandle, g_skins, model.num_skins * MAX_SKINNAME);
\r
448 // write out the texture coordinates
\r
451 for (i=0 ; i<model.num_st ; i++)
\r
453 base_st[i].s = LittleShort (base_st[i].s);
\r
454 base_st[i].t = LittleShort (base_st[i].t);
\r
457 SafeWrite (modelouthandle, base_st, model.num_st * sizeof(base_st[0]));
\r
460 // write out the triangles
\r
462 for (i=0 ; i<model.num_tris ; i++)
\r
467 for (j=0 ; j<3 ; j++)
\r
469 tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);
\r
470 tri.index_st[j] = LittleShort (triangles[i].index_st[j]);
\r
473 SafeWrite (modelouthandle, &tri, sizeof(tri));
\r
477 // write out the frames
\r
479 for (i=0 ; i<model.num_frames ; i++)
\r
482 out = (daliasframe_t *)buffer;
\r
484 strcpy (out->name, in->name);
\r
485 for (j=0 ; j<3 ; j++)
\r
487 out->scale[j] = (in->maxs[j] - in->mins[j])/255;
\r
488 out->translate[j] = in->mins[j];
\r
491 for (j=0 ; j<model.num_xyz ; j++)
\r
493 // all of these are byte values, so no need to deal with endianness
\r
494 out->verts[j].lightnormalindex = in->v[j].lightnormalindex;
\r
496 for (k=0 ; k<3 ; k++)
\r
498 // scale to byte values & min/max check
\r
499 v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );
\r
501 // clamp, so rounding doesn't wrap from 255.6 to 0
\r
506 out->verts[j].v[k] = v;
\r
510 for (j=0 ; j<3 ; j++)
\r
512 out->scale[j] = LittleFloat (out->scale[j]);
\r
513 out->translate[j] = LittleFloat (out->translate[j]);
\r
516 SafeWrite (modelouthandle, out, model.framesize);
\r
520 // write out glcmds
\r
522 SafeWrite (modelouthandle, commands, numcommands*4);
\r
531 void FinishModel (void)
\r
533 FILE *modelouthandle;
\r
537 if (!model.num_frames)
\r
541 // copy to release directory tree if doing a release build
\r
546 sprintf (name, "%s", modelname);
\r
548 sprintf (name, "%s/tris.md2", cdpartial);
\r
549 ReleaseFile (name);
\r
551 for (i=0 ; i<model.num_skins ; i++)
\r
553 ReleaseFile (g_skins[i]);
\r
555 model.num_frames = 0;
\r
560 // write the model output file
\r
563 sprintf (name, "%s%s", g_outputDir, modelname);
\r
565 sprintf (name, "%s/tris.md2", g_outputDir);
\r
566 printf ("saving to %s\n", name);
\r
568 modelouthandle = SafeOpenWrite (name);
\r
571 if(jointed != NOT_JOINTED)
\r
572 WriteJointedModelFile(modelouthandle);
\r
575 WriteModelFile(modelouthandle);
\r
577 printf ("%3dx%3d skin\n", model.skinwidth, model.skinheight);
\r
578 printf ("First frame boundaries:\n");
\r
579 printf (" minimum x: %3f\n", g_frames[0].mins[0]);
\r
580 printf (" maximum x: %3f\n", g_frames[0].maxs[0]);
\r
581 printf (" minimum y: %3f\n", g_frames[0].mins[1]);
\r
582 printf (" maximum y: %3f\n", g_frames[0].maxs[1]);
\r
583 printf (" minimum z: %3f\n", g_frames[0].mins[2]);
\r
584 printf (" maximum z: %3f\n", g_frames[0].maxs[2]);
\r
585 printf ("%4d vertices\n", model.num_xyz);
\r
586 printf ("%4d triangles\n", model.num_tris);
\r
587 printf ("%4d frame\n", model.num_frames);
\r
588 printf ("%4d glverts\n", numglverts);
\r
589 printf ("%4d glcmd\n", model.num_glcmds);
\r
590 printf ("%4d skins\n", model.num_skins);
\r
591 printf ("file size: %d\n", (int)ftell (modelouthandle) );
\r
592 printf ("---------------------\n");
\r
594 fclose (modelouthandle);
\r
596 // finish writing header file
\r
599 // scale_up is usefull to allow step distances to be adjusted
\r
600 H_printf("#define MODEL_SCALE\t\t%f\n", scale_up);
\r
602 fclose (headerouthandle);
\r
603 headerouthandle = NULL;
\r
608 =================================================================
\r
610 ALIAS MODEL DISPLAY LIST GENERATION
\r
612 =================================================================
\r
615 int strip_xyz[128];
\r
617 int strip_tris[128];
\r
625 static int StripLength (int starttri, int startv)
\r
630 dtriangle_t *last, *check;
\r
633 used[starttri] = 2;
\r
635 last = &triangles[starttri];
\r
637 strip_xyz[0] = last->index_xyz[(startv)%3];
\r
638 strip_xyz[1] = last->index_xyz[(startv+1)%3];
\r
639 strip_xyz[2] = last->index_xyz[(startv+2)%3];
\r
640 strip_st[0] = last->index_st[(startv)%3];
\r
641 strip_st[1] = last->index_st[(startv+1)%3];
\r
642 strip_st[2] = last->index_st[(startv+2)%3];
\r
644 strip_tris[0] = starttri;
\r
647 m1 = last->index_xyz[(startv+2)%3];
\r
648 st1 = last->index_st[(startv+2)%3];
\r
649 m2 = last->index_xyz[(startv+1)%3];
\r
650 st2 = last->index_st[(startv+1)%3];
\r
652 // look for a matching triangle
\r
654 for (j=starttri+1, check=&triangles[starttri+1]
\r
655 ; j<model.num_tris ; j++, check++)
\r
657 for (k=0 ; k<3 ; k++)
\r
659 if (check->index_xyz[k] != m1)
\r
661 if (check->index_st[k] != st1)
\r
663 if (check->index_xyz[ (k+1)%3 ] != m2)
\r
665 if (check->index_st[ (k+1)%3 ] != st2)
\r
668 // this is the next part of the fan
\r
670 // if we can't use this triangle, this tristrip is done
\r
675 if (stripcount & 1)
\r
677 m2 = check->index_xyz[ (k+2)%3 ];
\r
678 st2 = check->index_st[ (k+2)%3 ];
\r
682 m1 = check->index_xyz[ (k+2)%3 ];
\r
683 st1 = check->index_st[ (k+2)%3 ];
\r
686 strip_xyz[stripcount+2] = check->index_xyz[ (k+2)%3 ];
\r
687 strip_st[stripcount+2] = check->index_st[ (k+2)%3 ];
\r
688 strip_tris[stripcount] = j;
\r
697 // clear the temp used flags
\r
698 for (j=starttri+1 ; j<model.num_tris ; j++)
\r
711 static int FanLength (int starttri, int startv)
\r
716 dtriangle_t *last, *check;
\r
719 used[starttri] = 2;
\r
721 last = &triangles[starttri];
\r
723 strip_xyz[0] = last->index_xyz[(startv)%3];
\r
724 strip_xyz[1] = last->index_xyz[(startv+1)%3];
\r
725 strip_xyz[2] = last->index_xyz[(startv+2)%3];
\r
726 strip_st[0] = last->index_st[(startv)%3];
\r
727 strip_st[1] = last->index_st[(startv+1)%3];
\r
728 strip_st[2] = last->index_st[(startv+2)%3];
\r
730 strip_tris[0] = starttri;
\r
733 m1 = last->index_xyz[(startv+0)%3];
\r
734 st1 = last->index_st[(startv+0)%3];
\r
735 m2 = last->index_xyz[(startv+2)%3];
\r
736 st2 = last->index_st[(startv+2)%3];
\r
739 // look for a matching triangle
\r
741 for (j=starttri+1, check=&triangles[starttri+1]
\r
742 ; j<model.num_tris ; j++, check++)
\r
744 for (k=0 ; k<3 ; k++)
\r
746 if (check->index_xyz[k] != m1)
\r
748 if (check->index_st[k] != st1)
\r
750 if (check->index_xyz[ (k+1)%3 ] != m2)
\r
752 if (check->index_st[ (k+1)%3 ] != st2)
\r
755 // this is the next part of the fan
\r
757 // if we can't use this triangle, this tristrip is done
\r
762 m2 = check->index_xyz[ (k+2)%3 ];
\r
763 st2 = check->index_st[ (k+2)%3 ];
\r
765 strip_xyz[stripcount+2] = m2;
\r
766 strip_st[stripcount+2] = st2;
\r
767 strip_tris[stripcount] = j;
\r
776 // clear the temp used flags
\r
777 for (j=starttri+1 ; j<model.num_tris ; j++)
\r
790 Generate a list of trifans or strips
\r
791 for the model, which holds for all frames
\r
794 static void BuildGlCmds (void)
\r
799 int len, bestlen, besttype;
\r
800 int best_xyz[1024];
\r
802 int best_tris[1024];
\r
810 memset (used, 0, sizeof(used));
\r
811 for (i=0 ; i<model.num_tris ; i++)
\r
813 // pick an unused triangle and start the trifan
\r
818 for (type = 0 ; type < 2 ; type++)
\r
821 for (startv =0 ; startv < 3 ; startv++)
\r
824 len = StripLength (i, startv);
\r
826 len = FanLength (i, startv);
\r
831 for (j=0 ; j<bestlen+2 ; j++)
\r
833 best_st[j] = strip_st[j];
\r
834 best_xyz[j] = strip_xyz[j];
\r
836 for (j=0 ; j<bestlen ; j++)
\r
837 best_tris[j] = strip_tris[j];
\r
842 // mark the tris on the best strip/fan as used
\r
843 for (j=0 ; j<bestlen ; j++)
\r
844 used[best_tris[j]] = 1;
\r
847 commands[numcommands++] = (bestlen+2);
\r
849 commands[numcommands++] = -(bestlen+2);
\r
851 numglverts += bestlen+2;
\r
853 for (j=0 ; j<bestlen+2 ; j++)
\r
855 // emit a vertex into the reorder buffer
\r
858 // emit s/t coords into the commands stream
\r
862 s = (s + 0.5) / model.skinwidth;
\r
863 t = (t + 0.5) / model.skinheight;
\r
865 *(float *)&commands[numcommands++] = s;
\r
866 *(float *)&commands[numcommands++] = t;
\r
867 *(int *)&commands[numcommands++] = best_xyz[j];
\r
871 commands[numcommands++] = 0; // end of list marker
\r
876 ===============================================================
\r
880 ===============================================================
\r
887 Builds the triangle_st array for the base frame and
\r
888 model.skinwidth / model.skinheight
\r
890 FIXME: allow this to be loaded from a file for
\r
895 static void OldBuildST (triangle_t *ptri, int numtri)
\r
898 int width, height, iwidth, iheight, swidth;
\r
899 float basex, basey;
\r
900 float s_scale, t_scale;
\r
904 vec3_t vtemp1, vtemp2, normal;
\r
907 // find bounds of all the verts on the base frame
\r
909 ClearBounds (mins, maxs);
\r
911 for (i=0 ; i<numtri ; i++)
\r
912 for (j=0 ; j<3 ; j++)
\r
913 AddPointToBounds (ptri[i].verts[j], mins, maxs);
\r
915 for (i=0 ; i<3 ; i++)
\r
917 mins[i] = floor(mins[i]);
\r
918 maxs[i] = ceil(maxs[i]);
\r
921 width = maxs[0] - mins[0];
\r
922 height = maxs[2] - mins[2];
\r
927 if (width*scale >= 150)
\r
928 scale = 150.0 / width;
\r
929 if (height*scale >= 190)
\r
930 scale = 190.0 / height;
\r
932 s_scale = t_scale = scale;
\r
934 iwidth = ceil(width*s_scale);
\r
935 iheight = ceil(height*t_scale);
\r
942 iwidth = g_fixedwidth / 2;
\r
943 iheight = g_fixedheight;
\r
945 s_scale = (float)(iwidth-4) / width;
\r
946 t_scale = (float)(iheight-4) / height;
\r
950 // determine which side of each triangle to map the texture to
\r
952 for (i=0 ; i<numtri ; i++)
\r
954 VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
\r
955 VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
\r
956 CrossProduct (vtemp1, vtemp2, normal);
\r
960 basex = iwidth + 2;
\r
968 for (j=0 ; j<3 ; j++)
\r
970 pbasevert = ptri[i].verts[j];
\r
972 triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);
\r
973 triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);
\r
977 // make the width a multiple of 4; some hardware requires this, and it ensures
\r
978 // dword alignment for each scan
\r
980 model.skinwidth = (swidth + 3) & ~3;
\r
981 model.skinheight = iheight;
\r
985 //==========================================================================
\r
989 //==========================================================================
\r
991 void DrawScreen(float s_scale, float t_scale, float iwidth, float iheight)
\r
998 scrpos = &pic[(INFO_Y-2)*SKINPAGE_WIDTH];
\r
999 for(i = 0; i < SKINPAGE_WIDTH; i++)
\r
1004 sprintf(buffer, "GENSKIN: ");
\r
1005 DrawTextChar(16, INFO_Y, buffer);
\r
1007 sprintf(buffer, "( %03d * %03d ) SCALE %f %f, SKINWIDTH %d,"
\r
1008 " SKINHEIGHT %d", (int)ScaleWidth, (int)ScaleHeight, s_scale, t_scale, (int)iwidth*2, (int)iheight);
\r
1009 DrawTextChar(80, INFO_Y, buffer);
\r
1016 Builds the triangle_st array for the base frame and
\r
1017 model.skinwidth / model.skinheight
\r
1019 FIXME: allow this to be loaded from a file for
\r
1020 arbitrary mappings
\r
1023 void BuildST (triangle_t *ptri, int numtri, qboolean DrawSkin)
\r
1026 int width, height, iwidth, iheight, swidth;
\r
1027 float basex, basey;
\r
1029 vec3_t mins, maxs;
\r
1031 vec3_t vtemp1, vtemp2, normal;
\r
1032 float s_scale, t_scale;
\r
1037 // find bounds of all the verts on the base frame
\r
1039 ClearBounds (mins, maxs);
\r
1041 for (i=0 ; i<numtri ; i++)
\r
1042 for (j=0 ; j<3 ; j++)
\r
1043 AddPointToBounds (ptri[i].verts[j], mins, maxs);
\r
1045 for (i=0 ; i<3 ; i++)
\r
1047 mins[i] = floor(mins[i]);
\r
1048 maxs[i] = ceil(maxs[i]);
\r
1051 width = maxs[0] - mins[0];
\r
1052 height = maxs[2] - mins[2];
\r
1055 scWidth = (ScaleWidth/2)*SCALE_ADJUST_FACTOR;
\r
1056 scHeight = ScaleHeight*SCALE_ADJUST_FACTOR;
\r
1058 scale = scWidth/width;
\r
1060 if(height*scale >= scHeight)
\r
1062 scale = scHeight/height;
\r
1065 iwidth = ceil(width*scale)+4;
\r
1066 iheight = ceil(height*scale)+4;
\r
1068 s_scale = (float)(iwidth-4) / width;
\r
1069 t_scale = (float)(iheight-4) / height;
\r
1070 t_scale = s_scale;
\r
1073 DrawScreen(s_scale, t_scale, iwidth, iheight);
\r
1076 /* if (!g_fixedwidth)
\r
1079 if (width*scale >= 150)
\r
1080 scale = 150.0 / width;
\r
1081 if (height*scale >= 190)
\r
1082 scale = 190.0 / height;
\r
1084 s_scale = t_scale = scale;
\r
1086 iwidth = ceil(width*s_scale);
\r
1087 iheight = ceil(height*t_scale);
\r
1094 iwidth = g_fixedwidth / 2;
\r
1095 iheight = g_fixedheight;
\r
1097 s_scale = (float)(iwidth-4) / width;
\r
1098 t_scale = (float)(iheight-4) / height;
\r
1102 // determine which side of each triangle to map the texture to
\r
1104 for (i=0 ; i<numtri ; i++)
\r
1106 if (ptri[i].HasUV)
\r
1108 for (j=0 ; j<3 ; j++)
\r
1110 triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*iwidth);
\r
1111 triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*iheight);
\r
1116 VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
\r
1117 VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
\r
1118 CrossProduct (vtemp1, vtemp2, normal);
\r
1120 if (normal[1] > 0)
\r
1122 basex = iwidth + 2;
\r
1130 for (j=0 ; j<3 ; j++)
\r
1132 pbasevert = ptri[i].verts[j];
\r
1134 triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);
\r
1135 triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);
\r
1139 DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],
\r
1140 triangle_st[i][1][0], triangle_st[i][1][1]);
\r
1141 DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],
\r
1142 triangle_st[i][2][0], triangle_st[i][2][1]);
\r
1143 DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],
\r
1144 triangle_st[i][0][0], triangle_st[i][0][1]);
\r
1147 // make the width a multiple of 4; some hardware requires this, and it ensures
\r
1148 // dword alignment for each scan
\r
1150 swidth = iwidth*2;
\r
1151 model.skinwidth = (swidth + 3) & ~3;
\r
1152 model.skinheight = iheight;
\r
1156 static void ReplaceClusterIndex(int newIndex, int oldindex, int **clusters,
\r
1157 IntListNode_t **vertLists, int *num_verts, int *new_num_verts)
\r
1160 IntListNode_t *next;
\r
1162 for(j = 0; j < num_verts[0]; ++j)
\r
1164 for(i = 0; i < num_verts[j+1]; ++i)
\r
1166 if(clusters[j][i] == oldindex)
\r
1168 ++new_num_verts[j+1];
\r
1170 next = vertLists[j];
\r
1172 vertLists[j] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "ReplaceClusterIndex");
\r
1173 // Currently freed in WriteJointedModelFile only
\r
1175 vertLists[j]->data = newIndex;
\r
1176 vertLists[j]->next = next;
\r
1187 void Cmd_Base (void)
\r
1189 vec3_t base_xyz[MAX_VERTS];
\r
1199 GetScriptToken (false);
\r
1201 if (g_skipmodel || g_release || g_archive)
\r
1204 printf ("---------------------\n");
\r
1206 sprintf (file1, "%s/%s", cdpartial, token);
\r
1207 printf ("%s ", file1);
\r
1209 ExpandPathAndArchive (file1);
\r
1211 sprintf (file1, "%s/%s", cddir, token);
\r
1213 sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext);
\r
1214 printf ("%s\n", file1);
\r
1216 ExpandPathAndArchive (file1);
\r
1218 sprintf (file1, "%s/%s.%s", cddir, token, trifileext);
\r
1220 time1 = FileTime (file1);
\r
1222 Error ("%s doesn't exist", file1);
\r
1225 // load the base triangles
\r
1228 Load3DSTriangleList (file1, &ptri, &model.num_tris, NULL, NULL);
\r
1230 LoadTriangleList (file1, &ptri, &model.num_tris, NULL, NULL);
\r
1233 GetScriptToken (false);
\r
1234 sprintf (file2, "%s/%s.pcx", cddir, token);
\r
1235 // sprintf (trans_file, "%s/!%s_a.pcx", cddir, token);
\r
1237 printf ("skin: %s\n", file2);
\r
1238 Load256Image (file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight);
\r
1240 if (BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT)
\r
1242 if (g_allow_newskin)
\r
1244 ScaleWidth = BaseWidth;
\r
1245 ScaleHeight = BaseHeight;
\r
1249 Error("Invalid skin page size: (%d,%d) should be (%d,%d)",
\r
1250 BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT);
\r
1255 ScaleWidth = (float)ExtractNumber(BasePixels, ENCODED_WIDTH_X,
\r
1257 ScaleHeight = (float)ExtractNumber(BasePixels, ENCODED_HEIGHT_X,
\r
1258 ENCODED_HEIGHT_Y);
\r
1262 // get the ST values
\r
1264 BuildST (ptri, model.num_tris,false);
\r
1267 // run through all the base triangles, storing each unique vertex in the
\r
1268 // base vertex list and setting the indirect triangles to point to the base
\r
1271 for (i=0 ; i<model.num_tris ; i++)
\r
1273 for (j=0 ; j<3 ; j++)
\r
1275 // get the xyz index
\r
1276 for (k=0 ; k<model.num_xyz ; k++)
\r
1277 if (VectorCompare (ptri[i].verts[j], base_xyz[k]))
\r
1278 break; // this vertex is already in the base vertex list
\r
1280 if (k == model.num_xyz)
\r
1282 VectorCopy (ptri[i].verts[j], base_xyz[model.num_xyz]);
\r
1285 ReplaceClusterIndex(k, ptri[i].indicies[j], (int **)&clusters, (IntListNode_t **)&vertLists, (int *)&num_verts, (int *)&new_num_verts);
\r
1290 triangles[i].index_xyz[j] = k;
\r
1292 // get the st index
\r
1293 for (k=0 ; k<model.num_st ; k++)
\r
1294 if (triangle_st[i][j][0] == base_st[k].s
\r
1295 && triangle_st[i][j][1] == base_st[k].t)
\r
1296 break; // this vertex is already in the base vertex list
\r
1298 if (k == model.num_st)
\r
1300 base_st[model.num_st].s = triangle_st[i][j][0];
\r
1301 base_st[model.num_st].t = triangle_st[i][j][1];
\r
1305 triangles[i].index_st[j] = k;
\r
1309 // build triangle strips / fans
\r
1313 //===============================================================
\r
1315 char *FindFrameFile (char *frame)
\r
1319 static char retname[1024];
\r
1324 if (strstr (frame, "."))
\r
1325 return frame; // allready in dot format
\r
1327 // split 'run1' into 'run' and '1'
\r
1328 s = frame + strlen(frame)-1;
\r
1330 while (s != frame && *s >= '0' && *s <= '9')
\r
1333 strcpy (suffix, s+1);
\r
1334 strcpy (base, frame);
\r
1335 base[s-frame+1] = 0;
\r
1337 sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "hrc");
\r
1338 time1 = FileTime (file1);
\r
1341 sprintf (retname, "%s%s.%s", base, suffix, "hrc");
\r
1345 sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "asc");
\r
1346 time1 = FileTime (file1);
\r
1349 sprintf (retname, "%s%s.%s", base, suffix, "asc");
\r
1353 sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "tri");
\r
1354 time1 = FileTime (file1);
\r
1357 sprintf (retname, "%s%s.%s", base, suffix, "tri");
\r
1361 sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "3ds");
\r
1362 time1 = FileTime (file1);
\r
1365 sprintf (retname, "%s%s.%s", base, suffix, "3ds");
\r
1369 sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "htr");
\r
1370 time1 = FileTime (file1);
\r
1373 sprintf (retname, "%s%s.%s", base, suffix, "htr");
\r
1377 // check for 'run.1'
\r
1378 sprintf (file1, "%s/%s.%s",cddir, base, suffix);
\r
1379 time1 = FileTime (file1);
\r
1382 sprintf (retname, "%s.%s", base, suffix);
\r
1386 Error ("frame %s could not be found",frame);
\r
1395 static void GrabFrame (char *frame)
\r
1399 trivert_t *ptrivert;
\r
1403 vertexnormals_t vnorms[MAX_VERTS];
\r
1407 // the frame 'run1' will be looked for as either
\r
1408 // run.1 or run1.tri, so the new alias sequence save
\r
1409 // feature an be used
\r
1410 framefile = FindFrameFile (frame);
\r
1412 sprintf (file1, "%s/%s", cdarchive, framefile);
\r
1413 ExpandPathAndArchive (file1);
\r
1415 sprintf (file1, "%s/%s",cddir, framefile);
\r
1417 printf ("grabbing %s ", file1);
\r
1419 if (model.num_frames >= MAX_FRAMES)
\r
1420 Error ("model.num_frames >= MAX_FRAMES");
\r
1421 fr = &g_frames[model.num_frames];
\r
1422 model.num_frames++;
\r
1424 strcpy (fr->name, frame);
\r
1430 Load3DSTriangleList (file1, &ptri, &num_tris, NULL, NULL);
\r
1432 LoadTriangleList (file1, &ptri, &num_tris, NULL, NULL);
\r
1434 if (num_tris != model.num_tris)
\r
1435 Error ("%s: number of triangles doesn't match base frame\n", file1);
\r
1438 // allocate storage for the frame's vertices
\r
1442 for (i=0 ; i<model.num_xyz ; i++)
\r
1444 vnorms[i].numnormals = 0;
\r
1445 VectorClear (vnorms[i].normalsum);
\r
1447 ClearBounds (fr->mins, fr->maxs);
\r
1450 // store the frame's vertices in the same order as the base. This assumes the
\r
1451 // triangles and vertices in this frame are in exactly the same order as in the
\r
1454 for (i=0 ; i<num_tris ; i++)
\r
1456 vec3_t vtemp1, vtemp2, normal;
\r
1459 VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
\r
1460 VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
\r
1461 CrossProduct (vtemp1, vtemp2, normal);
\r
1463 VectorNormalize (normal, normal);
\r
1465 // rotate the normal so the model faces down the positive x axis
\r
1466 ftemp = normal[0];
\r
1467 normal[0] = -normal[1];
\r
1468 normal[1] = ftemp;
\r
1470 for (j=0 ; j<3 ; j++)
\r
1472 index_xyz = triangles[i].index_xyz[j];
\r
1474 // rotate the vertices so the model faces down the positive x axis
\r
1475 // also adjust the vertices to the desired origin
\r
1476 ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +
\r
1478 ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) +
\r
1480 ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) +
\r
1483 AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);
\r
1485 VectorAdd (vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum);
\r
1486 vnorms[index_xyz].numnormals++;
\r
1491 // calculate the vertex normals, match them to the template list, and store the
\r
1492 // index of the best match
\r
1494 for (i=0 ; i<model.num_xyz ; i++)
\r
1502 c = vnorms[i].numnormals;
\r
1504 Error ("Vertex with no triangles attached");
\r
1506 VectorScale (vnorms[i].normalsum, 1.0/c, v);
\r
1507 VectorNormalize (v, v);
\r
1509 maxdot = -999999.0;
\r
1512 for (j=0 ; j<NUMVERTEXNORMALS ; j++)
\r
1516 dot = DotProduct (v, avertexnormals[j]);
\r
1524 ptrivert[i].lightnormalindex = maxdotindex;
\r
1535 void GrabJointedFrame(char *frame)
\r
1541 framefile = FindFrameFile (frame);
\r
1543 sprintf (file1, "%s/%s", cdarchive, framefile);
\r
1544 ExpandPathAndArchive (file1);
\r
1546 sprintf (file1, "%s/%s",cddir, framefile);
\r
1548 printf ("grabbing %s\n", file1);
\r
1550 fr = &g_frames[model.num_frames - 1]; // last frame read in
\r
1552 LoadJointList(file1, fr->joints, jointed);
\r
1560 void GrabGlobals(char *frame)
\r
1566 framefile = FindFrameFile (frame);
\r
1568 sprintf (file1, "%s/%s", cdarchive, framefile);
\r
1569 ExpandPathAndArchive (file1);
\r
1571 sprintf (file1, "%s/%s",cddir, framefile);
\r
1573 printf ("grabbing %s\n", file1);
\r
1575 fr = &g_frames[model.num_frames - 1]; // last frame read in
\r
1577 LoadGlobals(file1);
\r
1585 void Cmd_Frame (void)
\r
1587 while (ScriptTokenAvailable())
\r
1589 GetScriptToken (false);
\r
1592 if (g_release || g_archive)
\r
1594 model.num_frames = 1; // don't skip the writeout
\r
1598 H_printf("#define FRAME_%-16s\t%i\n", token, model.num_frames);
\r
1600 GrabFrame (token);
\r
1608 Skins aren't actually stored in the file, only a reference
\r
1609 is saved out to the header file.
\r
1612 void Cmd_Skin (void)
\r
1616 int width, height;
\r
1619 char name[1024], savename[1024];
\r
1621 GetScriptToken (false);
\r
1623 if (model.num_skins == MAX_MD2SKINS)
\r
1624 Error ("model.num_skins == MAX_MD2SKINS");
\r
1630 sprintf (name, "%s/%s.pcx", cddir, token);
\r
1631 sprintf (savename, "%s/!%s.pcx", g_outputDir, token);
\r
1632 sprintf (g_skins[model.num_skins], "%s/!%s.pcx", cdpartial, token);
\r
1634 sprintf (name, "%s/%s.lbm", cdarchive, token);
\r
1635 strcpy (name, ExpandPathAndArchive( name ) );
\r
1636 // sprintf (name, "%s/%s.lbm", cddir, token);
\r
1638 if (ScriptTokenAvailable())
\r
1640 GetScriptToken (false);
\r
1641 sprintf (g_skins[model.num_skins], "%s.pcx", token);
\r
1642 sprintf (savename, "%s%s.pcx", g_outputDir, g_skins[model.num_skins]);
\r
1646 sprintf (savename, "%s/%s.pcx", g_outputDir, token);
\r
1647 sprintf (g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token);
\r
1651 model.num_skins++;
\r
1653 if (g_skipmodel || g_release || g_archive)
\r
1657 printf ("loading %s\n", name);
\r
1658 Load256Image (name, &pixels, &palette, &width, &height);
\r
1659 // RemapZero (pixels, palette, width, height);
\r
1661 // crop it to the proper size
\r
1662 cropped = (byte *) SafeMalloc (model.skinwidth*model.skinheight, "Cmd_Skin");
\r
1663 for (y=0 ; y<model.skinheight ; y++)
\r
1665 memcpy (cropped+y*model.skinwidth,
\r
1666 pixels+y*width, model.skinwidth);
\r
1669 // save off the new image
\r
1670 printf ("saving %s\n", savename);
\r
1671 CreatePath (savename);
\r
1672 WritePCXfile (savename, cropped, model.skinwidth,
\r
1673 model.skinheight, palette);
\r
1686 void Cmd_Origin (void)
\r
1688 // rotate points into frame of reference so model points down the
\r
1689 // positive x axis
\r
1690 GetScriptToken (false);
\r
1691 adjust[1] = -atof (token);
\r
1693 GetScriptToken (false);
\r
1694 adjust[0] = atof (token);
\r
1696 GetScriptToken (false);
\r
1697 adjust[2] = -atof (token);
\r
1706 void Cmd_ScaleUp (void)
\r
1708 GetScriptToken (false);
\r
1709 scale_up = atof (token);
\r
1710 if (g_skipmodel || g_release || g_archive)
\r
1713 printf ("Scale up: %f\n", scale_up);
\r
1721 Set a skin size other than the default
\r
1724 void Cmd_Skinsize (void)
\r
1726 GetScriptToken (false);
\r
1727 g_fixedwidth = atoi(token);
\r
1728 GetScriptToken (false);
\r
1729 g_fixedheight = atoi(token);
\r
1736 Gives a different name/location for the file, instead of the cddir
\r
1739 void Cmd_Modelname (void)
\r
1741 GetScriptToken (false);
\r
1742 strcpy (modelname, token);
\r
1750 void Cmd_Cd (void)
\r
1757 GetScriptToken (false);
\r
1759 // this is a silly mess...
\r
1760 sprintf (cdpartial, "models/%s", token);
\r
1761 sprintf (cdarchive, "%smodels/%s", gamedir+strlen(qdir), token);
\r
1762 sprintf (cddir, "%s%s", gamedir, cdpartial);
\r
1764 // Since we also changed directories on the output side (for mirror) make sure the outputdir is set properly too.
\r
1765 sprintf(temp, "%s%s", g_outputDir, cdpartial);
\r
1766 strcpy(g_outputDir, temp);
\r
1768 // if -only was specified and this cd doesn't match,
\r
1769 // skip the model (you only need to match leading chars,
\r
1770 // so you could regrab all monsters with -only monsters)
\r
1773 if (strncmp(token, g_only, strlen(g_only)))
\r
1775 g_skipmodel = true;
\r
1776 printf ("skipping %s\n", cdpartial);
\r
1785 void Cmd_Cluster()
\r
1789 GetScriptToken (false);
\r
1791 printf ("---------------------\n");
\r
1792 sprintf (file1, "%s/%s", cdpartial, token);
\r
1793 printf ("%s\n", file1);
\r
1795 ExpandPathAndArchive (file1);
\r
1797 sprintf (file1, "%s/%s", cddir, token);
\r
1799 LoadClusters(file1, (int **)&clusters, (int *)&num_verts, jointed);
\r
1801 new_num_verts[0] = num_verts[0];
\r
1806 // Model construction cover functions.
\r
1807 void MODELCMD_Modelname (int modeltype)
\r
1810 modeltype = g_forcemodel;
\r
1817 Cmd_Modelname ();
\r
1820 Cmd_FMModelname ();
\r
1826 void MODELCMD_Cd (int modeltype)
\r
1829 modeltype = g_forcemodel;
\r
1842 void MODELCMD_Origin (int modeltype)
\r
1845 modeltype = g_forcemodel;
\r
1848 /* switch(modeltype)
\r
1860 void MODELCMD_Cluster (int modeltype)
\r
1863 modeltype = g_forcemodel;
\r
1876 void MODELCMD_Base (int modeltype)
\r
1879 modeltype = g_forcemodel;
\r
1887 Cmd_FMBase (false);
\r
1892 void MODELCMD_BaseST (int modeltype)
\r
1895 modeltype = g_forcemodel;
\r
1903 Cmd_FMBase (true);
\r
1908 void MODELCMD_ScaleUp (int modeltype)
\r
1911 modeltype = g_forcemodel;
\r
1914 /* switch(modeltype)
\r
1926 void MODELCMD_Frame (int modeltype)
\r
1929 modeltype = g_forcemodel;
\r
1942 void MODELCMD_Skin (int modeltype)
\r
1945 modeltype = g_forcemodel;
\r
1958 void MODELCMD_Skinsize (int modeltype)
\r
1961 modeltype = g_forcemodel;
\r
1971 Cmd_FMSkinsize ();
\r
1977 void MODELCMD_Skeleton (int modeltype)
\r
1980 modeltype = g_forcemodel;
\r
1987 Cmd_FMSkeleton ();
\r
1992 void MODELCMD_BeginGroup(int modeltype)
\r
1995 modeltype = g_forcemodel;
\r
2002 Cmd_FMBeginGroup();
\r
2007 void MODELCMD_EndGroup(int modeltype)
\r
2010 modeltype = g_forcemodel;
\r
2022 void MODELCMD_Referenced(int modeltype)
\r
2025 modeltype = g_forcemodel;
\r
2032 Cmd_FMReferenced();
\r
2037 void MODELCMD_NodeOrder(int modeltype)
\r
2040 modeltype = g_forcemodel;
\r
2047 Cmd_FMNodeOrder();
\r