132001f49Smrg/*
232001f49Smrg *  GLM library.  Wavefront .obj file format reader/writer/manipulator.
332001f49Smrg *
432001f49Smrg *  Written by Nate Robins, 1997.
532001f49Smrg *  email: ndr@pobox.com
632001f49Smrg *  www: http://www.pobox.com/~ndr
732001f49Smrg */
832001f49Smrg
932001f49Smrg/* includes */
1032001f49Smrg#include <math.h>
1132001f49Smrg#include <stdio.h>
1232001f49Smrg#include <string.h>
1332001f49Smrg#include <stdlib.h>
1432001f49Smrg#include <assert.h>
1532001f49Smrg#include "glm.h"
1632001f49Smrg#include "readtex.h"
1732001f49Smrg
1832001f49Smrg
1932001f49Smrgtypedef unsigned char boolean;
2032001f49Smrg#define TRUE 1
2132001f49Smrg#define FALSE 0
2232001f49Smrg
2332001f49Smrg
2432001f49Smrg/* Some <math.h> files do not define M_PI... */
2532001f49Smrg#ifndef M_PI
2632001f49Smrg#define M_PI 3.14159265358979323846
2732001f49Smrg#endif
2832001f49Smrg
2932001f49Smrg/* defines */
3032001f49Smrg#define T(x) model->triangles[(x)]
3132001f49Smrg
3232001f49Smrg
3332001f49Smrg/* enums */
3432001f49Smrgenum { X, Y, Z, W };			/* elements of a vertex */
3532001f49Smrg
3632001f49Smrg
3732001f49Smrg/* typedefs */
3832001f49Smrg
3932001f49Smrg/* _GLMnode: general purpose node
4032001f49Smrg */
4132001f49Smrgtypedef struct _GLMnode {
4232001f49Smrg  uint           index;
4332001f49Smrg  boolean        averaged;
4432001f49Smrg  struct _GLMnode* next;
4532001f49Smrg} GLMnode;
4632001f49Smrg
4732001f49Smrg/* strdup is actually not a standard ANSI C or POSIX routine
4832001f49Smrg   so implement a private one.  OpenVMS does not have a strdup; Linux's
4932001f49Smrg   standard libc doesn't declare strdup by default (unless BSD or SVID
5032001f49Smrg   interfaces are requested). */
5132001f49Smrgstatic char *
5232001f49Smrgstralloc(const char *string)
5332001f49Smrg{
5432001f49Smrg  char *copy;
5532001f49Smrg
5632001f49Smrg  copy = malloc(strlen(string) + 1);
5732001f49Smrg  if (copy == NULL)
5832001f49Smrg    return NULL;
5932001f49Smrg  strcpy(copy, string);
6032001f49Smrg  return copy;
6132001f49Smrg}
6232001f49Smrg
6332001f49Smrg/* private functions */
6432001f49Smrg
6532001f49Smrg/* _glmMax: returns the maximum of two floats */
6632001f49Smrgstatic float
6732001f49Smrg_glmMax(float a, float b)
6832001f49Smrg{
6932001f49Smrg  if (a > b)
7032001f49Smrg    return a;
7132001f49Smrg  return b;
7232001f49Smrg}
7332001f49Smrg
7432001f49Smrg/* _glmAbs: returns the absolute value of a float */
7532001f49Smrgstatic float
7632001f49Smrg_glmAbs(float f)
7732001f49Smrg{
7832001f49Smrg  if (f < 0)
7932001f49Smrg    return -f;
8032001f49Smrg  return f;
8132001f49Smrg}
8232001f49Smrg
8332001f49Smrg/* _glmDot: compute the dot product of two vectors
8432001f49Smrg *
8532001f49Smrg * u - array of 3 floats (float u[3])
8632001f49Smrg * v - array of 3 floats (float v[3])
8732001f49Smrg */
8832001f49Smrgstatic float
8932001f49Smrg_glmDot(float* u, float* v)
9032001f49Smrg{
9132001f49Smrg  assert(u);
9232001f49Smrg  assert(v);
9332001f49Smrg
9432001f49Smrg  /* compute the dot product */
9532001f49Smrg  return u[X] * v[X] + u[Y] * v[Y] + u[Z] * v[Z];
9632001f49Smrg}
9732001f49Smrg
9832001f49Smrg/* _glmCross: compute the cross product of two vectors
9932001f49Smrg *
10032001f49Smrg * u - array of 3 floats (float u[3])
10132001f49Smrg * v - array of 3 floats (float v[3])
10232001f49Smrg * n - array of 3 floats (float n[3]) to return the cross product in
10332001f49Smrg */
10432001f49Smrgstatic void
10532001f49Smrg_glmCross(float* u, float* v, float* n)
10632001f49Smrg{
10732001f49Smrg  assert(u);
10832001f49Smrg  assert(v);
10932001f49Smrg  assert(n);
11032001f49Smrg
11132001f49Smrg  /* compute the cross product (u x v for right-handed [ccw]) */
11232001f49Smrg  n[X] = u[Y] * v[Z] - u[Z] * v[Y];
11332001f49Smrg  n[Y] = u[Z] * v[X] - u[X] * v[Z];
11432001f49Smrg  n[Z] = u[X] * v[Y] - u[Y] * v[X];
11532001f49Smrg}
11632001f49Smrg
11732001f49Smrg/* _glmNormalize: normalize a vector
11832001f49Smrg *
11932001f49Smrg * n - array of 3 floats (float n[3]) to be normalized
12032001f49Smrg */
12132001f49Smrgstatic void
12232001f49Smrg_glmNormalize(float* n)
12332001f49Smrg{
12432001f49Smrg  float l;
12532001f49Smrg
12632001f49Smrg  assert(n);
12732001f49Smrg
12832001f49Smrg  /* normalize */
12932001f49Smrg  l = (float)sqrt(n[X] * n[X] + n[Y] * n[Y] + n[Z] * n[Z]);
13032001f49Smrg  n[0] /= l;
13132001f49Smrg  n[1] /= l;
13232001f49Smrg  n[2] /= l;
13332001f49Smrg}
13432001f49Smrg
13532001f49Smrg/* _glmEqual: compares two vectors and returns TRUE if they are
13632001f49Smrg * equal (within a certain threshold) or FALSE if not. An epsilon
13732001f49Smrg * that works fairly well is 0.000001.
13832001f49Smrg *
13932001f49Smrg * u - array of 3 floats (float u[3])
14032001f49Smrg * v - array of 3 floats (float v[3])
14132001f49Smrg */
14232001f49Smrgstatic boolean
14332001f49Smrg_glmEqual(float* u, float* v, float epsilon)
14432001f49Smrg{
14532001f49Smrg  if (_glmAbs(u[0] - v[0]) < epsilon &&
14632001f49Smrg      _glmAbs(u[1] - v[1]) < epsilon &&
14732001f49Smrg      _glmAbs(u[2] - v[2]) < epsilon)
14832001f49Smrg  {
14932001f49Smrg    return TRUE;
15032001f49Smrg  }
15132001f49Smrg  return FALSE;
15232001f49Smrg}
15332001f49Smrg
15432001f49Smrg/* _glmWeldVectors: eliminate (weld) vectors that are within an
15532001f49Smrg * epsilon of each other.
15632001f49Smrg *
15732001f49Smrg * vectors    - array of float[3]'s to be welded
15832001f49Smrg * numvectors - number of float[3]'s in vectors
15932001f49Smrg * epsilon    - maximum difference between vectors
16032001f49Smrg *
16132001f49Smrg */
16232001f49Smrgstatic float*
16332001f49Smrg_glmWeldVectors(float* vectors, uint* numvectors, float epsilon)
16432001f49Smrg{
16532001f49Smrg  float* copies;
16632001f49Smrg  uint   copied;
16732001f49Smrg  uint   i, j;
16832001f49Smrg
16932001f49Smrg  copies = (float*)malloc(sizeof(float) * 3 * (*numvectors + 1));
17032001f49Smrg  memcpy(copies, vectors, (sizeof(float) * 3 * (*numvectors + 1)));
17132001f49Smrg
17232001f49Smrg  copied = 1;
17332001f49Smrg  for (i = 1; i <= *numvectors; i++) {
17432001f49Smrg    for (j = 1; j <= copied; j++) {
17532001f49Smrg      if (_glmEqual(&vectors[3 * i], &copies[3 * j], epsilon)) {
17632001f49Smrg	goto duplicate;
17732001f49Smrg      }
17832001f49Smrg    }
17932001f49Smrg
18032001f49Smrg    /* must not be any duplicates -- add to the copies array */
18132001f49Smrg    copies[3 * copied + 0] = vectors[3 * i + 0];
18232001f49Smrg    copies[3 * copied + 1] = vectors[3 * i + 1];
18332001f49Smrg    copies[3 * copied + 2] = vectors[3 * i + 2];
18432001f49Smrg    j = copied;				/* pass this along for below */
18532001f49Smrg    copied++;
18632001f49Smrg
18732001f49Smrg  duplicate:
18832001f49Smrg    /* set the first component of this vector to point at the correct
18932001f49Smrg       index into the new copies array */
19032001f49Smrg    vectors[3 * i + 0] = (float)j;
19132001f49Smrg  }
19232001f49Smrg
19332001f49Smrg  *numvectors = copied-1;
19432001f49Smrg  return copies;
19532001f49Smrg}
19632001f49Smrg
19732001f49Smrg/* _glmFindGroup: Find a group in the model
19832001f49Smrg */
19932001f49Smrgstatic GLMgroup*
20032001f49Smrg_glmFindGroup(GLMmodel* model, char* name)
20132001f49Smrg{
20232001f49Smrg  GLMgroup* group;
20332001f49Smrg
20432001f49Smrg  assert(model);
20532001f49Smrg
20632001f49Smrg  group = model->groups;
20732001f49Smrg  while(group) {
20832001f49Smrg    if (!strcmp(name, group->name))
20932001f49Smrg      break;
21032001f49Smrg    group = group->next;
21132001f49Smrg  }
21232001f49Smrg
21332001f49Smrg  return group;
21432001f49Smrg}
21532001f49Smrg
21632001f49Smrg/* _glmAddGroup: Add a group to the model
21732001f49Smrg */
21832001f49Smrgstatic GLMgroup*
21932001f49Smrg_glmAddGroup(GLMmodel* model, char* name)
22032001f49Smrg{
22132001f49Smrg  GLMgroup* group;
22232001f49Smrg
22332001f49Smrg  group = _glmFindGroup(model, name);
22432001f49Smrg  if (!group) {
22532001f49Smrg    group = (GLMgroup*)malloc(sizeof(GLMgroup));
22632001f49Smrg    group->name = stralloc(name);
22732001f49Smrg    group->material = 0;
22832001f49Smrg    group->numtriangles = 0;
22932001f49Smrg    group->triangles = NULL;
23032001f49Smrg    group->next = model->groups;
23132001f49Smrg    model->groups = group;
23232001f49Smrg    model->numgroups++;
23332001f49Smrg  }
23432001f49Smrg
23532001f49Smrg  return group;
23632001f49Smrg}
23732001f49Smrg
23832001f49Smrg/* _glmFindGroup: Find a material in the model
23932001f49Smrg */
24032001f49Smrgstatic uint
24132001f49Smrg_glmFindMaterial(GLMmodel* model, char* name)
24232001f49Smrg{
24332001f49Smrg  uint i;
24432001f49Smrg
24532001f49Smrg  for (i = 0; i < model->nummaterials; i++) {
24632001f49Smrg    if (!strcmp(model->materials[i].name, name))
24732001f49Smrg      goto found;
24832001f49Smrg  }
24932001f49Smrg
25032001f49Smrg  /* didn't find the name, so set it as the default material */
25132001f49Smrg  printf("_glmFindMaterial():  can't find material \"%s\".\n", name);
25232001f49Smrg  i = 0;
25332001f49Smrg
25432001f49Smrgfound:
25532001f49Smrg  return i;
25632001f49Smrg}
25732001f49Smrg
25832001f49Smrg
25932001f49Smrg/* _glmDirName: return the directory given a path
26032001f49Smrg *
26132001f49Smrg * path - filesystem path
26232001f49Smrg *
26332001f49Smrg * The return value should be free'd.
26432001f49Smrg */
26532001f49Smrgstatic char*
26632001f49Smrg_glmDirName(char* path)
26732001f49Smrg{
26832001f49Smrg  char* dir;
26932001f49Smrg  char* s;
27032001f49Smrg
27132001f49Smrg  dir = stralloc(path);
27232001f49Smrg
27332001f49Smrg  s = strrchr(dir, '/');
27432001f49Smrg  if (s)
27532001f49Smrg    s[1] = '\0';
27632001f49Smrg  else
27732001f49Smrg    dir[0] = '\0';
27832001f49Smrg
27932001f49Smrg  return dir;
28032001f49Smrg}
28132001f49Smrg
28232001f49Smrg
28332001f49Smrg/* _glmReadMTL: read a wavefront material library file
28432001f49Smrg *
28532001f49Smrg * model - properly initialized GLMmodel structure
28632001f49Smrg * name  - name of the material library
28732001f49Smrg */
28832001f49Smrgstatic void
28932001f49Smrg_glmReadMTL(GLMmodel* model, char* name)
29032001f49Smrg{
29132001f49Smrg  FILE* file;
29232001f49Smrg  char* dir;
29332001f49Smrg  char* filename;
29432001f49Smrg  char  buf[128], buf2[128];
29532001f49Smrg  uint nummaterials, i;
29632001f49Smrg  GLMmaterial *mat;
29732001f49Smrg
29832001f49Smrg  dir = _glmDirName(model->pathname);
29932001f49Smrg  filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(name) + 1));
30032001f49Smrg  strcpy(filename, dir);
30132001f49Smrg  strcat(filename, name);
30232001f49Smrg  free(dir);
30332001f49Smrg
30432001f49Smrg  /* open the file */
30532001f49Smrg  file = fopen(filename, "r");
30632001f49Smrg  if (!file) {
30732001f49Smrg    fprintf(stderr, "_glmReadMTL() failed: can't open material file \"%s\".\n",
30832001f49Smrg	    filename);
30932001f49Smrg    exit(1);
31032001f49Smrg  }
31132001f49Smrg  free(filename);
31232001f49Smrg
31332001f49Smrg  /* count the number of materials in the file */
31432001f49Smrg  nummaterials = 1;
31532001f49Smrg  while(fscanf(file, "%s", buf) != EOF) {
31632001f49Smrg    switch(buf[0]) {
31732001f49Smrg    case '#':				/* comment */
31832001f49Smrg      /* eat up rest of line */
31932001f49Smrg      fgets(buf, sizeof(buf), file);
32032001f49Smrg      break;
32132001f49Smrg    case 'n':				/* newmtl */
32232001f49Smrg      fgets(buf, sizeof(buf), file);
32332001f49Smrg      nummaterials++;
32432001f49Smrg      sscanf(buf, "%s %s", buf, buf);
32532001f49Smrg      break;
32632001f49Smrg    default:
32732001f49Smrg      /* eat up rest of line */
32832001f49Smrg      fgets(buf, sizeof(buf), file);
32932001f49Smrg      break;
33032001f49Smrg    }
33132001f49Smrg  }
33232001f49Smrg
33332001f49Smrg  rewind(file);
33432001f49Smrg
33532001f49Smrg  /* allocate memory for the materials */
33632001f49Smrg  model->materials = (GLMmaterial*)calloc(nummaterials, sizeof(GLMmaterial));
33732001f49Smrg  model->nummaterials = nummaterials;
33832001f49Smrg
33932001f49Smrg  /* set the default material */
34032001f49Smrg  for (i = 0; i < nummaterials; i++) {
34132001f49Smrg    model->materials[i].name = NULL;
34232001f49Smrg    model->materials[i].shininess = 0;
34332001f49Smrg    model->materials[i].diffuse[0] = 0.8;
34432001f49Smrg    model->materials[i].diffuse[1] = 0.8;
34532001f49Smrg    model->materials[i].diffuse[2] = 0.8;
34632001f49Smrg    model->materials[i].diffuse[3] = 1.0;
34732001f49Smrg    model->materials[i].ambient[0] = 0.2;
34832001f49Smrg    model->materials[i].ambient[1] = 0.2;
34932001f49Smrg    model->materials[i].ambient[2] = 0.2;
35032001f49Smrg    model->materials[i].ambient[3] = 0.0;
35132001f49Smrg    model->materials[i].specular[0] = 0.0;
35232001f49Smrg    model->materials[i].specular[1] = 0.0;
35332001f49Smrg    model->materials[i].specular[2] = 0.0;
35432001f49Smrg    model->materials[i].specular[3] = 0.0;
35532001f49Smrg  }
35632001f49Smrg  model->materials[0].name = stralloc("default");
35732001f49Smrg
35832001f49Smrg  /* now, read in the data */
35932001f49Smrg  nummaterials = 0;
36032001f49Smrg
36132001f49Smrg  mat = &model->materials[nummaterials];
36232001f49Smrg
36332001f49Smrg  while(fscanf(file, "%s", buf) != EOF) {
36432001f49Smrg    switch(buf[0]) {
36532001f49Smrg    case '#':				/* comment */
36632001f49Smrg      /* eat up rest of line */
36732001f49Smrg      fgets(buf, sizeof(buf), file);
36832001f49Smrg      break;
36932001f49Smrg    case 'n':				/* newmtl */
37032001f49Smrg      fgets(buf, sizeof(buf), file);
37132001f49Smrg      sscanf(buf, "%s %s", buf, buf);
37232001f49Smrg      nummaterials++;
37332001f49Smrg      model->materials[nummaterials].name = stralloc(buf);
37432001f49Smrg      break;
37532001f49Smrg    case 'N':
37632001f49Smrg      fscanf(file, "%f", &model->materials[nummaterials].shininess);
37732001f49Smrg      /* wavefront shininess is from [0, 1000], so scale for OpenGL */
37832001f49Smrg      model->materials[nummaterials].shininess /= 1000.0;
37932001f49Smrg      model->materials[nummaterials].shininess *= 128.0;
38032001f49Smrg      mat = &model->materials[nummaterials];
38132001f49Smrg      break;
38232001f49Smrg    case 'K':
38332001f49Smrg      switch(buf[1]) {
38432001f49Smrg      case 'd':
38532001f49Smrg	fscanf(file, "%f %f %f",
38632001f49Smrg	       &model->materials[nummaterials].diffuse[0],
38732001f49Smrg	       &model->materials[nummaterials].diffuse[1],
38832001f49Smrg	       &model->materials[nummaterials].diffuse[2]);
38932001f49Smrg	break;
39032001f49Smrg      case 's':
39132001f49Smrg	fscanf(file, "%f %f %f",
39232001f49Smrg	       &model->materials[nummaterials].specular[0],
39332001f49Smrg	       &model->materials[nummaterials].specular[1],
39432001f49Smrg	       &model->materials[nummaterials].specular[2]);
39532001f49Smrg	break;
39632001f49Smrg      case 'a':
39732001f49Smrg	fscanf(file, "%f %f %f",
39832001f49Smrg	       &model->materials[nummaterials].ambient[0],
39932001f49Smrg	       &model->materials[nummaterials].ambient[1],
40032001f49Smrg	       &model->materials[nummaterials].ambient[2]);
40132001f49Smrg	break;
40232001f49Smrg      default:
40332001f49Smrg	/* eat up rest of line */
40432001f49Smrg	fgets(buf, sizeof(buf), file);
40532001f49Smrg	break;
40632001f49Smrg      }
40732001f49Smrg      break;
40832001f49Smrg    case 'd':  /* alpha? */
40932001f49Smrg	fscanf(file, "%f",
41032001f49Smrg	       &model->materials[nummaterials].diffuse[3]);
41132001f49Smrg        break;
41232001f49Smrg    case 'm':  /* texture map */
41332001f49Smrg       fscanf(file, "%s", buf2);
41432001f49Smrg       /*printf("map %s\n", buf2);*/
41532001f49Smrg       mat->map_kd = strdup(buf2);
41632001f49Smrg       break;
41732001f49Smrg
41832001f49Smrg    default:
41932001f49Smrg      /* eat up rest of line */
42032001f49Smrg      fgets(buf, sizeof(buf), file);
42132001f49Smrg      break;
42232001f49Smrg    }
42332001f49Smrg  }
42432001f49Smrg  fclose(file);
42532001f49Smrg}
42632001f49Smrg
42732001f49Smrg
42832001f49Smrg/* _glmWriteMTL: write a wavefront material library file
42932001f49Smrg *
43032001f49Smrg * model      - properly initialized GLMmodel structure
43132001f49Smrg * modelpath  - pathname of the model being written
43232001f49Smrg * mtllibname - name of the material library to be written
43332001f49Smrg */
43432001f49Smrgstatic void
43532001f49Smrg_glmWriteMTL(GLMmodel* model, char* modelpath, char* mtllibname)
43632001f49Smrg{
43732001f49Smrg  FILE* file;
43832001f49Smrg  char* dir;
43932001f49Smrg  char* filename;
44032001f49Smrg  GLMmaterial* material;
44132001f49Smrg  uint i;
44232001f49Smrg
44332001f49Smrg  dir = _glmDirName(modelpath);
44432001f49Smrg  filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(mtllibname)));
44532001f49Smrg  strcpy(filename, dir);
44632001f49Smrg  strcat(filename, mtllibname);
44732001f49Smrg  free(dir);
44832001f49Smrg
44932001f49Smrg  /* open the file */
45032001f49Smrg  file = fopen(filename, "w");
45132001f49Smrg  if (!file) {
45232001f49Smrg    fprintf(stderr, "_glmWriteMTL() failed: can't open file \"%s\".\n",
45332001f49Smrg	    filename);
45432001f49Smrg    exit(1);
45532001f49Smrg  }
45632001f49Smrg  free(filename);
45732001f49Smrg
45832001f49Smrg  /* spit out a header */
45932001f49Smrg  fprintf(file, "#  \n");
46032001f49Smrg  fprintf(file, "#  Wavefront MTL generated by GLM library\n");
46132001f49Smrg  fprintf(file, "#  \n");
46232001f49Smrg  fprintf(file, "#  GLM library copyright (C) 1997 by Nate Robins\n");
46332001f49Smrg  fprintf(file, "#  email: ndr@pobox.com\n");
46432001f49Smrg  fprintf(file, "#  www:   http://www.pobox.com/~ndr\n");
46532001f49Smrg  fprintf(file, "#  \n\n");
46632001f49Smrg
46732001f49Smrg  for (i = 0; i < model->nummaterials; i++) {
46832001f49Smrg    material = &model->materials[i];
46932001f49Smrg    fprintf(file, "newmtl %s\n", material->name);
47032001f49Smrg    fprintf(file, "Ka %f %f %f\n",
47132001f49Smrg	    material->ambient[0], material->ambient[1], material->ambient[2]);
47232001f49Smrg    fprintf(file, "Kd %f %f %f\n",
47332001f49Smrg	    material->diffuse[0], material->diffuse[1], material->diffuse[2]);
47432001f49Smrg    fprintf(file, "Ks %f %f %f\n",
47532001f49Smrg	    material->specular[0],material->specular[1],material->specular[2]);
47632001f49Smrg    fprintf(file, "Ns %f\n", material->shininess);
47732001f49Smrg    fprintf(file, "\n");
47832001f49Smrg  }
47932001f49Smrg  fclose(file);
48032001f49Smrg}
48132001f49Smrg
48232001f49Smrg
48332001f49Smrg/* _glmFirstPass: first pass at a Wavefront OBJ file that gets all the
48432001f49Smrg * statistics of the model (such as #vertices, #normals, etc)
48532001f49Smrg *
48632001f49Smrg * model - properly initialized GLMmodel structure
48732001f49Smrg * file  - (fopen'd) file descriptor
48832001f49Smrg */
48932001f49Smrgstatic void
49032001f49Smrg_glmFirstPass(GLMmodel* model, FILE* file)
49132001f49Smrg{
49232001f49Smrg  uint    numvertices;		/* number of vertices in model */
49332001f49Smrg  uint    numnormals;			/* number of normals in model */
49432001f49Smrg  uint    numtexcoords;		/* number of texcoords in model */
49532001f49Smrg  uint    numtriangles;		/* number of triangles in model */
49632001f49Smrg  GLMgroup* group;			/* current group */
49732001f49Smrg  unsigned  v, n, t;
49832001f49Smrg  char      buf[128];
49932001f49Smrg
50032001f49Smrg  /* make a default group */
50132001f49Smrg  group = _glmAddGroup(model, "default");
50232001f49Smrg
50332001f49Smrg  numvertices = numnormals = numtexcoords = numtriangles = 0;
50432001f49Smrg  while(fscanf(file, "%s", buf) != EOF) {
50532001f49Smrg    switch(buf[0]) {
50632001f49Smrg    case '#':				/* comment */
50732001f49Smrg      /* eat up rest of line */
50832001f49Smrg      fgets(buf, sizeof(buf), file);
50932001f49Smrg      break;
51032001f49Smrg    case 'v':				/* v, vn, vt */
51132001f49Smrg      switch(buf[1]) {
51232001f49Smrg      case '\0':			/* vertex */
51332001f49Smrg	/* eat up rest of line */
51432001f49Smrg	fgets(buf, sizeof(buf), file);
51532001f49Smrg	numvertices++;
51632001f49Smrg	break;
51732001f49Smrg      case 'n':				/* normal */
51832001f49Smrg	/* eat up rest of line */
51932001f49Smrg	fgets(buf, sizeof(buf), file);
52032001f49Smrg	numnormals++;
52132001f49Smrg	break;
52232001f49Smrg      case 't':				/* texcoord */
52332001f49Smrg	/* eat up rest of line */
52432001f49Smrg	fgets(buf, sizeof(buf), file);
52532001f49Smrg	numtexcoords++;
52632001f49Smrg	break;
52732001f49Smrg      default:
52832001f49Smrg	printf("_glmFirstPass(): Unknown token \"%s\".\n", buf);
52932001f49Smrg	exit(1);
53032001f49Smrg	break;
53132001f49Smrg      }
53232001f49Smrg      break;
53332001f49Smrg    case 'm':
53432001f49Smrg      fgets(buf, sizeof(buf), file);
53532001f49Smrg      sscanf(buf, "%s %s", buf, buf);
53632001f49Smrg      model->mtllibname = stralloc(buf);
53732001f49Smrg      _glmReadMTL(model, buf);
53832001f49Smrg      break;
53932001f49Smrg    case 'u':
54032001f49Smrg      /* eat up rest of line */
54132001f49Smrg      fgets(buf, sizeof(buf), file);
54232001f49Smrg      break;
54332001f49Smrg    case 'g':				/* group */
54432001f49Smrg      /* eat up rest of line */
54532001f49Smrg      fgets(buf, sizeof(buf), file);
54632001f49Smrg      sscanf(buf, "%s", buf);
54732001f49Smrg      group = _glmAddGroup(model, buf);
54832001f49Smrg      break;
54932001f49Smrg    case 'f':				/* face */
55032001f49Smrg      v = n = t = 0;
55132001f49Smrg      fscanf(file, "%s", buf);
55232001f49Smrg      /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
55332001f49Smrg      if (strstr(buf, "//")) {
55432001f49Smrg	/* v//n */
55532001f49Smrg	sscanf(buf, "%u//%u", &v, &n);
55632001f49Smrg	fscanf(file, "%u//%u", &v, &n);
55732001f49Smrg	fscanf(file, "%u//%u", &v, &n);
55832001f49Smrg	numtriangles++;
55932001f49Smrg	group->numtriangles++;
56032001f49Smrg	while(fscanf(file, "%u//%u", &v, &n) > 0) {
56132001f49Smrg	  numtriangles++;
56232001f49Smrg	  group->numtriangles++;
56332001f49Smrg	}
56432001f49Smrg      } else if (sscanf(buf, "%u/%u/%u", &v, &t, &n) == 3) {
56532001f49Smrg	/* v/t/n */
56632001f49Smrg	fscanf(file, "%u/%u/%u", &v, &t, &n);
56732001f49Smrg	fscanf(file, "%u/%u/%u", &v, &t, &n);
56832001f49Smrg	numtriangles++;
56932001f49Smrg	group->numtriangles++;
57032001f49Smrg	while(fscanf(file, "%u/%u/%u", &v, &t, &n) > 0) {
57132001f49Smrg	  numtriangles++;
57232001f49Smrg	  group->numtriangles++;
57332001f49Smrg	}
57432001f49Smrg      } else if (sscanf(buf, "%u/%u", &v, &t) == 2) {
57532001f49Smrg	/* v/t */
57632001f49Smrg	fscanf(file, "%u/%u", &v, &t);
57732001f49Smrg	fscanf(file, "%u/%u", &v, &t);
57832001f49Smrg	numtriangles++;
57932001f49Smrg	group->numtriangles++;
58032001f49Smrg	while(fscanf(file, "%u/%u", &v, &t) > 0) {
58132001f49Smrg	  numtriangles++;
58232001f49Smrg	  group->numtriangles++;
58332001f49Smrg	}
58432001f49Smrg      } else {
58532001f49Smrg	/* v */
58632001f49Smrg	fscanf(file, "%u", &v);
58732001f49Smrg	fscanf(file, "%u", &v);
58832001f49Smrg	numtriangles++;
58932001f49Smrg	group->numtriangles++;
59032001f49Smrg	while(fscanf(file, "%u", &v) > 0) {
59132001f49Smrg	  numtriangles++;
59232001f49Smrg	  group->numtriangles++;
59332001f49Smrg	}
59432001f49Smrg      }
59532001f49Smrg      break;
59632001f49Smrg
59732001f49Smrg    default:
59832001f49Smrg      /* eat up rest of line */
59932001f49Smrg      fgets(buf, sizeof(buf), file);
60032001f49Smrg      break;
60132001f49Smrg    }
60232001f49Smrg  }
60332001f49Smrg
60432001f49Smrg#if 0
60532001f49Smrg  /* announce the model statistics */
60632001f49Smrg  printf(" Vertices: %d\n", numvertices);
60732001f49Smrg  printf(" Normals: %d\n", numnormals);
60832001f49Smrg  printf(" Texcoords: %d\n", numtexcoords);
60932001f49Smrg  printf(" Triangles: %d\n", numtriangles);
61032001f49Smrg  printf(" Groups: %d\n", model->numgroups);
61132001f49Smrg#endif
61232001f49Smrg
61332001f49Smrg  /* set the stats in the model structure */
61432001f49Smrg  model->numvertices  = numvertices;
61532001f49Smrg  model->numnormals   = numnormals;
61632001f49Smrg  model->numtexcoords = numtexcoords;
61732001f49Smrg  model->numtriangles = numtriangles;
61832001f49Smrg
61932001f49Smrg  /* allocate memory for the triangles in each group */
62032001f49Smrg  group = model->groups;
62132001f49Smrg  while(group) {
62232001f49Smrg    group->triangles = (uint*)malloc(sizeof(uint) * group->numtriangles);
62332001f49Smrg    group->numtriangles = 0;
62432001f49Smrg    group = group->next;
62532001f49Smrg  }
62632001f49Smrg}
62732001f49Smrg
62832001f49Smrg/* _glmSecondPass: second pass at a Wavefront OBJ file that gets all
62932001f49Smrg * the data.
63032001f49Smrg *
63132001f49Smrg * model - properly initialized GLMmodel structure
63232001f49Smrg * file  - (fopen'd) file descriptor
63332001f49Smrg */
63432001f49Smrgstatic void
63532001f49Smrg_glmSecondPass(GLMmodel* model, FILE* file)
63632001f49Smrg{
63732001f49Smrg  uint    numvertices;		/* number of vertices in model */
63832001f49Smrg  uint    numnormals;			/* number of normals in model */
63932001f49Smrg  uint    numtexcoords;		/* number of texcoords in model */
64032001f49Smrg  uint    numtriangles;		/* number of triangles in model */
64132001f49Smrg  float*  vertices;			/* array of vertices  */
64232001f49Smrg  float*  normals;			/* array of normals */
64332001f49Smrg  float*  texcoords;			/* array of texture coordinates */
64432001f49Smrg  GLMgroup* group;			/* current group pointer */
64532001f49Smrg  uint    material;			/* current material */
64632001f49Smrg  uint    v, n, t;
64732001f49Smrg  char      buf[128];
64832001f49Smrg
64932001f49Smrg  /* set the pointer shortcuts */
65032001f49Smrg  vertices     = model->vertices;
65132001f49Smrg  normals      = model->normals;
65232001f49Smrg  texcoords    = model->texcoords;
65332001f49Smrg  group        = model->groups;
65432001f49Smrg
65532001f49Smrg  /* on the second pass through the file, read all the data into the
65632001f49Smrg     allocated arrays */
65732001f49Smrg  numvertices = numnormals = numtexcoords = 1;
65832001f49Smrg  numtriangles = 0;
65932001f49Smrg  material = 0;
66032001f49Smrg  while(fscanf(file, "%s", buf) != EOF) {
66132001f49Smrg    switch(buf[0]) {
66232001f49Smrg    case '#':				/* comment */
66332001f49Smrg      /* eat up rest of line */
66432001f49Smrg      fgets(buf, sizeof(buf), file);
66532001f49Smrg      break;
66632001f49Smrg    case 'v':				/* v, vn, vt */
66732001f49Smrg      switch(buf[1]) {
66832001f49Smrg      case '\0':			/* vertex */
66932001f49Smrg	fscanf(file, "%f %f %f",
67032001f49Smrg	       &vertices[3 * numvertices + X],
67132001f49Smrg	       &vertices[3 * numvertices + Y],
67232001f49Smrg	       &vertices[3 * numvertices + Z]);
67332001f49Smrg	numvertices++;
67432001f49Smrg	break;
67532001f49Smrg      case 'n':				/* normal */
67632001f49Smrg	fscanf(file, "%f %f %f",
67732001f49Smrg	       &normals[3 * numnormals + X],
67832001f49Smrg	       &normals[3 * numnormals + Y],
67932001f49Smrg	       &normals[3 * numnormals + Z]);
68032001f49Smrg	numnormals++;
68132001f49Smrg	break;
68232001f49Smrg      case 't':				/* texcoord */
68332001f49Smrg	fscanf(file, "%f %f",
68432001f49Smrg	       &texcoords[2 * numtexcoords + X],
68532001f49Smrg	       &texcoords[2 * numtexcoords + Y]);
68632001f49Smrg	numtexcoords++;
68732001f49Smrg	break;
68832001f49Smrg      }
68932001f49Smrg      break;
69032001f49Smrg    case 'u':
69132001f49Smrg      fgets(buf, sizeof(buf), file);
69232001f49Smrg      sscanf(buf, "%s %s", buf, buf);
69332001f49Smrg      material = _glmFindMaterial(model, buf);
69432001f49Smrg      if (!group->material)
69532001f49Smrg         group->material = material;
69632001f49Smrg      /*printf("material %s = %u\n", buf, material);*/
69732001f49Smrg      break;
69832001f49Smrg    case 'g':				/* group */
69932001f49Smrg      /* eat up rest of line */
70032001f49Smrg      fgets(buf, sizeof(buf), file);
70132001f49Smrg      sscanf(buf, "%s", buf);
70232001f49Smrg      group = _glmFindGroup(model, buf);
70332001f49Smrg      group->material = material;
70432001f49Smrg      /*printf("GROUP %s  material %u\n", buf, material);*/
70532001f49Smrg      break;
70632001f49Smrg    case 'f':				/* face */
70732001f49Smrg      v = n = t = 0;
70832001f49Smrg      fscanf(file, "%s", buf);
70932001f49Smrg      /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
71032001f49Smrg      if (strstr(buf, "//")) {
71132001f49Smrg	/* v//n */
71232001f49Smrg	sscanf(buf, "%u//%u", &v, &n);
71332001f49Smrg	T(numtriangles).vindices[0] = v;
71432001f49Smrg	T(numtriangles).nindices[0] = n;
71532001f49Smrg	fscanf(file, "%u//%u", &v, &n);
71632001f49Smrg	T(numtriangles).vindices[1] = v;
71732001f49Smrg	T(numtriangles).nindices[1] = n;
71832001f49Smrg	fscanf(file, "%u//%u", &v, &n);
71932001f49Smrg	T(numtriangles).vindices[2] = v;
72032001f49Smrg	T(numtriangles).nindices[2] = n;
72132001f49Smrg	group->triangles[group->numtriangles++] = numtriangles;
72232001f49Smrg	numtriangles++;
72332001f49Smrg	while(fscanf(file, "%u//%u", &v, &n) > 0) {
72432001f49Smrg	  T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
72532001f49Smrg	  T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
72632001f49Smrg	  T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
72732001f49Smrg	  T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
72832001f49Smrg	  T(numtriangles).vindices[2] = v;
72932001f49Smrg	  T(numtriangles).nindices[2] = n;
73032001f49Smrg	  group->triangles[group->numtriangles++] = numtriangles;
73132001f49Smrg	  numtriangles++;
73232001f49Smrg	}
73332001f49Smrg      } else if (sscanf(buf, "%u/%u/%u", &v, &t, &n) == 3) {
73432001f49Smrg	/* v/t/n */
73532001f49Smrg	T(numtriangles).vindices[0] = v;
73632001f49Smrg	T(numtriangles).tindices[0] = t;
73732001f49Smrg	T(numtriangles).nindices[0] = n;
73832001f49Smrg	fscanf(file, "%u/%u/%u", &v, &t, &n);
73932001f49Smrg	T(numtriangles).vindices[1] = v;
74032001f49Smrg	T(numtriangles).tindices[1] = t;
74132001f49Smrg	T(numtriangles).nindices[1] = n;
74232001f49Smrg	fscanf(file, "%u/%u/%u", &v, &t, &n);
74332001f49Smrg	T(numtriangles).vindices[2] = v;
74432001f49Smrg	T(numtriangles).tindices[2] = t;
74532001f49Smrg	T(numtriangles).nindices[2] = n;
74632001f49Smrg	group->triangles[group->numtriangles++] = numtriangles;
74732001f49Smrg	numtriangles++;
74832001f49Smrg	while(fscanf(file, "%u/%u/%u", &v, &t, &n) > 0) {
74932001f49Smrg	  T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
75032001f49Smrg	  T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
75132001f49Smrg	  T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
75232001f49Smrg	  T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
75332001f49Smrg	  T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
75432001f49Smrg	  T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
75532001f49Smrg	  T(numtriangles).vindices[2] = v;
75632001f49Smrg	  T(numtriangles).tindices[2] = t;
75732001f49Smrg	  T(numtriangles).nindices[2] = n;
75832001f49Smrg	  group->triangles[group->numtriangles++] = numtriangles;
75932001f49Smrg	  numtriangles++;
76032001f49Smrg	}
76132001f49Smrg      } else if (sscanf(buf, "%u/%u", &v, &t) == 2) {
76232001f49Smrg	/* v/t */
76332001f49Smrg	T(numtriangles).vindices[0] = v;
76432001f49Smrg	T(numtriangles).tindices[0] = t;
76532001f49Smrg	fscanf(file, "%u/%u", &v, &t);
76632001f49Smrg	T(numtriangles).vindices[1] = v;
76732001f49Smrg	T(numtriangles).tindices[1] = t;
76832001f49Smrg	fscanf(file, "%u/%u", &v, &t);
76932001f49Smrg	T(numtriangles).vindices[2] = v;
77032001f49Smrg	T(numtriangles).tindices[2] = t;
77132001f49Smrg	group->triangles[group->numtriangles++] = numtriangles;
77232001f49Smrg	numtriangles++;
77332001f49Smrg	while(fscanf(file, "%u/%u", &v, &t) > 0) {
77432001f49Smrg	  T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
77532001f49Smrg	  T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
77632001f49Smrg	  T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
77732001f49Smrg	  T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
77832001f49Smrg	  T(numtriangles).vindices[2] = v;
77932001f49Smrg	  T(numtriangles).tindices[2] = t;
78032001f49Smrg	  group->triangles[group->numtriangles++] = numtriangles;
78132001f49Smrg	  numtriangles++;
78232001f49Smrg	}
78332001f49Smrg      } else {
78432001f49Smrg	/* v */
78532001f49Smrg	sscanf(buf, "%u", &v);
78632001f49Smrg	T(numtriangles).vindices[0] = v;
78732001f49Smrg	fscanf(file, "%u", &v);
78832001f49Smrg	T(numtriangles).vindices[1] = v;
78932001f49Smrg	fscanf(file, "%u", &v);
79032001f49Smrg	T(numtriangles).vindices[2] = v;
79132001f49Smrg	group->triangles[group->numtriangles++] = numtriangles;
79232001f49Smrg	numtriangles++;
79332001f49Smrg	while(fscanf(file, "%u", &v) > 0) {
79432001f49Smrg	  T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
79532001f49Smrg	  T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
79632001f49Smrg	  T(numtriangles).vindices[2] = v;
79732001f49Smrg	  group->triangles[group->numtriangles++] = numtriangles;
79832001f49Smrg	  numtriangles++;
79932001f49Smrg	}
80032001f49Smrg      }
80132001f49Smrg      break;
80232001f49Smrg
80332001f49Smrg    default:
80432001f49Smrg      /* eat up rest of line */
80532001f49Smrg      fgets(buf, sizeof(buf), file);
80632001f49Smrg      break;
80732001f49Smrg    }
80832001f49Smrg  }
80932001f49Smrg
81032001f49Smrg#if 0
81132001f49Smrg  /* announce the memory requirements */
81232001f49Smrg  printf(" Memory: %d bytes\n",
81332001f49Smrg	 numvertices  * 3*sizeof(float) +
81432001f49Smrg	 numnormals   * 3*sizeof(float) * (numnormals ? 1 : 0) +
81532001f49Smrg	 numtexcoords * 3*sizeof(float) * (numtexcoords ? 1 : 0) +
81632001f49Smrg	 numtriangles * sizeof(GLMtriangle));
81732001f49Smrg#endif
81832001f49Smrg}
81932001f49Smrg
82032001f49Smrg
82132001f49Smrg
82232001f49Smrg
82332001f49Smrg/* public functions */
82432001f49Smrg
82532001f49Smrg/* glmUnitize: "unitize" a model by translating it to the origin and
82632001f49Smrg * scaling it to fit in a unit cube around the origin.  Returns the
82732001f49Smrg * scalefactor used.
82832001f49Smrg *
82932001f49Smrg * model - properly initialized GLMmodel structure
83032001f49Smrg */
83132001f49Smrgfloat
83232001f49SmrgglmUnitize(GLMmodel* model)
83332001f49Smrg{
83432001f49Smrg  uint  i;
83532001f49Smrg  float maxx, minx, maxy, miny, maxz, minz;
83632001f49Smrg  float cx, cy, cz, w, h, d;
83732001f49Smrg  float scale;
83832001f49Smrg
83932001f49Smrg  assert(model);
84032001f49Smrg  assert(model->vertices);
84132001f49Smrg
84232001f49Smrg  /* get the max/mins */
84332001f49Smrg  maxx = minx = model->vertices[3 + X];
84432001f49Smrg  maxy = miny = model->vertices[3 + Y];
84532001f49Smrg  maxz = minz = model->vertices[3 + Z];
84632001f49Smrg  for (i = 1; i <= model->numvertices; i++) {
84732001f49Smrg    if (maxx < model->vertices[3 * i + X])
84832001f49Smrg      maxx = model->vertices[3 * i + X];
84932001f49Smrg    if (minx > model->vertices[3 * i + X])
85032001f49Smrg      minx = model->vertices[3 * i + X];
85132001f49Smrg
85232001f49Smrg    if (maxy < model->vertices[3 * i + Y])
85332001f49Smrg      maxy = model->vertices[3 * i + Y];
85432001f49Smrg    if (miny > model->vertices[3 * i + Y])
85532001f49Smrg      miny = model->vertices[3 * i + Y];
85632001f49Smrg
85732001f49Smrg    if (maxz < model->vertices[3 * i + Z])
85832001f49Smrg      maxz = model->vertices[3 * i + Z];
85932001f49Smrg    if (minz > model->vertices[3 * i + Z])
86032001f49Smrg      minz = model->vertices[3 * i + Z];
86132001f49Smrg  }
86232001f49Smrg
86332001f49Smrg  /* calculate model width, height, and depth */
86432001f49Smrg  w = _glmAbs(maxx) + _glmAbs(minx);
86532001f49Smrg  h = _glmAbs(maxy) + _glmAbs(miny);
86632001f49Smrg  d = _glmAbs(maxz) + _glmAbs(minz);
86732001f49Smrg
86832001f49Smrg  /* calculate center of the model */
86932001f49Smrg  cx = (maxx + minx) / 2.0;
87032001f49Smrg  cy = (maxy + miny) / 2.0;
87132001f49Smrg  cz = (maxz + minz) / 2.0;
87232001f49Smrg
87332001f49Smrg  /* calculate unitizing scale factor */
87432001f49Smrg  scale = 2.0 / _glmMax(_glmMax(w, h), d);
87532001f49Smrg
87632001f49Smrg  /* translate around center then scale */
87732001f49Smrg  for (i = 1; i <= model->numvertices; i++) {
87832001f49Smrg    model->vertices[3 * i + X] -= cx;
87932001f49Smrg    model->vertices[3 * i + Y] -= cy;
88032001f49Smrg    model->vertices[3 * i + Z] -= cz;
88132001f49Smrg    model->vertices[3 * i + X] *= scale;
88232001f49Smrg    model->vertices[3 * i + Y] *= scale;
88332001f49Smrg    model->vertices[3 * i + Z] *= scale;
88432001f49Smrg  }
88532001f49Smrg
88632001f49Smrg  return scale;
88732001f49Smrg}
88832001f49Smrg
88932001f49Smrg/* glmDimensions: Calculates the dimensions (width, height, depth) of
89032001f49Smrg * a model.
89132001f49Smrg *
89232001f49Smrg * model      - initialized GLMmodel structure
89332001f49Smrg * dimensions - array of 3 floats (float dimensions[3])
89432001f49Smrg */
89532001f49Smrgvoid
89632001f49SmrgglmDimensions(GLMmodel* model, float* dimensions)
89732001f49Smrg{
89832001f49Smrg  uint i;
89932001f49Smrg  float maxx, minx, maxy, miny, maxz, minz;
90032001f49Smrg
90132001f49Smrg  assert(model);
90232001f49Smrg  assert(model->vertices);
90332001f49Smrg  assert(dimensions);
90432001f49Smrg
90532001f49Smrg  /* get the max/mins */
90632001f49Smrg  maxx = minx = model->vertices[3 + X];
90732001f49Smrg  maxy = miny = model->vertices[3 + Y];
90832001f49Smrg  maxz = minz = model->vertices[3 + Z];
90932001f49Smrg  for (i = 1; i <= model->numvertices; i++) {
91032001f49Smrg    if (maxx < model->vertices[3 * i + X])
91132001f49Smrg      maxx = model->vertices[3 * i + X];
91232001f49Smrg    if (minx > model->vertices[3 * i + X])
91332001f49Smrg      minx = model->vertices[3 * i + X];
91432001f49Smrg
91532001f49Smrg    if (maxy < model->vertices[3 * i + Y])
91632001f49Smrg      maxy = model->vertices[3 * i + Y];
91732001f49Smrg    if (miny > model->vertices[3 * i + Y])
91832001f49Smrg      miny = model->vertices[3 * i + Y];
91932001f49Smrg
92032001f49Smrg    if (maxz < model->vertices[3 * i + Z])
92132001f49Smrg      maxz = model->vertices[3 * i + Z];
92232001f49Smrg    if (minz > model->vertices[3 * i + Z])
92332001f49Smrg      minz = model->vertices[3 * i + Z];
92432001f49Smrg  }
92532001f49Smrg
92632001f49Smrg  /* calculate model width, height, and depth */
92732001f49Smrg  dimensions[X] = _glmAbs(maxx) + _glmAbs(minx);
92832001f49Smrg  dimensions[Y] = _glmAbs(maxy) + _glmAbs(miny);
92932001f49Smrg  dimensions[Z] = _glmAbs(maxz) + _glmAbs(minz);
93032001f49Smrg}
93132001f49Smrg
93232001f49Smrg/* glmScale: Scales a model by a given amount.
93332001f49Smrg *
93432001f49Smrg * model - properly initialized GLMmodel structure
93532001f49Smrg * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
93632001f49Smrg */
93732001f49Smrgvoid
93832001f49SmrgglmScale(GLMmodel* model, float scale)
93932001f49Smrg{
94032001f49Smrg  uint i;
94132001f49Smrg
94232001f49Smrg  for (i = 1; i <= model->numvertices; i++) {
94332001f49Smrg    model->vertices[3 * i + X] *= scale;
94432001f49Smrg    model->vertices[3 * i + Y] *= scale;
94532001f49Smrg    model->vertices[3 * i + Z] *= scale;
94632001f49Smrg  }
94732001f49Smrg}
94832001f49Smrg
94932001f49Smrg/* glmReverseWinding: Reverse the polygon winding for all polygons in
95032001f49Smrg * this model.  Default winding is counter-clockwise.  Also changes
95132001f49Smrg * the direction of the normals.
95232001f49Smrg *
95332001f49Smrg * model - properly initialized GLMmodel structure
95432001f49Smrg */
95532001f49Smrgvoid
95632001f49SmrgglmReverseWinding(GLMmodel* model)
95732001f49Smrg{
95832001f49Smrg  uint i, swap;
95932001f49Smrg
96032001f49Smrg  assert(model);
96132001f49Smrg
96232001f49Smrg  for (i = 0; i < model->numtriangles; i++) {
96332001f49Smrg    swap = T(i).vindices[0];
96432001f49Smrg    T(i).vindices[0] = T(i).vindices[2];
96532001f49Smrg    T(i).vindices[2] = swap;
96632001f49Smrg
96732001f49Smrg    if (model->numnormals) {
96832001f49Smrg      swap = T(i).nindices[0];
96932001f49Smrg      T(i).nindices[0] = T(i).nindices[2];
97032001f49Smrg      T(i).nindices[2] = swap;
97132001f49Smrg    }
97232001f49Smrg
97332001f49Smrg    if (model->numtexcoords) {
97432001f49Smrg      swap = T(i).tindices[0];
97532001f49Smrg      T(i).tindices[0] = T(i).tindices[2];
97632001f49Smrg      T(i).tindices[2] = swap;
97732001f49Smrg    }
97832001f49Smrg  }
97932001f49Smrg
98032001f49Smrg  /* reverse facet normals */
98132001f49Smrg  for (i = 1; i <= model->numfacetnorms; i++) {
98232001f49Smrg    model->facetnorms[3 * i + X] = -model->facetnorms[3 * i + X];
98332001f49Smrg    model->facetnorms[3 * i + Y] = -model->facetnorms[3 * i + Y];
98432001f49Smrg    model->facetnorms[3 * i + Z] = -model->facetnorms[3 * i + Z];
98532001f49Smrg  }
98632001f49Smrg
98732001f49Smrg  /* reverse vertex normals */
98832001f49Smrg  for (i = 1; i <= model->numnormals; i++) {
98932001f49Smrg    model->normals[3 * i + X] = -model->normals[3 * i + X];
99032001f49Smrg    model->normals[3 * i + Y] = -model->normals[3 * i + Y];
99132001f49Smrg    model->normals[3 * i + Z] = -model->normals[3 * i + Z];
99232001f49Smrg  }
99332001f49Smrg}
99432001f49Smrg
99532001f49Smrg/* glmFacetNormals: Generates facet normals for a model (by taking the
99632001f49Smrg * cross product of the two vectors derived from the sides of each
99732001f49Smrg * triangle).  Assumes a counter-clockwise winding.
99832001f49Smrg *
99932001f49Smrg * model - initialized GLMmodel structure
100032001f49Smrg */
100132001f49Smrgvoid
100232001f49SmrgglmFacetNormals(GLMmodel* model)
100332001f49Smrg{
100432001f49Smrg  uint  i;
100532001f49Smrg  float u[3];
100632001f49Smrg  float v[3];
100732001f49Smrg
100832001f49Smrg  assert(model);
100932001f49Smrg  assert(model->vertices);
101032001f49Smrg
101132001f49Smrg  /* clobber any old facetnormals */
101232001f49Smrg  if (model->facetnorms)
101332001f49Smrg    free(model->facetnorms);
101432001f49Smrg
101532001f49Smrg  /* allocate memory for the new facet normals */
101632001f49Smrg  model->numfacetnorms = model->numtriangles;
101732001f49Smrg  model->facetnorms = (float*)malloc(sizeof(float) *
101832001f49Smrg				       3 * (model->numfacetnorms + 1));
101932001f49Smrg
102032001f49Smrg  for (i = 0; i < model->numtriangles; i++) {
102132001f49Smrg    model->triangles[i].findex = i+1;
102232001f49Smrg
102332001f49Smrg    u[X] = model->vertices[3 * T(i).vindices[1] + X] -
102432001f49Smrg           model->vertices[3 * T(i).vindices[0] + X];
102532001f49Smrg    u[Y] = model->vertices[3 * T(i).vindices[1] + Y] -
102632001f49Smrg           model->vertices[3 * T(i).vindices[0] + Y];
102732001f49Smrg    u[Z] = model->vertices[3 * T(i).vindices[1] + Z] -
102832001f49Smrg           model->vertices[3 * T(i).vindices[0] + Z];
102932001f49Smrg
103032001f49Smrg    v[X] = model->vertices[3 * T(i).vindices[2] + X] -
103132001f49Smrg           model->vertices[3 * T(i).vindices[0] + X];
103232001f49Smrg    v[Y] = model->vertices[3 * T(i).vindices[2] + Y] -
103332001f49Smrg           model->vertices[3 * T(i).vindices[0] + Y];
103432001f49Smrg    v[Z] = model->vertices[3 * T(i).vindices[2] + Z] -
103532001f49Smrg           model->vertices[3 * T(i).vindices[0] + Z];
103632001f49Smrg
103732001f49Smrg    _glmCross(u, v, &model->facetnorms[3 * (i+1)]);
103832001f49Smrg    _glmNormalize(&model->facetnorms[3 * (i+1)]);
103932001f49Smrg  }
104032001f49Smrg}
104132001f49Smrg
104232001f49Smrg/* glmVertexNormals: Generates smooth vertex normals for a model.
104332001f49Smrg * First builds a list of all the triangles each vertex is in.  Then
104432001f49Smrg * loops through each vertex in the list averaging all the facet
104532001f49Smrg * normals of the triangles each vertex is in.  Finally, sets the
104632001f49Smrg * normal index in the triangle for the vertex to the generated smooth
104732001f49Smrg * normal.  If the dot product of a facet normal and the facet normal
104832001f49Smrg * associated with the first triangle in the list of triangles the
104932001f49Smrg * current vertex is in is greater than the cosine of the angle
105032001f49Smrg * parameter to the function, that facet normal is not added into the
105132001f49Smrg * average normal calculation and the corresponding vertex is given
105232001f49Smrg * the facet normal.  This tends to preserve hard edges.  The angle to
105332001f49Smrg * use depends on the model, but 90 degrees is usually a good start.
105432001f49Smrg *
105532001f49Smrg * model - initialized GLMmodel structure
105632001f49Smrg * angle - maximum angle (in degrees) to smooth across
105732001f49Smrg */
105832001f49Smrgvoid
105932001f49SmrgglmVertexNormals(GLMmodel* model, float angle)
106032001f49Smrg{
106132001f49Smrg  GLMnode*  node;
106232001f49Smrg  GLMnode*  tail;
106332001f49Smrg  GLMnode** members;
106432001f49Smrg  float*  normals;
106532001f49Smrg  uint    numnormals;
106632001f49Smrg  float   average[3];
106732001f49Smrg  float   dot, cos_angle;
106832001f49Smrg  uint    i, avg;
106932001f49Smrg
107032001f49Smrg  assert(model);
107132001f49Smrg  assert(model->facetnorms);
107232001f49Smrg
107332001f49Smrg  /* calculate the cosine of the angle (in degrees) */
107432001f49Smrg  cos_angle = cos(angle * M_PI / 180.0);
107532001f49Smrg
107632001f49Smrg  /* nuke any previous normals */
107732001f49Smrg  if (model->normals)
107832001f49Smrg    free(model->normals);
107932001f49Smrg
108032001f49Smrg  /* allocate space for new normals */
108132001f49Smrg  model->numnormals = model->numtriangles * 3; /* 3 normals per triangle */
108232001f49Smrg  model->normals = (float*)malloc(sizeof(float)* 3* (model->numnormals+1));
108332001f49Smrg
108432001f49Smrg  /* allocate a structure that will hold a linked list of triangle
108532001f49Smrg     indices for each vertex */
108632001f49Smrg  members = (GLMnode**)malloc(sizeof(GLMnode*) * (model->numvertices + 1));
108732001f49Smrg  for (i = 1; i <= model->numvertices; i++)
108832001f49Smrg    members[i] = NULL;
108932001f49Smrg
109032001f49Smrg  /* for every triangle, create a node for each vertex in it */
109132001f49Smrg  for (i = 0; i < model->numtriangles; i++) {
109232001f49Smrg    node = (GLMnode*)malloc(sizeof(GLMnode));
109332001f49Smrg    node->index = i;
109432001f49Smrg    node->next  = members[T(i).vindices[0]];
109532001f49Smrg    members[T(i).vindices[0]] = node;
109632001f49Smrg
109732001f49Smrg    node = (GLMnode*)malloc(sizeof(GLMnode));
109832001f49Smrg    node->index = i;
109932001f49Smrg    node->next  = members[T(i).vindices[1]];
110032001f49Smrg    members[T(i).vindices[1]] = node;
110132001f49Smrg
110232001f49Smrg    node = (GLMnode*)malloc(sizeof(GLMnode));
110332001f49Smrg    node->index = i;
110432001f49Smrg    node->next  = members[T(i).vindices[2]];
110532001f49Smrg    members[T(i).vindices[2]] = node;
110632001f49Smrg  }
110732001f49Smrg
110832001f49Smrg  /* calculate the average normal for each vertex */
110932001f49Smrg  numnormals = 1;
111032001f49Smrg  for (i = 1; i <= model->numvertices; i++) {
111132001f49Smrg    /* calculate an average normal for this vertex by averaging the
111232001f49Smrg       facet normal of every triangle this vertex is in */
111332001f49Smrg    node = members[i];
111432001f49Smrg    if (!node)
111532001f49Smrg      fprintf(stderr, "glmVertexNormals(): vertex w/o a triangle\n");
111632001f49Smrg    average[0] = 0.0; average[1] = 0.0; average[2] = 0.0;
111732001f49Smrg    avg = 0;
111832001f49Smrg    while (node) {
111932001f49Smrg      /* only average if the dot product of the angle between the two
112032001f49Smrg         facet normals is greater than the cosine of the threshold
112132001f49Smrg         angle -- or, said another way, the angle between the two
112232001f49Smrg         facet normals is less than (or equal to) the threshold angle */
112332001f49Smrg      dot = _glmDot(&model->facetnorms[3 * T(node->index).findex],
112432001f49Smrg 		    &model->facetnorms[3 * T(members[i]->index).findex]);
112532001f49Smrg      if (dot > cos_angle) {
112632001f49Smrg	node->averaged = TRUE;
112732001f49Smrg	average[0] += model->facetnorms[3 * T(node->index).findex + 0];
112832001f49Smrg	average[1] += model->facetnorms[3 * T(node->index).findex + 1];
112932001f49Smrg	average[2] += model->facetnorms[3 * T(node->index).findex + 2];
113032001f49Smrg	avg = 1;			/* we averaged at least one normal! */
113132001f49Smrg      } else {
113232001f49Smrg	node->averaged = FALSE;
113332001f49Smrg      }
113432001f49Smrg      node = node->next;
113532001f49Smrg    }
113632001f49Smrg
113732001f49Smrg    if (avg) {
113832001f49Smrg      /* normalize the averaged normal */
113932001f49Smrg      _glmNormalize(average);
114032001f49Smrg
114132001f49Smrg      /* add the normal to the vertex normals list */
114232001f49Smrg      model->normals[3 * numnormals + 0] = average[0];
114332001f49Smrg      model->normals[3 * numnormals + 1] = average[1];
114432001f49Smrg      model->normals[3 * numnormals + 2] = average[2];
114532001f49Smrg      avg = numnormals;
114632001f49Smrg      numnormals++;
114732001f49Smrg    }
114832001f49Smrg
114932001f49Smrg    /* set the normal of this vertex in each triangle it is in */
115032001f49Smrg    node = members[i];
115132001f49Smrg    while (node) {
115232001f49Smrg      if (node->averaged) {
115332001f49Smrg	/* if this node was averaged, use the average normal */
115432001f49Smrg	if (T(node->index).vindices[0] == i)
115532001f49Smrg	  T(node->index).nindices[0] = avg;
115632001f49Smrg	else if (T(node->index).vindices[1] == i)
115732001f49Smrg	  T(node->index).nindices[1] = avg;
115832001f49Smrg	else if (T(node->index).vindices[2] == i)
115932001f49Smrg	  T(node->index).nindices[2] = avg;
116032001f49Smrg      } else {
116132001f49Smrg	/* if this node wasn't averaged, use the facet normal */
116232001f49Smrg	model->normals[3 * numnormals + 0] =
116332001f49Smrg	  model->facetnorms[3 * T(node->index).findex + 0];
116432001f49Smrg	model->normals[3 * numnormals + 1] =
116532001f49Smrg	  model->facetnorms[3 * T(node->index).findex + 1];
116632001f49Smrg	model->normals[3 * numnormals + 2] =
116732001f49Smrg	  model->facetnorms[3 * T(node->index).findex + 2];
116832001f49Smrg	if (T(node->index).vindices[0] == i)
116932001f49Smrg	  T(node->index).nindices[0] = numnormals;
117032001f49Smrg	else if (T(node->index).vindices[1] == i)
117132001f49Smrg	  T(node->index).nindices[1] = numnormals;
117232001f49Smrg	else if (T(node->index).vindices[2] == i)
117332001f49Smrg	  T(node->index).nindices[2] = numnormals;
117432001f49Smrg	numnormals++;
117532001f49Smrg      }
117632001f49Smrg      node = node->next;
117732001f49Smrg    }
117832001f49Smrg  }
117932001f49Smrg
118032001f49Smrg  model->numnormals = numnormals - 1;
118132001f49Smrg
118232001f49Smrg  /* free the member information */
118332001f49Smrg  for (i = 1; i <= model->numvertices; i++) {
118432001f49Smrg    node = members[i];
118532001f49Smrg    while (node) {
118632001f49Smrg      tail = node;
118732001f49Smrg      node = node->next;
118832001f49Smrg      free(tail);
118932001f49Smrg    }
119032001f49Smrg  }
119132001f49Smrg  free(members);
119232001f49Smrg
119332001f49Smrg  /* pack the normals array (we previously allocated the maximum
119432001f49Smrg     number of normals that could possibly be created (numtriangles *
119532001f49Smrg     3), so get rid of some of them (usually alot unless none of the
119632001f49Smrg     facet normals were averaged)) */
119732001f49Smrg  normals = model->normals;
119832001f49Smrg  model->normals = (float*)malloc(sizeof(float)* 3* (model->numnormals+1));
119932001f49Smrg  for (i = 1; i <= model->numnormals; i++) {
120032001f49Smrg    model->normals[3 * i + 0] = normals[3 * i + 0];
120132001f49Smrg    model->normals[3 * i + 1] = normals[3 * i + 1];
120232001f49Smrg    model->normals[3 * i + 2] = normals[3 * i + 2];
120332001f49Smrg  }
120432001f49Smrg  free(normals);
120532001f49Smrg
120632001f49Smrg  printf("glmVertexNormals(): %d normals generated\n", model->numnormals);
120732001f49Smrg}
120832001f49Smrg
120932001f49Smrg
121032001f49Smrg/* glmLinearTexture: Generates texture coordinates according to a
121132001f49Smrg * linear projection of the texture map.  It generates these by
121232001f49Smrg * linearly mapping the vertices onto a square.
121332001f49Smrg *
121432001f49Smrg * model - pointer to initialized GLMmodel structure
121532001f49Smrg */
121632001f49Smrgvoid
121732001f49SmrgglmLinearTexture(GLMmodel* model)
121832001f49Smrg{
121932001f49Smrg  GLMgroup *group;
122032001f49Smrg  float dimensions[3];
122132001f49Smrg  float x, y, scalefactor;
122232001f49Smrg  uint i;
122332001f49Smrg
122432001f49Smrg  assert(model);
122532001f49Smrg
122632001f49Smrg  if (model->texcoords)
122732001f49Smrg    free(model->texcoords);
122832001f49Smrg  model->numtexcoords = model->numvertices;
122932001f49Smrg  model->texcoords=(float*)malloc(sizeof(float)*2*(model->numtexcoords+1));
123032001f49Smrg
123132001f49Smrg  glmDimensions(model, dimensions);
123232001f49Smrg  scalefactor = 2.0 /
123332001f49Smrg    _glmAbs(_glmMax(_glmMax(dimensions[0], dimensions[1]), dimensions[2]));
123432001f49Smrg
123532001f49Smrg  /* do the calculations */
123632001f49Smrg  for(i = 1; i <= model->numvertices; i++) {
123732001f49Smrg    x = model->vertices[3 * i + 0] * scalefactor;
123832001f49Smrg    y = model->vertices[3 * i + 2] * scalefactor;
123932001f49Smrg    model->texcoords[2 * i + 0] = (x + 1.0) / 2.0;
124032001f49Smrg    model->texcoords[2 * i + 1] = (y + 1.0) / 2.0;
124132001f49Smrg  }
124232001f49Smrg
124332001f49Smrg  /* go through and put texture coordinate indices in all the triangles */
124432001f49Smrg  group = model->groups;
124532001f49Smrg  while(group) {
124632001f49Smrg    for(i = 0; i < group->numtriangles; i++) {
124732001f49Smrg      T(group->triangles[i]).tindices[0] = T(group->triangles[i]).vindices[0];
124832001f49Smrg      T(group->triangles[i]).tindices[1] = T(group->triangles[i]).vindices[1];
124932001f49Smrg      T(group->triangles[i]).tindices[2] = T(group->triangles[i]).vindices[2];
125032001f49Smrg    }
125132001f49Smrg    group = group->next;
125232001f49Smrg  }
125332001f49Smrg
125432001f49Smrg#if 0
125532001f49Smrg  printf("glmLinearTexture(): generated %d linear texture coordinates\n",
125632001f49Smrg	  model->numtexcoords);
125732001f49Smrg#endif
125832001f49Smrg}
125932001f49Smrg
126032001f49Smrg/* glmSpheremapTexture: Generates texture coordinates according to a
126132001f49Smrg * spherical projection of the texture map.  Sometimes referred to as
126232001f49Smrg * spheremap, or reflection map texture coordinates.  It generates
126332001f49Smrg * these by using the normal to calculate where that vertex would map
126432001f49Smrg * onto a sphere.  Since it is impossible to map something flat
126532001f49Smrg * perfectly onto something spherical, there is distortion at the
126632001f49Smrg * poles.  This particular implementation causes the poles along the X
126732001f49Smrg * axis to be distorted.
126832001f49Smrg *
126932001f49Smrg * model - pointer to initialized GLMmodel structure
127032001f49Smrg */
127132001f49Smrgvoid
127232001f49SmrgglmSpheremapTexture(GLMmodel* model)
127332001f49Smrg{
127432001f49Smrg  GLMgroup* group;
127532001f49Smrg  float theta, phi, rho, x, y, z, r;
127632001f49Smrg  uint i;
127732001f49Smrg
127832001f49Smrg  assert(model);
127932001f49Smrg  assert(model->normals);
128032001f49Smrg
128132001f49Smrg  if (model->texcoords)
128232001f49Smrg    free(model->texcoords);
128332001f49Smrg  model->numtexcoords = model->numnormals;
128432001f49Smrg  model->texcoords=(float*)malloc(sizeof(float)*2*(model->numtexcoords+1));
128532001f49Smrg
128632001f49Smrg  /* do the calculations */
128732001f49Smrg  for (i = 1; i <= model->numnormals; i++) {
128832001f49Smrg    z = model->normals[3 * i + 0];	/* re-arrange for pole distortion */
128932001f49Smrg    y = model->normals[3 * i + 1];
129032001f49Smrg    x = model->normals[3 * i + 2];
129132001f49Smrg    r = sqrt((x * x) + (y * y));
129232001f49Smrg    rho = sqrt((r * r) + (z * z));
129332001f49Smrg
129432001f49Smrg    if(r == 0.0) {
129532001f49Smrg	theta = 0.0;
129632001f49Smrg	phi = 0.0;
129732001f49Smrg    } else {
129832001f49Smrg      if(z == 0.0)
129932001f49Smrg	phi = M_PI / 2.0;
130032001f49Smrg      else
130132001f49Smrg	phi = acos(z / rho);
130232001f49Smrg
130332001f49Smrg#if WE_DONT_NEED_THIS_CODE
130432001f49Smrg      if(x == 0.0)
130532001f49Smrg	theta = M_PI / 2.0;	/* asin(y / r); */
130632001f49Smrg      else
130732001f49Smrg	theta = acos(x / r);
130832001f49Smrg#endif
130932001f49Smrg
131032001f49Smrg      if(y == 0.0)
131132001f49Smrg	theta = M_PI / 2.0;	/* acos(x / r); */
131232001f49Smrg      else
131332001f49Smrg	theta = asin(y / r) + (M_PI / 2.0);
131432001f49Smrg    }
131532001f49Smrg
131632001f49Smrg    model->texcoords[2 * i + 0] = theta / M_PI;
131732001f49Smrg    model->texcoords[2 * i + 1] = phi / M_PI;
131832001f49Smrg  }
131932001f49Smrg
132032001f49Smrg  /* go through and put texcoord indices in all the triangles */
132132001f49Smrg  group = model->groups;
132232001f49Smrg  while(group) {
132332001f49Smrg    for (i = 0; i < group->numtriangles; i++) {
132432001f49Smrg      T(group->triangles[i]).tindices[0] = T(group->triangles[i]).nindices[0];
132532001f49Smrg      T(group->triangles[i]).tindices[1] = T(group->triangles[i]).nindices[1];
132632001f49Smrg      T(group->triangles[i]).tindices[2] = T(group->triangles[i]).nindices[2];
132732001f49Smrg    }
132832001f49Smrg    group = group->next;
132932001f49Smrg  }
133032001f49Smrg
133132001f49Smrg#if 0
133232001f49Smrg  printf("glmSpheremapTexture(): generated %d spheremap texture coordinates\n",
133332001f49Smrg	 model->numtexcoords);
133432001f49Smrg#endif
133532001f49Smrg}
133632001f49Smrg
133732001f49Smrg/* glmDelete: Deletes a GLMmodel structure.
133832001f49Smrg *
133932001f49Smrg * model - initialized GLMmodel structure
134032001f49Smrg */
134132001f49Smrgvoid
134232001f49SmrgglmDelete(GLMmodel* model)
134332001f49Smrg{
134432001f49Smrg  GLMgroup* group;
134532001f49Smrg  uint i;
134632001f49Smrg
134732001f49Smrg  assert(model);
134832001f49Smrg
134932001f49Smrg  if (model->pathname)   free(model->pathname);
135032001f49Smrg  if (model->mtllibname) free(model->mtllibname);
135132001f49Smrg  if (model->vertices)   free(model->vertices);
135232001f49Smrg  if (model->normals)    free(model->normals);
135332001f49Smrg  if (model->texcoords)  free(model->texcoords);
135432001f49Smrg  if (model->facetnorms) free(model->facetnorms);
135532001f49Smrg  if (model->triangles)  free(model->triangles);
135632001f49Smrg  if (model->materials) {
135732001f49Smrg    for (i = 0; i < model->nummaterials; i++)
135832001f49Smrg      free(model->materials[i].name);
135932001f49Smrg  }
136032001f49Smrg  free(model->materials);
136132001f49Smrg  while(model->groups) {
136232001f49Smrg    group = model->groups;
136332001f49Smrg    model->groups = model->groups->next;
136432001f49Smrg    free(group->name);
136532001f49Smrg    free(group->triangles);
136632001f49Smrg    free(group);
136732001f49Smrg  }
136832001f49Smrg
136932001f49Smrg  free(model);
137032001f49Smrg}
137132001f49Smrg
137232001f49Smrgstatic GLMmaterial *
137332001f49SmrgglmDefaultMaterial(void)
137432001f49Smrg{
137532001f49Smrg   GLMmaterial *m = (GLMmaterial *) calloc(1, sizeof(GLMmaterial));
137632001f49Smrg
137732001f49Smrg   m->diffuse[0] = 0.75;
137832001f49Smrg   m->diffuse[1] = 0.75;
137932001f49Smrg   m->diffuse[2] = 0.75;
138032001f49Smrg   m->diffuse[3] = 1.0;
138132001f49Smrg
138232001f49Smrg   m->specular[0] = 1.0;
138332001f49Smrg   m->specular[1] = 1.0;
138432001f49Smrg   m->specular[2] = 1.0;
138532001f49Smrg   m->specular[3] = 1.0;
138632001f49Smrg
138732001f49Smrg   m->shininess = 5;
138832001f49Smrg
138932001f49Smrg   return m;
139032001f49Smrg}
139132001f49Smrg
139232001f49Smrg
139332001f49Smrg/* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
139432001f49Smrg * Returns a pointer to the created object which should be free'd with
139532001f49Smrg * glmDelete().
139632001f49Smrg *
139732001f49Smrg * filename - name of the file containing the Wavefront .OBJ format data.
139832001f49Smrg */
139932001f49SmrgGLMmodel*
140032001f49SmrgglmReadOBJ(char* filename)
140132001f49Smrg{
140232001f49Smrg  GLMmodel* model;
140332001f49Smrg  FILE*     file;
140432001f49Smrg
140532001f49Smrg  /* open the file */
140632001f49Smrg  file = fopen(filename, "r");
140732001f49Smrg  if (!file) {
140832001f49Smrg    fprintf(stderr, "glmReadOBJ() failed: can't open data file \"%s\".\n",
140932001f49Smrg	    filename);
141032001f49Smrg    exit(1);
141132001f49Smrg  }
141232001f49Smrg
141332001f49Smrg#if 0
141432001f49Smrg  /* announce the model name */
141532001f49Smrg  printf("Model: %s\n", filename);
141632001f49Smrg#endif
141732001f49Smrg
141832001f49Smrg  /* allocate a new model */
141932001f49Smrg  model = (GLMmodel*)malloc(sizeof(GLMmodel));
142032001f49Smrg  model->pathname      = stralloc(filename);
142132001f49Smrg  model->mtllibname    = NULL;
142232001f49Smrg  model->numvertices   = 0;
142332001f49Smrg  model->vertices      = NULL;
142432001f49Smrg  model->numnormals    = 0;
142532001f49Smrg  model->normals       = NULL;
142632001f49Smrg  model->numtexcoords  = 0;
142732001f49Smrg  model->texcoords     = NULL;
142832001f49Smrg  model->numfacetnorms = 0;
142932001f49Smrg  model->facetnorms    = NULL;
143032001f49Smrg  model->numtriangles  = 0;
143132001f49Smrg  model->triangles     = NULL;
143232001f49Smrg  model->nummaterials  = 0;
143332001f49Smrg  model->materials     = NULL;
143432001f49Smrg  model->numgroups     = 0;
143532001f49Smrg  model->groups        = NULL;
143632001f49Smrg  model->position[0]   = 0.0;
143732001f49Smrg  model->position[1]   = 0.0;
143832001f49Smrg  model->position[2]   = 0.0;
143932001f49Smrg  model->scale         = 1.0;
144032001f49Smrg
144132001f49Smrg  /* make a first pass through the file to get a count of the number
144232001f49Smrg     of vertices, normals, texcoords & triangles */
144332001f49Smrg  _glmFirstPass(model, file);
144432001f49Smrg
144532001f49Smrg  /* allocate memory */
144632001f49Smrg  model->vertices = (float*)malloc(sizeof(float) *
144732001f49Smrg				     3 * (model->numvertices + 1));
144832001f49Smrg  model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) *
144932001f49Smrg					  model->numtriangles);
145032001f49Smrg  if (model->numnormals) {
145132001f49Smrg    model->normals = (float*)malloc(sizeof(float) *
145232001f49Smrg				      3 * (model->numnormals + 1));
145332001f49Smrg  }
145432001f49Smrg  if (model->numtexcoords) {
145532001f49Smrg    model->texcoords = (float*)malloc(sizeof(float) *
145632001f49Smrg					2 * (model->numtexcoords + 1));
145732001f49Smrg  }
145832001f49Smrg
145932001f49Smrg  /* rewind to beginning of file and read in the data this pass */
146032001f49Smrg  rewind(file);
146132001f49Smrg
146232001f49Smrg  _glmSecondPass(model, file);
146332001f49Smrg
146432001f49Smrg  /* close the file */
146532001f49Smrg  fclose(file);
146632001f49Smrg
146732001f49Smrg  if (!model->materials) {
146832001f49Smrg     model->materials = glmDefaultMaterial();
146932001f49Smrg     model->nummaterials = 1;
147032001f49Smrg  }
147132001f49Smrg
147232001f49Smrg  return model;
147332001f49Smrg}
147432001f49Smrg
147532001f49Smrg/* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
147632001f49Smrg * a file.
147732001f49Smrg *
147832001f49Smrg * model    - initialized GLMmodel structure
147932001f49Smrg * filename - name of the file to write the Wavefront .OBJ format data to
148032001f49Smrg * mode     - a bitwise or of values describing what is written to the file
148132001f49Smrg *            GLM_NONE     -  render with only vertices
148232001f49Smrg *            GLM_FLAT     -  render with facet normals
148332001f49Smrg *            GLM_SMOOTH   -  render with vertex normals
148432001f49Smrg *            GLM_TEXTURE  -  render with texture coords
148532001f49Smrg *            GLM_COLOR    -  render with colors (color material)
148632001f49Smrg *            GLM_MATERIAL -  render with materials
148732001f49Smrg *            GLM_COLOR and GLM_MATERIAL should not both be specified.
148832001f49Smrg *            GLM_FLAT and GLM_SMOOTH should not both be specified.
148932001f49Smrg */
149032001f49Smrgvoid
149132001f49SmrgglmWriteOBJ(GLMmodel* model, char* filename, uint mode)
149232001f49Smrg{
149332001f49Smrg  uint    i;
149432001f49Smrg  FILE*     file;
149532001f49Smrg  GLMgroup* group;
149632001f49Smrg
149732001f49Smrg  assert(model);
149832001f49Smrg
149932001f49Smrg  /* do a bit of warning */
150032001f49Smrg  if (mode & GLM_FLAT && !model->facetnorms) {
150132001f49Smrg    printf("glmWriteOBJ() warning: flat normal output requested "
150232001f49Smrg	   "with no facet normals defined.\n");
150332001f49Smrg    mode &= ~GLM_FLAT;
150432001f49Smrg  }
150532001f49Smrg  if (mode & GLM_SMOOTH && !model->normals) {
150632001f49Smrg    printf("glmWriteOBJ() warning: smooth normal output requested "
150732001f49Smrg	   "with no normals defined.\n");
150832001f49Smrg    mode &= ~GLM_SMOOTH;
150932001f49Smrg  }
151032001f49Smrg  if (mode & GLM_TEXTURE && !model->texcoords) {
151132001f49Smrg    printf("glmWriteOBJ() warning: texture coordinate output requested "
151232001f49Smrg	   "with no texture coordinates defined.\n");
151332001f49Smrg    mode &= ~GLM_TEXTURE;
151432001f49Smrg  }
151532001f49Smrg  if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
151632001f49Smrg    printf("glmWriteOBJ() warning: flat normal output requested "
151732001f49Smrg	   "and smooth normal output requested (using smooth).\n");
151832001f49Smrg    mode &= ~GLM_FLAT;
151932001f49Smrg  }
152032001f49Smrg
152132001f49Smrg  /* open the file */
152232001f49Smrg  file = fopen(filename, "w");
152332001f49Smrg  if (!file) {
152432001f49Smrg    fprintf(stderr, "glmWriteOBJ() failed: can't open file \"%s\" to write.\n",
152532001f49Smrg	    filename);
152632001f49Smrg    exit(1);
152732001f49Smrg  }
152832001f49Smrg
152932001f49Smrg  /* spit out a header */
153032001f49Smrg  fprintf(file, "#  \n");
153132001f49Smrg  fprintf(file, "#  Wavefront OBJ generated by GLM library\n");
153232001f49Smrg  fprintf(file, "#  \n");
153332001f49Smrg  fprintf(file, "#  GLM library copyright (C) 1997 by Nate Robins\n");
153432001f49Smrg  fprintf(file, "#  email: ndr@pobox.com\n");
153532001f49Smrg  fprintf(file, "#  www:   http://www.pobox.com/~ndr\n");
153632001f49Smrg  fprintf(file, "#  \n");
153732001f49Smrg
153832001f49Smrg  if (mode & GLM_MATERIAL && model->mtllibname) {
153932001f49Smrg    fprintf(file, "\nmtllib %s\n\n", model->mtllibname);
154032001f49Smrg    _glmWriteMTL(model, filename, model->mtllibname);
154132001f49Smrg  }
154232001f49Smrg
154332001f49Smrg  /* spit out the vertices */
154432001f49Smrg  fprintf(file, "\n");
154532001f49Smrg  fprintf(file, "# %d vertices\n", model->numvertices);
154632001f49Smrg  for (i = 1; i <= model->numvertices; i++) {
154732001f49Smrg    fprintf(file, "v %f %f %f\n",
154832001f49Smrg	    model->vertices[3 * i + 0],
154932001f49Smrg	    model->vertices[3 * i + 1],
155032001f49Smrg	    model->vertices[3 * i + 2]);
155132001f49Smrg  }
155232001f49Smrg
155332001f49Smrg  /* spit out the smooth/flat normals */
155432001f49Smrg  if (mode & GLM_SMOOTH) {
155532001f49Smrg    fprintf(file, "\n");
155632001f49Smrg    fprintf(file, "# %d normals\n", model->numnormals);
155732001f49Smrg    for (i = 1; i <= model->numnormals; i++) {
155832001f49Smrg      fprintf(file, "vn %f %f %f\n",
155932001f49Smrg	      model->normals[3 * i + 0],
156032001f49Smrg	      model->normals[3 * i + 1],
156132001f49Smrg	      model->normals[3 * i + 2]);
156232001f49Smrg    }
156332001f49Smrg  } else if (mode & GLM_FLAT) {
156432001f49Smrg    fprintf(file, "\n");
156532001f49Smrg    fprintf(file, "# %d normals\n", model->numfacetnorms);
156632001f49Smrg    for (i = 1; i <= model->numnormals; i++) {
156732001f49Smrg      fprintf(file, "vn %f %f %f\n",
156832001f49Smrg	      model->facetnorms[3 * i + 0],
156932001f49Smrg	      model->facetnorms[3 * i + 1],
157032001f49Smrg	      model->facetnorms[3 * i + 2]);
157132001f49Smrg    }
157232001f49Smrg  }
157332001f49Smrg
157432001f49Smrg  /* spit out the texture coordinates */
157532001f49Smrg  if (mode & GLM_TEXTURE) {
157632001f49Smrg    fprintf(file, "\n");
157732001f49Smrg    fprintf(file, "# %d texcoords\n", model->numtexcoords);
157832001f49Smrg    for (i = 1; i <= model->numtexcoords; i++) {
157932001f49Smrg      fprintf(file, "vt %f %f\n",
158032001f49Smrg	      model->texcoords[2 * i + 0],
158132001f49Smrg	      model->texcoords[2 * i + 1]);
158232001f49Smrg    }
158332001f49Smrg  }
158432001f49Smrg
158532001f49Smrg  fprintf(file, "\n");
158632001f49Smrg  fprintf(file, "# %d groups\n", model->numgroups);
158732001f49Smrg  fprintf(file, "# %d faces (triangles)\n", model->numtriangles);
158832001f49Smrg  fprintf(file, "\n");
158932001f49Smrg
159032001f49Smrg  group = model->groups;
159132001f49Smrg  while(group) {
159232001f49Smrg    fprintf(file, "g %s\n", group->name);
159332001f49Smrg    if (mode & GLM_MATERIAL)
159432001f49Smrg      fprintf(file, "usemtl %s\n", model->materials[group->material].name);
159532001f49Smrg    for (i = 0; i < group->numtriangles; i++) {
159632001f49Smrg      if (mode & GLM_SMOOTH && mode & GLM_TEXTURE) {
159732001f49Smrg	fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
159832001f49Smrg		T(group->triangles[i]).vindices[0],
159932001f49Smrg		T(group->triangles[i]).nindices[0],
160032001f49Smrg		T(group->triangles[i]).tindices[0],
160132001f49Smrg		T(group->triangles[i]).vindices[1],
160232001f49Smrg		T(group->triangles[i]).nindices[1],
160332001f49Smrg		T(group->triangles[i]).tindices[1],
160432001f49Smrg		T(group->triangles[i]).vindices[2],
160532001f49Smrg		T(group->triangles[i]).nindices[2],
160632001f49Smrg		T(group->triangles[i]).tindices[2]);
160732001f49Smrg      } else if (mode & GLM_FLAT && mode & GLM_TEXTURE) {
160832001f49Smrg	fprintf(file, "f %d/%d %d/%d %d/%d\n",
160932001f49Smrg		T(group->triangles[i]).vindices[0],
161032001f49Smrg		T(group->triangles[i]).findex,
161132001f49Smrg		T(group->triangles[i]).vindices[1],
161232001f49Smrg		T(group->triangles[i]).findex,
161332001f49Smrg		T(group->triangles[i]).vindices[2],
161432001f49Smrg		T(group->triangles[i]).findex);
161532001f49Smrg      } else if (mode & GLM_TEXTURE) {
161632001f49Smrg	fprintf(file, "f %d/%d %d/%d %d/%d\n",
161732001f49Smrg		T(group->triangles[i]).vindices[0],
161832001f49Smrg		T(group->triangles[i]).tindices[0],
161932001f49Smrg		T(group->triangles[i]).vindices[1],
162032001f49Smrg		T(group->triangles[i]).tindices[1],
162132001f49Smrg		T(group->triangles[i]).vindices[2],
162232001f49Smrg		T(group->triangles[i]).tindices[2]);
162332001f49Smrg      } else if (mode & GLM_SMOOTH) {
162432001f49Smrg	fprintf(file, "f %d//%d %d//%d %d//%d\n",
162532001f49Smrg		T(group->triangles[i]).vindices[0],
162632001f49Smrg		T(group->triangles[i]).nindices[0],
162732001f49Smrg		T(group->triangles[i]).vindices[1],
162832001f49Smrg		T(group->triangles[i]).nindices[1],
162932001f49Smrg		T(group->triangles[i]).vindices[2],
163032001f49Smrg		T(group->triangles[i]).nindices[2]);
163132001f49Smrg      } else if (mode & GLM_FLAT) {
163232001f49Smrg	fprintf(file, "f %d//%d %d//%d %d//%d\n",
163332001f49Smrg		T(group->triangles[i]).vindices[0],
163432001f49Smrg		T(group->triangles[i]).findex,
163532001f49Smrg		T(group->triangles[i]).vindices[1],
163632001f49Smrg		T(group->triangles[i]).findex,
163732001f49Smrg		T(group->triangles[i]).vindices[2],
163832001f49Smrg		T(group->triangles[i]).findex);
163932001f49Smrg      } else {
164032001f49Smrg	fprintf(file, "f %d %d %d\n",
164132001f49Smrg		T(group->triangles[i]).vindices[0],
164232001f49Smrg		T(group->triangles[i]).vindices[1],
164332001f49Smrg		T(group->triangles[i]).vindices[2]);
164432001f49Smrg      }
164532001f49Smrg    }
164632001f49Smrg    fprintf(file, "\n");
164732001f49Smrg    group = group->next;
164832001f49Smrg  }
164932001f49Smrg
165032001f49Smrg  fclose(file);
165132001f49Smrg}
165232001f49Smrg
165332001f49Smrg/* glmWeld: eliminate (weld) vectors that are within an epsilon of
165432001f49Smrg * each other.
165532001f49Smrg *
165632001f49Smrg * model      - initialized GLMmodel structure
165732001f49Smrg * epsilon    - maximum difference between vertices
165832001f49Smrg *              ( 0.00001 is a good start for a unitized model)
165932001f49Smrg *
166032001f49Smrg */
166132001f49Smrgvoid
166232001f49SmrgglmWeld(GLMmodel* model, float epsilon)
166332001f49Smrg{
166432001f49Smrg  float* vectors;
166532001f49Smrg  float* copies;
166632001f49Smrg  uint   numvectors;
166732001f49Smrg  uint   i;
166832001f49Smrg
166932001f49Smrg  /* vertices */
167032001f49Smrg  numvectors = model->numvertices;
167132001f49Smrg  vectors    = model->vertices;
167232001f49Smrg  copies = _glmWeldVectors(vectors, &numvectors, epsilon);
167332001f49Smrg
167432001f49Smrg  printf("glmWeld(): %d redundant vertices.\n",
167532001f49Smrg	 model->numvertices - numvectors - 1);
167632001f49Smrg
167732001f49Smrg  for (i = 0; i < model->numtriangles; i++) {
167832001f49Smrg    T(i).vindices[0] = (uint)vectors[3 * T(i).vindices[0] + 0];
167932001f49Smrg    T(i).vindices[1] = (uint)vectors[3 * T(i).vindices[1] + 0];
168032001f49Smrg    T(i).vindices[2] = (uint)vectors[3 * T(i).vindices[2] + 0];
168132001f49Smrg  }
168232001f49Smrg
168332001f49Smrg  /* free space for old vertices */
168432001f49Smrg  free(vectors);
168532001f49Smrg
168632001f49Smrg  /* allocate space for the new vertices */
168732001f49Smrg  model->numvertices = numvectors;
168832001f49Smrg  model->vertices = (float*)malloc(sizeof(float) *
168932001f49Smrg				     3 * (model->numvertices + 1));
169032001f49Smrg
169132001f49Smrg  /* copy the optimized vertices into the actual vertex list */
169232001f49Smrg  for (i = 1; i <= model->numvertices; i++) {
169332001f49Smrg    model->vertices[3 * i + 0] = copies[3 * i + 0];
169432001f49Smrg    model->vertices[3 * i + 1] = copies[3 * i + 1];
169532001f49Smrg    model->vertices[3 * i + 2] = copies[3 * i + 2];
169632001f49Smrg  }
169732001f49Smrg
169832001f49Smrg  free(copies);
169932001f49Smrg}
170032001f49Smrg
170132001f49Smrg
170232001f49Smrgvoid
170332001f49SmrgglmReIndex(GLMmodel *model)
170432001f49Smrg{
170532001f49Smrg  uint i, j, n;
170632001f49Smrg  GLMgroup* group;
170732001f49Smrg  float *newNormals = NULL;
170832001f49Smrg  float *newTexcoords = NULL;
170932001f49Smrg  const uint numv = model->numvertices;
171032001f49Smrg
171132001f49Smrg  if (model->numnormals > 0)
171232001f49Smrg     newNormals = (float *) malloc((numv + 1) * 3 * sizeof(float));
171332001f49Smrg
171432001f49Smrg  if (model->numtexcoords > 0)
171532001f49Smrg     newTexcoords = (float *) malloc((numv + 1) * 2 * sizeof(float));
171632001f49Smrg
171732001f49Smrg  for (group = model->groups; group; group = group->next) {
171832001f49Smrg
171932001f49Smrg    n = group->numtriangles;
172032001f49Smrg
172132001f49Smrg    group->triIndexes = (uint *) malloc(n * 3 * sizeof(uint));
172232001f49Smrg
172332001f49Smrg    group->minIndex = 10000000;
172432001f49Smrg    group->maxIndex = 0;
172532001f49Smrg
172632001f49Smrg    for (i = 0; i < n; i++) {
172732001f49Smrg
172832001f49Smrg       for (j = 0; j < 3; j++) {
172932001f49Smrg          uint vindex = T(group->triangles[i]).vindices[j];
173032001f49Smrg          uint nindex = T(group->triangles[i]).nindices[j];
173132001f49Smrg          uint tindex = T(group->triangles[i]).tindices[j];
173232001f49Smrg
173332001f49Smrg          float *nrm = &model->normals[nindex * 3];
173432001f49Smrg          float *tex = &model->texcoords[tindex * 2];
173532001f49Smrg
173632001f49Smrg          if (newNormals) {
173732001f49Smrg             assert(vindex * 3 + 2 < (numv + 1) * 3);
173832001f49Smrg             newNormals[vindex * 3 + 0] = nrm[0];
173932001f49Smrg             newNormals[vindex * 3 + 1] = nrm[1];
174032001f49Smrg             newNormals[vindex * 3 + 2] = nrm[2];
174132001f49Smrg          }
174232001f49Smrg          if (newTexcoords) {
174332001f49Smrg             newTexcoords[vindex * 2 + 0] = tex[0];
174432001f49Smrg             newTexcoords[vindex * 2 + 1] = tex[1];
174532001f49Smrg          }
174632001f49Smrg
174732001f49Smrg          T(group->triangles[i]).nindices[j] = vindex;
174832001f49Smrg          T(group->triangles[i]).tindices[j] = vindex;
174932001f49Smrg
175032001f49Smrg          group->triIndexes[i * 3 + j] = vindex;
175132001f49Smrg
175232001f49Smrg          if (vindex > group->maxIndex)
175332001f49Smrg             group->maxIndex = vindex;
175432001f49Smrg          if (vindex < group->minIndex)
175532001f49Smrg             group->minIndex = vindex;
175632001f49Smrg       }
175732001f49Smrg    }
175832001f49Smrg  }
175932001f49Smrg
176032001f49Smrg  if (newNormals) {
176132001f49Smrg     free(model->normals);
176232001f49Smrg     model->normals = newNormals;
176332001f49Smrg     model->numnormals = model->numvertices;
176432001f49Smrg  }
176532001f49Smrg
176632001f49Smrg  if (newTexcoords) {
176732001f49Smrg     free(model->texcoords);
176832001f49Smrg     model->texcoords = newTexcoords;
176932001f49Smrg     model->numtexcoords = model->numvertices;
177032001f49Smrg  }
177132001f49Smrg}
177232001f49Smrg
177332001f49Smrg
177432001f49Smrg
177532001f49Smrgvoid
177632001f49SmrgglmPrint(const GLMmodel *model)
177732001f49Smrg{
177832001f49Smrg  uint i, j, grp, n;
177932001f49Smrg  GLMgroup* group;
178032001f49Smrg  uint totalTris = 0;
178132001f49Smrg
178232001f49Smrg  grp = 0;
178332001f49Smrg
178432001f49Smrg  printf("%u vertices\n", model->numvertices);
178532001f49Smrg  printf("%u normals\n", model->numnormals);
178632001f49Smrg  printf("%u texcoords\n", model->numtexcoords);
178732001f49Smrg
178832001f49Smrg  for (group = model->groups; group; group = group->next, grp++) {
178932001f49Smrg     printf("Group %u:\n", grp);
179032001f49Smrg     printf("  Min index %u, max index %u\n", group->minIndex, group->maxIndex);
179132001f49Smrg
179232001f49Smrg#if 0
179332001f49Smrg    if (mode & GLM_MATERIAL) {
179432001f49Smrg      glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,
179532001f49Smrg		   model->materials[group->material].ambient);
179632001f49Smrg      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,
179732001f49Smrg		   model->materials[group->material].diffuse);
179832001f49Smrg      glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,
179932001f49Smrg		   model->materials[group->material].specular);
180032001f49Smrg       glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS,
180132001f49Smrg  		  model->materials[group->material].shininess);
180232001f49Smrg    }
180332001f49Smrg
180432001f49Smrg    if (mode & GLM_COLOR) {
180532001f49Smrg      glColor3fv(model->materials[group->material].diffuse);
180632001f49Smrg    }
180732001f49Smrg#endif
180832001f49Smrg    totalTris += group->numtriangles;
180932001f49Smrg
181032001f49Smrg    printf("  %u triangles\n", group->numtriangles);
181132001f49Smrg    n = group->numtriangles;
181232001f49Smrg    if (n > 10)
181332001f49Smrg      n = 10;
181432001f49Smrg
181532001f49Smrg    for (i = 0; i < n; i++) {
181632001f49Smrg
181732001f49Smrg       printf("    %u: vert ", i);
181832001f49Smrg       for (j = 0; j < 3; j++) {
181932001f49Smrg          printf("%u ", T(group->triangles[i]).vindices[j]);
182032001f49Smrg       }
182132001f49Smrg
182232001f49Smrg       printf("    normal ");
182332001f49Smrg       for (j = 0; j < 3; j++) {
182432001f49Smrg          printf("%u ", T(group->triangles[i]).nindices[j]);
182532001f49Smrg       }
182632001f49Smrg
182732001f49Smrg       printf("    tex ");
182832001f49Smrg       for (j = 0; j < 3; j++) {
182932001f49Smrg          printf("%u ", T(group->triangles[i]).tindices[j]);
183032001f49Smrg       }
183132001f49Smrg
183232001f49Smrg       printf("\n");
183332001f49Smrg    }
183432001f49Smrg  }
183532001f49Smrg  printf("Total tris: %u\n", totalTris);
183632001f49Smrg}
183732001f49Smrg
183832001f49Smrg
183932001f49Smrg
184032001f49Smrg#if 0
184132001f49Smrg  /* normals */
184232001f49Smrg  if (model->numnormals) {
184332001f49Smrg  numvectors = model->numnormals;
184432001f49Smrg  vectors    = model->normals;
184532001f49Smrg  copies = _glmOptimizeVectors(vectors, &numvectors);
184632001f49Smrg
184732001f49Smrg  printf("glmOptimize(): %d redundant normals.\n",
184832001f49Smrg	 model->numnormals - numvectors);
184932001f49Smrg
185032001f49Smrg  for (i = 0; i < model->numtriangles; i++) {
185132001f49Smrg    T(i).nindices[0] = (uint)vectors[3 * T(i).nindices[0] + 0];
185232001f49Smrg    T(i).nindices[1] = (uint)vectors[3 * T(i).nindices[1] + 0];
185332001f49Smrg    T(i).nindices[2] = (uint)vectors[3 * T(i).nindices[2] + 0];
185432001f49Smrg  }
185532001f49Smrg
185632001f49Smrg  /* free space for old normals */
185732001f49Smrg  free(vectors);
185832001f49Smrg
185932001f49Smrg  /* allocate space for the new normals */
186032001f49Smrg  model->numnormals = numvectors;
186132001f49Smrg  model->normals = (float*)malloc(sizeof(float) *
186232001f49Smrg				    3 * (model->numnormals + 1));
186332001f49Smrg
186432001f49Smrg  /* copy the optimized vertices into the actual vertex list */
186532001f49Smrg  for (i = 1; i <= model->numnormals; i++) {
186632001f49Smrg    model->normals[3 * i + 0] = copies[3 * i + 0];
186732001f49Smrg    model->normals[3 * i + 1] = copies[3 * i + 1];
186832001f49Smrg    model->normals[3 * i + 2] = copies[3 * i + 2];
186932001f49Smrg  }
187032001f49Smrg
187132001f49Smrg  free(copies);
187232001f49Smrg  }
187332001f49Smrg
187432001f49Smrg  /* texcoords */
187532001f49Smrg  if (model->numtexcoords) {
187632001f49Smrg  numvectors = model->numtexcoords;
187732001f49Smrg  vectors    = model->texcoords;
187832001f49Smrg  copies = _glmOptimizeVectors(vectors, &numvectors);
187932001f49Smrg
188032001f49Smrg  printf("glmOptimize(): %d redundant texcoords.\n",
188132001f49Smrg	 model->numtexcoords - numvectors);
188232001f49Smrg
188332001f49Smrg  for (i = 0; i < model->numtriangles; i++) {
188432001f49Smrg    for (j = 0; j < 3; j++) {
188532001f49Smrg      T(i).tindices[j] = (uint)vectors[3 * T(i).tindices[j] + 0];
188632001f49Smrg    }
188732001f49Smrg  }
188832001f49Smrg
188932001f49Smrg  /* free space for old texcoords */
189032001f49Smrg  free(vectors);
189132001f49Smrg
189232001f49Smrg  /* allocate space for the new texcoords */
189332001f49Smrg  model->numtexcoords = numvectors;
189432001f49Smrg  model->texcoords = (float*)malloc(sizeof(float) *
189532001f49Smrg				      2 * (model->numtexcoords + 1));
189632001f49Smrg
189732001f49Smrg  /* copy the optimized vertices into the actual vertex list */
189832001f49Smrg  for (i = 1; i <= model->numtexcoords; i++) {
189932001f49Smrg    model->texcoords[2 * i + 0] = copies[2 * i + 0];
190032001f49Smrg    model->texcoords[2 * i + 1] = copies[2 * i + 1];
190132001f49Smrg  }
190232001f49Smrg
190332001f49Smrg  free(copies);
190432001f49Smrg  }
190532001f49Smrg#endif
190632001f49Smrg
190732001f49Smrg#if 0
190832001f49Smrg  /* look for unused vertices */
190932001f49Smrg  /* look for unused normals */
191032001f49Smrg  /* look for unused texcoords */
191132001f49Smrg  for (i = 1; i <= model->numvertices; i++) {
191232001f49Smrg    for (j = 0; j < model->numtriangles; i++) {
191332001f49Smrg      if (T(j).vindices[0] == i ||
191432001f49Smrg	  T(j).vindices[1] == i ||
191532001f49Smrg	  T(j).vindices[1] == i)
191632001f49Smrg	break;
191732001f49Smrg    }
191832001f49Smrg  }
191932001f49Smrg#endif
1920