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