Merge remote branch 'origin/7.8'
[mesa.git] / progs / objviewer / glm.c
1 /*
2 * GLM library. Wavefront .obj file format reader/writer/manipulator.
3 *
4 * Written by Nate Robins, 1997.
5 * email: ndr@pobox.com
6 * www: http://www.pobox.com/~ndr
7 */
8
9 /* includes */
10 #include <math.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <assert.h>
15 #include "glm.h"
16 #include "readtex.h"
17
18
19 typedef unsigned char boolean;
20 #define TRUE 1
21 #define FALSE 0
22
23
24 /* Some <math.h> files do not define M_PI... */
25 #ifndef M_PI
26 #define M_PI 3.14159265358979323846
27 #endif
28
29 /* defines */
30 #define T(x) model->triangles[(x)]
31
32
33 /* enums */
34 enum { X, Y, Z, W }; /* elements of a vertex */
35
36
37 /* typedefs */
38
39 /* _GLMnode: general purpose node
40 */
41 typedef struct _GLMnode {
42 uint index;
43 boolean averaged;
44 struct _GLMnode* next;
45 } GLMnode;
46
47 /* strdup is actually not a standard ANSI C or POSIX routine
48 so implement a private one. OpenVMS does not have a strdup; Linux's
49 standard libc doesn't declare strdup by default (unless BSD or SVID
50 interfaces are requested). */
51 static char *
52 stralloc(const char *string)
53 {
54 char *copy;
55
56 copy = malloc(strlen(string) + 1);
57 if (copy == NULL)
58 return NULL;
59 strcpy(copy, string);
60 return copy;
61 }
62
63 /* private functions */
64
65 /* _glmMax: returns the maximum of two floats */
66 static float
67 _glmMax(float a, float b)
68 {
69 if (a > b)
70 return a;
71 return b;
72 }
73
74 /* _glmAbs: returns the absolute value of a float */
75 static float
76 _glmAbs(float f)
77 {
78 if (f < 0)
79 return -f;
80 return f;
81 }
82
83 /* _glmDot: compute the dot product of two vectors
84 *
85 * u - array of 3 floats (float u[3])
86 * v - array of 3 floats (float v[3])
87 */
88 static float
89 _glmDot(float* u, float* v)
90 {
91 assert(u);
92 assert(v);
93
94 /* compute the dot product */
95 return u[X] * v[X] + u[Y] * v[Y] + u[Z] * v[Z];
96 }
97
98 /* _glmCross: compute the cross product of two vectors
99 *
100 * u - array of 3 floats (float u[3])
101 * v - array of 3 floats (float v[3])
102 * n - array of 3 floats (float n[3]) to return the cross product in
103 */
104 static void
105 _glmCross(float* u, float* v, float* n)
106 {
107 assert(u);
108 assert(v);
109 assert(n);
110
111 /* compute the cross product (u x v for right-handed [ccw]) */
112 n[X] = u[Y] * v[Z] - u[Z] * v[Y];
113 n[Y] = u[Z] * v[X] - u[X] * v[Z];
114 n[Z] = u[X] * v[Y] - u[Y] * v[X];
115 }
116
117 /* _glmNormalize: normalize a vector
118 *
119 * n - array of 3 floats (float n[3]) to be normalized
120 */
121 static void
122 _glmNormalize(float* n)
123 {
124 float l;
125
126 assert(n);
127
128 /* normalize */
129 l = (float)sqrt(n[X] * n[X] + n[Y] * n[Y] + n[Z] * n[Z]);
130 n[0] /= l;
131 n[1] /= l;
132 n[2] /= l;
133 }
134
135 /* _glmEqual: compares two vectors and returns TRUE if they are
136 * equal (within a certain threshold) or FALSE if not. An epsilon
137 * that works fairly well is 0.000001.
138 *
139 * u - array of 3 floats (float u[3])
140 * v - array of 3 floats (float v[3])
141 */
142 static boolean
143 _glmEqual(float* u, float* v, float epsilon)
144 {
145 if (_glmAbs(u[0] - v[0]) < epsilon &&
146 _glmAbs(u[1] - v[1]) < epsilon &&
147 _glmAbs(u[2] - v[2]) < epsilon)
148 {
149 return TRUE;
150 }
151 return FALSE;
152 }
153
154 /* _glmWeldVectors: eliminate (weld) vectors that are within an
155 * epsilon of each other.
156 *
157 * vectors - array of float[3]'s to be welded
158 * numvectors - number of float[3]'s in vectors
159 * epsilon - maximum difference between vectors
160 *
161 */
162 static float*
163 _glmWeldVectors(float* vectors, uint* numvectors, float epsilon)
164 {
165 float* copies;
166 uint copied;
167 uint i, j;
168
169 copies = (float*)malloc(sizeof(float) * 3 * (*numvectors + 1));
170 memcpy(copies, vectors, (sizeof(float) * 3 * (*numvectors + 1)));
171
172 copied = 1;
173 for (i = 1; i <= *numvectors; i++) {
174 for (j = 1; j <= copied; j++) {
175 if (_glmEqual(&vectors[3 * i], &copies[3 * j], epsilon)) {
176 goto duplicate;
177 }
178 }
179
180 /* must not be any duplicates -- add to the copies array */
181 copies[3 * copied + 0] = vectors[3 * i + 0];
182 copies[3 * copied + 1] = vectors[3 * i + 1];
183 copies[3 * copied + 2] = vectors[3 * i + 2];
184 j = copied; /* pass this along for below */
185 copied++;
186
187 duplicate:
188 /* set the first component of this vector to point at the correct
189 index into the new copies array */
190 vectors[3 * i + 0] = (float)j;
191 }
192
193 *numvectors = copied-1;
194 return copies;
195 }
196
197 /* _glmFindGroup: Find a group in the model
198 */
199 static GLMgroup*
200 _glmFindGroup(GLMmodel* model, char* name)
201 {
202 GLMgroup* group;
203
204 assert(model);
205
206 group = model->groups;
207 while(group) {
208 if (!strcmp(name, group->name))
209 break;
210 group = group->next;
211 }
212
213 return group;
214 }
215
216 /* _glmAddGroup: Add a group to the model
217 */
218 static GLMgroup*
219 _glmAddGroup(GLMmodel* model, char* name)
220 {
221 GLMgroup* group;
222
223 group = _glmFindGroup(model, name);
224 if (!group) {
225 group = (GLMgroup*)malloc(sizeof(GLMgroup));
226 group->name = stralloc(name);
227 group->material = 0;
228 group->numtriangles = 0;
229 group->triangles = NULL;
230 group->next = model->groups;
231 model->groups = group;
232 model->numgroups++;
233 }
234
235 return group;
236 }
237
238 /* _glmFindGroup: Find a material in the model
239 */
240 static uint
241 _glmFindMaterial(GLMmodel* model, char* name)
242 {
243 uint i;
244
245 for (i = 0; i < model->nummaterials; i++) {
246 if (!strcmp(model->materials[i].name, name))
247 goto found;
248 }
249
250 /* didn't find the name, so set it as the default material */
251 printf("_glmFindMaterial(): can't find material \"%s\".\n", name);
252 i = 0;
253
254 found:
255 return i;
256 }
257
258
259 /* _glmDirName: return the directory given a path
260 *
261 * path - filesystem path
262 *
263 * The return value should be free'd.
264 */
265 static char*
266 _glmDirName(char* path)
267 {
268 char* dir;
269 char* s;
270
271 dir = stralloc(path);
272
273 s = strrchr(dir, '/');
274 if (s)
275 s[1] = '\0';
276 else
277 dir[0] = '\0';
278
279 return dir;
280 }
281
282
283 /* _glmReadMTL: read a wavefront material library file
284 *
285 * model - properly initialized GLMmodel structure
286 * name - name of the material library
287 */
288 static void
289 _glmReadMTL(GLMmodel* model, char* name)
290 {
291 FILE* file;
292 char* dir;
293 char* filename;
294 char buf[128], buf2[128];
295 uint nummaterials, i;
296 GLMmaterial *mat;
297
298 dir = _glmDirName(model->pathname);
299 filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(name) + 1));
300 strcpy(filename, dir);
301 strcat(filename, name);
302 free(dir);
303
304 /* open the file */
305 file = fopen(filename, "r");
306 if (!file) {
307 fprintf(stderr, "_glmReadMTL() failed: can't open material file \"%s\".\n",
308 filename);
309 exit(1);
310 }
311 free(filename);
312
313 /* count the number of materials in the file */
314 nummaterials = 1;
315 while(fscanf(file, "%s", buf) != EOF) {
316 switch(buf[0]) {
317 case '#': /* comment */
318 /* eat up rest of line */
319 fgets(buf, sizeof(buf), file);
320 break;
321 case 'n': /* newmtl */
322 fgets(buf, sizeof(buf), file);
323 nummaterials++;
324 sscanf(buf, "%s %s", buf, buf);
325 break;
326 default:
327 /* eat up rest of line */
328 fgets(buf, sizeof(buf), file);
329 break;
330 }
331 }
332
333 rewind(file);
334
335 /* allocate memory for the materials */
336 model->materials = (GLMmaterial*)calloc(nummaterials, sizeof(GLMmaterial));
337 model->nummaterials = nummaterials;
338
339 /* set the default material */
340 for (i = 0; i < nummaterials; i++) {
341 model->materials[i].name = NULL;
342 model->materials[i].shininess = 0;
343 model->materials[i].diffuse[0] = 0.8;
344 model->materials[i].diffuse[1] = 0.8;
345 model->materials[i].diffuse[2] = 0.8;
346 model->materials[i].diffuse[3] = 1.0;
347 model->materials[i].ambient[0] = 0.2;
348 model->materials[i].ambient[1] = 0.2;
349 model->materials[i].ambient[2] = 0.2;
350 model->materials[i].ambient[3] = 0.0;
351 model->materials[i].specular[0] = 0.0;
352 model->materials[i].specular[1] = 0.0;
353 model->materials[i].specular[2] = 0.0;
354 model->materials[i].specular[3] = 0.0;
355 }
356 model->materials[0].name = stralloc("default");
357
358 /* now, read in the data */
359 nummaterials = 0;
360
361 mat = &model->materials[nummaterials];
362
363 while(fscanf(file, "%s", buf) != EOF) {
364 switch(buf[0]) {
365 case '#': /* comment */
366 /* eat up rest of line */
367 fgets(buf, sizeof(buf), file);
368 break;
369 case 'n': /* newmtl */
370 fgets(buf, sizeof(buf), file);
371 sscanf(buf, "%s %s", buf, buf);
372 nummaterials++;
373 model->materials[nummaterials].name = stralloc(buf);
374 break;
375 case 'N':
376 fscanf(file, "%f", &model->materials[nummaterials].shininess);
377 /* wavefront shininess is from [0, 1000], so scale for OpenGL */
378 model->materials[nummaterials].shininess /= 1000.0;
379 model->materials[nummaterials].shininess *= 128.0;
380 mat = &model->materials[nummaterials];
381 break;
382 case 'K':
383 switch(buf[1]) {
384 case 'd':
385 fscanf(file, "%f %f %f",
386 &model->materials[nummaterials].diffuse[0],
387 &model->materials[nummaterials].diffuse[1],
388 &model->materials[nummaterials].diffuse[2]);
389 break;
390 case 's':
391 fscanf(file, "%f %f %f",
392 &model->materials[nummaterials].specular[0],
393 &model->materials[nummaterials].specular[1],
394 &model->materials[nummaterials].specular[2]);
395 break;
396 case 'a':
397 fscanf(file, "%f %f %f",
398 &model->materials[nummaterials].ambient[0],
399 &model->materials[nummaterials].ambient[1],
400 &model->materials[nummaterials].ambient[2]);
401 break;
402 default:
403 /* eat up rest of line */
404 fgets(buf, sizeof(buf), file);
405 break;
406 }
407 break;
408 case 'd': /* alpha? */
409 fscanf(file, "%f",
410 &model->materials[nummaterials].diffuse[3]);
411 break;
412 case 'm': /* texture map */
413 fscanf(file, "%s", buf2);
414 /*printf("map %s\n", buf2);*/
415 mat->map_kd = strdup(buf2);
416 break;
417
418 default:
419 /* eat up rest of line */
420 fgets(buf, sizeof(buf), file);
421 break;
422 }
423 }
424 fclose(file);
425 }
426
427
428 /* _glmWriteMTL: write a wavefront material library file
429 *
430 * model - properly initialized GLMmodel structure
431 * modelpath - pathname of the model being written
432 * mtllibname - name of the material library to be written
433 */
434 static void
435 _glmWriteMTL(GLMmodel* model, char* modelpath, char* mtllibname)
436 {
437 FILE* file;
438 char* dir;
439 char* filename;
440 GLMmaterial* material;
441 uint i;
442
443 dir = _glmDirName(modelpath);
444 filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(mtllibname)));
445 strcpy(filename, dir);
446 strcat(filename, mtllibname);
447 free(dir);
448
449 /* open the file */
450 file = fopen(filename, "w");
451 if (!file) {
452 fprintf(stderr, "_glmWriteMTL() failed: can't open file \"%s\".\n",
453 filename);
454 exit(1);
455 }
456 free(filename);
457
458 /* spit out a header */
459 fprintf(file, "# \n");
460 fprintf(file, "# Wavefront MTL generated by GLM library\n");
461 fprintf(file, "# \n");
462 fprintf(file, "# GLM library copyright (C) 1997 by Nate Robins\n");
463 fprintf(file, "# email: ndr@pobox.com\n");
464 fprintf(file, "# www: http://www.pobox.com/~ndr\n");
465 fprintf(file, "# \n\n");
466
467 for (i = 0; i < model->nummaterials; i++) {
468 material = &model->materials[i];
469 fprintf(file, "newmtl %s\n", material->name);
470 fprintf(file, "Ka %f %f %f\n",
471 material->ambient[0], material->ambient[1], material->ambient[2]);
472 fprintf(file, "Kd %f %f %f\n",
473 material->diffuse[0], material->diffuse[1], material->diffuse[2]);
474 fprintf(file, "Ks %f %f %f\n",
475 material->specular[0],material->specular[1],material->specular[2]);
476 fprintf(file, "Ns %f\n", material->shininess);
477 fprintf(file, "\n");
478 }
479 fclose(file);
480 }
481
482
483 /* _glmFirstPass: first pass at a Wavefront OBJ file that gets all the
484 * statistics of the model (such as #vertices, #normals, etc)
485 *
486 * model - properly initialized GLMmodel structure
487 * file - (fopen'd) file descriptor
488 */
489 static void
490 _glmFirstPass(GLMmodel* model, FILE* file)
491 {
492 uint numvertices; /* number of vertices in model */
493 uint numnormals; /* number of normals in model */
494 uint numtexcoords; /* number of texcoords in model */
495 uint numtriangles; /* number of triangles in model */
496 GLMgroup* group; /* current group */
497 unsigned v, n, t;
498 char buf[128];
499
500 /* make a default group */
501 group = _glmAddGroup(model, "default");
502
503 numvertices = numnormals = numtexcoords = numtriangles = 0;
504 while(fscanf(file, "%s", buf) != EOF) {
505 switch(buf[0]) {
506 case '#': /* comment */
507 /* eat up rest of line */
508 fgets(buf, sizeof(buf), file);
509 break;
510 case 'v': /* v, vn, vt */
511 switch(buf[1]) {
512 case '\0': /* vertex */
513 /* eat up rest of line */
514 fgets(buf, sizeof(buf), file);
515 numvertices++;
516 break;
517 case 'n': /* normal */
518 /* eat up rest of line */
519 fgets(buf, sizeof(buf), file);
520 numnormals++;
521 break;
522 case 't': /* texcoord */
523 /* eat up rest of line */
524 fgets(buf, sizeof(buf), file);
525 numtexcoords++;
526 break;
527 default:
528 printf("_glmFirstPass(): Unknown token \"%s\".\n", buf);
529 exit(1);
530 break;
531 }
532 break;
533 case 'm':
534 fgets(buf, sizeof(buf), file);
535 sscanf(buf, "%s %s", buf, buf);
536 model->mtllibname = stralloc(buf);
537 _glmReadMTL(model, buf);
538 break;
539 case 'u':
540 /* eat up rest of line */
541 fgets(buf, sizeof(buf), file);
542 break;
543 case 'g': /* group */
544 /* eat up rest of line */
545 fgets(buf, sizeof(buf), file);
546 sscanf(buf, "%s", buf);
547 group = _glmAddGroup(model, buf);
548 break;
549 case 'f': /* face */
550 v = n = t = 0;
551 fscanf(file, "%s", buf);
552 /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
553 if (strstr(buf, "//")) {
554 /* v//n */
555 sscanf(buf, "%u//%u", &v, &n);
556 fscanf(file, "%u//%u", &v, &n);
557 fscanf(file, "%u//%u", &v, &n);
558 numtriangles++;
559 group->numtriangles++;
560 while(fscanf(file, "%u//%u", &v, &n) > 0) {
561 numtriangles++;
562 group->numtriangles++;
563 }
564 } else if (sscanf(buf, "%u/%u/%u", &v, &t, &n) == 3) {
565 /* v/t/n */
566 fscanf(file, "%u/%u/%u", &v, &t, &n);
567 fscanf(file, "%u/%u/%u", &v, &t, &n);
568 numtriangles++;
569 group->numtriangles++;
570 while(fscanf(file, "%u/%u/%u", &v, &t, &n) > 0) {
571 numtriangles++;
572 group->numtriangles++;
573 }
574 } else if (sscanf(buf, "%u/%u", &v, &t) == 2) {
575 /* v/t */
576 fscanf(file, "%u/%u", &v, &t);
577 fscanf(file, "%u/%u", &v, &t);
578 numtriangles++;
579 group->numtriangles++;
580 while(fscanf(file, "%u/%u", &v, &t) > 0) {
581 numtriangles++;
582 group->numtriangles++;
583 }
584 } else {
585 /* v */
586 fscanf(file, "%u", &v);
587 fscanf(file, "%u", &v);
588 numtriangles++;
589 group->numtriangles++;
590 while(fscanf(file, "%u", &v) > 0) {
591 numtriangles++;
592 group->numtriangles++;
593 }
594 }
595 break;
596
597 default:
598 /* eat up rest of line */
599 fgets(buf, sizeof(buf), file);
600 break;
601 }
602 }
603
604 #if 0
605 /* announce the model statistics */
606 printf(" Vertices: %d\n", numvertices);
607 printf(" Normals: %d\n", numnormals);
608 printf(" Texcoords: %d\n", numtexcoords);
609 printf(" Triangles: %d\n", numtriangles);
610 printf(" Groups: %d\n", model->numgroups);
611 #endif
612
613 /* set the stats in the model structure */
614 model->numvertices = numvertices;
615 model->numnormals = numnormals;
616 model->numtexcoords = numtexcoords;
617 model->numtriangles = numtriangles;
618
619 /* allocate memory for the triangles in each group */
620 group = model->groups;
621 while(group) {
622 group->triangles = (uint*)malloc(sizeof(uint) * group->numtriangles);
623 group->numtriangles = 0;
624 group = group->next;
625 }
626 }
627
628 /* _glmSecondPass: second pass at a Wavefront OBJ file that gets all
629 * the data.
630 *
631 * model - properly initialized GLMmodel structure
632 * file - (fopen'd) file descriptor
633 */
634 static void
635 _glmSecondPass(GLMmodel* model, FILE* file)
636 {
637 uint numvertices; /* number of vertices in model */
638 uint numnormals; /* number of normals in model */
639 uint numtexcoords; /* number of texcoords in model */
640 uint numtriangles; /* number of triangles in model */
641 float* vertices; /* array of vertices */
642 float* normals; /* array of normals */
643 float* texcoords; /* array of texture coordinates */
644 GLMgroup* group; /* current group pointer */
645 uint material; /* current material */
646 uint v, n, t;
647 char buf[128];
648
649 /* set the pointer shortcuts */
650 vertices = model->vertices;
651 normals = model->normals;
652 texcoords = model->texcoords;
653 group = model->groups;
654
655 /* on the second pass through the file, read all the data into the
656 allocated arrays */
657 numvertices = numnormals = numtexcoords = 1;
658 numtriangles = 0;
659 material = 0;
660 while(fscanf(file, "%s", buf) != EOF) {
661 switch(buf[0]) {
662 case '#': /* comment */
663 /* eat up rest of line */
664 fgets(buf, sizeof(buf), file);
665 break;
666 case 'v': /* v, vn, vt */
667 switch(buf[1]) {
668 case '\0': /* vertex */
669 fscanf(file, "%f %f %f",
670 &vertices[3 * numvertices + X],
671 &vertices[3 * numvertices + Y],
672 &vertices[3 * numvertices + Z]);
673 numvertices++;
674 break;
675 case 'n': /* normal */
676 fscanf(file, "%f %f %f",
677 &normals[3 * numnormals + X],
678 &normals[3 * numnormals + Y],
679 &normals[3 * numnormals + Z]);
680 numnormals++;
681 break;
682 case 't': /* texcoord */
683 fscanf(file, "%f %f",
684 &texcoords[2 * numtexcoords + X],
685 &texcoords[2 * numtexcoords + Y]);
686 numtexcoords++;
687 break;
688 }
689 break;
690 case 'u':
691 fgets(buf, sizeof(buf), file);
692 sscanf(buf, "%s %s", buf, buf);
693 material = _glmFindMaterial(model, buf);
694 if (!group->material)
695 group->material = material;
696 /*printf("material %s = %u\n", buf, material);*/
697 break;
698 case 'g': /* group */
699 /* eat up rest of line */
700 fgets(buf, sizeof(buf), file);
701 sscanf(buf, "%s", buf);
702 group = _glmFindGroup(model, buf);
703 group->material = material;
704 /*printf("GROUP %s material %u\n", buf, material);*/
705 break;
706 case 'f': /* face */
707 v = n = t = 0;
708 fscanf(file, "%s", buf);
709 /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
710 if (strstr(buf, "//")) {
711 /* v//n */
712 sscanf(buf, "%u//%u", &v, &n);
713 T(numtriangles).vindices[0] = v;
714 T(numtriangles).nindices[0] = n;
715 fscanf(file, "%u//%u", &v, &n);
716 T(numtriangles).vindices[1] = v;
717 T(numtriangles).nindices[1] = n;
718 fscanf(file, "%u//%u", &v, &n);
719 T(numtriangles).vindices[2] = v;
720 T(numtriangles).nindices[2] = n;
721 group->triangles[group->numtriangles++] = numtriangles;
722 numtriangles++;
723 while(fscanf(file, "%u//%u", &v, &n) > 0) {
724 T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
725 T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
726 T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
727 T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
728 T(numtriangles).vindices[2] = v;
729 T(numtriangles).nindices[2] = n;
730 group->triangles[group->numtriangles++] = numtriangles;
731 numtriangles++;
732 }
733 } else if (sscanf(buf, "%u/%u/%u", &v, &t, &n) == 3) {
734 /* v/t/n */
735 T(numtriangles).vindices[0] = v;
736 T(numtriangles).tindices[0] = t;
737 T(numtriangles).nindices[0] = n;
738 fscanf(file, "%u/%u/%u", &v, &t, &n);
739 T(numtriangles).vindices[1] = v;
740 T(numtriangles).tindices[1] = t;
741 T(numtriangles).nindices[1] = n;
742 fscanf(file, "%u/%u/%u", &v, &t, &n);
743 T(numtriangles).vindices[2] = v;
744 T(numtriangles).tindices[2] = t;
745 T(numtriangles).nindices[2] = n;
746 group->triangles[group->numtriangles++] = numtriangles;
747 numtriangles++;
748 while(fscanf(file, "%u/%u/%u", &v, &t, &n) > 0) {
749 T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
750 T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
751 T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
752 T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
753 T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
754 T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
755 T(numtriangles).vindices[2] = v;
756 T(numtriangles).tindices[2] = t;
757 T(numtriangles).nindices[2] = n;
758 group->triangles[group->numtriangles++] = numtriangles;
759 numtriangles++;
760 }
761 } else if (sscanf(buf, "%u/%u", &v, &t) == 2) {
762 /* v/t */
763 T(numtriangles).vindices[0] = v;
764 T(numtriangles).tindices[0] = t;
765 fscanf(file, "%u/%u", &v, &t);
766 T(numtriangles).vindices[1] = v;
767 T(numtriangles).tindices[1] = t;
768 fscanf(file, "%u/%u", &v, &t);
769 T(numtriangles).vindices[2] = v;
770 T(numtriangles).tindices[2] = t;
771 group->triangles[group->numtriangles++] = numtriangles;
772 numtriangles++;
773 while(fscanf(file, "%u/%u", &v, &t) > 0) {
774 T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
775 T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
776 T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
777 T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
778 T(numtriangles).vindices[2] = v;
779 T(numtriangles).tindices[2] = t;
780 group->triangles[group->numtriangles++] = numtriangles;
781 numtriangles++;
782 }
783 } else {
784 /* v */
785 sscanf(buf, "%u", &v);
786 T(numtriangles).vindices[0] = v;
787 fscanf(file, "%u", &v);
788 T(numtriangles).vindices[1] = v;
789 fscanf(file, "%u", &v);
790 T(numtriangles).vindices[2] = v;
791 group->triangles[group->numtriangles++] = numtriangles;
792 numtriangles++;
793 while(fscanf(file, "%u", &v) > 0) {
794 T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
795 T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
796 T(numtriangles).vindices[2] = v;
797 group->triangles[group->numtriangles++] = numtriangles;
798 numtriangles++;
799 }
800 }
801 break;
802
803 default:
804 /* eat up rest of line */
805 fgets(buf, sizeof(buf), file);
806 break;
807 }
808 }
809
810 #if 0
811 /* announce the memory requirements */
812 printf(" Memory: %d bytes\n",
813 numvertices * 3*sizeof(float) +
814 numnormals * 3*sizeof(float) * (numnormals ? 1 : 0) +
815 numtexcoords * 3*sizeof(float) * (numtexcoords ? 1 : 0) +
816 numtriangles * sizeof(GLMtriangle));
817 #endif
818 }
819
820
821
822
823 /* public functions */
824
825 /* glmUnitize: "unitize" a model by translating it to the origin and
826 * scaling it to fit in a unit cube around the origin. Returns the
827 * scalefactor used.
828 *
829 * model - properly initialized GLMmodel structure
830 */
831 float
832 glmUnitize(GLMmodel* model)
833 {
834 uint i;
835 float maxx, minx, maxy, miny, maxz, minz;
836 float cx, cy, cz, w, h, d;
837 float scale;
838
839 assert(model);
840 assert(model->vertices);
841
842 /* get the max/mins */
843 maxx = minx = model->vertices[3 + X];
844 maxy = miny = model->vertices[3 + Y];
845 maxz = minz = model->vertices[3 + Z];
846 for (i = 1; i <= model->numvertices; i++) {
847 if (maxx < model->vertices[3 * i + X])
848 maxx = model->vertices[3 * i + X];
849 if (minx > model->vertices[3 * i + X])
850 minx = model->vertices[3 * i + X];
851
852 if (maxy < model->vertices[3 * i + Y])
853 maxy = model->vertices[3 * i + Y];
854 if (miny > model->vertices[3 * i + Y])
855 miny = model->vertices[3 * i + Y];
856
857 if (maxz < model->vertices[3 * i + Z])
858 maxz = model->vertices[3 * i + Z];
859 if (minz > model->vertices[3 * i + Z])
860 minz = model->vertices[3 * i + Z];
861 }
862
863 /* calculate model width, height, and depth */
864 w = _glmAbs(maxx) + _glmAbs(minx);
865 h = _glmAbs(maxy) + _glmAbs(miny);
866 d = _glmAbs(maxz) + _glmAbs(minz);
867
868 /* calculate center of the model */
869 cx = (maxx + minx) / 2.0;
870 cy = (maxy + miny) / 2.0;
871 cz = (maxz + minz) / 2.0;
872
873 /* calculate unitizing scale factor */
874 scale = 2.0 / _glmMax(_glmMax(w, h), d);
875
876 /* translate around center then scale */
877 for (i = 1; i <= model->numvertices; i++) {
878 model->vertices[3 * i + X] -= cx;
879 model->vertices[3 * i + Y] -= cy;
880 model->vertices[3 * i + Z] -= cz;
881 model->vertices[3 * i + X] *= scale;
882 model->vertices[3 * i + Y] *= scale;
883 model->vertices[3 * i + Z] *= scale;
884 }
885
886 return scale;
887 }
888
889 /* glmDimensions: Calculates the dimensions (width, height, depth) of
890 * a model.
891 *
892 * model - initialized GLMmodel structure
893 * dimensions - array of 3 floats (float dimensions[3])
894 */
895 void
896 glmDimensions(GLMmodel* model, float* dimensions)
897 {
898 uint i;
899 float maxx, minx, maxy, miny, maxz, minz;
900
901 assert(model);
902 assert(model->vertices);
903 assert(dimensions);
904
905 /* get the max/mins */
906 maxx = minx = model->vertices[3 + X];
907 maxy = miny = model->vertices[3 + Y];
908 maxz = minz = model->vertices[3 + Z];
909 for (i = 1; i <= model->numvertices; i++) {
910 if (maxx < model->vertices[3 * i + X])
911 maxx = model->vertices[3 * i + X];
912 if (minx > model->vertices[3 * i + X])
913 minx = model->vertices[3 * i + X];
914
915 if (maxy < model->vertices[3 * i + Y])
916 maxy = model->vertices[3 * i + Y];
917 if (miny > model->vertices[3 * i + Y])
918 miny = model->vertices[3 * i + Y];
919
920 if (maxz < model->vertices[3 * i + Z])
921 maxz = model->vertices[3 * i + Z];
922 if (minz > model->vertices[3 * i + Z])
923 minz = model->vertices[3 * i + Z];
924 }
925
926 /* calculate model width, height, and depth */
927 dimensions[X] = _glmAbs(maxx) + _glmAbs(minx);
928 dimensions[Y] = _glmAbs(maxy) + _glmAbs(miny);
929 dimensions[Z] = _glmAbs(maxz) + _glmAbs(minz);
930 }
931
932 /* glmScale: Scales a model by a given amount.
933 *
934 * model - properly initialized GLMmodel structure
935 * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
936 */
937 void
938 glmScale(GLMmodel* model, float scale)
939 {
940 uint i;
941
942 for (i = 1; i <= model->numvertices; i++) {
943 model->vertices[3 * i + X] *= scale;
944 model->vertices[3 * i + Y] *= scale;
945 model->vertices[3 * i + Z] *= scale;
946 }
947 }
948
949 /* glmReverseWinding: Reverse the polygon winding for all polygons in
950 * this model. Default winding is counter-clockwise. Also changes
951 * the direction of the normals.
952 *
953 * model - properly initialized GLMmodel structure
954 */
955 void
956 glmReverseWinding(GLMmodel* model)
957 {
958 uint i, swap;
959
960 assert(model);
961
962 for (i = 0; i < model->numtriangles; i++) {
963 swap = T(i).vindices[0];
964 T(i).vindices[0] = T(i).vindices[2];
965 T(i).vindices[2] = swap;
966
967 if (model->numnormals) {
968 swap = T(i).nindices[0];
969 T(i).nindices[0] = T(i).nindices[2];
970 T(i).nindices[2] = swap;
971 }
972
973 if (model->numtexcoords) {
974 swap = T(i).tindices[0];
975 T(i).tindices[0] = T(i).tindices[2];
976 T(i).tindices[2] = swap;
977 }
978 }
979
980 /* reverse facet normals */
981 for (i = 1; i <= model->numfacetnorms; i++) {
982 model->facetnorms[3 * i + X] = -model->facetnorms[3 * i + X];
983 model->facetnorms[3 * i + Y] = -model->facetnorms[3 * i + Y];
984 model->facetnorms[3 * i + Z] = -model->facetnorms[3 * i + Z];
985 }
986
987 /* reverse vertex normals */
988 for (i = 1; i <= model->numnormals; i++) {
989 model->normals[3 * i + X] = -model->normals[3 * i + X];
990 model->normals[3 * i + Y] = -model->normals[3 * i + Y];
991 model->normals[3 * i + Z] = -model->normals[3 * i + Z];
992 }
993 }
994
995 /* glmFacetNormals: Generates facet normals for a model (by taking the
996 * cross product of the two vectors derived from the sides of each
997 * triangle). Assumes a counter-clockwise winding.
998 *
999 * model - initialized GLMmodel structure
1000 */
1001 void
1002 glmFacetNormals(GLMmodel* model)
1003 {
1004 uint i;
1005 float u[3];
1006 float v[3];
1007
1008 assert(model);
1009 assert(model->vertices);
1010
1011 /* clobber any old facetnormals */
1012 if (model->facetnorms)
1013 free(model->facetnorms);
1014
1015 /* allocate memory for the new facet normals */
1016 model->numfacetnorms = model->numtriangles;
1017 model->facetnorms = (float*)malloc(sizeof(float) *
1018 3 * (model->numfacetnorms + 1));
1019
1020 for (i = 0; i < model->numtriangles; i++) {
1021 model->triangles[i].findex = i+1;
1022
1023 u[X] = model->vertices[3 * T(i).vindices[1] + X] -
1024 model->vertices[3 * T(i).vindices[0] + X];
1025 u[Y] = model->vertices[3 * T(i).vindices[1] + Y] -
1026 model->vertices[3 * T(i).vindices[0] + Y];
1027 u[Z] = model->vertices[3 * T(i).vindices[1] + Z] -
1028 model->vertices[3 * T(i).vindices[0] + Z];
1029
1030 v[X] = model->vertices[3 * T(i).vindices[2] + X] -
1031 model->vertices[3 * T(i).vindices[0] + X];
1032 v[Y] = model->vertices[3 * T(i).vindices[2] + Y] -
1033 model->vertices[3 * T(i).vindices[0] + Y];
1034 v[Z] = model->vertices[3 * T(i).vindices[2] + Z] -
1035 model->vertices[3 * T(i).vindices[0] + Z];
1036
1037 _glmCross(u, v, &model->facetnorms[3 * (i+1)]);
1038 _glmNormalize(&model->facetnorms[3 * (i+1)]);
1039 }
1040 }
1041
1042 /* glmVertexNormals: Generates smooth vertex normals for a model.
1043 * First builds a list of all the triangles each vertex is in. Then
1044 * loops through each vertex in the list averaging all the facet
1045 * normals of the triangles each vertex is in. Finally, sets the
1046 * normal index in the triangle for the vertex to the generated smooth
1047 * normal. If the dot product of a facet normal and the facet normal
1048 * associated with the first triangle in the list of triangles the
1049 * current vertex is in is greater than the cosine of the angle
1050 * parameter to the function, that facet normal is not added into the
1051 * average normal calculation and the corresponding vertex is given
1052 * the facet normal. This tends to preserve hard edges. The angle to
1053 * use depends on the model, but 90 degrees is usually a good start.
1054 *
1055 * model - initialized GLMmodel structure
1056 * angle - maximum angle (in degrees) to smooth across
1057 */
1058 void
1059 glmVertexNormals(GLMmodel* model, float angle)
1060 {
1061 GLMnode* node;
1062 GLMnode* tail;
1063 GLMnode** members;
1064 float* normals;
1065 uint numnormals;
1066 float average[3];
1067 float dot, cos_angle;
1068 uint i, avg;
1069
1070 assert(model);
1071 assert(model->facetnorms);
1072
1073 /* calculate the cosine of the angle (in degrees) */
1074 cos_angle = cos(angle * M_PI / 180.0);
1075
1076 /* nuke any previous normals */
1077 if (model->normals)
1078 free(model->normals);
1079
1080 /* allocate space for new normals */
1081 model->numnormals = model->numtriangles * 3; /* 3 normals per triangle */
1082 model->normals = (float*)malloc(sizeof(float)* 3* (model->numnormals+1));
1083
1084 /* allocate a structure that will hold a linked list of triangle
1085 indices for each vertex */
1086 members = (GLMnode**)malloc(sizeof(GLMnode*) * (model->numvertices + 1));
1087 for (i = 1; i <= model->numvertices; i++)
1088 members[i] = NULL;
1089
1090 /* for every triangle, create a node for each vertex in it */
1091 for (i = 0; i < model->numtriangles; i++) {
1092 node = (GLMnode*)malloc(sizeof(GLMnode));
1093 node->index = i;
1094 node->next = members[T(i).vindices[0]];
1095 members[T(i).vindices[0]] = node;
1096
1097 node = (GLMnode*)malloc(sizeof(GLMnode));
1098 node->index = i;
1099 node->next = members[T(i).vindices[1]];
1100 members[T(i).vindices[1]] = node;
1101
1102 node = (GLMnode*)malloc(sizeof(GLMnode));
1103 node->index = i;
1104 node->next = members[T(i).vindices[2]];
1105 members[T(i).vindices[2]] = node;
1106 }
1107
1108 /* calculate the average normal for each vertex */
1109 numnormals = 1;
1110 for (i = 1; i <= model->numvertices; i++) {
1111 /* calculate an average normal for this vertex by averaging the
1112 facet normal of every triangle this vertex is in */
1113 node = members[i];
1114 if (!node)
1115 fprintf(stderr, "glmVertexNormals(): vertex w/o a triangle\n");
1116 average[0] = 0.0; average[1] = 0.0; average[2] = 0.0;
1117 avg = 0;
1118 while (node) {
1119 /* only average if the dot product of the angle between the two
1120 facet normals is greater than the cosine of the threshold
1121 angle -- or, said another way, the angle between the two
1122 facet normals is less than (or equal to) the threshold angle */
1123 dot = _glmDot(&model->facetnorms[3 * T(node->index).findex],
1124 &model->facetnorms[3 * T(members[i]->index).findex]);
1125 if (dot > cos_angle) {
1126 node->averaged = TRUE;
1127 average[0] += model->facetnorms[3 * T(node->index).findex + 0];
1128 average[1] += model->facetnorms[3 * T(node->index).findex + 1];
1129 average[2] += model->facetnorms[3 * T(node->index).findex + 2];
1130 avg = 1; /* we averaged at least one normal! */
1131 } else {
1132 node->averaged = FALSE;
1133 }
1134 node = node->next;
1135 }
1136
1137 if (avg) {
1138 /* normalize the averaged normal */
1139 _glmNormalize(average);
1140
1141 /* add the normal to the vertex normals list */
1142 model->normals[3 * numnormals + 0] = average[0];
1143 model->normals[3 * numnormals + 1] = average[1];
1144 model->normals[3 * numnormals + 2] = average[2];
1145 avg = numnormals;
1146 numnormals++;
1147 }
1148
1149 /* set the normal of this vertex in each triangle it is in */
1150 node = members[i];
1151 while (node) {
1152 if (node->averaged) {
1153 /* if this node was averaged, use the average normal */
1154 if (T(node->index).vindices[0] == i)
1155 T(node->index).nindices[0] = avg;
1156 else if (T(node->index).vindices[1] == i)
1157 T(node->index).nindices[1] = avg;
1158 else if (T(node->index).vindices[2] == i)
1159 T(node->index).nindices[2] = avg;
1160 } else {
1161 /* if this node wasn't averaged, use the facet normal */
1162 model->normals[3 * numnormals + 0] =
1163 model->facetnorms[3 * T(node->index).findex + 0];
1164 model->normals[3 * numnormals + 1] =
1165 model->facetnorms[3 * T(node->index).findex + 1];
1166 model->normals[3 * numnormals + 2] =
1167 model->facetnorms[3 * T(node->index).findex + 2];
1168 if (T(node->index).vindices[0] == i)
1169 T(node->index).nindices[0] = numnormals;
1170 else if (T(node->index).vindices[1] == i)
1171 T(node->index).nindices[1] = numnormals;
1172 else if (T(node->index).vindices[2] == i)
1173 T(node->index).nindices[2] = numnormals;
1174 numnormals++;
1175 }
1176 node = node->next;
1177 }
1178 }
1179
1180 model->numnormals = numnormals - 1;
1181
1182 /* free the member information */
1183 for (i = 1; i <= model->numvertices; i++) {
1184 node = members[i];
1185 while (node) {
1186 tail = node;
1187 node = node->next;
1188 free(tail);
1189 }
1190 }
1191 free(members);
1192
1193 /* pack the normals array (we previously allocated the maximum
1194 number of normals that could possibly be created (numtriangles *
1195 3), so get rid of some of them (usually alot unless none of the
1196 facet normals were averaged)) */
1197 normals = model->normals;
1198 model->normals = (float*)malloc(sizeof(float)* 3* (model->numnormals+1));
1199 for (i = 1; i <= model->numnormals; i++) {
1200 model->normals[3 * i + 0] = normals[3 * i + 0];
1201 model->normals[3 * i + 1] = normals[3 * i + 1];
1202 model->normals[3 * i + 2] = normals[3 * i + 2];
1203 }
1204 free(normals);
1205
1206 printf("glmVertexNormals(): %d normals generated\n", model->numnormals);
1207 }
1208
1209
1210 /* glmLinearTexture: Generates texture coordinates according to a
1211 * linear projection of the texture map. It generates these by
1212 * linearly mapping the vertices onto a square.
1213 *
1214 * model - pointer to initialized GLMmodel structure
1215 */
1216 void
1217 glmLinearTexture(GLMmodel* model)
1218 {
1219 GLMgroup *group;
1220 float dimensions[3];
1221 float x, y, scalefactor;
1222 uint i;
1223
1224 assert(model);
1225
1226 if (model->texcoords)
1227 free(model->texcoords);
1228 model->numtexcoords = model->numvertices;
1229 model->texcoords=(float*)malloc(sizeof(float)*2*(model->numtexcoords+1));
1230
1231 glmDimensions(model, dimensions);
1232 scalefactor = 2.0 /
1233 _glmAbs(_glmMax(_glmMax(dimensions[0], dimensions[1]), dimensions[2]));
1234
1235 /* do the calculations */
1236 for(i = 1; i <= model->numvertices; i++) {
1237 x = model->vertices[3 * i + 0] * scalefactor;
1238 y = model->vertices[3 * i + 2] * scalefactor;
1239 model->texcoords[2 * i + 0] = (x + 1.0) / 2.0;
1240 model->texcoords[2 * i + 1] = (y + 1.0) / 2.0;
1241 }
1242
1243 /* go through and put texture coordinate indices in all the triangles */
1244 group = model->groups;
1245 while(group) {
1246 for(i = 0; i < group->numtriangles; i++) {
1247 T(group->triangles[i]).tindices[0] = T(group->triangles[i]).vindices[0];
1248 T(group->triangles[i]).tindices[1] = T(group->triangles[i]).vindices[1];
1249 T(group->triangles[i]).tindices[2] = T(group->triangles[i]).vindices[2];
1250 }
1251 group = group->next;
1252 }
1253
1254 #if 0
1255 printf("glmLinearTexture(): generated %d linear texture coordinates\n",
1256 model->numtexcoords);
1257 #endif
1258 }
1259
1260 /* glmSpheremapTexture: Generates texture coordinates according to a
1261 * spherical projection of the texture map. Sometimes referred to as
1262 * spheremap, or reflection map texture coordinates. It generates
1263 * these by using the normal to calculate where that vertex would map
1264 * onto a sphere. Since it is impossible to map something flat
1265 * perfectly onto something spherical, there is distortion at the
1266 * poles. This particular implementation causes the poles along the X
1267 * axis to be distorted.
1268 *
1269 * model - pointer to initialized GLMmodel structure
1270 */
1271 void
1272 glmSpheremapTexture(GLMmodel* model)
1273 {
1274 GLMgroup* group;
1275 float theta, phi, rho, x, y, z, r;
1276 uint i;
1277
1278 assert(model);
1279 assert(model->normals);
1280
1281 if (model->texcoords)
1282 free(model->texcoords);
1283 model->numtexcoords = model->numnormals;
1284 model->texcoords=(float*)malloc(sizeof(float)*2*(model->numtexcoords+1));
1285
1286 /* do the calculations */
1287 for (i = 1; i <= model->numnormals; i++) {
1288 z = model->normals[3 * i + 0]; /* re-arrange for pole distortion */
1289 y = model->normals[3 * i + 1];
1290 x = model->normals[3 * i + 2];
1291 r = sqrt((x * x) + (y * y));
1292 rho = sqrt((r * r) + (z * z));
1293
1294 if(r == 0.0) {
1295 theta = 0.0;
1296 phi = 0.0;
1297 } else {
1298 if(z == 0.0)
1299 phi = M_PI / 2.0;
1300 else
1301 phi = acos(z / rho);
1302
1303 #if WE_DONT_NEED_THIS_CODE
1304 if(x == 0.0)
1305 theta = M_PI / 2.0; /* asin(y / r); */
1306 else
1307 theta = acos(x / r);
1308 #endif
1309
1310 if(y == 0.0)
1311 theta = M_PI / 2.0; /* acos(x / r); */
1312 else
1313 theta = asin(y / r) + (M_PI / 2.0);
1314 }
1315
1316 model->texcoords[2 * i + 0] = theta / M_PI;
1317 model->texcoords[2 * i + 1] = phi / M_PI;
1318 }
1319
1320 /* go through and put texcoord indices in all the triangles */
1321 group = model->groups;
1322 while(group) {
1323 for (i = 0; i < group->numtriangles; i++) {
1324 T(group->triangles[i]).tindices[0] = T(group->triangles[i]).nindices[0];
1325 T(group->triangles[i]).tindices[1] = T(group->triangles[i]).nindices[1];
1326 T(group->triangles[i]).tindices[2] = T(group->triangles[i]).nindices[2];
1327 }
1328 group = group->next;
1329 }
1330
1331 #if 0
1332 printf("glmSpheremapTexture(): generated %d spheremap texture coordinates\n",
1333 model->numtexcoords);
1334 #endif
1335 }
1336
1337 /* glmDelete: Deletes a GLMmodel structure.
1338 *
1339 * model - initialized GLMmodel structure
1340 */
1341 void
1342 glmDelete(GLMmodel* model)
1343 {
1344 GLMgroup* group;
1345 uint i;
1346
1347 assert(model);
1348
1349 if (model->pathname) free(model->pathname);
1350 if (model->mtllibname) free(model->mtllibname);
1351 if (model->vertices) free(model->vertices);
1352 if (model->normals) free(model->normals);
1353 if (model->texcoords) free(model->texcoords);
1354 if (model->facetnorms) free(model->facetnorms);
1355 if (model->triangles) free(model->triangles);
1356 if (model->materials) {
1357 for (i = 0; i < model->nummaterials; i++)
1358 free(model->materials[i].name);
1359 }
1360 free(model->materials);
1361 while(model->groups) {
1362 group = model->groups;
1363 model->groups = model->groups->next;
1364 free(group->name);
1365 free(group->triangles);
1366 free(group);
1367 }
1368
1369 free(model);
1370 }
1371
1372 static GLMmaterial *
1373 glmDefaultMaterial(void)
1374 {
1375 GLMmaterial *m = (GLMmaterial *) calloc(1, sizeof(GLMmaterial));
1376
1377 m->diffuse[0] = 0.75;
1378 m->diffuse[1] = 0.75;
1379 m->diffuse[2] = 0.75;
1380 m->diffuse[3] = 1.0;
1381
1382 m->specular[0] = 1.0;
1383 m->specular[1] = 1.0;
1384 m->specular[2] = 1.0;
1385 m->specular[3] = 1.0;
1386
1387 m->shininess = 5;
1388
1389 return m;
1390 }
1391
1392
1393 /* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
1394 * Returns a pointer to the created object which should be free'd with
1395 * glmDelete().
1396 *
1397 * filename - name of the file containing the Wavefront .OBJ format data.
1398 */
1399 GLMmodel*
1400 glmReadOBJ(char* filename)
1401 {
1402 GLMmodel* model;
1403 FILE* file;
1404
1405 /* open the file */
1406 file = fopen(filename, "r");
1407 if (!file) {
1408 fprintf(stderr, "glmReadOBJ() failed: can't open data file \"%s\".\n",
1409 filename);
1410 exit(1);
1411 }
1412
1413 #if 0
1414 /* announce the model name */
1415 printf("Model: %s\n", filename);
1416 #endif
1417
1418 /* allocate a new model */
1419 model = (GLMmodel*)malloc(sizeof(GLMmodel));
1420 model->pathname = stralloc(filename);
1421 model->mtllibname = NULL;
1422 model->numvertices = 0;
1423 model->vertices = NULL;
1424 model->numnormals = 0;
1425 model->normals = NULL;
1426 model->numtexcoords = 0;
1427 model->texcoords = NULL;
1428 model->numfacetnorms = 0;
1429 model->facetnorms = NULL;
1430 model->numtriangles = 0;
1431 model->triangles = NULL;
1432 model->nummaterials = 0;
1433 model->materials = NULL;
1434 model->numgroups = 0;
1435 model->groups = NULL;
1436 model->position[0] = 0.0;
1437 model->position[1] = 0.0;
1438 model->position[2] = 0.0;
1439 model->scale = 1.0;
1440
1441 /* make a first pass through the file to get a count of the number
1442 of vertices, normals, texcoords & triangles */
1443 _glmFirstPass(model, file);
1444
1445 /* allocate memory */
1446 model->vertices = (float*)malloc(sizeof(float) *
1447 3 * (model->numvertices + 1));
1448 model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) *
1449 model->numtriangles);
1450 if (model->numnormals) {
1451 model->normals = (float*)malloc(sizeof(float) *
1452 3 * (model->numnormals + 1));
1453 }
1454 if (model->numtexcoords) {
1455 model->texcoords = (float*)malloc(sizeof(float) *
1456 2 * (model->numtexcoords + 1));
1457 }
1458
1459 /* rewind to beginning of file and read in the data this pass */
1460 rewind(file);
1461
1462 _glmSecondPass(model, file);
1463
1464 /* close the file */
1465 fclose(file);
1466
1467 if (!model->materials) {
1468 model->materials = glmDefaultMaterial();
1469 model->nummaterials = 1;
1470 }
1471
1472 return model;
1473 }
1474
1475 /* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
1476 * a file.
1477 *
1478 * model - initialized GLMmodel structure
1479 * filename - name of the file to write the Wavefront .OBJ format data to
1480 * mode - a bitwise or of values describing what is written to the file
1481 * GLM_NONE - render with only vertices
1482 * GLM_FLAT - render with facet normals
1483 * GLM_SMOOTH - render with vertex normals
1484 * GLM_TEXTURE - render with texture coords
1485 * GLM_COLOR - render with colors (color material)
1486 * GLM_MATERIAL - render with materials
1487 * GLM_COLOR and GLM_MATERIAL should not both be specified.
1488 * GLM_FLAT and GLM_SMOOTH should not both be specified.
1489 */
1490 void
1491 glmWriteOBJ(GLMmodel* model, char* filename, uint mode)
1492 {
1493 uint i;
1494 FILE* file;
1495 GLMgroup* group;
1496
1497 assert(model);
1498
1499 /* do a bit of warning */
1500 if (mode & GLM_FLAT && !model->facetnorms) {
1501 printf("glmWriteOBJ() warning: flat normal output requested "
1502 "with no facet normals defined.\n");
1503 mode &= ~GLM_FLAT;
1504 }
1505 if (mode & GLM_SMOOTH && !model->normals) {
1506 printf("glmWriteOBJ() warning: smooth normal output requested "
1507 "with no normals defined.\n");
1508 mode &= ~GLM_SMOOTH;
1509 }
1510 if (mode & GLM_TEXTURE && !model->texcoords) {
1511 printf("glmWriteOBJ() warning: texture coordinate output requested "
1512 "with no texture coordinates defined.\n");
1513 mode &= ~GLM_TEXTURE;
1514 }
1515 if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
1516 printf("glmWriteOBJ() warning: flat normal output requested "
1517 "and smooth normal output requested (using smooth).\n");
1518 mode &= ~GLM_FLAT;
1519 }
1520
1521 /* open the file */
1522 file = fopen(filename, "w");
1523 if (!file) {
1524 fprintf(stderr, "glmWriteOBJ() failed: can't open file \"%s\" to write.\n",
1525 filename);
1526 exit(1);
1527 }
1528
1529 /* spit out a header */
1530 fprintf(file, "# \n");
1531 fprintf(file, "# Wavefront OBJ generated by GLM library\n");
1532 fprintf(file, "# \n");
1533 fprintf(file, "# GLM library copyright (C) 1997 by Nate Robins\n");
1534 fprintf(file, "# email: ndr@pobox.com\n");
1535 fprintf(file, "# www: http://www.pobox.com/~ndr\n");
1536 fprintf(file, "# \n");
1537
1538 if (mode & GLM_MATERIAL && model->mtllibname) {
1539 fprintf(file, "\nmtllib %s\n\n", model->mtllibname);
1540 _glmWriteMTL(model, filename, model->mtllibname);
1541 }
1542
1543 /* spit out the vertices */
1544 fprintf(file, "\n");
1545 fprintf(file, "# %d vertices\n", model->numvertices);
1546 for (i = 1; i <= model->numvertices; i++) {
1547 fprintf(file, "v %f %f %f\n",
1548 model->vertices[3 * i + 0],
1549 model->vertices[3 * i + 1],
1550 model->vertices[3 * i + 2]);
1551 }
1552
1553 /* spit out the smooth/flat normals */
1554 if (mode & GLM_SMOOTH) {
1555 fprintf(file, "\n");
1556 fprintf(file, "# %d normals\n", model->numnormals);
1557 for (i = 1; i <= model->numnormals; i++) {
1558 fprintf(file, "vn %f %f %f\n",
1559 model->normals[3 * i + 0],
1560 model->normals[3 * i + 1],
1561 model->normals[3 * i + 2]);
1562 }
1563 } else if (mode & GLM_FLAT) {
1564 fprintf(file, "\n");
1565 fprintf(file, "# %d normals\n", model->numfacetnorms);
1566 for (i = 1; i <= model->numnormals; i++) {
1567 fprintf(file, "vn %f %f %f\n",
1568 model->facetnorms[3 * i + 0],
1569 model->facetnorms[3 * i + 1],
1570 model->facetnorms[3 * i + 2]);
1571 }
1572 }
1573
1574 /* spit out the texture coordinates */
1575 if (mode & GLM_TEXTURE) {
1576 fprintf(file, "\n");
1577 fprintf(file, "# %d texcoords\n", model->numtexcoords);
1578 for (i = 1; i <= model->numtexcoords; i++) {
1579 fprintf(file, "vt %f %f\n",
1580 model->texcoords[2 * i + 0],
1581 model->texcoords[2 * i + 1]);
1582 }
1583 }
1584
1585 fprintf(file, "\n");
1586 fprintf(file, "# %d groups\n", model->numgroups);
1587 fprintf(file, "# %d faces (triangles)\n", model->numtriangles);
1588 fprintf(file, "\n");
1589
1590 group = model->groups;
1591 while(group) {
1592 fprintf(file, "g %s\n", group->name);
1593 if (mode & GLM_MATERIAL)
1594 fprintf(file, "usemtl %s\n", model->materials[group->material].name);
1595 for (i = 0; i < group->numtriangles; i++) {
1596 if (mode & GLM_SMOOTH && mode & GLM_TEXTURE) {
1597 fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
1598 T(group->triangles[i]).vindices[0],
1599 T(group->triangles[i]).nindices[0],
1600 T(group->triangles[i]).tindices[0],
1601 T(group->triangles[i]).vindices[1],
1602 T(group->triangles[i]).nindices[1],
1603 T(group->triangles[i]).tindices[1],
1604 T(group->triangles[i]).vindices[2],
1605 T(group->triangles[i]).nindices[2],
1606 T(group->triangles[i]).tindices[2]);
1607 } else if (mode & GLM_FLAT && mode & GLM_TEXTURE) {
1608 fprintf(file, "f %d/%d %d/%d %d/%d\n",
1609 T(group->triangles[i]).vindices[0],
1610 T(group->triangles[i]).findex,
1611 T(group->triangles[i]).vindices[1],
1612 T(group->triangles[i]).findex,
1613 T(group->triangles[i]).vindices[2],
1614 T(group->triangles[i]).findex);
1615 } else if (mode & GLM_TEXTURE) {
1616 fprintf(file, "f %d/%d %d/%d %d/%d\n",
1617 T(group->triangles[i]).vindices[0],
1618 T(group->triangles[i]).tindices[0],
1619 T(group->triangles[i]).vindices[1],
1620 T(group->triangles[i]).tindices[1],
1621 T(group->triangles[i]).vindices[2],
1622 T(group->triangles[i]).tindices[2]);
1623 } else if (mode & GLM_SMOOTH) {
1624 fprintf(file, "f %d//%d %d//%d %d//%d\n",
1625 T(group->triangles[i]).vindices[0],
1626 T(group->triangles[i]).nindices[0],
1627 T(group->triangles[i]).vindices[1],
1628 T(group->triangles[i]).nindices[1],
1629 T(group->triangles[i]).vindices[2],
1630 T(group->triangles[i]).nindices[2]);
1631 } else if (mode & GLM_FLAT) {
1632 fprintf(file, "f %d//%d %d//%d %d//%d\n",
1633 T(group->triangles[i]).vindices[0],
1634 T(group->triangles[i]).findex,
1635 T(group->triangles[i]).vindices[1],
1636 T(group->triangles[i]).findex,
1637 T(group->triangles[i]).vindices[2],
1638 T(group->triangles[i]).findex);
1639 } else {
1640 fprintf(file, "f %d %d %d\n",
1641 T(group->triangles[i]).vindices[0],
1642 T(group->triangles[i]).vindices[1],
1643 T(group->triangles[i]).vindices[2]);
1644 }
1645 }
1646 fprintf(file, "\n");
1647 group = group->next;
1648 }
1649
1650 fclose(file);
1651 }
1652
1653 /* glmWeld: eliminate (weld) vectors that are within an epsilon of
1654 * each other.
1655 *
1656 * model - initialized GLMmodel structure
1657 * epsilon - maximum difference between vertices
1658 * ( 0.00001 is a good start for a unitized model)
1659 *
1660 */
1661 void
1662 glmWeld(GLMmodel* model, float epsilon)
1663 {
1664 float* vectors;
1665 float* copies;
1666 uint numvectors;
1667 uint i;
1668
1669 /* vertices */
1670 numvectors = model->numvertices;
1671 vectors = model->vertices;
1672 copies = _glmWeldVectors(vectors, &numvectors, epsilon);
1673
1674 printf("glmWeld(): %d redundant vertices.\n",
1675 model->numvertices - numvectors - 1);
1676
1677 for (i = 0; i < model->numtriangles; i++) {
1678 T(i).vindices[0] = (uint)vectors[3 * T(i).vindices[0] + 0];
1679 T(i).vindices[1] = (uint)vectors[3 * T(i).vindices[1] + 0];
1680 T(i).vindices[2] = (uint)vectors[3 * T(i).vindices[2] + 0];
1681 }
1682
1683 /* free space for old vertices */
1684 free(vectors);
1685
1686 /* allocate space for the new vertices */
1687 model->numvertices = numvectors;
1688 model->vertices = (float*)malloc(sizeof(float) *
1689 3 * (model->numvertices + 1));
1690
1691 /* copy the optimized vertices into the actual vertex list */
1692 for (i = 1; i <= model->numvertices; i++) {
1693 model->vertices[3 * i + 0] = copies[3 * i + 0];
1694 model->vertices[3 * i + 1] = copies[3 * i + 1];
1695 model->vertices[3 * i + 2] = copies[3 * i + 2];
1696 }
1697
1698 free(copies);
1699 }
1700
1701
1702 void
1703 glmReIndex(GLMmodel *model)
1704 {
1705 uint i, j, n;
1706 GLMgroup* group;
1707 float *newNormals = NULL;
1708 float *newTexcoords = NULL;
1709 const uint numv = model->numvertices;
1710
1711 if (model->numnormals > 0)
1712 newNormals = (float *) malloc((numv + 1) * 3 * sizeof(float));
1713
1714 if (model->numtexcoords > 0)
1715 newTexcoords = (float *) malloc((numv + 1) * 2 * sizeof(float));
1716
1717 for (group = model->groups; group; group = group->next) {
1718
1719 n = group->numtriangles;
1720
1721 group->triIndexes = (uint *) malloc(n * 3 * sizeof(uint));
1722
1723 group->minIndex = 10000000;
1724 group->maxIndex = 0;
1725
1726 for (i = 0; i < n; i++) {
1727
1728 for (j = 0; j < 3; j++) {
1729 uint vindex = T(group->triangles[i]).vindices[j];
1730 uint nindex = T(group->triangles[i]).nindices[j];
1731 uint tindex = T(group->triangles[i]).tindices[j];
1732
1733 float *nrm = &model->normals[nindex * 3];
1734 float *tex = &model->texcoords[tindex * 2];
1735
1736 if (newNormals) {
1737 assert(vindex * 3 + 2 < (numv + 1) * 3);
1738 newNormals[vindex * 3 + 0] = nrm[0];
1739 newNormals[vindex * 3 + 1] = nrm[1];
1740 newNormals[vindex * 3 + 2] = nrm[2];
1741 }
1742 if (newTexcoords) {
1743 newTexcoords[vindex * 2 + 0] = tex[0];
1744 newTexcoords[vindex * 2 + 1] = tex[1];
1745 }
1746
1747 T(group->triangles[i]).nindices[j] = vindex;
1748 T(group->triangles[i]).tindices[j] = vindex;
1749
1750 group->triIndexes[i * 3 + j] = vindex;
1751
1752 if (vindex > group->maxIndex)
1753 group->maxIndex = vindex;
1754 if (vindex < group->minIndex)
1755 group->minIndex = vindex;
1756 }
1757 }
1758 }
1759
1760 if (newNormals) {
1761 free(model->normals);
1762 model->normals = newNormals;
1763 model->numnormals = model->numvertices;
1764 }
1765
1766 if (newTexcoords) {
1767 free(model->texcoords);
1768 model->texcoords = newTexcoords;
1769 model->numtexcoords = model->numvertices;
1770 }
1771 }
1772
1773
1774
1775 void
1776 glmPrint(const GLMmodel *model)
1777 {
1778 uint i, j, grp, n;
1779 GLMgroup* group;
1780 uint totalTris = 0;
1781
1782 grp = 0;
1783
1784 printf("%u vertices\n", model->numvertices);
1785 printf("%u normals\n", model->numnormals);
1786 printf("%u texcoords\n", model->numtexcoords);
1787
1788 for (group = model->groups; group; group = group->next, grp++) {
1789 printf("Group %u:\n", grp);
1790 printf(" Min index %u, max index %u\n", group->minIndex, group->maxIndex);
1791
1792 #if 0
1793 if (mode & GLM_MATERIAL) {
1794 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,
1795 model->materials[group->material].ambient);
1796 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,
1797 model->materials[group->material].diffuse);
1798 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,
1799 model->materials[group->material].specular);
1800 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS,
1801 model->materials[group->material].shininess);
1802 }
1803
1804 if (mode & GLM_COLOR) {
1805 glColor3fv(model->materials[group->material].diffuse);
1806 }
1807 #endif
1808 totalTris += group->numtriangles;
1809
1810 printf(" %u triangles\n", group->numtriangles);
1811 n = group->numtriangles;
1812 if (n > 10)
1813 n = 10;
1814
1815 for (i = 0; i < n; i++) {
1816
1817 printf(" %u: vert ", i);
1818 for (j = 0; j < 3; j++) {
1819 printf("%u ", T(group->triangles[i]).vindices[j]);
1820 }
1821
1822 printf(" normal ");
1823 for (j = 0; j < 3; j++) {
1824 printf("%u ", T(group->triangles[i]).nindices[j]);
1825 }
1826
1827 printf(" tex ");
1828 for (j = 0; j < 3; j++) {
1829 printf("%u ", T(group->triangles[i]).tindices[j]);
1830 }
1831
1832 printf("\n");
1833 }
1834 }
1835 printf("Total tris: %u\n", totalTris);
1836 }
1837
1838
1839
1840 #if 0
1841 /* normals */
1842 if (model->numnormals) {
1843 numvectors = model->numnormals;
1844 vectors = model->normals;
1845 copies = _glmOptimizeVectors(vectors, &numvectors);
1846
1847 printf("glmOptimize(): %d redundant normals.\n",
1848 model->numnormals - numvectors);
1849
1850 for (i = 0; i < model->numtriangles; i++) {
1851 T(i).nindices[0] = (uint)vectors[3 * T(i).nindices[0] + 0];
1852 T(i).nindices[1] = (uint)vectors[3 * T(i).nindices[1] + 0];
1853 T(i).nindices[2] = (uint)vectors[3 * T(i).nindices[2] + 0];
1854 }
1855
1856 /* free space for old normals */
1857 free(vectors);
1858
1859 /* allocate space for the new normals */
1860 model->numnormals = numvectors;
1861 model->normals = (float*)malloc(sizeof(float) *
1862 3 * (model->numnormals + 1));
1863
1864 /* copy the optimized vertices into the actual vertex list */
1865 for (i = 1; i <= model->numnormals; i++) {
1866 model->normals[3 * i + 0] = copies[3 * i + 0];
1867 model->normals[3 * i + 1] = copies[3 * i + 1];
1868 model->normals[3 * i + 2] = copies[3 * i + 2];
1869 }
1870
1871 free(copies);
1872 }
1873
1874 /* texcoords */
1875 if (model->numtexcoords) {
1876 numvectors = model->numtexcoords;
1877 vectors = model->texcoords;
1878 copies = _glmOptimizeVectors(vectors, &numvectors);
1879
1880 printf("glmOptimize(): %d redundant texcoords.\n",
1881 model->numtexcoords - numvectors);
1882
1883 for (i = 0; i < model->numtriangles; i++) {
1884 for (j = 0; j < 3; j++) {
1885 T(i).tindices[j] = (uint)vectors[3 * T(i).tindices[j] + 0];
1886 }
1887 }
1888
1889 /* free space for old texcoords */
1890 free(vectors);
1891
1892 /* allocate space for the new texcoords */
1893 model->numtexcoords = numvectors;
1894 model->texcoords = (float*)malloc(sizeof(float) *
1895 2 * (model->numtexcoords + 1));
1896
1897 /* copy the optimized vertices into the actual vertex list */
1898 for (i = 1; i <= model->numtexcoords; i++) {
1899 model->texcoords[2 * i + 0] = copies[2 * i + 0];
1900 model->texcoords[2 * i + 1] = copies[2 * i + 1];
1901 }
1902
1903 free(copies);
1904 }
1905 #endif
1906
1907 #if 0
1908 /* look for unused vertices */
1909 /* look for unused normals */
1910 /* look for unused texcoords */
1911 for (i = 1; i <= model->numvertices; i++) {
1912 for (j = 0; j < model->numtriangles; i++) {
1913 if (T(j).vindices[0] == i ||
1914 T(j).vindices[1] == i ||
1915 T(j).vindices[1] == i)
1916 break;
1917 }
1918 }
1919 #endif