glm.c revision 32001f49
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
19typedef 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 */
34enum { X, Y, Z, W };			/* elements of a vertex */
35
36
37/* typedefs */
38
39/* _GLMnode: general purpose node
40 */
41typedef 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). */
51static char *
52stralloc(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 */
66static 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 */
75static 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 */
88static 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 */
104static 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 */
121static 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 */
142static 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 */
162static 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 */
199static 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 */
218static 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 */
240static 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
254found:
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 */
265static 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 */
288static 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 */
434static 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 */
489static 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 */
634static 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 */
831float
832glmUnitize(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 */
895void
896glmDimensions(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 */
937void
938glmScale(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 */
955void
956glmReverseWinding(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 */
1001void
1002glmFacetNormals(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 */
1058void
1059glmVertexNormals(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 */
1216void
1217glmLinearTexture(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 */
1271void
1272glmSpheremapTexture(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 */
1341void
1342glmDelete(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
1372static GLMmaterial *
1373glmDefaultMaterial(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 */
1399GLMmodel*
1400glmReadOBJ(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 */
1490void
1491glmWriteOBJ(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 */
1661void
1662glmWeld(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
1702void
1703glmReIndex(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
1775void
1776glmPrint(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
1920