glm.c
上传用户:xk288cn
上传日期:2007-05-28
资源大小:4876k
文件大小:53k
源码类别:

GIS编程

开发平台:

Visual C++

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