1 /*
2  * Copyright (c) 2004-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
3  *
4  * This program is distributed under the GNU General Public License
5  * version 2 (or, at your option, any later version).  Please visit
6  * http://www.gnu.org/licenses/gpl.html for details.
7  *
8  * GL commands generation is from id software's qdata (with some
9  * slight modifications).  All models are handled internally as DMDs.
10  */
11 
12 // HEADER FILES ------------------------------------------------------------
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <math.h>
18 #include <ctype.h>
19 
20 #ifdef _WIN32
21 #include <conio.h>
22 #endif
23 
24 #include "md2tool.h"
25 
26 // MACROS ------------------------------------------------------------------
27 
28 #ifdef _WIN32
29 #define stricmp     _stricmp
30 #define cprintf     _cprintf
31 #endif
32 
33 #ifdef __GNUC__
34 #include <strings.h>
35 #define stricmp     strcasecmp
36 #define cprintf     printf
37 #endif
38 
39 // TYPES -------------------------------------------------------------------
40 
41 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
42 
43 void ModelSetSaveFormat(model_t *mo, int magic);
44 
45 // PUBLIC DATA DEFINITIONS -------------------------------------------------
46 
47 int     commands[16384];
48 int     numcommands;
49 int     numglverts;
50 int     used[MAX_TRIANGLES];
51 int     strip_xyz[128];
52 int     strip_st[128];
53 int     strip_tris[128];
54 int     stripcount;
55 dtriangle_t triangles[MAX_TRIANGLES];
56 
57 float avertexnormals[NUMVERTEXNORMALS][3] = {
58 #include "anorms.h"
59 };
60 
61 int     myargc, argpos;
62 char    **myargv;
63 int     savelod = 0;
64 int     num_optimization_passes = 1;
65 float   error_factor = 1;
66 
67 // CODE --------------------------------------------------------------------
68 
69 //===========================================================================
70 // PackVector
71 //  Converts a float vector to yaw9/pitch7.
72 //===========================================================================
PackVector(float vec[3])73 unsigned short PackVector(float vec[3])
74 {
75     float yaw = 0, pitch = 0, len;
76     int iyaw, ipitch;
77 
78     // First check for special cases (up/down).
79     if (vec[VX] == 0 && vec[VY] == 0)
80     {
81         if (vec[VZ] == 0) return 0; // This is not a good vector.
82         // Up or down...
83         pitch = PI/2;
84         if (vec[VZ] < 0) pitch = -pitch;
85     }
86     else
87     {
88         // First determine yaw (XY plane).
89         yaw = (float) atan2(vec[VY], vec[VX]);
90         len = (float) sqrt(vec[VX]*vec[VX] + vec[VY]*vec[VY]);
91         pitch = (float) atan2(vec[VZ], len);
92     }
93     // Now we have yaw and pitch angles, encode them into a word.
94     // (packed: pppppppy yyyyyyyy)
95     iyaw = (int) (yaw/PI*256); // Convert 2*PI => 512
96     while (iyaw > 511) iyaw -= 512;
97     while (iyaw < 0) iyaw += 512;
98 
99     ipitch = (int) ((pitch/(PI/2) + 1)*64);
100     if (ipitch > 127) ipitch = 127;
101     if (ipitch < 0) ipitch = 0;
102 
103 /*  //----DEBUG----
104     printf("PackVector: (%f,%f,%f) => (%i,%i)\n",
105         vec[0], vec[1], vec[2], iyaw, ipitch);
106     //----DEBUG----*/
107 
108     return iyaw | (ipitch << 9);
109 }
110 
111 //===========================================================================
112 // UnpackVector
113 //  Packed: pppppppy yyyyyyyy. Yaw is on the XY plane.
114 //===========================================================================
UnpackVector(unsigned short packed,float vec[3])115 void UnpackVector(unsigned short packed, float vec[3])
116 {
117     float yaw = (packed & 511)/512.0f * 2*PI;
118     float pitch = ((packed >> 9)/127.0f - 0.5f) * PI;
119     float cosp = (float) cos(pitch);
120     vec[VX] = (float) cos(yaw) * cosp;
121     vec[VY] = (float) sin(yaw) * cosp;
122     vec[VZ] = (float) sin(pitch);
123 
124 /*  //----DEBUG----
125     printf("UnpackVector: (%i,%i) => (%f,%f,%f)\n",
126         packed&511, packed>>9, vec[0], vec[1], vec[2]);
127     //----DEBUG----*/
128 }
129 
130 //===========================================================================
131 // GetNormalIndex
132 //===========================================================================
GetNormalIndex(float vec[3])133 int GetNormalIndex(float vec[3])
134 {
135     float dot, maxprod;
136     int j, n, idx;
137 
138     // Find closest match in the normals list.
139     for (j = 0; j < NUMVERTEXNORMALS; j++)
140     {
141         // Dot product.
142         for (dot = 0, n = 0; n < 3; n++)
143             dot += avertexnormals[j][n] * vec[n];
144         if (!j || dot > maxprod)
145         {
146             maxprod = dot;
147             idx = j;
148         }
149     }
150     return idx;
151 }
152 
153 //===========================================================================
154 // CheckOption
155 //  Checks the command line for the given option, and returns its index
156 //  number if it exists. Zero is returned if the option is not found.
157 //===========================================================================
CheckOption(char * opt)158 int CheckOption(char *opt)
159 {
160     int i;
161 
162     for (i = 1; i < myargc; i++)
163         if (!stricmp(myargv[i], opt))
164             return argpos = i;
165     return 0;
166 }
167 
168 //===========================================================================
169 // NextOption
170 //  Returns the next option the command line, or NULL if there are no more
171 //  options.
172 //===========================================================================
NextOption(void)173 const char *NextOption(void)
174 {
175     if (++argpos >= myargc) return NULL;
176     return myargv[argpos];
177 }
178 
179 //===========================================================================
180 // SkipWhite
181 //===========================================================================
SkipWhite(const char * str)182 const char *SkipWhite(const char *str)
183 {
184     while (isspace(*str) && *str) str++;
185     return str;
186 }
187 
188 //===========================================================================
189 // PrintBanner
190 //===========================================================================
PrintBanner(void)191 void PrintBanner(void)
192 {
193     printf("\n### md2tool v"MD2TOOL_VERSION" by Jaakko Keränen "
194            "<jaakko.keranen@iki.fi> ###\n\n");
195 }
196 
197 //===========================================================================
198 // PrintUsage
199 //===========================================================================
PrintUsage(void)200 void PrintUsage(void)
201 {
202     printf("Usage: md2tool [-flip] [-renorm] [-dsk] [-s <skinfile>]\n");
203     printf("       [-skin <num> <skinfile>] [-del <num>]\n");
204     printf("       [-delframes to|from|<num> <num>] [-delskin <num>]\n");
205     printf("       [-skinsize <width> <height>] [-gl] [-info] [-create <framelistfile>]\n");
206     printf("       [-md2] [-dmd] [-savelod <num>] [-lod] [-ef <num>] [-op <num>] [-tcmap]\n");
207     printf("       [-mg] [-fn <filename>] [-weld] [-weldtc] model[.md2|.dmd] ...\n\n");
208     printf("-create     Create an empty model based on a frame list (each line specifies\n");
209     printf("            a frame name, empty lines are skipped, comments begin with ; ).\n");
210     printf("-del        Delete one frame.\n");
211     printf("-delframes  Delete a range of frames.\n");
212     printf("-delskin    Delete one skin.\n");
213     printf("-dmd        Save model as DMD.\n");
214     printf("-dsk        Set skin zero to the default skin name (model name + PCX).\n");
215     printf("-ef         Set error factor for mesh optimization (default: 1.0).\n");
216     printf("-flip       Flip triangles. Automatically builds GL commands.\n");
217     printf("-fn         Change the name of the model file.\n");
218     printf("-gl         Build GL commands.\n");
219     printf("-info       Display model information.\n");
220     printf("-lod        Generate detail levels (automatically saved as DMD).\n");
221     printf("-md2        Save model as MD2 (the default).\n");
222     printf("-mg         Display triangle groups in the texture coordinate map.\n");
223     printf("-op         Set the number of mesh optimization passes.\n");
224     printf("-renorm     Calculate vertex normals.\n");
225     printf("-s          Set skin zero.\n");
226     printf("-savelod    The level to save when saving as MD2 (default: 0).\n");
227     printf("-skin       Set the specified skin.\n");
228     printf("-skinsize   Set skin dimensions.\n");
229     printf("-tcmap      Display texture coordinate map when optimizing.\n");
230     printf("-weld       Weld vertices (only for models with one frame).\n");
231     printf("-weldtc     Weld texture coordinates (removes all duplicates).\n");
232 }
233 
234 //===========================================================================
235 // DoError
236 //  Fatal error messages cause the execution of the program to be aborted.
237 //===========================================================================
DoError(int code)238 void DoError(int code)
239 {
240     printf("\nERROR: ");
241     switch (code)
242     {
243     case MTERR_INVALID_OPTION:
244         printf("Invalid usage of a command line option.\n");
245         PrintUsage();
246         exit(1);
247 
248     case MTERR_INVALID_SKIN_NUMBER:
249         printf("Invalid skin number.\n");
250         exit(2);
251 
252     case MTERR_INVALID_FRAME_NUMBER:
253         printf("Invalid frame number.\n");
254         exit(3);
255 
256     case MTERR_BAD_MAGIC:
257         printf("The file doesn't appear to be a valid MD2/DMD model.\n");
258         break;
259 
260     case MTERR_NO_FILES:
261         printf("No model files specified.\n");
262         break;
263 
264     case MTERR_LISTFILE_NA:
265         printf("The specified list file doesn't exist.\n");
266         break;
267 
268     case MTERR_READ_FAILED:
269         printf("Failed reading from file.\n");
270         exit(4);
271     }
272 }
273 
274 //===========================================================================
275 // Load
276 //  Allocates and loads in the given data.
277 //===========================================================================
Load(FILE * file,int offset,int len)278 void *Load(FILE *file, int offset, int len)
279 {
280     if (len > 0)
281     {
282         void *ptr = malloc(len);
283         fseek(file, offset, SEEK_SET);
284         if (fread(ptr, len, 1, file) == 0)
285         {
286             DoError(MTERR_READ_FAILED);
287         }
288         return ptr;
289     }
290     return NULL;
291 }
292 
293 //===========================================================================
294 // ModelNew
295 //  Create an empty MD2 model.
296 //===========================================================================
ModelNew(model_t * mo,const char * filename)297 void ModelNew(model_t *mo, const char *filename)
298 {
299     dmd_header_t *hd = &mo->header;
300     dmd_info_t *inf = &mo->info;
301 
302     printf("Creating new model \"%s\"...\n", filename);
303 
304     memset(mo, 0, sizeof(*mo));
305     strcpy(mo->fileName, filename);
306     mo->modified = true;
307     // Setup the header.
308     hd->magic = MD2_MAGIC;
309     hd->version = 8;
310     inf->skinWidth = 1;
311     inf->skinHeight = 1;
312     inf->frameSize = MD2_FRAMESIZE(0);
313 }
314 
315 //===========================================================================
316 // ModelOpen
317 //  Open an MD2 or DMD model.
318 //===========================================================================
ModelOpen(model_t * mo,const char * filename)319 int ModelOpen(model_t *mo, const char *filename)
320 {
321     FILE *file;
322     dmd_header_t *hd;
323     dmd_chunk_t chunk;
324     dmd_info_t *inf;
325     md2_header_t oldhd;
326     md2_frame_t *oldframes;
327     char fn[256];
328     int i, k;
329     void *ptr;
330 
331     strcpy(fn, filename);
332     if ((file = fopen(fn, "rb")) == NULL)
333     {
334         strcat(fn, ".md2");
335         if ((file = fopen(fn, "rb")) == NULL)
336         {
337             strcpy(fn, filename);
338             strcat(fn, ".dmd");
339             if ((file = fopen(fn, "rb")) == NULL)
340             {
341                 printf("Couldn't open the model \"%s\".\n", filename);
342                 return false;
343             }
344         }
345     }
346     printf("Opening model \"%s\"...\n", fn);
347 
348     memset(mo, 0, sizeof(*mo));
349     strcpy(mo->fileName, fn);
350     if (!fread(&mo->header, sizeof(mo->header), 1, file))
351     {
352         DoError(MTERR_READ_FAILED);
353     }
354     hd = &mo->header;
355     inf = &mo->info;
356     if (hd->magic == DMD_MAGIC)
357     {
358         // Read the chunks.
359         if (!fread(&chunk, sizeof(chunk), 1, file))
360         {
361             DoError(MTERR_READ_FAILED);
362         }
363         while (chunk.type != DMC_END)
364         {
365             switch (chunk.type)
366             {
367             case DMC_INFO:
368                 if (!fread(inf, sizeof(*inf), 1, file))
369                 {
370                     DoError(MTERR_READ_FAILED);
371                 }
372                 break;
373 
374             default:
375                 // Just skip all unknown chunks.
376                 fseek(file, chunk.length, SEEK_CUR);
377                 printf("Skipping unknown chunk (type %i, length %i).\n",
378                     chunk.type, chunk.length);
379             }
380             // Read the next chunk.
381             if (!fread(&chunk, sizeof(chunk), 1, file))
382             {
383                 DoError(MTERR_READ_FAILED);
384             }
385         }
386 
387         // Allocate and load in the data.
388         mo->skins = Load(file, inf->offsetSkins, sizeof(dmd_skin_t)
389             * inf->numSkins);
390         mo->texCoords = Load(file, inf->offsetTexCoords,
391             sizeof(dmd_textureCoordinate_t) * inf->numTexCoords);
392         mo->frames = Load(file, inf->offsetFrames, inf->frameSize
393             * inf->numFrames);
394         ptr = Load(file, inf->offsetLODs, sizeof(dmd_levelOfDetail_t)
395             * inf->numLODs);
396         memcpy(mo->lodinfo, ptr, sizeof(dmd_levelOfDetail_t) * inf->numLODs);
397         free(ptr);
398         for (i = 0; i < inf->numLODs; i++)
399         {
400             mo->lods[i].triangles = Load(file, mo->lodinfo[i].offsetTriangles,
401                 sizeof(dmd_triangle_t) * mo->lodinfo[i].numTriangles);
402             mo->lods[i].glCommands = Load(file, mo->lodinfo[i].offsetGlCommands,
403                 sizeof(int) * mo->lodinfo[i].numGlCommands);
404         }
405     }
406     else if (hd->magic == MD2_MAGIC)
407     {
408         rewind(file);
409         if (!fread(&oldhd, sizeof(oldhd), 1, file))
410         {
411             DoError(MTERR_READ_FAILED);
412         }
413 
414         // Convert it to DMD data but keep it as an MD2.
415         hd->magic = MD2_MAGIC;
416         hd->version = 8;
417         hd->flags = 0;
418         inf->skinWidth = oldhd.skinWidth;
419         inf->skinHeight = oldhd.skinHeight;
420         inf->frameSize = DMD_FRAMESIZE(oldhd.numVertices);
421         inf->numLODs = 1;
422         inf->numSkins = oldhd.numSkins;
423         inf->numTexCoords = oldhd.numTexCoords;
424         inf->numVertices = oldhd.numVertices;
425         inf->numFrames = oldhd.numFrames;
426         inf->offsetSkins = oldhd.offsetSkins;
427         inf->offsetTexCoords = oldhd.offsetTexCoords;
428         inf->offsetFrames = oldhd.offsetFrames;
429         inf->offsetLODs = oldhd.offsetEnd; // Doesn't exist.
430         mo->lodinfo[0].numTriangles = oldhd.numTriangles;
431         mo->lodinfo[0].numGlCommands = oldhd.numGlCommands;
432         mo->lodinfo[0].offsetTriangles = oldhd.offsetTriangles;
433         mo->lodinfo[0].offsetGlCommands = oldhd.offsetGlCommands;
434         inf->offsetEnd = oldhd.offsetEnd;
435 
436         // Allocate and load in the data.
437         mo->skins = Load(file, inf->offsetSkins, sizeof(md2_skin_t)
438             * inf->numSkins);
439         mo->texCoords = Load(file, inf->offsetTexCoords,
440             sizeof(md2_textureCoordinate_t) * inf->numTexCoords);
441         mo->lods[0].triangles = Load(file, mo->lodinfo[0].offsetTriangles,
442             sizeof(md2_triangle_t) * mo->lodinfo[0].numTriangles);
443         mo->lods[0].glCommands = Load(file, mo->lodinfo[0].offsetGlCommands,
444             sizeof(int) * mo->lodinfo[0].numGlCommands);
445 
446         oldframes = Load(file, inf->offsetFrames, oldhd.frameSize
447             * inf->numFrames);
448         mo->frames = malloc(inf->frameSize * inf->numFrames);
449         // Convert to DMD frames.
450         for (i = 0; i < inf->numFrames; i++)
451         {
452             md2_frame_t *md2f = (md2_frame_t*) ((byte*)oldframes + i*oldhd.frameSize);
453             dmd_frame_t *dmdf = (dmd_frame_t*) ((byte*)mo->frames + i*inf->frameSize);
454             memcpy(dmdf->name, md2f->name, sizeof(md2f->name));
455             memcpy(dmdf->scale, md2f->scale, sizeof(md2f->scale));
456             memcpy(dmdf->translate, md2f->translate, sizeof(md2f->translate));
457             for (k = 0; k < inf->numVertices; k++)
458             {
459                 memcpy(dmdf->vertices[k].vertex,
460                     md2f->vertices[k].vertex, sizeof(byte)*3);
461                 dmdf->vertices[k].normal = PackVector
462                     (avertexnormals[md2f->vertices[k].lightNormalIndex]);
463             }
464         }
465         free(oldframes);
466     }
467     else
468     {
469         DoError(MTERR_BAD_MAGIC);
470         fclose(file);
471         return false;
472     }
473     fclose(file);
474 
475     // Print some DMD information.
476     printf("%i triangles, %i vertices, %i frames, %i skin%s (%ix%i).\n",
477         mo->lodinfo[0].numTriangles, inf->numVertices, inf->numFrames,
478         inf->numSkins, inf->numSkins != 1? "s" : "",
479         inf->skinWidth, inf->skinHeight);
480     /*for (i = 1; i < inf->numLODs; i++)
481     {
482         printf("  Level %i: %i triangles, %i GL commands.\n", i,
483             mo->lodinfo[i].numTriangles, mo->lodinfo[i].numGlCommands);
484     }*/
485     return true;
486 }
487 
488 //===========================================================================
489 // ModelSaveMD2
490 //===========================================================================
ModelSaveMD2(model_t * mo,FILE * file)491 void ModelSaveMD2(model_t *mo, FILE *file)
492 {
493     dmd_info_t *inf = &mo->info;
494     md2_header_t hd;
495     float vec[3];
496     int i, k;
497     byte n;
498 
499     // Check that the level-to-save is valid.
500     if (savelod < 0 || savelod >= inf->numLODs)
501     {
502         printf("Invalid savelod (%i), saving level 0 instead.\n", savelod);
503         savelod = 0;
504     }
505 
506     hd.magic = MD2_MAGIC;
507     hd.version = 8;
508     hd.skinWidth = inf->skinWidth;
509     hd.skinHeight = inf->skinHeight;
510     hd.frameSize = MD2_FRAMESIZE(inf->numVertices);
511     hd.numSkins = inf->numSkins;
512     hd.numVertices = inf->numVertices;
513     hd.numTexCoords = inf->numTexCoords;
514     hd.numTriangles = mo->lodinfo[savelod].numTriangles;
515     hd.numGlCommands = mo->lodinfo[savelod].numGlCommands;
516     hd.numFrames = inf->numFrames;
517 
518     // The header will be written again after we know the locations
519     // of the other data.
520     fwrite(&hd, sizeof(hd), 1, file);
521 
522     // Skins.
523     hd.offsetSkins = ftell(file);
524     fwrite(mo->skins, sizeof(*mo->skins), hd.numSkins, file);
525     // Texture coordinates.
526     hd.offsetTexCoords = ftell(file);
527     fwrite(mo->texCoords, sizeof(*mo->texCoords), hd.numTexCoords, file);
528     // Triangles.
529     hd.offsetTriangles = ftell(file);
530     fwrite(mo->lods[savelod].triangles, sizeof(md2_triangle_t),
531         hd.numTriangles, file);
532     // Frames must be written separately (because of the normals).
533     hd.offsetFrames = ftell(file);
534     for (i = 0; i < hd.numFrames; i++)
535     {
536         dmd_frame_t *dmdf = (dmd_frame_t*) ((byte*)mo->frames
537             + i*inf->frameSize);
538         fwrite(dmdf->scale, sizeof(float) * 3, 1, file);
539         fwrite(dmdf->translate, sizeof(float) * 3, 1, file);
540         fwrite(dmdf->name, 16, 1, file);
541         for (k = 0; k < inf->numVertices; k++)
542         {
543             fwrite(dmdf->vertices[k].vertex, 3, 1, file);
544             UnpackVector(dmdf->vertices[k].normal, vec);
545             n = GetNormalIndex(vec);
546             fwrite(&n, 1, 1, file);
547         }
548     }
549     // GL commands.
550     hd.offsetGlCommands = ftell(file);
551     fwrite(mo->lods[savelod].glCommands, sizeof(int), hd.numGlCommands, file);
552     // The end.
553     hd.offsetEnd = ftell(file);
554 
555     // Rewrite the header.
556     rewind(file);
557     fwrite(&hd, sizeof(hd), 1, file);
558 }
559 
560 //===========================================================================
561 // ModelSaveDMD
562 //===========================================================================
ModelSaveDMD(model_t * mo,FILE * file)563 void ModelSaveDMD(model_t *mo, FILE *file)
564 {
565     dmd_info_t *inf = &mo->info;
566     int offsetInfo, i;
567     dmd_chunk_t chunk;
568 
569     // First the header.
570     mo->header.version = 1; // This is version 1.
571     fwrite(&mo->header, sizeof(mo->header), 1, file);
572 
573     // Then the chunks.
574     chunk.type = DMC_INFO;
575     chunk.length = sizeof(*inf);
576     fwrite(&chunk, sizeof(chunk), 1, file);
577     offsetInfo = ftell(file);
578     fwrite(inf, sizeof(*inf), 1, file);
579 
580     // That was the only chunk, write the end marker.
581     chunk.type = DMC_END;
582     chunk.length = 0;
583     fwrite(&chunk, sizeof(chunk), 1, file);
584 
585     // Write the data.
586     inf->offsetSkins = ftell(file);
587     fwrite(mo->skins, sizeof(dmd_skin_t), inf->numSkins, file);
588     inf->offsetTexCoords = ftell(file);
589     fwrite(mo->texCoords, sizeof(dmd_textureCoordinate_t), inf->numTexCoords,
590         file);
591     inf->offsetFrames = ftell(file);
592     fwrite(mo->frames, DMD_FRAMESIZE(inf->numVertices), inf->numFrames,
593         file);
594     // Write the LODs.
595     for (i = 0; i < inf->numLODs; i++)
596     {
597         mo->lodinfo[i].offsetTriangles = ftell(file);
598         fwrite(mo->lods[i].triangles, sizeof(dmd_triangle_t),
599             mo->lodinfo[i].numTriangles, file);
600 
601         mo->lodinfo[i].offsetGlCommands = ftell(file);
602         fwrite(mo->lods[i].glCommands, sizeof(int),
603             mo->lodinfo[i].numGlCommands, file);
604     }
605     // And the LOD info.
606     inf->offsetLODs = ftell(file);
607     fwrite(mo->lodinfo, sizeof(dmd_levelOfDetail_t), inf->numLODs, file);
608 
609     inf->offsetEnd = ftell(file);
610 
611     // Rewrite the info chunk (now that the offsets are known).
612     fseek(file, offsetInfo, SEEK_SET);
613     fwrite(inf, sizeof(*inf), 1, file);
614 }
615 
616 //===========================================================================
617 // ModelClose
618 //  The model is saved as MD2 or DMD depending on the magic number.
619 //===========================================================================
ModelClose(model_t * mo)620 void ModelClose(model_t *mo)
621 {
622     FILE *file;
623     int i;
624 
625     printf("Closing model \"%s\"...\n", mo->fileName);
626 
627     if (mo->modified)
628     {
629         // Open the file for writing.
630         if ((file = fopen(mo->fileName, "wb")) == NULL)
631         {
632             printf("Can't open \"%s\" for writing.\n", mo->fileName);
633             return;
634         }
635         if (mo->header.magic == DMD_MAGIC)
636             ModelSaveDMD(mo, file);
637         else if (mo->header.magic == MD2_MAGIC)
638             ModelSaveMD2(mo, file);
639 
640         fclose(file);
641         mo->modified = false;
642     }
643 
644     // Free the memory allocated for the model.
645     free(mo->skins);
646     free(mo->texCoords);
647     free(mo->frames);
648     for (i = 0; i < mo->info.numLODs; i++)
649     {
650         free(mo->lods[i].triangles);
651         free(mo->lods[i].glCommands);
652     }
653 }
654 
655 //===========================================================================
656 // CrossProd
657 //  Calculates the cross product of two vectors, which are specified by
658 //  three points.
659 //===========================================================================
CrossProd(float * v1,float * v2,float * v3,float * out)660 void CrossProd(float *v1, float *v2, float *v3, float *out)
661 {
662     float a[3], b[3];
663     int i;
664 
665     for (i = 0; i < 3; i++)
666     {
667         a[i] = v2[i] - v1[i];
668         b[i] = v3[i] - v1[i];
669     }
670     out[VX] = a[VY]*b[VZ] - a[VZ]*b[VY];
671     out[VY] = a[VZ]*b[VX] - a[VX]*b[VZ];
672     out[VZ] = a[VX]*b[VY] - a[VY]*b[VX];
673 }
674 
675 //===========================================================================
676 // Norm
677 //  Normalize a vector.
678 //===========================================================================
Norm(float * vector)679 void Norm(float *vector)
680 {
681     float length = (float) sqrt(vector[VX]*vector[VX]
682         + vector[VY]*vector[VY]
683         + vector[VZ]*vector[VZ]);
684 
685     if (length)
686     {
687         vector[VX] /= length;
688         vector[VY] /= length;
689         vector[VZ] /= length;
690     }
691 }
692 
693 //===========================================================================
694 // GetFrame
695 //===========================================================================
GetFrame(model_t * mo,int framenum)696 dmd_frame_t *GetFrame(model_t *mo, int framenum)
697 {
698     return (dmd_frame_t*) ((byte*) mo->frames + mo->info.frameSize
699         * framenum);
700 }
701 
702 //===========================================================================
703 // StripLength
704 //===========================================================================
StripLength(model_t * model,int lod,int starttri,int startv)705 int StripLength(model_t *model, int lod, int starttri, int startv)
706 {
707     int         m1, m2;
708     int         st1, st2;
709     int         j;
710     dtriangle_t *last, *check;
711     int         k;
712 
713     used[starttri] = 2;
714 
715     last = &triangles[starttri];
716 
717     strip_xyz[0] = last->index_xyz[(startv)%3];
718     strip_xyz[1] = last->index_xyz[(startv+1)%3];
719     strip_xyz[2] = last->index_xyz[(startv+2)%3];
720     strip_st[0] = last->index_st[(startv)%3];
721     strip_st[1] = last->index_st[(startv+1)%3];
722     strip_st[2] = last->index_st[(startv+2)%3];
723 
724     strip_tris[0] = starttri;
725     stripcount = 1;
726 
727     m1 = last->index_xyz[(startv+2)%3];
728     st1 = last->index_st[(startv+2)%3];
729     m2 = last->index_xyz[(startv+1)%3];
730     st2 = last->index_st[(startv+1)%3];
731 
732     // look for a matching triangle
733 nexttri:
734     for (j=starttri+1, check=&triangles[starttri+1]
735         ; j < model->lodinfo[lod].numTriangles; j++, check++)
736     {
737         for (k=0 ; k<3 ; k++)
738         {
739             if (check->index_xyz[k] != m1)
740                 continue;
741             if (check->index_st[k] != st1)
742                 continue;
743             if (check->index_xyz[ (k+1)%3 ] != m2)
744                 continue;
745             if (check->index_st[ (k+1)%3 ] != st2)
746                 continue;
747 
748             // this is the next part of the fan
749 
750             // if we can't use this triangle, this tristrip is done
751             if (used[j])
752                 goto done;
753 
754             // the new edge
755             if (stripcount & 1)
756             {
757                 m2 = check->index_xyz[ (k+2)%3 ];
758                 st2 = check->index_st[ (k+2)%3 ];
759             }
760             else
761             {
762                 m1 = check->index_xyz[ (k+2)%3 ];
763                 st1 = check->index_st[ (k+2)%3 ];
764             }
765 
766             strip_xyz[stripcount+2] = check->index_xyz[ (k+2)%3 ];
767             strip_st[stripcount+2] = check->index_st[ (k+2)%3 ];
768             strip_tris[stripcount] = j;
769             stripcount++;
770 
771             used[j] = 2;
772             goto nexttri;
773         }
774     }
775 done:
776 
777     // clear the temp used flags
778     for (j=starttri+1 ; j < model->lodinfo[lod].numTriangles; j++)
779         if (used[j] == 2) used[j] = 0;
780 
781     return stripcount;
782 }
783 
784 //===========================================================================
785 // FanLength
786 //===========================================================================
FanLength(model_t * model,int lod,int starttri,int startv)787 int FanLength(model_t *model, int lod, int starttri, int startv)
788 {
789     int     m1, m2;
790     int     st1, st2;
791     int     j;
792     dtriangle_t *last, *check;
793     int     k;
794 
795     used[starttri] = 2;
796 
797     last = &triangles[starttri];
798 
799     strip_xyz[0] = last->index_xyz[(startv)%3];
800     strip_xyz[1] = last->index_xyz[(startv+1)%3];
801     strip_xyz[2] = last->index_xyz[(startv+2)%3];
802     strip_st[0] = last->index_st[(startv)%3];
803     strip_st[1] = last->index_st[(startv+1)%3];
804     strip_st[2] = last->index_st[(startv+2)%3];
805 
806     strip_tris[0] = starttri;
807     stripcount = 1;
808 
809     m1 = last->index_xyz[(startv+0)%3];
810     st1 = last->index_st[(startv+0)%3];
811     m2 = last->index_xyz[(startv+2)%3];
812     st2 = last->index_st[(startv+2)%3];
813 
814 
815     // look for a matching triangle
816 nexttri:
817     for (j=starttri+1, check=&triangles[starttri+1]
818         ; j < model->lodinfo[lod].numTriangles; j++, check++)
819     {
820         for (k=0 ; k<3 ; k++)
821         {
822             if (check->index_xyz[k] != m1)
823                 continue;
824             if (check->index_st[k] != st1)
825                 continue;
826             if (check->index_xyz[ (k+1)%3 ] != m2)
827                 continue;
828             if (check->index_st[ (k+1)%3 ] != st2)
829                 continue;
830 
831             // this is the next part of the fan
832 
833             // if we can't use this triangle, this tristrip is done
834             if (used[j])
835                 goto done;
836 
837             // the new edge
838             m2 = check->index_xyz[ (k+2)%3 ];
839             st2 = check->index_st[ (k+2)%3 ];
840 
841             strip_xyz[stripcount+2] = m2;
842             strip_st[stripcount+2] = st2;
843             strip_tris[stripcount] = j;
844             stripcount++;
845 
846             used[j] = 2;
847             goto nexttri;
848         }
849     }
850 done:
851 
852     // clear the temp used flags
853     for (j=starttri+1 ; j < model->lodinfo[lod].numTriangles; j++)
854         if (used[j] == 2) used[j] = 0;
855 
856     return stripcount;
857 }
858 
859 //===========================================================================
860 // OptimizeTexCoords
861 //  "Optimize" the texcoords. The tristrip builders only work correctly
862 //  if some triangles share texcoord indices, so we need to remove all
863 //  redundant references: the first (s,t) match for a texcoord is used
864 //  for all similar (u,v)s. -jk
865 //===========================================================================
OptimizeTexCoords(model_t * mo,dtriangle_t * tris,int count)866 void OptimizeTexCoords(model_t *mo, dtriangle_t *tris, int count)
867 {
868     int i, j, k;
869     short u, v;
870 
871     for (i = 0; i < count; i++)
872         for (k = 0; k < 3; k++)
873         {
874             u = mo->texCoords[tris[i].index_st[k]].s;
875             v = mo->texCoords[tris[i].index_st[k]].t;
876 
877             for (j = 0; j < mo->info.numTexCoords; j++)
878             {
879                 if (mo->texCoords[j].s == u
880                     && mo->texCoords[j].t == v)
881                 {
882                     tris[i].index_st[k] = j;
883                     break;
884                 }
885             }
886         }
887 }
888 
889 //===========================================================================
890 // BuildGlCmds
891 //  Generate a list of trifans or strips for the model, which holds
892 //  for all frames. All the LODs present are processed.
893 //===========================================================================
BuildGlCmds(model_t * mo)894 void BuildGlCmds(model_t *mo)
895 {
896     int     i, j, k, lod;
897     int     numtris;
898     int     startv;
899     float   s, t;
900     int     len, bestlen, besttype;
901     int     best_xyz[1024];
902     int     best_st[1024];
903     int     best_tris[1024];
904     int     type;
905     int     numfans, numstrips, avgfan, avgstrip;
906 
907     printf("Building GL commands.\n");
908     mo->modified = true;
909 
910     for (lod = 0; lod < mo->info.numLODs; lod++)
911     {
912         numfans = numstrips = avgfan = avgstrip = 0;
913         numtris = mo->lodinfo[lod].numTriangles;
914 
915         // Init triangles.
916         memcpy(triangles, mo->lods[lod].triangles,
917             sizeof(dtriangle_t) * numtris);
918         OptimizeTexCoords(mo, triangles, numtris);
919 
920         //
921         // build tristrips
922         //
923         numcommands = 0;
924         numglverts = 0;
925         memset(used, 0, sizeof(used));
926         for (i = 0; i < numtris; i++)
927         {
928             // pick an unused triangle and start the trifan
929             if (used[i]) continue;
930 
931             bestlen = 0;
932             for (type = 0; type < 2 ; type++)
933             {
934                 for (startv =0 ; startv < 3 ; startv++)
935                 {
936                     if (type == 1)
937                         len = StripLength(mo, lod, i, startv);
938                     else
939                         len = FanLength(mo, lod, i, startv);
940                     if (len > bestlen)
941                     {
942                         besttype = type;
943                         bestlen = len;
944                         for (j=0 ; j<bestlen+2 ; j++)
945                         {
946                             best_st[j] = strip_st[j];
947                             best_xyz[j] = strip_xyz[j];
948                         }
949                         for (j=0 ; j<bestlen ; j++)
950                             best_tris[j] = strip_tris[j];
951                     }
952                 }
953             }
954 
955             // mark the tris on the best strip/fan as used
956             for (j=0 ; j<bestlen ; j++)
957                 used[best_tris[j]] = 1;
958 
959             if (besttype == 1)
960             {
961                 numstrips++;
962                 avgstrip += bestlen + 2;
963                 commands[numcommands++] = (bestlen+2);
964             }
965             else
966             {
967                 numfans++;
968                 avgfan += bestlen + 2;
969                 commands[numcommands++] = -(bestlen+2);
970             }
971 
972             numglverts += bestlen+2;
973 
974             for (j=0 ; j<bestlen+2 ; j++)
975             {
976                 // emit a vertex into the reorder buffer
977                 k = best_st[j];
978 
979                 // emit s/t coords into the commands stream
980                 s = mo->texCoords[k].s; //base_st[k].s;
981                 t = mo->texCoords[k].t; //base_st[k].t;
982 
983                 s = (s + 0.5f) / mo->info.skinWidth;
984                 t = (t + 0.5f) / mo->info.skinHeight;
985 
986                 *(float *)&commands[numcommands++] = s;
987                 *(float *)&commands[numcommands++] = t;
988                 *(int *)&commands[numcommands++] = best_xyz[j];
989             }
990         }
991         commands[numcommands++] = 0;        // end of list marker
992 
993         mo->lodinfo[lod].numGlCommands = numcommands;
994         mo->lods[lod].glCommands = realloc(mo->lods[lod].glCommands,
995             sizeof(int) * numcommands);
996         memcpy(mo->lods[lod].glCommands, commands, sizeof(int) * numcommands);
997 
998         // Some statistics.
999         printf("(Level %i)\n", lod);
1000         printf("  Number of strips: %-3i (%.3f vertices on average)\n",
1001             numstrips, numstrips? avgstrip/(float)numstrips : 0);
1002         printf("  Number of fans:   %-3i (%.3f vertices on average)\n",
1003             numfans, numfans? avgfan/(float)numfans : 0);
1004     }
1005 }
1006 
1007 //===========================================================================
1008 // IsTexCoordConnected
1009 //  Returns true if the two triangles can be connected via a chain of
1010 //  shared texture coordinates.
1011 //===========================================================================
IsTexCoordConnected(model_t * mo,dtriangle_t * src,dtriangle_t * dest,dtriangle_t * origtris,int orignumtris)1012 int IsTexCoordConnected(model_t *mo, dtriangle_t *src, dtriangle_t *dest,
1013                         dtriangle_t *origtris, int orignumtris)
1014 {
1015     int num = mo->info.numTexCoords;
1016     int result = false;
1017     int *tcmap = malloc(sizeof(int) * num);
1018     int i, j, k, didspread;
1019     int found;
1020 
1021     // The map tells us which texcoords have been checked.
1022     // 0 = not checked, 1 = newly spread, 2 = checked.
1023     memset(tcmap, 0, sizeof(*tcmap) * num);
1024 
1025     // Initial step: spread to the source triangle's texcoords.
1026     for (i = 0; i < 3; i++) tcmap[src->index_st[i]] = 1;
1027     didspread = true;
1028     while (didspread)
1029     {
1030         // Check if the newly spread-to vertices include one of the
1031         // target texcoords.
1032         for (i = 0; i < num; i++)
1033         {
1034             if (tcmap[i] != 1) continue;
1035             for (k = 0; k < 3; k++)
1036                 if (dest->index_st[k] == i)
1037                 {
1038                     // Hey, we reached the destination.
1039                     result = true;
1040                     goto tcdone;
1041                 }
1042             // Not a match, mark it as checked.
1043             tcmap[i] = 2;
1044         }
1045         // Try spreading to new texcoords.
1046         didspread = false;
1047         // Spread to the coords of all triangles connected to this one.
1048         // Level zero triangles are used in the test.
1049         for (j = 0; j < orignumtris; j++)
1050         {
1051             // We'll only spread from checked texcoords.
1052             for (found = false, k = 0; k < 3; k++)
1053                 if (tcmap[origtris[j].index_st[k]] > 0 /*== 2*/)
1054                 {
1055                     found = true;
1056                     break;
1057                 }
1058             if (!found) continue;
1059             // Spread to coords not yet spread to.
1060             for (k = 0; k < 3; k++)
1061                 if (tcmap[origtris[j].index_st[k]] < 2)
1062                 {
1063                     // Spread to this triangle.
1064                     for (k = 0; k < 3; k++)
1065                         if (!tcmap[origtris[j].index_st[k]])
1066                             tcmap[origtris[j].index_st[k]] = 1;
1067                     didspread = true;
1068                     break;
1069                 }
1070         }
1071     }
1072 tcdone:
1073     free(tcmap);
1074     return result;
1075 }
1076 
1077 #if 0
1078 //===========================================================================
1079 // FindGroupEdges
1080 //===========================================================================
1081 void FindGroupEdges(model_t *mo, dtriangle_t *tris, int numtris, int *ved)
1082 {
1083     int i, j, k;
1084     int done, found, spread;
1085 
1086     // -1 means the vertex hasn't been checked yet.
1087     memset(ved, -1, sizeof(ved));
1088 
1089     done = false;
1090     while (!done)
1091     {
1092         done = true;
1093         // Find a good starting place.
1094         for (found = false, i = 0; i < mo->info.numTexCoords; i++)
1095         {
1096             if (ved[i] >= 0) continue; // Already checked.
1097             found = true;
1098             break; // This is good.
1099         }
1100         if (!found) break;
1101 
1102         // Start spreading from the starting vertex.
1103         ved[i] = 2;
1104         spread = true;
1105         while (spread)
1106         {
1107 /*          for (spread = false, j = 0; j < numtris; j++)
1108             {
1109                 // Does this triangle have an edge to spread along?
1110                 for (k = 0; k < 3; k++)
1111                     if (ved[tris[j].index_st[k]] == 2
1112                         || ved[tris[j].index_st[(k+1)%3]] == 2)
1113                     {
1114                         // Spread along this edge.
1115                         if (ved[tris[j].index_st[k]] == 2)
1116                             ved[tris[j].index_st[(k+1)%3]] = 3;
1117                         else
1118                             ved[tris[j].index_st[k]] = 3;
1119 
1120                         // The ones we spread from certainly aren't edges.
1121                         if (ved[tris[j].index_st[k] == 2) ved[tris[j].index_st[k] = 0;
1122                         if (ved[tris[j].index_st[(k+1)%3] == 2) ved[tris[j].index_st[(k+1)%3] = 0;
1123                         spread = true;
1124                     }
1125             }
1126             if (spread)
1127             {
1128                 for (k = 0; k < mo->info.numTexCoords; k++)
1129                     if (ved[k] == 3) ved[k] = 2;
1130             }*/
1131 
1132         }
1133     }
1134 }
1135 #endif
1136 
1137 //===========================================================================
1138 // HaveSharedTexCoord
1139 //  Returns true if the two triangles have at least one common texcoord.
1140 //===========================================================================
HaveSharedTexCoord(dtriangle_t * a,dtriangle_t * b)1141 int HaveSharedTexCoord(dtriangle_t *a, dtriangle_t *b)
1142 {
1143     int i, k;
1144 
1145     for (i = 0; i < 3; i++)
1146         for (k = 0; k < 3; k++)
1147             if (a->index_st[i] == b->index_st[k])
1148                 return true;
1149     return false;
1150 }
1151 
1152 //===========================================================================
1153 // TriangleNormal
1154 //  Calculated using the normals in the first frame.
1155 //===========================================================================
TriangleNormal(model_t * mo,dtriangle_t * tri,float vec[3])1156 void TriangleNormal(model_t *mo, dtriangle_t *tri, float vec[3])
1157 {
1158     dmd_frame_t *fr = GetFrame(mo, 0);
1159     dmd_vertex_t *vtx = fr->vertices;
1160     float pos[3][3];
1161     int i, j;
1162 
1163     for (i = 0; i < 3; i++)
1164         for (j = 0; j < 3; j++)
1165         {
1166             pos[i][j] = vtx[tri->index_xyz[i]].vertex[j] * fr->scale[j] +
1167                 fr->translate[j];
1168         }
1169     CrossProd(pos[0], pos[2], pos[1], vec);
1170     Norm(vec);
1171 }
1172 
1173 //===========================================================================
1174 // PointOnLineSide
1175 //===========================================================================
PointOnLineSide(float x1,float y1,float x2,float y2,float cx,float cy)1176 int PointOnLineSide(float x1, float y1, float x2, float y2, float cx, float cy)
1177 {
1178     // (YA-YC)(XB-XA)-(XA-XC)(YB-YA)
1179     return ((y1-cy)*(x2-x1) - (x1-cx)*(y2-y1)) >= 0;
1180 }
1181 
1182 //===========================================================================
1183 // IsValidTexTriangle
1184 //===========================================================================
IsValidTexTriangle(dtriangle_t * tri)1185 int IsValidTexTriangle(dtriangle_t *tri)
1186 {
1187     int i, j;
1188 
1189     for (i = 0; i < 3; i++)
1190         for (j = 0; j < 3; j++)
1191             if (i != j && tri->index_st[i] == tri->index_st[j])
1192             {
1193                 // Hmm!! This is a bad texture triangle, with no area!
1194                 return false;
1195             }
1196     return true;
1197 }
1198 
1199 //===========================================================================
1200 // IsClockwiseTexTriangle
1201 //===========================================================================
IsClockwiseTexTriangle(model_t * mo,dtriangle_t * tri)1202 int IsClockwiseTexTriangle(model_t *mo, dtriangle_t *tri)
1203 {
1204     dmd_textureCoordinate_t *tc = mo->texCoords;
1205     return PointOnLineSide(tc[tri->index_st[0]].s, tc[tri->index_st[0]].t,
1206         tc[tri->index_st[1]].s, tc[tri->index_st[1]].t,
1207         tc[tri->index_st[2]].s, tc[tri->index_st[2]].t);
1208 }
1209 
1210 //===========================================================================
1211 // InsideTexTriangle
1212 //===========================================================================
InsideTexTriangle(model_t * mo,float x,float y,dtriangle_t * tri)1213 int InsideTexTriangle(model_t *mo, float x, float y, dtriangle_t *tri)
1214 {
1215     int i;
1216     dmd_textureCoordinate_t *tc = mo->texCoords;
1217     int test = false;
1218 
1219     if (!IsValidTexTriangle(tri)) return false;
1220 
1221     // First determine is this a clockwise or a counterclockwise triangle.
1222     if (!IsClockwiseTexTriangle(mo, tri))
1223     {
1224         // Counterclockwise.
1225         test = true;
1226     }
1227 
1228     for (i = 0; i < 3; i++)
1229     {
1230         if (PointOnLineSide(
1231             tc[tri->index_st[i]].s,
1232             tc[tri->index_st[i]].t,
1233             tc[tri->index_st[(i+1)%3]].s,
1234             tc[tri->index_st[(i+1)%3]].t,
1235             x, y) == test) return false;
1236     }
1237     return true;
1238 }
1239 
1240 //===========================================================================
1241 // GroupTriangles
1242 //  Returns the number of groups.
1243 //===========================================================================
GroupTriangles(model_t * mo,optriangle_t * tris,int numtris)1244 int GroupTriangles(model_t *mo, optriangle_t *tris, int numtris)
1245 {
1246     int high = 0;
1247     int start, i, j;
1248     char spreadto[MAX_TRIANGLES], didspread;
1249 
1250     (void) mo; // unused
1251 
1252     memset(spreadto, 0, sizeof(spreadto));
1253 
1254     // 1. Find an ungrouped triangle (stop if not found).
1255     // 2. Give it a new group number.
1256     // 3. Spread as much as possible.
1257     // 4. Continue from step 1.
1258 
1259     for (;;)
1260     {
1261         // Find an ungrouped triangle.
1262         for (start = 0; start < numtris; start++)
1263             if (!tris[start].group) break;
1264         if (start == numtris) break; // Nothing further to do.
1265         // Spread to this triangle.
1266         spreadto[start] = 2;
1267         tris[start].group = ++high;
1268         didspread = true;
1269         while (didspread)
1270         {
1271             for (didspread = false, i = 0; i < numtris; i++)
1272             {
1273                 if (spreadto[i] != 2) continue;
1274                 spreadto[i] = 1;
1275                 for (j = 0; j < numtris; j++)
1276                 {
1277                     if (spreadto[j]) continue;
1278                     if (HaveSharedTexCoord(&tris[i].tri, &tris[j].tri))
1279                     {
1280                         didspread = true;
1281                         spreadto[j] = 2;
1282                         tris[j].group = high;
1283                     }
1284                 }
1285             }
1286         }
1287     }
1288     return high;
1289 }
1290 
1291 //===========================================================================
1292 // DrawTexCoordMap
1293 //  A debugging aid.
1294 //  Draw a nice picture of the texture coordinates.
1295 //===========================================================================
DrawTexCoordMap(model_t * mo,optriangle_t * tris,int numtris,int * vertex_on_edge)1296 void DrawTexCoordMap(model_t *mo, optriangle_t *tris, int numtris,
1297                      int *vertex_on_edge)
1298 {
1299     int cols = 160;
1300     int rows = 60;
1301     int i, k, t, m, c, found;
1302     int hitgroup;
1303     int withgroups = CheckOption("-mg");
1304 
1305     for (i = 0; i < rows; i++)
1306     {
1307         for (k = 0; k < cols; k++)
1308         {
1309             for (found = 0, t = 0; t < numtris; t++)
1310             {
1311                 for (m = 0; m < 3; m++)
1312                 {
1313                     c = tris[t].tri.index_st[m];
1314                     if ((int)(mo->texCoords[c].s/(float)mo->info.skinWidth*(cols-1)) == k
1315                         && (int)(mo->texCoords[c].t/(float)mo->info.skinHeight*(rows-1)) == i)
1316                     {
1317                         if (!found) found = 1;
1318                         if (vertex_on_edge[c]) found = 2;
1319                     }
1320                 }
1321             }
1322             if (!found)
1323             {
1324                 for (t = 0; t < numtris; t++)
1325                     if (InsideTexTriangle(mo, k/(float)(cols-1)*mo->info.skinWidth,
1326                         i/(float)(rows-1)*mo->info.skinHeight, &tris[t].tri))
1327                     {
1328                         found = 3;
1329                         hitgroup = tris[t].group;
1330                         break;
1331                     }
1332             }
1333             printf("%c", !found? ' ' : found==1? '+' : found==2? '#'
1334                 : (withgroups? 'A'+hitgroup-1 : ':'));
1335         }
1336         printf("\n");
1337     }
1338 }
1339 
1340 //===========================================================================
1341 // OptimizeMesh
1342 //  Returns the number of triangles left.
1343 //===========================================================================
1344 #define MAX_MERGED 64
OptimizeMesh(model_t * mo,dtriangle_t * origtris,int orignumtris,int level)1345 int OptimizeMesh(model_t *mo, dtriangle_t *origtris, int orignumtris,
1346                  int level)
1347 {
1348     optriangle_t *tris;
1349     int numtris;
1350     int i, k, j, m, t, c, highgroup;
1351     int tc1, tc2;
1352     int found;
1353     int vused[MAX_VERTS];
1354     int thisvtx, testvtx;
1355     int numconnected, nummerged, numbest;
1356     int connected[MAX_MERGED];
1357     int convtx[MAX_VERTS], contc[MAX_VERTS], numconvtx;
1358     dtriangle_t merged[MAX_MERGED], best[MAX_MERGED];
1359     float connected_normal[3], merged_normal[3], vec[3];
1360     float bestdot, dot;
1361     float min_correlation = (float) (1 - error_factor
1362         * pow(0.1, MAX_LODS - level));
1363     int bestfound;
1364     int vertex_on_edge[4096];
1365     int numpasses = num_optimization_passes;
1366 
1367     if (!orignumtris) return 0;
1368 
1369     // Allocate the buffers we will be working with.
1370     tris = malloc(sizeof(*tris) * orignumtris);
1371 beginpass:
1372     numtris = orignumtris;
1373     for (i = 0; i < numtris; i++)
1374     {
1375         memcpy(&tris[i].tri, origtris + i, sizeof(*origtris));
1376         tris[i].group = 0;
1377     }
1378 
1379     // First thing we need to do is divide the triangles up into groups
1380     // based on their texcoord connections (we can't really optimize over
1381     // texcoord boundaries).
1382     highgroup = GroupTriangles(mo, tris, numtris);
1383     printf("  Number of groups: %i\n", highgroup);
1384 
1385     // Determine which vertices are on group edges.
1386     memset(vertex_on_edge, 0, sizeof(vertex_on_edge));
1387     for (i = 0; i < numtris; i++)
1388     {
1389         if (!IsValidTexTriangle(&tris[i].tri)) continue;
1390         // Test each edge.
1391         for (k = 0; k < 3; k++)
1392         {
1393             tc1 = tris[i].tri.index_st[k];
1394             tc2 = tris[i].tri.index_st[(k+1) % 3];
1395             if (!IsClockwiseTexTriangle(mo, &tris[i].tri))
1396             {
1397                 // Make it a clockwise edge.
1398                 m = tc1;
1399                 tc1 = tc2;
1400                 tc2 = m;
1401             }
1402             // Is there a neighbor that...?
1403             // - belongs in the same group
1404             // - shares the edge's texcoords
1405             // - has its third vertex on the other side of the edge
1406             // If there isn't, this is a group edge.
1407             for (bestfound = false, j = 0; j < numtris && !bestfound; j++)
1408             {
1409                 if (i == j || tris[i].group != tris[j].group) continue;
1410                 if (!IsValidTexTriangle(&tris[j].tri)) continue;
1411                 //clockwise = IsClockwiseTexTriangle(mo, &tris[j].tri);
1412                 for (found = 0, m = 0; m < 3; m++)
1413                 {
1414                     // Is this the edge we're looking for?
1415                     if ((tris[j].tri.index_st[m] == tc1 && tris[j].tri.index_st[(m+1)%3] == tc2)
1416                         || (tris[j].tri.index_st[m] == tc2 && tris[j].tri.index_st[(m+1)%3] == tc1))
1417                     {
1418                         // Try the third vertex.
1419                         if (!PointOnLineSide(mo->texCoords[tc1].s,
1420                             mo->texCoords[tc1].t,
1421                             mo->texCoords[tc2].s,
1422                             mo->texCoords[tc2].t,
1423                             mo->texCoords[tris[j].tri.index_st[(m+2)%3]].s,
1424                             mo->texCoords[tris[j].tri.index_st[(m+2)%3]].t))
1425                         {
1426                             bestfound = true;
1427                             break;
1428                         }
1429                     }
1430                 }
1431             }
1432             if (!bestfound)
1433             {
1434                 vertex_on_edge[tc1] = true;
1435                 vertex_on_edge[tc2] = true;
1436             }
1437         }
1438     }
1439 
1440     if (level == 1 && CheckOption("-tcmap"))
1441     {
1442         DrawTexCoordMap(mo, tris, numtris, vertex_on_edge);
1443     }
1444 
1445     // The actual optimization tries to remove individual vertices
1446     // by merging triangles together.
1447     memset(vused, 0, sizeof(vused));
1448     for (i = 0; i < numtris; i++)
1449     {
1450         if (!tris[i].group) continue; // This triangle has been removed.
1451         for (k = 0; k < 3; k++)
1452         {
1453             thisvtx = tris[i].tri.index_xyz[k];
1454 
1455             // We can't remove vertices that are at the edge of a group!
1456             // (edge preservation)
1457             if (vertex_on_edge[tris[i].tri.index_st[k]]) continue;
1458 
1459             // Check if this vertex has already been processed.
1460             if (vused[thisvtx]) continue;
1461             vused[thisvtx] = true; // Now it's used.
1462 
1463             // The Big Question: can this vertex be removed?
1464             // It can if...
1465             // - it has suitable neighbors (same group, real XYZ connection)
1466             // - the merging doesn't alter the surface normals too much
1467 
1468             // The difficult test is to find the suitable neighbors.
1469             // First find all the triangles connected to this vertex.
1470             for (numconnected = 0, j = 0; j < numtris; j++)
1471             {
1472                 if (tris[j].group != tris[i].group) continue;
1473                 if (!HaveSharedTexCoord(&tris[i].tri, &tris[j].tri)) continue;
1474                 for (m = 0; m < 3; m++)
1475                     if (tris[j].tri.index_xyz[m] == thisvtx)
1476                     {
1477                         connected[numconnected++] = j;
1478                         // Stop if there are too many triangles.
1479                         if (numconnected == MAX_MERGED) j = numtris;
1480                         break;
1481                     }
1482             }
1483             // If nothing is connected to this triangle, we can't process
1484             // it during this stage.
1485             if (!numconnected) continue;
1486 
1487             // Calculate the average normal vector for the connected
1488             // triangles.
1489             memset(connected_normal, 0, sizeof(connected_normal));
1490             for (j = 0; j < numconnected; j++)
1491             {
1492                 TriangleNormal(mo, &tris[connected[j]].tri, vec);
1493                 connected_normal[VX] += vec[VX];
1494                 connected_normal[VY] += vec[VY];
1495                 connected_normal[VZ] += vec[VZ];
1496             }
1497             Norm(connected_normal);
1498             bestdot = -1;
1499 
1500             // Figure out the connected vertices.
1501             for (numconvtx = j = 0; j < numconnected; j++)
1502             {
1503                 for (t = 0; t < 3; t++)
1504                 {
1505                     testvtx = tris[connected[j]].tri.index_xyz[t];
1506                     if (testvtx == thisvtx) continue;
1507                     // Check if this is already listed.
1508                     for (found = false, m = 0; m < j; m++)
1509                         if (convtx[m] == testvtx)
1510                         {
1511                             // Already listed, move on.
1512                             found = true;
1513                             break;
1514                         }
1515                     if (found) continue;
1516                     contc[numconvtx] = tris[connected[j]].tri.index_st[t];
1517                     convtx[numconvtx++] = testvtx;
1518                 }
1519             }
1520 
1521             for (bestfound = false, j = 0; j < numconvtx; j++)
1522             {
1523                 // Make a copy of the triangles.
1524                 for (m = 0; m < numconnected; m++)
1525                 {
1526                     memcpy(merged + m, &tris[connected[m]].tri,
1527                         sizeof(dtriangle_t));
1528                 }
1529                 // Try thisvtx => j.
1530                 nummerged = numconnected;
1531                 for (m = 0; m < nummerged; m++)
1532                 {
1533                     for (t = 0; t < 3; t++)
1534                         if (merged[m].index_xyz[t] == thisvtx)
1535                         {
1536                             merged[m].index_xyz[t] = convtx[j];
1537                             merged[m].index_st[t] = contc[j];
1538                         }
1539                 }
1540                 // Remove triangles that don't have 3 different vertices.
1541                 for (m = 0; m < nummerged; m++)
1542                 {
1543                     for (found = false, t = 0; t < 3; t++)
1544                         for (c = 0; c < 3; c++)
1545                             if (c != t && merged[m].index_xyz[t]
1546                                 == merged[m].index_xyz[c])
1547                             {
1548                                 found = true;
1549                             }
1550                     if (found)
1551                     {
1552                         // This triangle doesn't exist any longer.
1553                         memmove(merged + m, merged + m+1,
1554                             sizeof(dtriangle_t) * (nummerged - m - 1));
1555                         nummerged--;
1556                         m--;
1557                     }
1558                 }
1559                 if (!nummerged) continue; // Hmm?
1560 
1561                 //if (nummerged < numconvtx-4) continue;
1562 
1563                 // Now we must check the validity of the 'new' triangles.
1564                 // Calculate the average normal for the merged triangles.
1565                 memset(merged_normal, 0, sizeof(merged_normal));
1566                 for (m = 0; m < nummerged; m++)
1567                 {
1568                     TriangleNormal(mo, merged + m, vec);
1569                     merged_normal[VX] += vec[VX];
1570                     merged_normal[VY] += vec[VY];
1571                     merged_normal[VZ] += vec[VZ];
1572                 }
1573                 Norm(merged_normal);
1574 
1575                 // Calculate the dot product of the average normals.
1576                 for (dot = 0, m = 0; m < 3; m++)
1577                     dot += merged_normal[m] * connected_normal[m];
1578                 if (dot > bestdot && dot > min_correlation)
1579                 {
1580                     bestfound = true;
1581                     bestdot = dot;
1582                     numbest = nummerged;
1583                     memcpy(best, merged, sizeof(dtriangle_t) * nummerged);
1584                 }
1585             }
1586             if (!bestfound || numbest >= numconnected)
1587                 continue; // Not much point in continuing...
1588 
1589             // Our best choice is now in the 'best' array.
1590             // Replace the actual connected triangles with it.
1591             for (j = 0; j < numconnected; j++)
1592             {
1593                 if (j < numbest)
1594                 {
1595                     memcpy(&tris[connected[j]].tri, &best[j],
1596                         sizeof(dtriangle_t));
1597                 }
1598                 else
1599                 {
1600                     // This triangle is no longer needed.
1601                     tris[connected[j]].group = 0;
1602                 }
1603             }
1604         }
1605     }
1606 
1607     // We've done as much as we can...
1608     // Remove all triangles that are in group zero.
1609     for (m = i = 0; i < numtris; i++)
1610     {
1611         if (!tris[i].group) continue;
1612         memcpy(&origtris[m++], &tris[i].tri, sizeof(tris[i].tri));
1613     }
1614 
1615     // Should we start another pass?
1616     if (--numpasses > 0 && m > 0)
1617     {
1618         orignumtris = m;
1619         goto beginpass;
1620     }
1621     free(tris);
1622     return m;
1623 }
1624 
1625 //===========================================================================
1626 // BuildLODs
1627 //===========================================================================
BuildLODs(model_t * mo)1628 void BuildLODs(model_t *mo)
1629 {
1630     int lod, numtris;
1631 
1632     printf("Building detail levels.\n");
1633     mo->modified = true;
1634 
1635     if (!mo->lodinfo[0].numTriangles) return; // No triangles at all?!
1636 
1637     // We'll try to build as many LOD levels as we can.
1638     for (lod = 1; lod < MAX_LODS; lod++)
1639     {
1640         printf("(Level %i)\n", lod);
1641 
1642         // We'll start working with level zero data.
1643         numtris = mo->lodinfo[0].numTriangles;
1644         memcpy(triangles, mo->lods[0].triangles,
1645             sizeof(dtriangle_t) * numtris);
1646         OptimizeTexCoords(mo, triangles, numtris); // Weld texcoords.
1647 
1648         // Take away as many triangles as is suitable for this level.
1649         numtris = OptimizeMesh(mo, triangles, numtris, lod);
1650 
1651         // Copy them over to the model's LOD data.
1652         mo->lods[lod].triangles = realloc(mo->lods[lod].triangles,
1653             sizeof(dmd_triangle_t) * numtris);
1654         memcpy(mo->lods[lod].triangles, triangles,
1655             sizeof(dmd_triangle_t) * numtris);
1656         mo->lodinfo[lod].numTriangles = numtris;
1657 
1658         // Print information about our success.
1659         printf("  Number of triangles: %-3i (%.2f%% decrease from level zero)\n",
1660             numtris, (1 - numtris/(float)mo->lodinfo[0].numTriangles)*100);
1661 
1662     }
1663     // Target reduction levels (minimum): 10%, 30%, 50%
1664     // See if we reached enough benefit. We can drop levels starting from
1665     // the last one.
1666 
1667 
1668     // Update the model's number of detail levels.
1669     mo->info.numLODs = lod;
1670 
1671     // Rebuild GL commands, too.
1672     BuildGlCmds(mo);
1673 
1674     // Now that we have detail information, save as DMD.
1675     if (mo->header.magic != DMD_MAGIC)
1676     {
1677         printf("Detail levels require DMD, changing...\n");
1678         ModelSetSaveFormat(mo, DMD_MAGIC);
1679     }
1680 }
1681 
1682 //===========================================================================
1683 // ModelRenormalize
1684 //  According to level zero.
1685 //===========================================================================
ModelRenormalize(model_t * mo)1686 void ModelRenormalize(model_t *mo)
1687 {
1688     int tris = mo->lodinfo[0].numTriangles;
1689     int verts = mo->info.numVertices;
1690     int i, k, j, n, cnt;
1691     vector_t *normals, norm;
1692     vector_t *list;
1693     dmd_triangle_t *tri;
1694     dmd_frame_t *fr;
1695     //float maxprod, diff;
1696     //int idx;
1697 
1698     cprintf("Calculating new surface normals (  0%%).\b\b\b\b\b\b");
1699     mo->modified = true;
1700 
1701     // Triangle normals.
1702     list = malloc(sizeof(*list) * verts);
1703     normals = malloc(sizeof(*normals) * tris);
1704 
1705     // Calculate the normal for each vertex.
1706     for (i = 0; i < mo->info.numFrames; i++)
1707     {
1708         fr = GetFrame(mo, i);
1709 
1710         cprintf("%3i\b\b\b", mo->info.numFrames > 1? 100 * i
1711             / (mo->info.numFrames - 1) : 100);
1712 
1713         // Unpack vertices.
1714         for (k = 0; k < verts; k++)
1715             for (j = 0; j < 3; j++)
1716             {
1717                 list[k].pos[j] = fr->vertices[k].vertex[j] * fr->scale[j] +
1718                     fr->translate[j];
1719             }
1720 
1721         // First calculate the normal for each triangle.
1722         for (k = 0, tri = mo->lods[0].triangles; k < tris; k++, tri++)
1723         {
1724             CrossProd(list[tri->vertexIndices[0]].pos,
1725                 list[tri->vertexIndices[2]].pos,
1726                 list[tri->vertexIndices[1]].pos, normals[k].pos);
1727         }
1728 
1729         for (k = 0; k < verts; k++)
1730         {
1731             memset(&norm, 0, sizeof(norm));
1732             for (j = 0, cnt = 0, tri = mo->lods[0].triangles; j < tris;
1733                 j++, tri++)
1734             {
1735                 tri = mo->lods[0].triangles + j;
1736                 for (n = 0; n < 3; n++)
1737                     if (tri->vertexIndices[n] == k)
1738                     {
1739                         cnt++;
1740                         for (n = 0; n < 3; n++)
1741                             norm.pos[n] += normals[j].pos[n];
1742                         break;
1743                     }
1744             }
1745             if (!cnt) continue;
1746             // Calculate the average.
1747             for (n = 0; n < 3; n++) norm.pos[n] /= cnt;
1748             Norm(norm.pos);
1749             //fr->vertices[k].lightNormalIndex = GetNormalIndex(norm.pos);
1750             fr->vertices[k].normal = PackVector(norm.pos);
1751         }
1752     }
1753     free(list);
1754     free(normals);
1755     printf("\n");
1756 }
1757 
1758 //===========================================================================
1759 // ModelFlipNormals
1760 //  Flips all detail levels.
1761 //===========================================================================
ModelFlipNormals(model_t * mo)1762 void ModelFlipNormals(model_t *mo)
1763 {
1764     int lod, i;
1765     short v;
1766     dmd_triangle_t *tri;
1767 
1768     printf("Flipping all triangles.\n");
1769     mo->modified = true;
1770     for (lod = 0; lod < mo->info.numLODs; lod++)
1771     {
1772         for (i = 0, tri = mo->lods[lod].triangles;
1773             i < mo->lodinfo[lod].numTriangles; i++, tri++)
1774         {
1775             v = tri->vertexIndices[1];
1776             tri->vertexIndices[1] = tri->vertexIndices[2];
1777             tri->vertexIndices[2] = v;
1778 
1779             v = tri->textureIndices[1];
1780             tri->textureIndices[1] = tri->textureIndices[2];
1781             tri->textureIndices[2] = v;
1782         }
1783     }
1784     // Also automatically renormalize.
1785     ModelRenormalize(mo);
1786     BuildGlCmds(mo);
1787 }
1788 
1789 //===========================================================================
1790 // ReplaceVertex
1791 //===========================================================================
ReplaceVertex(model_t * mo,int to,int from)1792 void ReplaceVertex(model_t *mo, int to, int from)
1793 {
1794     int lod, j, c;
1795 
1796     mo->modified = true;
1797 
1798     // Change all references.
1799     for (lod = 0; lod < mo->info.numLODs; lod++)
1800         for (j = 0; j < mo->lodinfo[lod].numTriangles; j++)
1801             for (c = 0; c < 3; c++)
1802                 if (mo->lods[lod].triangles[j].vertexIndices[c] == from)
1803                     mo->lods[lod].triangles[j].vertexIndices[c] = to;
1804 }
1805 
1806 //===========================================================================
1807 // ModelWeldVertices
1808 //===========================================================================
ModelWeldVertices(model_t * mo)1809 void ModelWeldVertices(model_t *mo)
1810 {
1811     int k;
1812     int i, j;
1813 
1814     printf("Welding vertices...\n");
1815 
1816     if (mo->info.numFrames > 1)
1817     {
1818         // Welding is a bit more problematic in the case of multiple
1819         // frames, because a vertex can't be removed unless it's a
1820         // duplicate in all frames.
1821 
1822         printf("Model has multiple frames: welding not supported.\n");
1823         return;
1824     }
1825 
1826     for (k = 0; k < mo->info.numFrames; ++k)
1827     {
1828         dmd_frame_t *frame = &mo->frames[k];
1829 
1830         for (i = 0; i < mo->info.numVertices; ++i)
1831         {
1832             for (j = i + 1; j < mo->info.numVertices; ++j)
1833             {
1834                 dmd_vertex_t *a = &frame->vertices[i];
1835                 dmd_vertex_t *b = &frame->vertices[j];
1836 
1837                 if (a->vertex[0] == b->vertex[0] &&
1838                    a->vertex[1] == b->vertex[1] &&
1839                    a->vertex[2] == b->vertex[2])
1840                 {
1841                     printf("Duplicate found: %i and %i.\n", i, j);
1842 
1843                     // Remove the latter one.
1844                     ReplaceVertex(mo, j, i);
1845 
1846 /*                    if (j < mo->info.numVertices - 1)
1847                     {
1848                         memmove(&frame->vertices[j], &frame->vertices[j + 1],
1849                                 sizeof(dmd_vertex_t) *
1850                                 (mo->info.numVertices - j - 1));
1851                                 }*/
1852 
1853                     /*--mo->info.numVertices;*/
1854                 }
1855             }
1856         }
1857     }
1858     // Also automatically renormalize and update GL commands.
1859     ModelRenormalize(mo);
1860     BuildGlCmds(mo);
1861 }
1862 
1863 //===========================================================================
1864 // MoveTexCoord
1865 //===========================================================================
MoveTexCoord(model_t * mo,int to,int from)1866 void MoveTexCoord(model_t *mo, int to, int from)
1867 {
1868     int lod, j, c;
1869 
1870     mo->modified = true;
1871     memcpy(mo->texCoords + to, mo->texCoords + from,
1872         sizeof(dmd_textureCoordinate_t));
1873 
1874     // Change all references.
1875     for (lod = 0; lod < mo->info.numLODs; lod++)
1876         for (j = 0; j < mo->lodinfo[lod].numTriangles; j++)
1877             for (c = 0; c < 3; c++)
1878                 if (mo->lods[lod].triangles[j].textureIndices[c] == from)
1879                     mo->lods[lod].triangles[j].textureIndices[c] = to;
1880 }
1881 
1882 //===========================================================================
1883 // ModelWeldTexCoords
1884 //===========================================================================
ModelWeldTexCoords(model_t * mo)1885 void ModelWeldTexCoords(model_t *mo)
1886 {
1887     int oldnum = mo->info.numTexCoords;
1888     int i, k, high = 0;
1889     char refd[4096];
1890     int numtris = mo->lodinfo[0].numTriangles;
1891     int numcoords = mo->info.numTexCoords;
1892     int num_unrefd;
1893     dtriangle_t *tris = (dtriangle_t*) mo->lods[0].triangles;
1894 
1895     printf("Welding texture coordinates: ");
1896     OptimizeTexCoords(mo, tris, numtris);
1897 
1898     // First remove all unused texcoords.
1899     memset(refd, 0, sizeof(refd));
1900     for (i = 0; i < numtris; i++)
1901         for (k = 0; k < 3; k++)
1902             refd[tris[i].index_st[k]] = true;
1903 
1904     // Count how many unreferenced texcoords there are.
1905     for (num_unrefd = 0, i = 0; i < numcoords; i++)
1906         if (!refd[i]) num_unrefd++;
1907 
1908     if (num_unrefd)
1909     {
1910         printf("%i unused, ", num_unrefd);
1911         // Let's get rid of them.
1912         for (i = 0; i < numcoords; i++)
1913         {
1914             if (refd[i]) continue;
1915             // This is a free spot, move a texcoord down here.
1916             // Find the last used coord.
1917             for (k = numcoords - 1; k > i; k--) if (refd[k]) break;
1918             if (k == i) break; // Nothing more can be done.
1919             refd[i] = true;
1920             refd[k] = false;
1921             MoveTexCoord(mo, i, k);
1922         }
1923     }
1924 
1925     // Now let's find the highest index in use.
1926     for (i = 0; i < numcoords; i++)
1927         if (refd[i]) high = i;
1928     // Now we know what is the number of the last used index.
1929     mo->info.numTexCoords = high + 1;
1930     i = oldnum - mo->info.numTexCoords;
1931     if (!i)
1932         printf("no duplicates.\n");
1933     else
1934     {
1935         printf("%i removed.\n", i);
1936         mo->modified = true;
1937     }
1938 }
1939 
1940 //===========================================================================
1941 // ReadText
1942 //===========================================================================
ReadText(FILE * file,char * buf,int size)1943 void ReadText(FILE *file, char *buf, int size)
1944 {
1945     int i;
1946 
1947     memset(buf, 0, size);
1948     if (fgets(buf, size - 1, file))
1949     {
1950         i = strlen(buf) - 1;
1951         if (buf[i] == '\n') buf[i] = 0;
1952     }
1953 }
1954 
1955 //===========================================================================
1956 // ModelNewFrame
1957 //===========================================================================
ModelNewFrame(model_t * mo,const char * name)1958 void ModelNewFrame(model_t *mo, const char *name)
1959 {
1960     int idx = mo->info.numFrames;
1961     dmd_frame_t *fr;
1962 
1963     mo->modified = true;
1964     mo->frames = realloc(mo->frames, mo->info.frameSize *
1965         ++mo->info.numFrames);
1966     fr = GetFrame(mo, idx);
1967     memset(fr, 0, mo->info.frameSize);
1968     strncpy(fr->name, name, 15);
1969     fr->scale[VX] = fr->scale[VY] = fr->scale[VZ] = 1;
1970 }
1971 
1972 //===========================================================================
1973 // ModelCreateFrames
1974 //===========================================================================
ModelCreateFrames(model_t * mo,const char * listfile)1975 void ModelCreateFrames(model_t *mo, const char *listfile)
1976 {
1977     int cnt = 0;
1978     FILE *file;
1979     char line[100];
1980     const char *ptr;
1981 
1982     printf("Creating new frames according to \"%s\"...\n", listfile);
1983     mo->modified = true;
1984     if ((file = fopen(listfile, "rt")) == NULL)
1985     {
1986         DoError(MTERR_LISTFILE_NA);
1987         return;
1988     }
1989     for (;;)
1990     {
1991         ReadText(file, line, 100);
1992         ptr = SkipWhite(line);
1993         if (*ptr && *ptr != ';') // Not an empty line?
1994         {
1995             // Create a new frame.
1996             ModelNewFrame(mo, ptr);
1997             cnt++;
1998         }
1999         if (feof(file)) break;
2000     }
2001     fclose(file);
2002 
2003     printf("%i frames were created.\n", cnt);
2004 }
2005 
2006 //===========================================================================
2007 // ModelDelFrames
2008 //===========================================================================
ModelDelFrames(model_t * mo,int from,int to)2009 void ModelDelFrames(model_t *mo, int from, int to)
2010 {
2011     int num = mo->info.numFrames;
2012 
2013     mo->modified = true;
2014     if (from < 0 || from >= num || from > to
2015         || to < 0 || to >= num)
2016     {
2017         DoError(MTERR_INVALID_FRAME_NUMBER);
2018         return;
2019     }
2020     if (from != to)
2021         printf("Deleting frames from %i to %i.\n", from, to);
2022     else
2023         printf("Deleting frame %i.\n", from);
2024 
2025     memmove(GetFrame(mo, from), GetFrame(mo, to + 1),
2026         mo->info.frameSize * (num - to - 1));
2027     num -= to - from + 1;
2028     mo->frames = realloc(mo->frames, mo->info.frameSize * num);
2029     mo->info.numFrames = num;
2030 }
2031 
2032 //===========================================================================
2033 // ModelSetSkin
2034 //===========================================================================
ModelSetSkin(model_t * mo,int idx,const char * skinfile)2035 void ModelSetSkin(model_t *mo, int idx, const char *skinfile)
2036 {
2037     if (idx < 0) DoError(MTERR_INVALID_SKIN_NUMBER);
2038     printf("Setting skin %i to \"%s\".\n", idx, skinfile);
2039     mo->modified = true;
2040 
2041     // Are there enough skins allocated?
2042     if (idx >= mo->info.numSkins)
2043     {
2044         // Allocate more skins.
2045         mo->skins = realloc(mo->skins, sizeof(*mo->skins) * (idx + 1));
2046         // Clear the new skin names.
2047         memset(&mo->skins[mo->info.numSkins], 0,
2048             sizeof(*mo->skins) * (idx + 1 - mo->info.numSkins));
2049         mo->info.numSkins = idx + 1;
2050     }
2051     strcpy(mo->skins[idx].name, skinfile);
2052 }
2053 
2054 //===========================================================================
2055 // ModelSetDefaultSkin
2056 //===========================================================================
ModelSetDefaultSkin(model_t * mo,int idx)2057 void ModelSetDefaultSkin(model_t *mo, int idx)
2058 {
2059     char buf[256], *ptr;
2060 
2061     if (idx < 0) DoError(MTERR_INVALID_SKIN_NUMBER);
2062 
2063     strcpy(buf, mo->fileName);
2064     ptr = strrchr(buf, '\\');
2065     if (!ptr) ptr = strrchr(buf, '/');
2066     if (ptr) memmove(ptr + 1, buf, strlen(ptr));
2067     ptr = strrchr(buf, '.');
2068     if (ptr) strcpy(ptr, ".pcx"); else strcat(buf, ".pcx");
2069     buf[63] = 0; // Can't be longer.
2070     ModelSetSkin(mo, 0, buf);
2071 }
2072 
2073 //===========================================================================
2074 // ModelSetSkinSize
2075 //===========================================================================
ModelSetSkinSize(model_t * mo,int width,int height)2076 void ModelSetSkinSize(model_t *mo, int width, int height)
2077 {
2078     printf("Setting skin size to %i x %i.\n", width, height);
2079     mo->info.skinWidth = width;
2080     mo->info.skinHeight = height;
2081     mo->modified = true;
2082 }
2083 
2084 //===========================================================================
2085 // ModelDelSkin
2086 //===========================================================================
ModelDelSkin(model_t * mo,int idx)2087 void ModelDelSkin(model_t *mo, int idx)
2088 {
2089     if (idx < 0 || idx >= mo->info.numSkins)
2090     {
2091         DoError(MTERR_INVALID_SKIN_NUMBER);
2092         return;
2093     }
2094     printf("Deleting skin %i (\"%s\").\n", idx, mo->skins[idx].name);
2095 
2096     if (idx < mo->info.numSkins - 1)
2097     {
2098         memmove(&mo->skins[idx], &mo->skins[idx + 1],
2099             sizeof(*mo->skins) * (mo->info.numSkins - idx - 1));
2100     }
2101     mo->info.numSkins--;
2102     mo->skins = realloc(mo->skins, sizeof(*mo->skins)
2103         * mo->info.numSkins);
2104     mo->modified = true;
2105 }
2106 
2107 //===========================================================================
2108 // ModelSetFileName
2109 //===========================================================================
ModelSetFileName(model_t * mo,const char * fn)2110 void ModelSetFileName(model_t *mo, const char *fn)
2111 {
2112     strcpy(mo->fileName, fn);
2113     printf("Filename changed to: \"%s\"\n", mo->fileName);
2114     mo->modified = true;
2115 }
2116 
2117 //===========================================================================
2118 // ModelSetSaveFormat
2119 //===========================================================================
ModelSetSaveFormat(model_t * mo,int magic)2120 void ModelSetSaveFormat(model_t *mo, int magic)
2121 {
2122     char *newext, *ptr;
2123 
2124     mo->modified = true;
2125     mo->header.magic = magic;
2126     if (magic == MD2_MAGIC && mo->info.numLODs > 1)
2127     {
2128         printf("Saving as MD2, all levels except level %i will be discarded.\n",
2129             savelod);
2130     }
2131     // Change the extension of the file name.
2132     newext = (magic == MD2_MAGIC? ".md2" : ".dmd");
2133     ptr = strrchr(mo->fileName, '.');
2134     if (!ptr) // What?
2135         strcat(mo->fileName, newext);
2136     else
2137         strcpy(ptr, newext);
2138 
2139     printf("Filename set to: \"%s\"\n", mo->fileName);
2140 }
2141 
2142 //===========================================================================
2143 // ModelPrintInfo
2144 //===========================================================================
ModelPrintInfo(model_t * mo)2145 void ModelPrintInfo(model_t *mo)
2146 {
2147     int dmd = (mo->header.magic == DMD_MAGIC);
2148     dmd_info_t *inf = &mo->info;
2149     int i, k, frame_index, num_cols, per_col;
2150 
2151     printf("--- Information about %s:\n", mo->fileName);
2152     printf("Format: %s\n", !dmd? "MD2 (Quake II)"
2153         : "DMD (Detailed/Doomsday Model)");
2154     printf("Version: %i\n", mo->header.version);
2155     printf("%i vertices, %i texcoords, %i frames, %i level%s.\n",
2156         inf->numVertices, inf->numTexCoords, inf->numFrames,
2157         inf->numLODs, inf->numLODs != 1? "s" : "");
2158     for (i = 0; i < inf->numLODs; i++)
2159     {
2160         printf("Level %i: %i triangles, %i GL commands", i,
2161             mo->lodinfo[i].numTriangles, mo->lodinfo[i].numGlCommands);
2162         if (i && mo->lodinfo[0].numTriangles)
2163         {
2164             printf(" (%.2f%% reduction).\n",
2165                 100 - mo->lodinfo[i].numTriangles
2166                 / (float) mo->lodinfo[0].numTriangles*100);
2167         }
2168         else
2169         {
2170             printf(".\n");
2171         }
2172     }
2173     printf("Frames are %i bytes long.\n", dmd? DMD_FRAMESIZE(inf->numVertices)
2174         : MD2_FRAMESIZE(inf->numVertices));
2175     printf("Offsets in file: skin=%i txc=%i fr=%i",
2176         inf->offsetSkins, inf->offsetTexCoords, inf->offsetFrames);
2177     if (dmd)
2178     {
2179         printf(" lodinfo=%i", inf->offsetLODs);
2180     }
2181     printf(" end=%i\n", inf->offsetEnd);
2182     for (i = 0; i < inf->numLODs; i++)
2183     {
2184         printf("Level %i offsets: tri=%i gl=%i\n", i,
2185             mo->lodinfo[i].offsetTriangles,
2186             mo->lodinfo[i].offsetGlCommands);
2187     }
2188     // Print the frame list in three columns.
2189     printf("Frame list:\n");
2190     num_cols = 3;
2191     per_col = (inf->numFrames+2)/num_cols;
2192     for (i = 0; i < per_col; i++)
2193     {
2194         for (k = 0; k < num_cols; k++)
2195         {
2196             frame_index = i + k*per_col;
2197             if (frame_index >= inf->numFrames) break;
2198             printf(" %3i: %-16s", frame_index,
2199                 GetFrame(mo, frame_index)->name);
2200         }
2201         printf("\n");
2202     }
2203     printf("%i skin%s of size %ix%i:\n", inf->numSkins,
2204         inf->numSkins != 1? "s" : "", inf->skinWidth, inf->skinHeight);
2205     for (i = 0; i < inf->numSkins; i++)
2206         printf("  %i: %s\n", i, mo->skins[i].name);
2207 }
2208 
2209 //===========================================================================
2210 // main
2211 //===========================================================================
main(int argc,char ** argv)2212 int main(int argc, char **argv)
2213 {
2214     int         i, from, to, skin_num;
2215     const char  *skin_file, *ptr, *opt;
2216     model_t     model;
2217     boolean     nofiles = true;
2218 
2219     PrintBanner();
2220 
2221     // Init cmdline handling.
2222     myargc = argc;
2223     myargv = argv;
2224     argpos = 0;
2225 
2226     // What are we supposed to do?
2227     if (argc == 1)
2228     {
2229         PrintUsage();
2230         return 0;
2231     }
2232 
2233     // Scan for all file names.
2234     for (i = 1; i < argc; i++)
2235     {
2236         if (!stricmp(argv[i], "-skin")
2237             || !stricmp(argv[i], "-skinsize")
2238             || !stricmp(argv[i], "-delframes"))
2239         {
2240             i += 2;
2241             continue;
2242         }
2243         if (!stricmp(argv[i], "-delskin")
2244             || !stricmp(argv[i], "-del")
2245             || !stricmp(argv[i], "-create")
2246             || !stricmp(argv[i], "-s")
2247             || !stricmp(argv[i], "-savelod")
2248             || !stricmp(argv[i], "-ef")
2249             || !stricmp(argv[i], "-op")
2250             || !stricmp(argv[i], "-fn"))
2251 
2252         {
2253             i++;
2254             continue;
2255         }
2256         if (argv[i][0] != '-') // Not an option?
2257         {
2258             // Process this.
2259             if (CheckOption("-create"))
2260             {
2261                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2262                 ModelNew(&model, argv[i]);
2263                 ModelCreateFrames(&model, ptr);
2264             }
2265             else if (!ModelOpen(&model, argv[i])) continue; // No success.
2266             nofiles = false;
2267             if (CheckOption("-del"))
2268             {
2269                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2270                 from = to = strtoul(ptr, 0, 0);
2271                 ModelDelFrames(&model, from, to);
2272             }
2273             if (CheckOption("-delframes"))
2274             {
2275                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2276                 if (!(opt = NextOption())) DoError(MTERR_INVALID_OPTION);
2277                 if (!stricmp(ptr, "from"))
2278                 {
2279                     from = strtoul(opt, 0, 0);
2280                     to = model.info.numFrames - 1;
2281                 }
2282                 else if (!stricmp(ptr, "to"))
2283                 {
2284                     from = 0;
2285                     to = strtoul(opt, 0, 0);
2286                 }
2287                 else
2288                 {
2289                     from = strtoul(ptr, 0, 0);
2290                     to = strtoul(opt, 0, 0);
2291                 }
2292                 ModelDelFrames(&model, from, to);
2293             }
2294             if (CheckOption("-weld")) ModelWeldVertices(&model);
2295             if (CheckOption("-weldtc")) ModelWeldTexCoords(&model);
2296             if (CheckOption("-flip")) ModelFlipNormals(&model);
2297             if (CheckOption("-renorm")) ModelRenormalize(&model);
2298             if (CheckOption("-skin"))
2299             {
2300                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2301                 skin_num = strtoul(ptr, 0, 0);
2302                 if (!(skin_file = NextOption())) DoError(MTERR_INVALID_OPTION);
2303                 ModelSetSkin(&model, skin_num, skin_file);
2304             }
2305             if (CheckOption("-s"))
2306             {
2307                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2308                 ModelSetSkin(&model, 0, ptr);
2309             }
2310             if (CheckOption("-dsk"))
2311             {
2312                 ModelSetDefaultSkin(&model, 0);
2313             }
2314             if (CheckOption("-skinsize"))
2315             {
2316                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2317                 if (!(opt = NextOption())) DoError(MTERR_INVALID_OPTION);
2318                 ModelSetSkinSize(&model, strtoul(ptr, 0, 0),
2319                     strtoul(opt, 0, 0));
2320             }
2321             if (CheckOption("-delskin"))
2322             {
2323                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2324                 skin_num = strtoul(ptr, 0, 0);
2325                 ModelDelSkin(&model, skin_num);
2326             }
2327             if (CheckOption("-ef"))
2328             {
2329                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2330                 error_factor = (float) strtod(ptr, 0);
2331                 printf("Using optimization error factor %.3f.\n", error_factor);
2332             }
2333             if (CheckOption("-op"))
2334             {
2335                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2336                 num_optimization_passes = strtoul(ptr, 0, 0);
2337                 printf("Using %i mesh optimization passes.\n",
2338                     num_optimization_passes);
2339             }
2340             if (CheckOption("-fn"))
2341             {
2342                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2343                 ModelSetFileName(&model, ptr);
2344             }
2345             if (CheckOption("-lod")) BuildLODs(&model);
2346             if (CheckOption("-gl")) BuildGlCmds(&model);
2347             if (CheckOption("-info")) ModelPrintInfo(&model);
2348             if (CheckOption("-savelod"))
2349             {
2350                 if (!(ptr = NextOption())) DoError(MTERR_INVALID_OPTION);
2351                 savelod = strtoul(ptr, 0, 0);
2352             }
2353             if (CheckOption("-dmd")) ModelSetSaveFormat(&model, DMD_MAGIC);
2354             if (CheckOption("-md2")) ModelSetSaveFormat(&model, MD2_MAGIC);
2355             // We're done, close the model.
2356             ModelClose(&model);
2357         }
2358     }
2359     if (nofiles) DoError(MTERR_NO_FILES);
2360     return 0;
2361 }
2362