Merge branch 'mesa_7_6_branch'
[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 }
425
426
427 /* _glmWriteMTL: write a wavefront material library file
428 *
429 * model - properly initialized GLMmodel structure
430 * modelpath - pathname of the model being written
431 * mtllibname - name of the material library to be written
432 */
433 static void
434 _glmWriteMTL(GLMmodel* model, char* modelpath, char* mtllibname)
435 {
436 FILE* file;
437 char* dir;
438 char* filename;
439 GLMmaterial* material;
440 uint i;
441
442 dir = _glmDirName(modelpath);
443 filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(mtllibname)));
444 strcpy(filename, dir);
445 strcat(filename, mtllibname);
446 free(dir);
447
448 /* open the file */
449 file = fopen(filename, "w");
450 if (!file) {
451 fprintf(stderr, "_glmWriteMTL() failed: can't open file \"%s\".\n",
452 filename);
453 exit(1);
454 }
455 free(filename);
456
457 /* spit out a header */
458 fprintf(file, "# \n");
459 fprintf(file, "# Wavefront MTL generated by GLM library\n");
460 fprintf(file, "# \n");
461 fprintf(file, "# GLM library copyright (C) 1997 by Nate Robins\n");
462 fprintf(file, "# email: ndr@pobox.com\n");
463 fprintf(file, "# www: http://www.pobox.com/~ndr\n");
464 fprintf(file, "# \n\n");
465
466 for (i = 0; i < model->nummaterials; i++) {
467 material = &model->materials[i];
468 fprintf(file, "newmtl %s\n", material->name);
469 fprintf(file, "Ka %f %f %f\n",
470 material->ambient[0], material->ambient[1], material->ambient[2]);
471 fprintf(file, "Kd %f %f %f\n",
472 material->diffuse[0], material->diffuse[1], material->diffuse[2]);
473 fprintf(file, "Ks %f %f %f\n",
474 material->specular[0],material->specular[1],material->specular[2]);
475 fprintf(file, "Ns %f\n", material->shininess);
476 fprintf(file, "\n");
477 }
478 }
479
480
481 /* _glmFirstPass: first pass at a Wavefront OBJ file that gets all the
482 * statistics of the model (such as #vertices, #normals, etc)
483 *
484 * model - properly initialized GLMmodel structure
485 * file - (fopen'd) file descriptor
486 */
487 static void
488 _glmFirstPass(GLMmodel* model, FILE* file)
489 {
490 uint numvertices; /* number of vertices in model */
491 uint numnormals; /* number of normals in model */
492 uint numtexcoords; /* number of texcoords in model */
493 uint numtriangles; /* number of triangles in model */
494 GLMgroup* group; /* current group */
495 unsigned v, n, t;
496 char buf[128];
497
498 /* make a default group */
499 group = _glmAddGroup(model, "default");
500
501 numvertices = numnormals = numtexcoords = numtriangles = 0;
502 while(fscanf(file, "%s", buf) != EOF) {
503 switch(buf[0]) {
504 case '#': /* comment */
505 /* eat up rest of line */
506 fgets(buf, sizeof(buf), file);
507 break;
508 case 'v': /* v, vn, vt */
509 switch(buf[1]) {
510 case '\0': /* vertex */
511 /* eat up rest of line */
512 fgets(buf, sizeof(buf), file);
513 numvertices++;
514 break;
515 case 'n': /* normal */
516 /* eat up rest of line */
517 fgets(buf, sizeof(buf), file);
518 numnormals++;
519 break;
520 case 't': /* texcoord */
521 /* eat up rest of line */
522 fgets(buf, sizeof(buf), file);
523 numtexcoords++;
524 break;
525 default:
526 printf("_glmFirstPass(): Unknown token \"%s\".\n", buf);
527 exit(1);
528 break;
529 }
530 break;
531 case 'm':
532 fgets(buf, sizeof(buf), file);
533 sscanf(buf, "%s %s", buf, buf);
534 model->mtllibname = stralloc(buf);
535 _glmReadMTL(model, buf);
536 break;
537 case 'u':
538 /* eat up rest of line */
539 fgets(buf, sizeof(buf), file);
540 break;
541 case 'g': /* group */
542 /* eat up rest of line */
543 fgets(buf, sizeof(buf), file);
544 sscanf(buf, "%s", buf);
545 group = _glmAddGroup(model, buf);
546 break;
547 case 'f': /* face */
548 v = n = t = 0;
549 fscanf(file, "%s", buf);
550 /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
551 if (strstr(buf, "//")) {
552 /* v//n */
553 sscanf(buf, "%u//%u", &v, &n);
554 fscanf(file, "%u//%u", &v, &n);
555 fscanf(file, "%u//%u", &v, &n);
556 numtriangles++;
557 group->numtriangles++;
558 while(fscanf(file, "%u//%u", &v, &n) > 0) {
559 numtriangles++;
560 group->numtriangles++;
561 }
562 } else if (sscanf(buf, "%u/%u/%u", &v, &t, &n) == 3) {
563 /* v/t/n */
564 fscanf(file, "%u/%u/%u", &v, &t, &n);
565 fscanf(file, "%u/%u/%u", &v, &t, &n);
566 numtriangles++;
567 group->numtriangles++;
568 while(fscanf(file, "%u/%u/%u", &v, &t, &n) > 0) {
569 numtriangles++;
570 group->numtriangles++;
571 }
572 } else if (sscanf(buf, "%u/%u", &v, &t) == 2) {
573 /* v/t */
574 fscanf(file, "%u/%u", &v, &t);
575 fscanf(file, "%u/%u", &v, &t);
576 numtriangles++;
577 group->numtriangles++;
578 while(fscanf(file, "%u/%u", &v, &t) > 0) {
579 numtriangles++;
580 group->numtriangles++;
581 }
582 } else {
583 /* v */
584 fscanf(file, "%u", &v);
585 fscanf(file, "%u", &v);
586 numtriangles++;
587 group->numtriangles++;
588 while(fscanf(file, "%u", &v) > 0) {
589 numtriangles++;
590 group->numtriangles++;
591 }
592 }
593 break;
594
595 default:
596 /* eat up rest of line */
597 fgets(buf, sizeof(buf), file);
598 break;
599 }
600 }
601
602 #if 0
603 /* announce the model statistics */
604 printf(" Vertices: %d\n", numvertices);
605 printf(" Normals: %d\n", numnormals);
606 printf(" Texcoords: %d\n", numtexcoords);
607 printf(" Triangles: %d\n", numtriangles);
608 printf(" Groups: %d\n", model->numgroups);
609 #endif
610
611 /* set the stats in the model structure */
612 model->numvertices = numvertices;
613 model->numnormals = numnormals;
614 model->numtexcoords = numtexcoords;
615 model->numtriangles = numtriangles;
616
617 /* allocate memory for the triangles in each group */
618 group = model->groups;
619 while(group) {
620 group->triangles = (uint*)malloc(sizeof(uint) * group->numtriangles);
621 group->numtriangles = 0;
622 group = group->next;
623 }
624 }
625
626 /* _glmSecondPass: second pass at a Wavefront OBJ file that gets all
627 * the data.
628 *
629 * model - properly initialized GLMmodel structure
630 * file - (fopen'd) file descriptor
631 */
632 static void
633 _glmSecondPass(GLMmodel* model, FILE* file)
634 {
635 uint numvertices; /* number of vertices in model */
636 uint numnormals; /* number of normals in model */
637 uint numtexcoords; /* number of texcoords in model */
638 uint numtriangles; /* number of triangles in model */
639 float* vertices; /* array of vertices */
640 float* normals; /* array of normals */
641 float* texcoords; /* array of texture coordinates */
642 GLMgroup* group; /* current group pointer */
643 uint material; /* current material */
644 uint v, n, t;
645 char buf[128];
646
647 /* set the pointer shortcuts */
648 vertices = model->vertices;
649 normals = model->normals;
650 texcoords = model->texcoords;
651 group = model->groups;
652
653 /* on the second pass through the file, read all the data into the
654 allocated arrays */
655 numvertices = numnormals = numtexcoords = 1;
656 numtriangles = 0;
657 material = 0;
658 while(fscanf(file, "%s", buf) != EOF) {
659 switch(buf[0]) {
660 case '#': /* comment */
661 /* eat up rest of line */
662 fgets(buf, sizeof(buf), file);
663 break;
664 case 'v': /* v, vn, vt */
665 switch(buf[1]) {
666 case '\0': /* vertex */
667 fscanf(file, "%f %f %f",
668 &vertices[3 * numvertices + X],
669 &vertices[3 * numvertices + Y],
670 &vertices[3 * numvertices + Z]);
671 numvertices++;
672 break;
673 case 'n': /* normal */
674 fscanf(file, "%f %f %f",
675 &normals[3 * numnormals + X],
676 &normals[3 * numnormals + Y],
677 &normals[3 * numnormals + Z]);
678 numnormals++;
679 break;
680 case 't': /* texcoord */
681 fscanf(file, "%f %f",
682 &texcoords[2 * numtexcoords + X],
683 &texcoords[2 * numtexcoords + Y]);
684 numtexcoords++;
685 break;
686 }
687 break;
688 case 'u':
689 fgets(buf, sizeof(buf), file);
690 sscanf(buf, "%s %s", buf, buf);
691 material = _glmFindMaterial(model, buf);
692 if (!group->material)
693 group->material = material;
694 /*printf("material %s = %u\n", buf, material);*/
695 break;
696 case 'g': /* group */
697 /* eat up rest of line */
698 fgets(buf, sizeof(buf), file);
699 sscanf(buf, "%s", buf);
700 group = _glmFindGroup(model, buf);
701 group->material = material;
702 /*printf("GROUP %s material %u\n", buf, material);*/
703 break;
704 case 'f': /* face */
705 v = n = t = 0;
706 fscanf(file, "%s", buf);
707 /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
708 if (strstr(buf, "//")) {
709 /* v//n */
710 sscanf(buf, "%u//%u", &v, &n);
711 T(numtriangles).vindices[0] = v;
712 T(numtriangles).nindices[0] = n;
713 fscanf(file, "%u//%u", &v, &n);
714 T(numtriangles).vindices[1] = v;
715 T(numtriangles).nindices[1] = n;
716 fscanf(file, "%u//%u", &v, &n);
717 T(numtriangles).vindices[2] = v;
718 T(numtriangles).nindices[2] = n;
719 group->triangles[group->numtriangles++] = numtriangles;
720 numtriangles++;
721 while(fscanf(file, "%u//%u", &v, &n) > 0) {
722 T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
723 T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
724 T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
725 T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
726 T(numtriangles).vindices[2] = v;
727 T(numtriangles).nindices[2] = n;
728 group->triangles[group->numtriangles++] = numtriangles;
729 numtriangles++;
730 }
731 } else if (sscanf(buf, "%u/%u/%u", &v, &t, &n) == 3) {
732 /* v/t/n */
733 T(numtriangles).vindices[0] = v;
734 T(numtriangles).tindices[0] = t;
735 T(numtriangles).nindices[0] = n;
736 fscanf(file, "%u/%u/%u", &v, &t, &n);
737 T(numtriangles).vindices[1] = v;
738 T(numtriangles).tindices[1] = t;
739 T(numtriangles).nindices[1] = n;
740 fscanf(file, "%u/%u/%u", &v, &t, &n);
741 T(numtriangles).vindices[2] = v;
742 T(numtriangles).tindices[2] = t;
743 T(numtriangles).nindices[2] = n;
744 group->triangles[group->numtriangles++] = numtriangles;
745 numtriangles++;
746 while(fscanf(file, "%u/%u/%u", &v, &t, &n) > 0) {
747 T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
748 T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
749 T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
750 T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
751 T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
752 T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
753 T(numtriangles).vindices[2] = v;
754 T(numtriangles).tindices[2] = t;
755 T(numtriangles).nindices[2] = n;
756 group->triangles[group->numtriangles++] = numtriangles;
757 numtriangles++;
758 }
759 } else if (sscanf(buf, "%u/%u", &v, &t) == 2) {
760 /* v/t */
761 T(numtriangles).vindices[0] = v;
762 T(numtriangles).tindices[0] = t;
763 fscanf(file, "%u/%u", &v, &t);
764 T(numtriangles).vindices[1] = v;
765 T(numtriangles).tindices[1] = t;
766 fscanf(file, "%u/%u", &v, &t);
767 T(numtriangles).vindices[2] = v;
768 T(numtriangles).tindices[2] = t;
769 group->triangles[group->numtriangles++] = numtriangles;
770 numtriangles++;
771 while(fscanf(file, "%u/%u", &v, &t) > 0) {
772 T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
773 T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
774 T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
775 T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
776 T(numtriangles).vindices[2] = v;
777 T(numtriangles).tindices[2] = t;
778 group->triangles[group->numtriangles++] = numtriangles;
779 numtriangles++;
780 }
781 } else {
782 /* v */
783 sscanf(buf, "%u", &v);
784 T(numtriangles).vindices[0] = v;
785 fscanf(file, "%u", &v);
786 T(numtriangles).vindices[1] = v;
787 fscanf(file, "%u", &v);
788 T(numtriangles).vindices[2] = v;
789 group->triangles[group->numtriangles++] = numtriangles;
790 numtriangles++;
791 while(fscanf(file, "%u", &v) > 0) {
792 T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
793 T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
794 T(numtriangles).vindices[2] = v;
795 group->triangles[group->numtriangles++] = numtriangles;
796 numtriangles++;
797 }
798 }
799 break;
800
801 default:
802 /* eat up rest of line */
803 fgets(buf, sizeof(buf), file);
804 break;
805 }
806 }
807
808 #if 0
809 /* announce the memory requirements */
810 printf(" Memory: %d bytes\n",
811 numvertices * 3*sizeof(float) +
812 numnormals * 3*sizeof(float) * (numnormals ? 1 : 0) +
813 numtexcoords * 3*sizeof(float) * (numtexcoords ? 1 : 0) +
814 numtriangles * sizeof(GLMtriangle));
815 #endif
816 }
817
818
819
820
821 /* public functions */
822
823 /* glmUnitize: "unitize" a model by translating it to the origin and
824 * scaling it to fit in a unit cube around the origin. Returns the
825 * scalefactor used.
826 *
827 * model - properly initialized GLMmodel structure
828 */
829 float
830 glmUnitize(GLMmodel* model)
831 {
832 uint i;
833 float maxx, minx, maxy, miny, maxz, minz;
834 float cx, cy, cz, w, h, d;
835 float scale;
836
837 assert(model);
838 assert(model->vertices);
839
840 /* get the max/mins */
841 maxx = minx = model->vertices[3 + X];
842 maxy = miny = model->vertices[3 + Y];
843 maxz = minz = model->vertices[3 + Z];
844 for (i = 1; i <= model->numvertices; i++) {
845 if (maxx < model->vertices[3 * i + X])
846 maxx = model->vertices[3 * i + X];
847 if (minx > model->vertices[3 * i + X])
848 minx = model->vertices[3 * i + X];
849
850 if (maxy < model->vertices[3 * i + Y])
851 maxy = model->vertices[3 * i + Y];
852 if (miny > model->vertices[3 * i + Y])
853 miny = model->vertices[3 * i + Y];
854
855 if (maxz < model->vertices[3 * i + Z])
856 maxz = model->vertices[3 * i + Z];
857 if (minz > model->vertices[3 * i + Z])
858 minz = model->vertices[3 * i + Z];
859 }
860
861 /* calculate model width, height, and depth */
862 w = _glmAbs(maxx) + _glmAbs(minx);
863 h = _glmAbs(maxy) + _glmAbs(miny);
864 d = _glmAbs(maxz) + _glmAbs(minz);
865
866 /* calculate center of the model */
867 cx = (maxx + minx) / 2.0;
868 cy = (maxy + miny) / 2.0;
869 cz = (maxz + minz) / 2.0;
870
871 /* calculate unitizing scale factor */
872 scale = 2.0 / _glmMax(_glmMax(w, h), d);
873
874 /* translate around center then scale */
875 for (i = 1; i <= model->numvertices; i++) {
876 model->vertices[3 * i + X] -= cx;
877 model->vertices[3 * i + Y] -= cy;
878 model->vertices[3 * i + Z] -= cz;
879 model->vertices[3 * i + X] *= scale;
880 model->vertices[3 * i + Y] *= scale;
881 model->vertices[3 * i + Z] *= scale;
882 }
883
884 return scale;
885 }
886
887 /* glmDimensions: Calculates the dimensions (width, height, depth) of
888 * a model.
889 *
890 * model - initialized GLMmodel structure
891 * dimensions - array of 3 floats (float dimensions[3])
892 */
893 void
894 glmDimensions(GLMmodel* model, float* dimensions)
895 {
896 uint i;
897 float maxx, minx, maxy, miny, maxz, minz;
898
899 assert(model);
900 assert(model->vertices);
901 assert(dimensions);
902
903 /* get the max/mins */
904 maxx = minx = model->vertices[3 + X];
905 maxy = miny = model->vertices[3 + Y];
906 maxz = minz = model->vertices[3 + Z];
907 for (i = 1; i <= model->numvertices; i++) {
908 if (maxx < model->vertices[3 * i + X])
909 maxx = model->vertices[3 * i + X];
910 if (minx > model->vertices[3 * i + X])
911 minx = model->vertices[3 * i + X];
912
913 if (maxy < model->vertices[3 * i + Y])
914 maxy = model->vertices[3 * i + Y];
915 if (miny > model->vertices[3 * i + Y])
916 miny = model->vertices[3 * i + Y];
917
918 if (maxz < model->vertices[3 * i + Z])
919 maxz = model->vertices[3 * i + Z];
920 if (minz > model->vertices[3 * i + Z])
921 minz = model->vertices[3 * i + Z];
922 }
923
924 /* calculate model width, height, and depth */
925 dimensions[X] = _glmAbs(maxx) + _glmAbs(minx);
926 dimensions[Y] = _glmAbs(maxy) + _glmAbs(miny);
927 dimensions[Z] = _glmAbs(maxz) + _glmAbs(minz);
928 }
929
930 /* glmScale: Scales a model by a given amount.
931 *
932 * model - properly initialized GLMmodel structure
933 * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
934 */
935 void
936 glmScale(GLMmodel* model, float scale)
937 {
938 uint i;
939
940 for (i = 1; i <= model->numvertices; i++) {
941 model->vertices[3 * i + X] *= scale;
942 model->vertices[3 * i + Y] *= scale;
943 model->vertices[3 * i + Z] *= scale;
944 }
945 }
946
947 /* glmReverseWinding: Reverse the polygon winding for all polygons in
948 * this model. Default winding is counter-clockwise. Also changes
949 * the direction of the normals.
950 *
951 * model - properly initialized GLMmodel structure
952 */
953 void
954 glmReverseWinding(GLMmodel* model)
955 {
956 uint i, swap;
957
958 assert(model);
959
960 for (i = 0; i < model->numtriangles; i++) {
961 swap = T(i).vindices[0];
962 T(i).vindices[0] = T(i).vindices[2];
963 T(i).vindices[2] = swap;
964
965 if (model->numnormals) {
966 swap = T(i).nindices[0];
967 T(i).nindices[0] = T(i).nindices[2];
968 T(i).nindices[2] = swap;
969 }
970
971 if (model->numtexcoords) {
972 swap = T(i).tindices[0];
973 T(i).tindices[0] = T(i).tindices[2];
974 T(i).tindices[2] = swap;
975 }
976 }
977
978 /* reverse facet normals */
979 for (i = 1; i <= model->numfacetnorms; i++) {
980 model->facetnorms[3 * i + X] = -model->facetnorms[3 * i + X];
981 model->facetnorms[3 * i + Y] = -model->facetnorms[3 * i + Y];
982 model->facetnorms[3 * i + Z] = -model->facetnorms[3 * i + Z];
983 }
984
985 /* reverse vertex normals */
986 for (i = 1; i <= model->numnormals; i++) {
987 model->normals[3 * i + X] = -model->normals[3 * i + X];
988 model->normals[3 * i + Y] = -model->normals[3 * i + Y];
989 model->normals[3 * i + Z] = -model->normals[3 * i + Z];
990 }
991 }
992
993 /* glmFacetNormals: Generates facet normals for a model (by taking the
994 * cross product of the two vectors derived from the sides of each
995 * triangle). Assumes a counter-clockwise winding.
996 *
997 * model - initialized GLMmodel structure
998 */
999 void
1000 glmFacetNormals(GLMmodel* model)
1001 {
1002 uint i;
1003 float u[3];
1004 float v[3];
1005
1006 assert(model);
1007 assert(model->vertices);
1008
1009 /* clobber any old facetnormals */
1010 if (model->facetnorms)
1011 free(model->facetnorms);
1012
1013 /* allocate memory for the new facet normals */
1014 model->numfacetnorms = model->numtriangles;
1015 model->facetnorms = (float*)malloc(sizeof(float) *
1016 3 * (model->numfacetnorms + 1));
1017
1018 for (i = 0; i < model->numtriangles; i++) {
1019 model->triangles[i].findex = i+1;
1020
1021 u[X] = model->vertices[3 * T(i).vindices[1] + X] -
1022 model->vertices[3 * T(i).vindices[0] + X];
1023 u[Y] = model->vertices[3 * T(i).vindices[1] + Y] -
1024 model->vertices[3 * T(i).vindices[0] + Y];
1025 u[Z] = model->vertices[3 * T(i).vindices[1] + Z] -
1026 model->vertices[3 * T(i).vindices[0] + Z];
1027
1028 v[X] = model->vertices[3 * T(i).vindices[2] + X] -
1029 model->vertices[3 * T(i).vindices[0] + X];
1030 v[Y] = model->vertices[3 * T(i).vindices[2] + Y] -
1031 model->vertices[3 * T(i).vindices[0] + Y];
1032 v[Z] = model->vertices[3 * T(i).vindices[2] + Z] -
1033 model->vertices[3 * T(i).vindices[0] + Z];
1034
1035 _glmCross(u, v, &model->facetnorms[3 * (i+1)]);
1036 _glmNormalize(&model->facetnorms[3 * (i+1)]);
1037 }
1038 }
1039
1040 /* glmVertexNormals: Generates smooth vertex normals for a model.
1041 * First builds a list of all the triangles each vertex is in. Then
1042 * loops through each vertex in the the list averaging all the facet
1043 * normals of the triangles each vertex is in. Finally, sets the
1044 * normal index in the triangle for the vertex to the generated smooth
1045 * normal. If the dot product of a facet normal and the facet normal
1046 * associated with the first triangle in the list of triangles the
1047 * current vertex is in is greater than the cosine of the angle
1048 * parameter to the function, that facet normal is not added into the
1049 * average normal calculation and the corresponding vertex is given
1050 * the facet normal. This tends to preserve hard edges. The angle to
1051 * use depends on the model, but 90 degrees is usually a good start.
1052 *
1053 * model - initialized GLMmodel structure
1054 * angle - maximum angle (in degrees) to smooth across
1055 */
1056 void
1057 glmVertexNormals(GLMmodel* model, float angle)
1058 {
1059 GLMnode* node;
1060 GLMnode* tail;
1061 GLMnode** members;
1062 float* normals;
1063 uint numnormals;
1064 float average[3];
1065 float dot, cos_angle;
1066 uint i, avg;
1067
1068 assert(model);
1069 assert(model->facetnorms);
1070
1071 /* calculate the cosine of the angle (in degrees) */
1072 cos_angle = cos(angle * M_PI / 180.0);
1073
1074 /* nuke any previous normals */
1075 if (model->normals)
1076 free(model->normals);
1077
1078 /* allocate space for new normals */
1079 model->numnormals = model->numtriangles * 3; /* 3 normals per triangle */
1080 model->normals = (float*)malloc(sizeof(float)* 3* (model->numnormals+1));
1081
1082 /* allocate a structure that will hold a linked list of triangle
1083 indices for each vertex */
1084 members = (GLMnode**)malloc(sizeof(GLMnode*) * (model->numvertices + 1));
1085 for (i = 1; i <= model->numvertices; i++)
1086 members[i] = NULL;
1087
1088 /* for every triangle, create a node for each vertex in it */
1089 for (i = 0; i < model->numtriangles; i++) {
1090 node = (GLMnode*)malloc(sizeof(GLMnode));
1091 node->index = i;
1092 node->next = members[T(i).vindices[0]];
1093 members[T(i).vindices[0]] = node;
1094
1095 node = (GLMnode*)malloc(sizeof(GLMnode));
1096 node->index = i;
1097 node->next = members[T(i).vindices[1]];
1098 members[T(i).vindices[1]] = node;
1099
1100 node = (GLMnode*)malloc(sizeof(GLMnode));
1101 node->index = i;
1102 node->next = members[T(i).vindices[2]];
1103 members[T(i).vindices[2]] = node;
1104 }
1105
1106 /* calculate the average normal for each vertex */
1107 numnormals = 1;
1108 for (i = 1; i <= model->numvertices; i++) {
1109 /* calculate an average normal for this vertex by averaging the
1110 facet normal of every triangle this vertex is in */
1111 node = members[i];
1112 if (!node)
1113 fprintf(stderr, "glmVertexNormals(): vertex w/o a triangle\n");
1114 average[0] = 0.0; average[1] = 0.0; average[2] = 0.0;
1115 avg = 0;
1116 while (node) {
1117 /* only average if the dot product of the angle between the two
1118 facet normals is greater than the cosine of the threshold
1119 angle -- or, said another way, the angle between the two
1120 facet normals is less than (or equal to) the threshold angle */
1121 dot = _glmDot(&model->facetnorms[3 * T(node->index).findex],
1122 &model->facetnorms[3 * T(members[i]->index).findex]);
1123 if (dot > cos_angle) {
1124 node->averaged = TRUE;
1125 average[0] += model->facetnorms[3 * T(node->index).findex + 0];
1126 average[1] += model->facetnorms[3 * T(node->index).findex + 1];
1127 average[2] += model->facetnorms[3 * T(node->index).findex + 2];
1128 avg = 1; /* we averaged at least one normal! */
1129 } else {
1130 node->averaged = FALSE;
1131 }
1132 node = node->next;
1133 }
1134
1135 if (avg) {
1136 /* normalize the averaged normal */
1137 _glmNormalize(average);
1138
1139 /* add the normal to the vertex normals list */
1140 model->normals[3 * numnormals + 0] = average[0];
1141 model->normals[3 * numnormals + 1] = average[1];
1142 model->normals[3 * numnormals + 2] = average[2];
1143 avg = numnormals;
1144 numnormals++;
1145 }
1146
1147 /* set the normal of this vertex in each triangle it is in */
1148 node = members[i];
1149 while (node) {
1150 if (node->averaged) {
1151 /* if this node was averaged, use the average normal */
1152 if (T(node->index).vindices[0] == i)
1153 T(node->index).nindices[0] = avg;
1154 else if (T(node->index).vindices[1] == i)
1155 T(node->index).nindices[1] = avg;
1156 else if (T(node->index).vindices[2] == i)
1157 T(node->index).nindices[2] = avg;
1158 } else {
1159 /* if this node wasn't averaged, use the facet normal */
1160 model->normals[3 * numnormals + 0] =
1161 model->facetnorms[3 * T(node->index).findex + 0];
1162 model->normals[3 * numnormals + 1] =
1163 model->facetnorms[3 * T(node->index).findex + 1];
1164 model->normals[3 * numnormals + 2] =
1165 model->facetnorms[3 * T(node->index).findex + 2];
1166 if (T(node->index).vindices[0] == i)
1167 T(node->index).nindices[0] = numnormals;
1168 else if (T(node->index).vindices[1] == i)
1169 T(node->index).nindices[1] = numnormals;
1170 else if (T(node->index).vindices[2] == i)
1171 T(node->index).nindices[2] = numnormals;
1172 numnormals++;
1173 }
1174 node = node->next;
1175 }
1176 }
1177
1178 model->numnormals = numnormals - 1;
1179
1180 /* free the member information */
1181 for (i = 1; i <= model->numvertices; i++) {
1182 node = members[i];
1183 while (node) {
1184 tail = node;
1185 node = node->next;
1186 free(tail);
1187 }
1188 }
1189 free(members);
1190
1191 /* pack the normals array (we previously allocated the maximum
1192 number of normals that could possibly be created (numtriangles *
1193 3), so get rid of some of them (usually alot unless none of the
1194 facet normals were averaged)) */
1195 normals = model->normals;
1196 model->normals = (float*)malloc(sizeof(float)* 3* (model->numnormals+1));
1197 for (i = 1; i <= model->numnormals; i++) {
1198 model->normals[3 * i + 0] = normals[3 * i + 0];
1199 model->normals[3 * i + 1] = normals[3 * i + 1];
1200 model->normals[3 * i + 2] = normals[3 * i + 2];
1201 }
1202 free(normals);
1203
1204 printf("glmVertexNormals(): %d normals generated\n", model->numnormals);
1205 }
1206
1207
1208 /* glmLinearTexture: Generates texture coordinates according to a
1209 * linear projection of the texture map. It generates these by
1210 * linearly mapping the vertices onto a square.
1211 *
1212 * model - pointer to initialized GLMmodel structure
1213 */
1214 void
1215 glmLinearTexture(GLMmodel* model)
1216 {
1217 GLMgroup *group;
1218 float dimensions[3];
1219 float x, y, scalefactor;
1220 uint i;
1221
1222 assert(model);
1223
1224 if (model->texcoords)
1225 free(model->texcoords);
1226 model->numtexcoords = model->numvertices;
1227 model->texcoords=(float*)malloc(sizeof(float)*2*(model->numtexcoords+1));
1228
1229 glmDimensions(model, dimensions);
1230 scalefactor = 2.0 /
1231 _glmAbs(_glmMax(_glmMax(dimensions[0], dimensions[1]), dimensions[2]));
1232
1233 /* do the calculations */
1234 for(i = 1; i <= model->numvertices; i++) {
1235 x = model->vertices[3 * i + 0] * scalefactor;
1236 y = model->vertices[3 * i + 2] * scalefactor;
1237 model->texcoords[2 * i + 0] = (x + 1.0) / 2.0;
1238 model->texcoords[2 * i + 1] = (y + 1.0) / 2.0;
1239 }
1240
1241 /* go through and put texture coordinate indices in all the triangles */
1242 group = model->groups;
1243 while(group) {
1244 for(i = 0; i < group->numtriangles; i++) {
1245 T(group->triangles[i]).tindices[0] = T(group->triangles[i]).vindices[0];
1246 T(group->triangles[i]).tindices[1] = T(group->triangles[i]).vindices[1];
1247 T(group->triangles[i]).tindices[2] = T(group->triangles[i]).vindices[2];
1248 }
1249 group = group->next;
1250 }
1251
1252 #if 0
1253 printf("glmLinearTexture(): generated %d linear texture coordinates\n",
1254 model->numtexcoords);
1255 #endif
1256 }
1257
1258 /* glmSpheremapTexture: Generates texture coordinates according to a
1259 * spherical projection of the texture map. Sometimes referred to as
1260 * spheremap, or reflection map texture coordinates. It generates
1261 * these by using the normal to calculate where that vertex would map
1262 * onto a sphere. Since it is impossible to map something flat
1263 * perfectly onto something spherical, there is distortion at the
1264 * poles. This particular implementation causes the poles along the X
1265 * axis to be distorted.
1266 *
1267 * model - pointer to initialized GLMmodel structure
1268 */
1269 void
1270 glmSpheremapTexture(GLMmodel* model)
1271 {
1272 GLMgroup* group;
1273 float theta, phi, rho, x, y, z, r;
1274 uint i;
1275
1276 assert(model);
1277 assert(model->normals);
1278
1279 if (model->texcoords)
1280 free(model->texcoords);
1281 model->numtexcoords = model->numnormals;
1282 model->texcoords=(float*)malloc(sizeof(float)*2*(model->numtexcoords+1));
1283
1284 /* do the calculations */
1285 for (i = 1; i <= model->numnormals; i++) {
1286 z = model->normals[3 * i + 0]; /* re-arrange for pole distortion */
1287 y = model->normals[3 * i + 1];
1288 x = model->normals[3 * i + 2];
1289 r = sqrt((x * x) + (y * y));
1290 rho = sqrt((r * r) + (z * z));
1291
1292 if(r == 0.0) {
1293 theta = 0.0;
1294 phi = 0.0;
1295 } else {
1296 if(z == 0.0)
1297 phi = M_PI / 2.0;
1298 else
1299 phi = acos(z / rho);
1300
1301 #if WE_DONT_NEED_THIS_CODE
1302 if(x == 0.0)
1303 theta = M_PI / 2.0; /* asin(y / r); */
1304 else
1305 theta = acos(x / r);
1306 #endif
1307
1308 if(y == 0.0)
1309 theta = M_PI / 2.0; /* acos(x / r); */
1310 else
1311 theta = asin(y / r) + (M_PI / 2.0);
1312 }
1313
1314 model->texcoords[2 * i + 0] = theta / M_PI;
1315 model->texcoords[2 * i + 1] = phi / M_PI;
1316 }
1317
1318 /* go through and put texcoord indices in all the triangles */
1319 group = model->groups;
1320 while(group) {
1321 for (i = 0; i < group->numtriangles; i++) {
1322 T(group->triangles[i]).tindices[0] = T(group->triangles[i]).nindices[0];
1323 T(group->triangles[i]).tindices[1] = T(group->triangles[i]).nindices[1];
1324 T(group->triangles[i]).tindices[2] = T(group->triangles[i]).nindices[2];
1325 }
1326 group = group->next;
1327 }
1328
1329 #if 0
1330 printf("glmSpheremapTexture(): generated %d spheremap texture coordinates\n",
1331 model->numtexcoords);
1332 #endif
1333 }
1334
1335 /* glmDelete: Deletes a GLMmodel structure.
1336 *
1337 * model - initialized GLMmodel structure
1338 */
1339 void
1340 glmDelete(GLMmodel* model)
1341 {
1342 GLMgroup* group;
1343 uint i;
1344
1345 assert(model);
1346
1347 if (model->pathname) free(model->pathname);
1348 if (model->mtllibname) free(model->mtllibname);
1349 if (model->vertices) free(model->vertices);
1350 if (model->normals) free(model->normals);
1351 if (model->texcoords) free(model->texcoords);
1352 if (model->facetnorms) free(model->facetnorms);
1353 if (model->triangles) free(model->triangles);
1354 if (model->materials) {
1355 for (i = 0; i < model->nummaterials; i++)
1356 free(model->materials[i].name);
1357 }
1358 free(model->materials);
1359 while(model->groups) {
1360 group = model->groups;
1361 model->groups = model->groups->next;
1362 free(group->name);
1363 free(group->triangles);
1364 free(group);
1365 }
1366
1367 free(model);
1368 }
1369
1370 static GLMmaterial *
1371 glmDefaultMaterial(void)
1372 {
1373 GLMmaterial *m = (GLMmaterial *) calloc(1, sizeof(GLMmaterial));
1374
1375 m->diffuse[0] = 0.75;
1376 m->diffuse[1] = 0.75;
1377 m->diffuse[2] = 0.75;
1378 m->diffuse[3] = 1.0;
1379
1380 m->specular[0] = 1.0;
1381 m->specular[1] = 1.0;
1382 m->specular[2] = 1.0;
1383 m->specular[3] = 1.0;
1384
1385 m->shininess = 5;
1386
1387 return m;
1388 }
1389
1390
1391 /* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
1392 * Returns a pointer to the created object which should be free'd with
1393 * glmDelete().
1394 *
1395 * filename - name of the file containing the Wavefront .OBJ format data.
1396 */
1397 GLMmodel*
1398 glmReadOBJ(char* filename)
1399 {
1400 GLMmodel* model;
1401 FILE* file;
1402
1403 /* open the file */
1404 file = fopen(filename, "r");
1405 if (!file) {
1406 fprintf(stderr, "glmReadOBJ() failed: can't open data file \"%s\".\n",
1407 filename);
1408 exit(1);
1409 }
1410
1411 #if 0
1412 /* announce the model name */
1413 printf("Model: %s\n", filename);
1414 #endif
1415
1416 /* allocate a new model */
1417 model = (GLMmodel*)malloc(sizeof(GLMmodel));
1418 model->pathname = stralloc(filename);
1419 model->mtllibname = NULL;
1420 model->numvertices = 0;
1421 model->vertices = NULL;
1422 model->numnormals = 0;
1423 model->normals = NULL;
1424 model->numtexcoords = 0;
1425 model->texcoords = NULL;
1426 model->numfacetnorms = 0;
1427 model->facetnorms = NULL;
1428 model->numtriangles = 0;
1429 model->triangles = NULL;
1430 model->nummaterials = 0;
1431 model->materials = NULL;
1432 model->numgroups = 0;
1433 model->groups = NULL;
1434 model->position[0] = 0.0;
1435 model->position[1] = 0.0;
1436 model->position[2] = 0.0;
1437 model->scale = 1.0;
1438
1439 /* make a first pass through the file to get a count of the number
1440 of vertices, normals, texcoords & triangles */
1441 _glmFirstPass(model, file);
1442
1443 /* allocate memory */
1444 model->vertices = (float*)malloc(sizeof(float) *
1445 3 * (model->numvertices + 1));
1446 model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) *
1447 model->numtriangles);
1448 if (model->numnormals) {
1449 model->normals = (float*)malloc(sizeof(float) *
1450 3 * (model->numnormals + 1));
1451 }
1452 if (model->numtexcoords) {
1453 model->texcoords = (float*)malloc(sizeof(float) *
1454 2 * (model->numtexcoords + 1));
1455 }
1456
1457 /* rewind to beginning of file and read in the data this pass */
1458 rewind(file);
1459
1460 _glmSecondPass(model, file);
1461
1462 /* close the file */
1463 fclose(file);
1464
1465 if (!model->materials) {
1466 model->materials = glmDefaultMaterial();
1467 model->nummaterials = 1;
1468 }
1469
1470 return model;
1471 }
1472
1473 /* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
1474 * a file.
1475 *
1476 * model - initialized GLMmodel structure
1477 * filename - name of the file to write the Wavefront .OBJ format data to
1478 * mode - a bitwise or of values describing what is written to the file
1479 * GLM_NONE - render with only vertices
1480 * GLM_FLAT - render with facet normals
1481 * GLM_SMOOTH - render with vertex normals
1482 * GLM_TEXTURE - render with texture coords
1483 * GLM_COLOR - render with colors (color material)
1484 * GLM_MATERIAL - render with materials
1485 * GLM_COLOR and GLM_MATERIAL should not both be specified.
1486 * GLM_FLAT and GLM_SMOOTH should not both be specified.
1487 */
1488 void
1489 glmWriteOBJ(GLMmodel* model, char* filename, uint mode)
1490 {
1491 uint i;
1492 FILE* file;
1493 GLMgroup* group;
1494
1495 assert(model);
1496
1497 /* do a bit of warning */
1498 if (mode & GLM_FLAT && !model->facetnorms) {
1499 printf("glmWriteOBJ() warning: flat normal output requested "
1500 "with no facet normals defined.\n");
1501 mode &= ~GLM_FLAT;
1502 }
1503 if (mode & GLM_SMOOTH && !model->normals) {
1504 printf("glmWriteOBJ() warning: smooth normal output requested "
1505 "with no normals defined.\n");
1506 mode &= ~GLM_SMOOTH;
1507 }
1508 if (mode & GLM_TEXTURE && !model->texcoords) {
1509 printf("glmWriteOBJ() warning: texture coordinate output requested "
1510 "with no texture coordinates defined.\n");
1511 mode &= ~GLM_TEXTURE;
1512 }
1513 if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
1514 printf("glmWriteOBJ() warning: flat normal output requested "
1515 "and smooth normal output requested (using smooth).\n");
1516 mode &= ~GLM_FLAT;
1517 }
1518
1519 /* open the file */
1520 file = fopen(filename, "w");
1521 if (!file) {
1522 fprintf(stderr, "glmWriteOBJ() failed: can't open file \"%s\" to write.\n",
1523 filename);
1524 exit(1);
1525 }
1526
1527 /* spit out a header */
1528 fprintf(file, "# \n");
1529 fprintf(file, "# Wavefront OBJ generated by GLM library\n");
1530 fprintf(file, "# \n");
1531 fprintf(file, "# GLM library copyright (C) 1997 by Nate Robins\n");
1532 fprintf(file, "# email: ndr@pobox.com\n");
1533 fprintf(file, "# www: http://www.pobox.com/~ndr\n");
1534 fprintf(file, "# \n");
1535
1536 if (mode & GLM_MATERIAL && model->mtllibname) {
1537 fprintf(file, "\nmtllib %s\n\n", model->mtllibname);
1538 _glmWriteMTL(model, filename, model->mtllibname);
1539 }
1540
1541 /* spit out the vertices */
1542 fprintf(file, "\n");
1543 fprintf(file, "# %d vertices\n", model->numvertices);
1544 for (i = 1; i <= model->numvertices; i++) {
1545 fprintf(file, "v %f %f %f\n",
1546 model->vertices[3 * i + 0],
1547 model->vertices[3 * i + 1],
1548 model->vertices[3 * i + 2]);
1549 }
1550
1551 /* spit out the smooth/flat normals */
1552 if (mode & GLM_SMOOTH) {
1553 fprintf(file, "\n");
1554 fprintf(file, "# %d normals\n", model->numnormals);
1555 for (i = 1; i <= model->numnormals; i++) {
1556 fprintf(file, "vn %f %f %f\n",
1557 model->normals[3 * i + 0],
1558 model->normals[3 * i + 1],
1559 model->normals[3 * i + 2]);
1560 }
1561 } else if (mode & GLM_FLAT) {
1562 fprintf(file, "\n");
1563 fprintf(file, "# %d normals\n", model->numfacetnorms);
1564 for (i = 1; i <= model->numnormals; i++) {
1565 fprintf(file, "vn %f %f %f\n",
1566 model->facetnorms[3 * i + 0],
1567 model->facetnorms[3 * i + 1],
1568 model->facetnorms[3 * i + 2]);
1569 }
1570 }
1571
1572 /* spit out the texture coordinates */
1573 if (mode & GLM_TEXTURE) {
1574 fprintf(file, "\n");
1575 fprintf(file, "# %d texcoords\n", model->numtexcoords);
1576 for (i = 1; i <= model->numtexcoords; i++) {
1577 fprintf(file, "vt %f %f\n",
1578 model->texcoords[2 * i + 0],
1579 model->texcoords[2 * i + 1]);
1580 }
1581 }
1582
1583 fprintf(file, "\n");
1584 fprintf(file, "# %d groups\n", model->numgroups);
1585 fprintf(file, "# %d faces (triangles)\n", model->numtriangles);
1586 fprintf(file, "\n");
1587
1588 group = model->groups;
1589 while(group) {
1590 fprintf(file, "g %s\n", group->name);
1591 if (mode & GLM_MATERIAL)
1592 fprintf(file, "usemtl %s\n", model->materials[group->material].name);
1593 for (i = 0; i < group->numtriangles; i++) {
1594 if (mode & GLM_SMOOTH && mode & GLM_TEXTURE) {
1595 fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
1596 T(group->triangles[i]).vindices[0],
1597 T(group->triangles[i]).nindices[0],
1598 T(group->triangles[i]).tindices[0],
1599 T(group->triangles[i]).vindices[1],
1600 T(group->triangles[i]).nindices[1],
1601 T(group->triangles[i]).tindices[1],
1602 T(group->triangles[i]).vindices[2],
1603 T(group->triangles[i]).nindices[2],
1604 T(group->triangles[i]).tindices[2]);
1605 } else if (mode & GLM_FLAT && mode & GLM_TEXTURE) {
1606 fprintf(file, "f %d/%d %d/%d %d/%d\n",
1607 T(group->triangles[i]).vindices[0],
1608 T(group->triangles[i]).findex,
1609 T(group->triangles[i]).vindices[1],
1610 T(group->triangles[i]).findex,
1611 T(group->triangles[i]).vindices[2],
1612 T(group->triangles[i]).findex);
1613 } else if (mode & GLM_TEXTURE) {
1614 fprintf(file, "f %d/%d %d/%d %d/%d\n",
1615 T(group->triangles[i]).vindices[0],
1616 T(group->triangles[i]).tindices[0],
1617 T(group->triangles[i]).vindices[1],
1618 T(group->triangles[i]).tindices[1],
1619 T(group->triangles[i]).vindices[2],
1620 T(group->triangles[i]).tindices[2]);
1621 } else if (mode & GLM_SMOOTH) {
1622 fprintf(file, "f %d//%d %d//%d %d//%d\n",
1623 T(group->triangles[i]).vindices[0],
1624 T(group->triangles[i]).nindices[0],
1625 T(group->triangles[i]).vindices[1],
1626 T(group->triangles[i]).nindices[1],
1627 T(group->triangles[i]).vindices[2],
1628 T(group->triangles[i]).nindices[2]);
1629 } else if (mode & GLM_FLAT) {
1630 fprintf(file, "f %d//%d %d//%d %d//%d\n",
1631 T(group->triangles[i]).vindices[0],
1632 T(group->triangles[i]).findex,
1633 T(group->triangles[i]).vindices[1],
1634 T(group->triangles[i]).findex,
1635 T(group->triangles[i]).vindices[2],
1636 T(group->triangles[i]).findex);
1637 } else {
1638 fprintf(file, "f %d %d %d\n",
1639 T(group->triangles[i]).vindices[0],
1640 T(group->triangles[i]).vindices[1],
1641 T(group->triangles[i]).vindices[2]);
1642 }
1643 }
1644 fprintf(file, "\n");
1645 group = group->next;
1646 }
1647
1648 fclose(file);
1649 }
1650
1651 /* glmWeld: eliminate (weld) vectors that are within an epsilon of
1652 * each other.
1653 *
1654 * model - initialized GLMmodel structure
1655 * epsilon - maximum difference between vertices
1656 * ( 0.00001 is a good start for a unitized model)
1657 *
1658 */
1659 void
1660 glmWeld(GLMmodel* model, float epsilon)
1661 {
1662 float* vectors;
1663 float* copies;
1664 uint numvectors;
1665 uint i;
1666
1667 /* vertices */
1668 numvectors = model->numvertices;
1669 vectors = model->vertices;
1670 copies = _glmWeldVectors(vectors, &numvectors, epsilon);
1671
1672 printf("glmWeld(): %d redundant vertices.\n",
1673 model->numvertices - numvectors - 1);
1674
1675 for (i = 0; i < model->numtriangles; i++) {
1676 T(i).vindices[0] = (uint)vectors[3 * T(i).vindices[0] + 0];
1677 T(i).vindices[1] = (uint)vectors[3 * T(i).vindices[1] + 0];
1678 T(i).vindices[2] = (uint)vectors[3 * T(i).vindices[2] + 0];
1679 }
1680
1681 /* free space for old vertices */
1682 free(vectors);
1683
1684 /* allocate space for the new vertices */
1685 model->numvertices = numvectors;
1686 model->vertices = (float*)malloc(sizeof(float) *
1687 3 * (model->numvertices + 1));
1688
1689 /* copy the optimized vertices into the actual vertex list */
1690 for (i = 1; i <= model->numvertices; i++) {
1691 model->vertices[3 * i + 0] = copies[3 * i + 0];
1692 model->vertices[3 * i + 1] = copies[3 * i + 1];
1693 model->vertices[3 * i + 2] = copies[3 * i + 2];
1694 }
1695
1696 free(copies);
1697 }
1698
1699
1700 void
1701 glmReIndex(GLMmodel *model)
1702 {
1703 uint i, j, n;
1704 GLMgroup* group;
1705 float *newNormals = NULL;
1706 float *newTexcoords = NULL;
1707 const uint numv = model->numvertices;
1708
1709 if (model->numnormals > 0)
1710 newNormals = (float *) malloc((numv + 1) * 3 * sizeof(float));
1711
1712 if (model->numtexcoords > 0)
1713 newTexcoords = (float *) malloc((numv + 1) * 2 * sizeof(float));
1714
1715 for (group = model->groups; group; group = group->next) {
1716
1717 n = group->numtriangles;
1718
1719 group->triIndexes = (uint *) malloc(n * 3 * sizeof(uint));
1720
1721 group->minIndex = 10000000;
1722 group->maxIndex = 0;
1723
1724 for (i = 0; i < n; i++) {
1725
1726 for (j = 0; j < 3; j++) {
1727 uint vindex = T(group->triangles[i]).vindices[j];
1728 uint nindex = T(group->triangles[i]).nindices[j];
1729 uint tindex = T(group->triangles[i]).tindices[j];
1730
1731 float *nrm = &model->normals[nindex * 3];
1732 float *tex = &model->texcoords[tindex * 2];
1733
1734 if (newNormals) {
1735 assert(vindex * 3 + 2 < (numv + 1) * 3);
1736 newNormals[vindex * 3 + 0] = nrm[0];
1737 newNormals[vindex * 3 + 1] = nrm[1];
1738 newNormals[vindex * 3 + 2] = nrm[2];
1739 }
1740 if (newTexcoords) {
1741 newTexcoords[vindex * 2 + 0] = tex[0];
1742 newTexcoords[vindex * 2 + 1] = tex[1];
1743 }
1744
1745 T(group->triangles[i]).nindices[j] = vindex;
1746 T(group->triangles[i]).tindices[j] = vindex;
1747
1748 group->triIndexes[i * 3 + j] = vindex;
1749
1750 if (vindex > group->maxIndex)
1751 group->maxIndex = vindex;
1752 if (vindex < group->minIndex)
1753 group->minIndex = vindex;
1754 }
1755 }
1756 }
1757
1758 if (newNormals) {
1759 free(model->normals);
1760 model->normals = newNormals;
1761 model->numnormals = model->numvertices;
1762 }
1763
1764 if (newTexcoords) {
1765 free(model->texcoords);
1766 model->texcoords = newTexcoords;
1767 model->numtexcoords = model->numvertices;
1768 }
1769 }
1770
1771
1772
1773 void
1774 glmPrint(const GLMmodel *model)
1775 {
1776 uint i, j, grp, n;
1777 GLMgroup* group;
1778 uint totalTris = 0;
1779
1780 grp = 0;
1781
1782 printf("%u vertices\n", model->numvertices);
1783 printf("%u normals\n", model->numnormals);
1784 printf("%u texcoords\n", model->numtexcoords);
1785
1786 for (group = model->groups; group; group = group->next, grp++) {
1787 printf("Group %u:\n", grp);
1788 printf(" Min index %u, max index %u\n", group->minIndex, group->maxIndex);
1789
1790 #if 0
1791 if (mode & GLM_MATERIAL) {
1792 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,
1793 model->materials[group->material].ambient);
1794 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,
1795 model->materials[group->material].diffuse);
1796 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,
1797 model->materials[group->material].specular);
1798 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS,
1799 model->materials[group->material].shininess);
1800 }
1801
1802 if (mode & GLM_COLOR) {
1803 glColor3fv(model->materials[group->material].diffuse);
1804 }
1805 #endif
1806 totalTris += group->numtriangles;
1807
1808 printf(" %u triangles\n", group->numtriangles);
1809 n = group->numtriangles;
1810 if (n > 10)
1811 n = 10;
1812
1813 for (i = 0; i < n; i++) {
1814
1815 printf(" %u: vert ", i);
1816 for (j = 0; j < 3; j++) {
1817 printf("%u ", T(group->triangles[i]).vindices[j]);
1818 }
1819
1820 printf(" normal ");
1821 for (j = 0; j < 3; j++) {
1822 printf("%u ", T(group->triangles[i]).nindices[j]);
1823 }
1824
1825 printf(" tex ");
1826 for (j = 0; j < 3; j++) {
1827 printf("%u ", T(group->triangles[i]).tindices[j]);
1828 }
1829
1830 printf("\n");
1831 }
1832 }
1833 printf("Total tris: %u\n", totalTris);
1834 }
1835
1836
1837
1838 #if 0
1839 /* normals */
1840 if (model->numnormals) {
1841 numvectors = model->numnormals;
1842 vectors = model->normals;
1843 copies = _glmOptimizeVectors(vectors, &numvectors);
1844
1845 printf("glmOptimize(): %d redundant normals.\n",
1846 model->numnormals - numvectors);
1847
1848 for (i = 0; i < model->numtriangles; i++) {
1849 T(i).nindices[0] = (uint)vectors[3 * T(i).nindices[0] + 0];
1850 T(i).nindices[1] = (uint)vectors[3 * T(i).nindices[1] + 0];
1851 T(i).nindices[2] = (uint)vectors[3 * T(i).nindices[2] + 0];
1852 }
1853
1854 /* free space for old normals */
1855 free(vectors);
1856
1857 /* allocate space for the new normals */
1858 model->numnormals = numvectors;
1859 model->normals = (float*)malloc(sizeof(float) *
1860 3 * (model->numnormals + 1));
1861
1862 /* copy the optimized vertices into the actual vertex list */
1863 for (i = 1; i <= model->numnormals; i++) {
1864 model->normals[3 * i + 0] = copies[3 * i + 0];
1865 model->normals[3 * i + 1] = copies[3 * i + 1];
1866 model->normals[3 * i + 2] = copies[3 * i + 2];
1867 }
1868
1869 free(copies);
1870 }
1871
1872 /* texcoords */
1873 if (model->numtexcoords) {
1874 numvectors = model->numtexcoords;
1875 vectors = model->texcoords;
1876 copies = _glmOptimizeVectors(vectors, &numvectors);
1877
1878 printf("glmOptimize(): %d redundant texcoords.\n",
1879 model->numtexcoords - numvectors);
1880
1881 for (i = 0; i < model->numtriangles; i++) {
1882 for (j = 0; j < 3; j++) {
1883 T(i).tindices[j] = (uint)vectors[3 * T(i).tindices[j] + 0];
1884 }
1885 }
1886
1887 /* free space for old texcoords */
1888 free(vectors);
1889
1890 /* allocate space for the new texcoords */
1891 model->numtexcoords = numvectors;
1892 model->texcoords = (float*)malloc(sizeof(float) *
1893 2 * (model->numtexcoords + 1));
1894
1895 /* copy the optimized vertices into the actual vertex list */
1896 for (i = 1; i <= model->numtexcoords; i++) {
1897 model->texcoords[2 * i + 0] = copies[2 * i + 0];
1898 model->texcoords[2 * i + 1] = copies[2 * i + 1];
1899 }
1900
1901 free(copies);
1902 }
1903 #endif
1904
1905 #if 0
1906 /* look for unused vertices */
1907 /* look for unused normals */
1908 /* look for unused texcoords */
1909 for (i = 1; i <= model->numvertices; i++) {
1910 for (j = 0; j < model->numtriangles; i++) {
1911 if (T(j).vindices[0] == i ||
1912 T(j).vindices[1] == i ||
1913 T(j).vindices[1] == i)
1914 break;
1915 }
1916 }
1917 #endif