glm.c

Go to the documentation of this file.
00001 /*    
00002       glm.c
00003       Nate Robins, 1997
00004       ndr@pobox.com, http://www.pobox.com/~ndr/
00005  
00006       Wavefront OBJ model file format reader/writer/manipulator.
00007 
00008       Includes routines for generating smooth normals with
00009       preservation of edges, welding redundant vertices & texture
00010       coordinate generation (spheremap and planar projections) + more.
00011 
00012  */
00013 
00014 
00015 #include <math.h>
00016 #include <stdio.h>
00017 #include <stdlib.h>
00018 #include <string.h>
00019 #include <assert.h>
00020 #include <esg/parser/glm.h>
00021 
00022 
00023 #define T(x) (model->triangles[(x)])
00024 
00025 
00026 /* _GLMnode: general purpose node
00027  */
00028 typedef struct _GLMnode {
00029   GLuint           index;
00030   GLboolean        averaged;
00031   struct _GLMnode* next;
00032 } GLMnode;
00033 
00034 
00035 /* glmMax: returns the maximum of two floats */
00036 static GLfloat
00037 glmMax(GLfloat a, GLfloat b) 
00038 {
00039   if (b > a)
00040     return b;
00041   return a;
00042 }
00043 
00044 /* glmAbs: returns the absolute value of a float */
00045 static GLfloat
00046 glmAbs(GLfloat f)
00047 {
00048   if (f < 0)
00049     return -f;
00050   return f;
00051 }
00052 
00053 /* glmDot: compute the dot product of two vectors
00054  *
00055  * u - array of 3 GLfloats (GLfloat u[3])
00056  * v - array of 3 GLfloats (GLfloat v[3])
00057  */
00058 static GLfloat
00059 glmDot(GLfloat* u, GLfloat* v)
00060 {
00061   assert(u); assert(v);
00062 
00063   return u[0]*v[0] + u[1]*v[1] + u[2]*v[2];
00064 }
00065 
00066 /* glmCross: compute the cross product of two vectors
00067  *
00068  * u - array of 3 GLfloats (GLfloat u[3])
00069  * v - array of 3 GLfloats (GLfloat v[3])
00070  * n - array of 3 GLfloats (GLfloat n[3]) to return the cross product in
00071  */
00072 static GLvoid
00073 glmCross(GLfloat* u, GLfloat* v, GLfloat* n)
00074 {
00075   assert(u); assert(v); assert(n);
00076 
00077   n[0] = u[1]*v[2] - u[2]*v[1];
00078   n[1] = u[2]*v[0] - u[0]*v[2];
00079   n[2] = u[0]*v[1] - u[1]*v[0];
00080 }
00081 
00082 /* glmNormalize: normalize a vector
00083  *
00084  * v - array of 3 GLfloats (GLfloat v[3]) to be normalized
00085  */
00086 static GLvoid
00087 glmNormalize(GLfloat* v)
00088 {
00089   GLfloat l;
00090 
00091   assert(v);
00092 
00093   l = (GLfloat)sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
00094   v[0] /= l;
00095   v[1] /= l;
00096   v[2] /= l;
00097 }
00098 
00099 /* glmEqual: compares two vectors and returns GL_TRUE if they are
00100  * equal (within a certain threshold) or GL_FALSE if not. An epsilon
00101  * that works fairly well is 0.000001.
00102  *
00103  * u - array of 3 GLfloats (GLfloat u[3])
00104  * v - array of 3 GLfloats (GLfloat v[3]) 
00105  */
00106 static GLboolean
00107 glmEqual(GLfloat* u, GLfloat* v, GLfloat epsilon)
00108 {
00109   if (glmAbs(u[0] - v[0]) < epsilon &&
00110       glmAbs(u[1] - v[1]) < epsilon &&
00111       glmAbs(u[2] - v[2]) < epsilon) 
00112   {
00113     return GL_TRUE;
00114   }
00115   return GL_FALSE;
00116 }
00117 
00118 /* glmWeldVectors: eliminate (weld) vectors that are within an
00119  * epsilon of each other.
00120  *
00121  * vectors    - array of GLfloat[3]'s to be welded
00122  * numvectors - number of GLfloat[3]'s in vectors
00123  * epsilon    - maximum difference between vectors 
00124  *
00125  */
00126 GLfloat*
00127 glmWeldVectors(GLfloat* vectors, GLuint* numvectors, GLfloat epsilon)
00128 {
00129   GLfloat* copies;
00130   GLuint   copied;
00131   GLuint   i, j;
00132 
00133   copies = (GLfloat*)malloc(sizeof(GLfloat) * 3 * (*numvectors + 1));
00134   memcpy(copies, vectors, (sizeof(GLfloat) * 3 * (*numvectors + 1)));
00135 
00136   copied = 1;
00137   for (i = 1; i <= *numvectors; i++) {
00138     for (j = 1; j <= copied; j++) {
00139       if (glmEqual(&vectors[3 * i], &copies[3 * j], epsilon)) {
00140         goto duplicate;
00141       }
00142     }
00143 
00144     /* must not be any duplicates -- add to the copies array */
00145     copies[3 * copied + 0] = vectors[3 * i + 0];
00146     copies[3 * copied + 1] = vectors[3 * i + 1];
00147     copies[3 * copied + 2] = vectors[3 * i + 2];
00148     j = copied;                         /* pass this along for below */
00149     copied++;
00150 
00151   duplicate:
00152     /* set the first component of this vector to point at the correct
00153        index into the new copies array */
00154     vectors[3 * i + 0] = (GLfloat)j;
00155   }
00156 
00157   *numvectors = copied-1;
00158   return copies;
00159 }
00160 
00161 /* glmFindGroup: Find a group in the model
00162  */
00163 GLMgroup*
00164 glmFindGroup(GLMmodel* model, char* name)
00165 {
00166   GLMgroup* group;
00167 
00168   assert(model);
00169 
00170   group = model->groups;
00171   while(group) {
00172     if (!strcmp(name, group->name))
00173       break;
00174     group = group->next;
00175   }
00176 
00177   return group;
00178 }
00179 
00180 /* glmAddGroup: Add a group to the model
00181  */
00182 GLMgroup*
00183 glmAddGroup(GLMmodel* model, char* name)
00184 {
00185   GLMgroup* group;
00186 
00187   group = glmFindGroup(model, name);
00188   if (!group) {
00189     group = (GLMgroup*)malloc(sizeof(GLMgroup));
00190     group->name = strdup(name);
00191     group->material = 0;
00192     group->numtriangles = 0;
00193     group->triangles = NULL;
00194     group->next = model->groups;
00195     model->groups = group;
00196     model->numgroups++;
00197   }
00198 
00199   return group;
00200 }
00201 
00202 /* glmFindGroup: Find a material in the model
00203  */
00204 GLuint
00205 glmFindMaterial(GLMmodel* model, char* name)
00206 {
00207   GLuint i;
00208 
00209   /* XXX doing a linear search on a string key'd list is pretty lame,
00210      but it works and is fast enough for now. */
00211   for (i = 0; i < model->nummaterials; i++) {
00212     if (!strcmp(model->materials[i].name, name))
00213       goto found;
00214   }
00215 
00216   /* didn't find the name, so print a warning and return the default
00217      material (0). */
00218   printf("glmFindMaterial():  can't find material \"%s\".\n", name);
00219   i = 0;
00220 
00221 found:
00222   return i;
00223 }
00224 
00225 
00226 /* glmDirName: return the directory given a path
00227  *
00228  * path - filesystem path
00229  *
00230  * NOTE: the return value should be free'd.
00231  */
00232 static char*
00233 glmDirName(char* path)
00234 {
00235   char* dir;
00236   char* s;
00237 
00238   dir = strdup(path);
00239 
00240   s = strrchr(dir, '/');
00241   if (s)
00242     s[1] = '\0';
00243   else
00244     dir[0] = '\0';
00245 
00246   return dir;
00247 }
00248 
00249 
00250 /* glmReadMTL: read a wavefront material library file
00251  *
00252  * model - properly initialized GLMmodel structure
00253  * name  - name of the material library
00254  */
00255 static GLvoid
00256 glmReadMTL(GLMmodel* model, char* name)
00257 {
00258   FILE* file;
00259   char* dir;
00260   char* filename;
00261   char  buf[128];
00262   GLuint nummaterials, i;
00263 
00264   dir = glmDirName(model->pathname);
00265   filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(name) + 1));
00266   strcpy(filename, dir);
00267   strcat(filename, name);
00268   free(dir);
00269 
00270   file = fopen(filename, "r");
00271   if (!file) {
00272     fprintf(stderr, "glmReadMTL() failed: can't open material file \"%s\".\n",
00273             filename);
00274     exit(1);
00275   }
00276   free(filename);
00277 
00278   /* count the number of materials in the file */
00279   nummaterials = 1;
00280   while(fscanf(file, "%s", buf) != EOF) {
00281     switch(buf[0]) {
00282     case '#':                           /* comment */
00283       /* eat up rest of line */
00284       fgets(buf, sizeof(buf), file);
00285       break;
00286     case 'n':                           /* newmtl */
00287       fgets(buf, sizeof(buf), file);
00288       nummaterials++;
00289       sscanf(buf, "%s %s", buf, buf);
00290       break;
00291     default:
00292       /* eat up rest of line */
00293       fgets(buf, sizeof(buf), file);
00294       break;
00295     }
00296   }
00297 
00298   rewind(file);
00299 
00300   model->materials = (GLMmaterial*)malloc(sizeof(GLMmaterial) * nummaterials);
00301   model->nummaterials = nummaterials;
00302 
00303   /* set the default material */
00304   for (i = 0; i < nummaterials; i++) {
00305     model->materials[i].name = NULL;
00306     model->materials[i].shininess = 65.0;
00307     model->materials[i].diffuse[0] = 0.8;
00308     model->materials[i].diffuse[1] = 0.8;
00309     model->materials[i].diffuse[2] = 0.8;
00310     model->materials[i].diffuse[3] = 1.0;
00311     model->materials[i].ambient[0] = 0.2;
00312     model->materials[i].ambient[1] = 0.2;
00313     model->materials[i].ambient[2] = 0.2;
00314     model->materials[i].ambient[3] = 1.0;
00315     model->materials[i].specular[0] = 0.0;
00316     model->materials[i].specular[1] = 0.0;
00317     model->materials[i].specular[2] = 0.0;
00318     model->materials[i].specular[3] = 1.0;
00319   }
00320   model->materials[0].name = strdup("default");
00321 
00322   /* now, read in the data */
00323   nummaterials = 0;
00324   while(fscanf(file, "%s", buf) != EOF) {
00325     switch(buf[0]) {
00326     case '#':                           /* comment */
00327       /* eat up rest of line */
00328       fgets(buf, sizeof(buf), file);
00329       break;
00330     case 'n':                           /* newmtl */
00331       fgets(buf, sizeof(buf), file);
00332       sscanf(buf, "%s %s", buf, buf);
00333       nummaterials++;
00334       model->materials[nummaterials].name = strdup(buf);
00335       break;
00336     case 'N':
00337       fscanf(file, "%f", &model->materials[nummaterials].shininess);
00338       /* wavefront shininess is from [0, 1000], so scale for OpenGL */
00339       model->materials[nummaterials].shininess /= 1000.0;
00340       model->materials[nummaterials].shininess *= 128.0;
00341       break;
00342     case 'K':
00343       switch(buf[1]) {
00344       case 'd':
00345         fscanf(file, "%f %f %f",
00346                &model->materials[nummaterials].diffuse[0],
00347                &model->materials[nummaterials].diffuse[1],
00348                &model->materials[nummaterials].diffuse[2]);
00349         break;
00350       case 's':
00351         fscanf(file, "%f %f %f",
00352                &model->materials[nummaterials].specular[0],
00353                &model->materials[nummaterials].specular[1],
00354                &model->materials[nummaterials].specular[2]);
00355         break;
00356       case 'a':
00357         fscanf(file, "%f %f %f",
00358                &model->materials[nummaterials].ambient[0],
00359                &model->materials[nummaterials].ambient[1],
00360                &model->materials[nummaterials].ambient[2]);
00361         break;
00362       default:
00363         /* eat up rest of line */
00364         fgets(buf, sizeof(buf), file);
00365         break;
00366       }
00367       break;
00368     default:
00369       /* eat up rest of line */
00370       fgets(buf, sizeof(buf), file);
00371       break;
00372     }
00373   }
00374 }
00375 
00376 /* glmWriteMTL: write a wavefront material library file
00377  *
00378  * model      - properly initialized GLMmodel structure
00379  * modelpath  - pathname of the model being written
00380  * mtllibname - name of the material library to be written
00381  */
00382 static GLvoid
00383 glmWriteMTL(GLMmodel* model, char* modelpath, char* mtllibname)
00384 {
00385   FILE* file;
00386   char* dir;
00387   char* filename;
00388   GLMmaterial* material;
00389   GLuint i;
00390 
00391   dir = glmDirName(modelpath);
00392   filename = (char*)malloc(sizeof(char) * (strlen(dir)+strlen(mtllibname)));
00393   strcpy(filename, dir);
00394   strcat(filename, mtllibname);
00395   free(dir);
00396 
00397   /* open the file */
00398   file = fopen(filename, "w");
00399   if (!file) {
00400     fprintf(stderr, "glmWriteMTL() failed: can't open file \"%s\".\n",
00401             filename);
00402     exit(1);
00403   }
00404   free(filename);
00405 
00406   /* spit out a header */
00407   fprintf(file, "#  \n");
00408   fprintf(file, "#  Wavefront MTL generated by GLM library\n");
00409   fprintf(file, "#  \n");
00410   fprintf(file, "#  GLM library\n");
00411   fprintf(file, "#  Nate Robins\n");
00412   fprintf(file, "#  ndr@pobox.com\n");
00413   fprintf(file, "#  http://www.pobox.com/~ndr\n");
00414   fprintf(file, "#  \n\n");
00415 
00416   for (i = 0; i < model->nummaterials; i++) {
00417     material = &model->materials[i];
00418     fprintf(file, "newmtl %s\n", material->name);
00419     fprintf(file, "Ka %f %f %f\n", 
00420             material->ambient[0], material->ambient[1], material->ambient[2]);
00421     fprintf(file, "Kd %f %f %f\n", 
00422             material->diffuse[0], material->diffuse[1], material->diffuse[2]);
00423     fprintf(file, "Ks %f %f %f\n", 
00424             material->specular[0],material->specular[1],material->specular[2]);
00425     fprintf(file, "Ns %f\n", material->shininess / 128.0 * 1000.0);
00426     fprintf(file, "\n");
00427   }
00428 }
00429 
00430 
00431 /* glmFirstPass: first pass at a Wavefront OBJ file that gets all the
00432  * statistics of the model (such as #vertices, #normals, etc)
00433  *
00434  * model - properly initialized GLMmodel structure
00435  * file  - (fopen'd) file descriptor 
00436  */
00437 static GLvoid
00438 glmFirstPass(GLMmodel* model, FILE* file) 
00439 {
00440   GLuint    numvertices;                /* number of vertices in model */
00441   GLuint    numnormals;                 /* number of normals in model */
00442   GLuint    numtexcoords;               /* number of texcoords in model */
00443   GLuint    numtriangles;               /* number of triangles in model */
00444   GLMgroup* group;                      /* current group */
00445   unsigned  v, n, t;
00446   char      buf[128];
00447 
00448   /* make a default group */
00449   group = glmAddGroup(model, "default");
00450 
00451   numvertices = numnormals = numtexcoords = numtriangles = 0;
00452   while(fscanf(file, "%s", buf) != EOF) {
00453     switch(buf[0]) {
00454     case '#':                           /* comment */
00455       /* eat up rest of line */
00456       fgets(buf, sizeof(buf), file);
00457       break;
00458     case 'v':                           /* v, vn, vt */
00459       switch(buf[1]) {
00460       case '\0':                        /* vertex */
00461         /* eat up rest of line */
00462         fgets(buf, sizeof(buf), file);
00463         numvertices++;
00464         break;
00465       case 'n':                         /* normal */
00466         /* eat up rest of line */
00467         fgets(buf, sizeof(buf), file);
00468         numnormals++;
00469         break;
00470       case 't':                         /* texcoord */
00471         /* eat up rest of line */
00472         fgets(buf, sizeof(buf), file);
00473         numtexcoords++;
00474         break;
00475       default:
00476         printf("glmFirstPass(): Unknown token \"%s\".\n", buf);
00477         exit(1);
00478         break;
00479       }
00480       break;
00481     case 'm':
00482       fgets(buf, sizeof(buf), file);
00483       sscanf(buf, "%s %s", buf, buf);
00484       model->mtllibname = strdup(buf);
00485       glmReadMTL(model, buf);
00486       break;
00487     case 'u':
00488       /* eat up rest of line */
00489       fgets(buf, sizeof(buf), file);
00490       break;
00491     case 'g':                           /* group */
00492       /* eat up rest of line */
00493       fgets(buf, sizeof(buf), file);
00494 #if SINGLE_STRING_GROUP_NAMES
00495       sscanf(buf, "%s", buf);
00496 #else
00497       buf[strlen(buf)-1] = '\0';        /* nuke '\n' */
00498 #endif
00499       group = glmAddGroup(model, buf);
00500       break;
00501     case 'f':                           /* face */
00502       v = n = t = 0;
00503       fscanf(file, "%s", buf);
00504       /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
00505       if (strstr(buf, "//")) {
00506         /* v//n */
00507         sscanf(buf, "%d//%d", &v, &n);
00508         fscanf(file, "%d//%d", &v, &n);
00509         fscanf(file, "%d//%d", &v, &n);
00510         numtriangles++;
00511         group->numtriangles++;
00512         while(fscanf(file, "%d//%d", &v, &n) > 0) {
00513           numtriangles++;
00514           group->numtriangles++;
00515         }
00516       } else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
00517         /* v/t/n */
00518         fscanf(file, "%d/%d/%d", &v, &t, &n);
00519         fscanf(file, "%d/%d/%d", &v, &t, &n);
00520         numtriangles++;
00521         group->numtriangles++;
00522         while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
00523           numtriangles++;
00524           group->numtriangles++;
00525         }
00526       } else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
00527         /* v/t */
00528         fscanf(file, "%d/%d", &v, &t);
00529         fscanf(file, "%d/%d", &v, &t);
00530         numtriangles++;
00531         group->numtriangles++;
00532         while(fscanf(file, "%d/%d", &v, &t) > 0) {
00533           numtriangles++;
00534           group->numtriangles++;
00535         }
00536       } else {
00537         /* v */
00538         fscanf(file, "%d", &v);
00539         fscanf(file, "%d", &v);
00540         numtriangles++;
00541         group->numtriangles++;
00542         while(fscanf(file, "%d", &v) > 0) {
00543           numtriangles++;
00544           group->numtriangles++;
00545         }
00546       }
00547       break;
00548 
00549     default:
00550       /* eat up rest of line */
00551       fgets(buf, sizeof(buf), file);
00552       break;
00553     }
00554   }
00555 
00556   /* set the stats in the model structure */
00557   model->numvertices  = numvertices;
00558   model->numnormals   = numnormals;
00559   model->numtexcoords = numtexcoords;
00560   model->numtriangles = numtriangles;
00561 
00562   /* allocate memory for the triangles in each group */
00563   group = model->groups;
00564   while(group) {
00565     group->triangles = (GLuint*)malloc(sizeof(GLuint) * group->numtriangles);
00566     group->numtriangles = 0;
00567     group = group->next;
00568   }
00569 }
00570 
00571 /* glmSecondPass: second pass at a Wavefront OBJ file that gets all
00572  * the data.
00573  *
00574  * model - properly initialized GLMmodel structure
00575  * file  - (fopen'd) file descriptor 
00576  */
00577 static GLvoid
00578 glmSecondPass(GLMmodel* model, FILE* file) 
00579 {
00580   GLuint    numvertices;                /* number of vertices in model */
00581   GLuint    numnormals;                 /* number of normals in model */
00582   GLuint    numtexcoords;               /* number of texcoords in model */
00583   GLuint    numtriangles;               /* number of triangles in model */
00584   GLfloat*  vertices;                   /* array of vertices  */
00585   GLfloat*  normals;                    /* array of normals */
00586   GLfloat*  texcoords;                  /* array of texture coordinates */
00587   GLMgroup* group;                      /* current group pointer */
00588   GLuint    material;                   /* current material */
00589   GLuint    v, n, t;
00590   char      buf[128];
00591 
00592   /* set the pointer shortcuts */
00593   vertices     = model->vertices;
00594   normals      = model->normals;
00595   texcoords    = model->texcoords;
00596   group        = model->groups;
00597 
00598   /* on the second pass through the file, read all the data into the
00599      allocated arrays */
00600   numvertices = numnormals = numtexcoords = 1;
00601   numtriangles = 0;
00602   material = 0;
00603   while(fscanf(file, "%s", buf) != EOF) {
00604     switch(buf[0]) {
00605     case '#':                           /* comment */
00606       /* eat up rest of line */
00607       fgets(buf, sizeof(buf), file);
00608       break;
00609     case 'v':                           /* v, vn, vt */
00610       switch(buf[1]) {
00611       case '\0':                        /* vertex */
00612         fscanf(file, "%f %f %f", 
00613                &vertices[3 * numvertices + 0], 
00614                &vertices[3 * numvertices + 1], 
00615                &vertices[3 * numvertices + 2]);
00616         numvertices++;
00617         break;
00618       case 'n':                         /* normal */
00619         fscanf(file, "%f %f %f", 
00620                &normals[3 * numnormals + 0],
00621                &normals[3 * numnormals + 1], 
00622                &normals[3 * numnormals + 2]);
00623         numnormals++;
00624         break;
00625       case 't':                         /* texcoord */
00626         fscanf(file, "%f %f", 
00627                &texcoords[2 * numtexcoords + 0],
00628                &texcoords[2 * numtexcoords + 1]);
00629         numtexcoords++;
00630         break;
00631       }
00632       break;
00633     case 'u':
00634       fgets(buf, sizeof(buf), file);
00635       sscanf(buf, "%s %s", buf, buf);
00636       group->material = material = glmFindMaterial(model, buf);
00637       break;
00638     case 'g':                           /* group */
00639       /* eat up rest of line */
00640       fgets(buf, sizeof(buf), file);
00641 #if SINGLE_STRING_GROUP_NAMES
00642       sscanf(buf, "%s", buf);
00643 #else
00644       buf[strlen(buf)-1] = '\0';        /* nuke '\n' */
00645 #endif
00646       group = glmFindGroup(model, buf);
00647       group->material = material;
00648       break;
00649     case 'f':                           /* face */
00650       v = n = t = 0;
00651       fscanf(file, "%s", buf);
00652       /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
00653       if (strstr(buf, "//")) {
00654         /* v//n */
00655         sscanf(buf, "%d//%d", &v, &n);
00656         T(numtriangles).vindices[0] = v;
00657         T(numtriangles).nindices[0] = n;
00658         fscanf(file, "%d//%d", &v, &n);
00659         T(numtriangles).vindices[1] = v;
00660         T(numtriangles).nindices[1] = n;
00661         fscanf(file, "%d//%d", &v, &n);
00662         T(numtriangles).vindices[2] = v;
00663         T(numtriangles).nindices[2] = n;
00664         group->triangles[group->numtriangles++] = numtriangles;
00665         numtriangles++;
00666         while(fscanf(file, "%d//%d", &v, &n) > 0) {
00667           T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
00668           T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
00669           T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
00670           T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
00671           T(numtriangles).vindices[2] = v;
00672           T(numtriangles).nindices[2] = n;
00673           group->triangles[group->numtriangles++] = numtriangles;
00674           numtriangles++;
00675         }
00676       } else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
00677         /* v/t/n */
00678         T(numtriangles).vindices[0] = v;
00679         T(numtriangles).tindices[0] = t;
00680         T(numtriangles).nindices[0] = n;
00681         fscanf(file, "%d/%d/%d", &v, &t, &n);
00682         T(numtriangles).vindices[1] = v;
00683         T(numtriangles).tindices[1] = t;
00684         T(numtriangles).nindices[1] = n;
00685         fscanf(file, "%d/%d/%d", &v, &t, &n);
00686         T(numtriangles).vindices[2] = v;
00687         T(numtriangles).tindices[2] = t;
00688         T(numtriangles).nindices[2] = n;
00689         group->triangles[group->numtriangles++] = numtriangles;
00690         numtriangles++;
00691         while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
00692           T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
00693           T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
00694           T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
00695           T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
00696           T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
00697           T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
00698           T(numtriangles).vindices[2] = v;
00699           T(numtriangles).tindices[2] = t;
00700           T(numtriangles).nindices[2] = n;
00701           group->triangles[group->numtriangles++] = numtriangles;
00702           numtriangles++;
00703         }
00704       } else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
00705         /* v/t */
00706         T(numtriangles).vindices[0] = v;
00707         T(numtriangles).tindices[0] = t;
00708         fscanf(file, "%d/%d", &v, &t);
00709         T(numtriangles).vindices[1] = v;
00710         T(numtriangles).tindices[1] = t;
00711         fscanf(file, "%d/%d", &v, &t);
00712         T(numtriangles).vindices[2] = v;
00713         T(numtriangles).tindices[2] = t;
00714         group->triangles[group->numtriangles++] = numtriangles;
00715         numtriangles++;
00716         while(fscanf(file, "%d/%d", &v, &t) > 0) {
00717           T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
00718           T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
00719           T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
00720           T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
00721           T(numtriangles).vindices[2] = v;
00722           T(numtriangles).tindices[2] = t;
00723           group->triangles[group->numtriangles++] = numtriangles;
00724           numtriangles++;
00725         }
00726       } else {
00727         /* v */
00728         sscanf(buf, "%d", &v);
00729         T(numtriangles).vindices[0] = v;
00730         fscanf(file, "%d", &v);
00731         T(numtriangles).vindices[1] = v;
00732         fscanf(file, "%d", &v);
00733         T(numtriangles).vindices[2] = v;
00734         group->triangles[group->numtriangles++] = numtriangles;
00735         numtriangles++;
00736         while(fscanf(file, "%d", &v) > 0) {
00737           T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
00738           T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
00739           T(numtriangles).vindices[2] = v;
00740           group->triangles[group->numtriangles++] = numtriangles;
00741           numtriangles++;
00742         }
00743       }
00744       break;
00745 
00746     default:
00747       /* eat up rest of line */
00748       fgets(buf, sizeof(buf), file);
00749       break;
00750     }
00751   }
00752 
00753 #if 0
00754   /* announce the memory requirements */
00755   printf(" Memory: %d bytes\n",
00756          numvertices  * 3*sizeof(GLfloat) +
00757          numnormals   * 3*sizeof(GLfloat) * (numnormals ? 1 : 0) +
00758          numtexcoords * 3*sizeof(GLfloat) * (numtexcoords ? 1 : 0) +
00759          numtriangles * sizeof(GLMtriangle));
00760 #endif
00761 }
00762 
00763 
00764 /* public functions */
00765 
00766 
00767 /* glmUnitize: "unitize" a model by translating it to the origin and
00768  * scaling it to fit in a unit cube around the origin.  Returns the
00769  * scalefactor used.
00770  *
00771  * model - properly initialized GLMmodel structure 
00772  */
00773 GLfloat
00774 glmUnitize(GLMmodel* model)
00775 {
00776   GLuint  i;
00777   GLfloat maxx, minx, maxy, miny, maxz, minz;
00778   GLfloat cx, cy, cz, w, h, d;
00779   GLfloat scale;
00780 
00781   assert(model);
00782   assert(model->vertices);
00783 
00784   /* get the max/mins */
00785   maxx = minx = model->vertices[3 + 0];
00786   maxy = miny = model->vertices[3 + 1];
00787   maxz = minz = model->vertices[3 + 2];
00788   for (i = 1; i <= model->numvertices; i++) {
00789     if (maxx < model->vertices[3 * i + 0])
00790       maxx = model->vertices[3 * i + 0];
00791     if (minx > model->vertices[3 * i + 0])
00792       minx = model->vertices[3 * i + 0];
00793 
00794     if (maxy < model->vertices[3 * i + 1])
00795       maxy = model->vertices[3 * i + 1];
00796     if (miny > model->vertices[3 * i + 1])
00797       miny = model->vertices[3 * i + 1];
00798 
00799     if (maxz < model->vertices[3 * i + 2])
00800       maxz = model->vertices[3 * i + 2];
00801     if (minz > model->vertices[3 * i + 2])
00802       minz = model->vertices[3 * i + 2];
00803   }
00804 
00805   /* calculate model width, height, and depth */
00806   w = glmAbs(maxx) + glmAbs(minx);
00807   h = glmAbs(maxy) + glmAbs(miny);
00808   d = glmAbs(maxz) + glmAbs(minz);
00809 
00810   /* calculate center of the model */
00811   cx = (maxx + minx) / 2.0;
00812   cy = (maxy + miny) / 2.0;
00813   cz = (maxz + minz) / 2.0;
00814 
00815   /* calculate unitizing scale factor */
00816   scale = 2.0 / glmMax(glmMax(w, h), d);
00817 
00818   /* translate around center then scale */
00819   for (i = 1; i <= model->numvertices; i++) {
00820     model->vertices[3 * i + 0] -= cx;
00821     model->vertices[3 * i + 1] -= cy;
00822     model->vertices[3 * i + 2] -= cz;
00823     model->vertices[3 * i + 0] *= scale;
00824     model->vertices[3 * i + 1] *= scale;
00825     model->vertices[3 * i + 2] *= scale;
00826   }
00827 
00828   return scale;
00829 }
00830 
00831 /* glmDimensions: Calculates the dimensions (width, height, depth) of
00832  * a model.
00833  *
00834  * model      - initialized GLMmodel structure
00835  * dimensions - array of 3 GLfloats (GLfloat dimensions[3])
00836  */
00837 GLvoid
00838 glmDimensions(GLMmodel* model, GLfloat* dimensions)
00839 {
00840   GLuint i;
00841   GLfloat maxx, minx, maxy, miny, maxz, minz;
00842 
00843   assert(model);
00844   assert(model->vertices);
00845   assert(dimensions);
00846 
00847   /* get the max/mins */
00848   maxx = minx = model->vertices[3 + 0];
00849   maxy = miny = model->vertices[3 + 1];
00850   maxz = minz = model->vertices[3 + 2];
00851   for (i = 1; i <= model->numvertices; i++) {
00852     if (maxx < model->vertices[3 * i + 0])
00853       maxx = model->vertices[3 * i + 0];
00854     if (minx > model->vertices[3 * i + 0])
00855       minx = model->vertices[3 * i + 0];
00856 
00857     if (maxy < model->vertices[3 * i + 1])
00858       maxy = model->vertices[3 * i + 1];
00859     if (miny > model->vertices[3 * i + 1])
00860       miny = model->vertices[3 * i + 1];
00861 
00862     if (maxz < model->vertices[3 * i + 2])
00863       maxz = model->vertices[3 * i + 2];
00864     if (minz > model->vertices[3 * i + 2])
00865       minz = model->vertices[3 * i + 2];
00866   }
00867 
00868   /* calculate model width, height, and depth */
00869   dimensions[0] = glmAbs(maxx) + glmAbs(minx);
00870   dimensions[1] = glmAbs(maxy) + glmAbs(miny);
00871   dimensions[2] = glmAbs(maxz) + glmAbs(minz);
00872 }
00873 
00874 /* glmScale: Scales a model by a given amount.
00875  * 
00876  * model - properly initialized GLMmodel structure
00877  * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
00878  */
00879 GLvoid
00880 glmScale(GLMmodel* model, GLfloat scale)
00881 {
00882   GLuint i;
00883 
00884   for (i = 1; i <= model->numvertices; i++) {
00885     model->vertices[3 * i + 0] *= scale;
00886     model->vertices[3 * i + 1] *= scale;
00887     model->vertices[3 * i + 2] *= scale;
00888   }
00889 }
00890 
00891 /* glmReverseWinding: Reverse the polygon winding for all polygons in
00892  * this model.  Default winding is counter-clockwise.  Also changes
00893  * the direction of the normals.
00894  * 
00895  * model - properly initialized GLMmodel structure 
00896  */
00897 GLvoid
00898 glmReverseWinding(GLMmodel* model)
00899 {
00900   GLuint i, swap;
00901 
00902   assert(model);
00903 
00904   for (i = 0; i < model->numtriangles; i++) {
00905     swap = T(i).vindices[0];
00906     T(i).vindices[0] = T(i).vindices[2];
00907     T(i).vindices[2] = swap;
00908 
00909     if (model->numnormals) {
00910       swap = T(i).nindices[0];
00911       T(i).nindices[0] = T(i).nindices[2];
00912       T(i).nindices[2] = swap;
00913     }
00914 
00915     if (model->numtexcoords) {
00916       swap = T(i).tindices[0];
00917       T(i).tindices[0] = T(i).tindices[2];
00918       T(i).tindices[2] = swap;
00919     }
00920   }
00921 
00922   /* reverse facet normals */
00923   for (i = 1; i <= model->numfacetnorms; i++) {
00924     model->facetnorms[3 * i + 0] = -model->facetnorms[3 * i + 0];
00925     model->facetnorms[3 * i + 1] = -model->facetnorms[3 * i + 1];
00926     model->facetnorms[3 * i + 2] = -model->facetnorms[3 * i + 2];
00927   }
00928 
00929   /* reverse vertex normals */
00930   for (i = 1; i <= model->numnormals; i++) {
00931     model->normals[3 * i + 0] = -model->normals[3 * i + 0];
00932     model->normals[3 * i + 1] = -model->normals[3 * i + 1];
00933     model->normals[3 * i + 2] = -model->normals[3 * i + 2];
00934   }
00935 }
00936 
00937 /* glmFacetNormals: Generates facet normals for a model (by taking the
00938  * cross product of the two vectors derived from the sides of each
00939  * triangle).  Assumes a counter-clockwise winding.
00940  *
00941  * model - initialized GLMmodel structure
00942  */
00943 GLvoid
00944 glmFacetNormals(GLMmodel* model)
00945 {
00946   GLuint  i;
00947   GLfloat u[3];
00948   GLfloat v[3];
00949   
00950   assert(model);
00951   assert(model->vertices);
00952 
00953   /* clobber any old facetnormals */
00954   if (model->facetnorms)
00955     free(model->facetnorms);
00956 
00957   /* allocate memory for the new facet normals */
00958   model->numfacetnorms = model->numtriangles;
00959   model->facetnorms = (GLfloat*)malloc(sizeof(GLfloat) *
00960                                        3 * (model->numfacetnorms + 1));
00961 
00962   for (i = 0; i < model->numtriangles; i++) {
00963     model->triangles[i].findex = i+1;
00964 
00965     u[0] = model->vertices[3 * T(i).vindices[1] + 0] -
00966            model->vertices[3 * T(i).vindices[0] + 0];
00967     u[1] = model->vertices[3 * T(i).vindices[1] + 1] -
00968            model->vertices[3 * T(i).vindices[0] + 1];
00969     u[2] = model->vertices[3 * T(i).vindices[1] + 2] -
00970            model->vertices[3 * T(i).vindices[0] + 2];
00971 
00972     v[0] = model->vertices[3 * T(i).vindices[2] + 0] -
00973            model->vertices[3 * T(i).vindices[0] + 0];
00974     v[1] = model->vertices[3 * T(i).vindices[2] + 1] -
00975            model->vertices[3 * T(i).vindices[0] + 1];
00976     v[2] = model->vertices[3 * T(i).vindices[2] + 2] -
00977            model->vertices[3 * T(i).vindices[0] + 2];
00978 
00979     glmCross(u, v, &model->facetnorms[3 * (i+1)]);
00980     glmNormalize(&model->facetnorms[3 * (i+1)]);
00981   }
00982 }
00983 
00984 /* glmVertexNormals: Generates smooth vertex normals for a model.
00985  * First builds a list of all the triangles each vertex is in.  Then
00986  * loops through each vertex in the the list averaging all the facet
00987  * normals of the triangles each vertex is in.  Finally, sets the
00988  * normal index in the triangle for the vertex to the generated smooth
00989  * normal.  If the dot product of a facet normal and the facet normal
00990  * associated with the first triangle in the list of triangles the
00991  * current vertex is in is greater than the cosine of the angle
00992  * parameter to the function, that facet normal is not added into the
00993  * average normal calculation and the corresponding vertex is given
00994  * the facet normal.  This tends to preserve hard edges.  The angle to
00995  * use depends on the model, but 90 degrees is usually a good start.
00996  *
00997  * model - initialized GLMmodel structure
00998  * angle - maximum angle (in degrees) to smooth across
00999  */
01000 GLvoid
01001 glmVertexNormals(GLMmodel* model, GLfloat angle)
01002 {
01003   GLMnode*  node;
01004   GLMnode*  tail;
01005   GLMnode** members;
01006   GLfloat*  normals;
01007   GLuint    numnormals;
01008   GLfloat   average[3];
01009   GLfloat   dot, cos_angle;
01010   GLuint    i, avg;
01011 
01012   assert(model);
01013   assert(model->facetnorms);
01014 
01015   /* calculate the cosine of the angle (in degrees) */
01016   cos_angle = cos(angle * M_PI / 180.0);
01017 
01018   /* nuke any previous normals */
01019   if (model->normals)
01020     free(model->normals);
01021 
01022   /* allocate space for new normals */
01023   model->numnormals = model->numtriangles * 3; /* 3 normals per triangle */
01024   model->normals = (GLfloat*)malloc(sizeof(GLfloat)* 3* (model->numnormals+1));
01025 
01026   /* allocate a structure that will hold a linked list of triangle
01027      indices for each vertex */
01028   members = (GLMnode**)malloc(sizeof(GLMnode*) * (model->numvertices + 1));
01029   for (i = 1; i <= model->numvertices; i++)
01030     members[i] = NULL;
01031   
01032   /* for every triangle, create a node for each vertex in it */
01033   for (i = 0; i < model->numtriangles; i++) {
01034     node = (GLMnode*)malloc(sizeof(GLMnode));
01035     node->index = i;
01036     node->next  = members[T(i).vindices[0]];
01037     members[T(i).vindices[0]] = node;
01038 
01039     node = (GLMnode*)malloc(sizeof(GLMnode));
01040     node->index = i;
01041     node->next  = members[T(i).vindices[1]];
01042     members[T(i).vindices[1]] = node;
01043 
01044     node = (GLMnode*)malloc(sizeof(GLMnode));
01045     node->index = i;
01046     node->next  = members[T(i).vindices[2]];
01047     members[T(i).vindices[2]] = node;
01048   }
01049 
01050   /* calculate the average normal for each vertex */
01051   numnormals = 1;
01052   for (i = 1; i <= model->numvertices; i++) {
01053     /* calculate an average normal for this vertex by averaging the
01054        facet normal of every triangle this vertex is in */
01055     node = members[i];
01056     if (!node)
01057       fprintf(stderr, "glmVertexNormals(): vertex w/o a triangle\n");
01058     average[0] = 0.0; average[1] = 0.0; average[2] = 0.0;
01059     avg = 0;
01060     while (node) {
01061       /* only average if the dot product of the angle between the two
01062          facet normals is greater than the cosine of the threshold
01063          angle -- or, said another way, the angle between the two
01064          facet normals is less than (or equal to) the threshold angle */
01065       dot = glmDot(&model->facetnorms[3 * T(node->index).findex],
01066                     &model->facetnorms[3 * T(members[i]->index).findex]);
01067       if (dot > cos_angle) {
01068         node->averaged = GL_TRUE;
01069         average[0] += model->facetnorms[3 * T(node->index).findex + 0];
01070         average[1] += model->facetnorms[3 * T(node->index).findex + 1];
01071         average[2] += model->facetnorms[3 * T(node->index).findex + 2];
01072         avg = 1;                        /* we averaged at least one normal! */
01073       } else {
01074         node->averaged = GL_FALSE;
01075       }
01076       node = node->next;
01077     }
01078 
01079     if (avg) {
01080       /* normalize the averaged normal */
01081       glmNormalize(average);
01082 
01083       /* add the normal to the vertex normals list */
01084       model->normals[3 * numnormals + 0] = average[0];
01085       model->normals[3 * numnormals + 1] = average[1];
01086       model->normals[3 * numnormals + 2] = average[2];
01087       avg = numnormals;
01088       numnormals++;
01089     }
01090 
01091     /* set the normal of this vertex in each triangle it is in */
01092     node = members[i];
01093     while (node) {
01094       if (node->averaged) {
01095         /* if this node was averaged, use the average normal */
01096         if (T(node->index).vindices[0] == i)
01097           T(node->index).nindices[0] = avg;
01098         else if (T(node->index).vindices[1] == i)
01099           T(node->index).nindices[1] = avg;
01100         else if (T(node->index).vindices[2] == i)
01101           T(node->index).nindices[2] = avg;
01102       } else {
01103         /* if this node wasn't averaged, use the facet normal */
01104         model->normals[3 * numnormals + 0] = 
01105           model->facetnorms[3 * T(node->index).findex + 0];
01106         model->normals[3 * numnormals + 1] = 
01107           model->facetnorms[3 * T(node->index).findex + 1];
01108         model->normals[3 * numnormals + 2] = 
01109           model->facetnorms[3 * T(node->index).findex + 2];
01110         if (T(node->index).vindices[0] == i)
01111           T(node->index).nindices[0] = numnormals;
01112         else if (T(node->index).vindices[1] == i)
01113           T(node->index).nindices[1] = numnormals;
01114         else if (T(node->index).vindices[2] == i)
01115           T(node->index).nindices[2] = numnormals;
01116         numnormals++;
01117       }
01118       node = node->next;
01119     }
01120   }
01121   
01122   model->numnormals = numnormals - 1;
01123 
01124   /* free the member information */
01125   for (i = 1; i <= model->numvertices; i++) {
01126     node = members[i];
01127     while (node) {
01128       tail = node;
01129       node = node->next;
01130       free(tail);
01131     }
01132   }
01133   free(members);
01134 
01135   /* pack the normals array (we previously allocated the maximum
01136      number of normals that could possibly be created (numtriangles *
01137      3), so get rid of some of them (usually alot unless none of the
01138      facet normals were averaged)) */
01139   normals = model->normals;
01140   model->normals = (GLfloat*)malloc(sizeof(GLfloat)* 3* (model->numnormals+1));
01141   for (i = 1; i <= model->numnormals; i++) {
01142     model->normals[3 * i + 0] = normals[3 * i + 0];
01143     model->normals[3 * i + 1] = normals[3 * i + 1];
01144     model->normals[3 * i + 2] = normals[3 * i + 2];
01145   }
01146   free(normals);
01147 }
01148 
01149 
01150 /* glmLinearTexture: Generates texture coordinates according to a
01151  * linear projection of the texture map.  It generates these by
01152  * linearly mapping the vertices onto a square.
01153  *
01154  * model - pointer to initialized GLMmodel structure
01155  */
01156 GLvoid
01157 glmLinearTexture(GLMmodel* model)
01158 {
01159   GLMgroup *group;
01160   GLfloat dimensions[3];
01161   GLfloat x, y, scalefactor;
01162   GLuint i;
01163   
01164   assert(model);
01165 
01166   if (model->texcoords)
01167     free(model->texcoords);
01168   model->numtexcoords = model->numvertices;
01169   model->texcoords=(GLfloat*)malloc(sizeof(GLfloat)*2*(model->numtexcoords+1));
01170   
01171   glmDimensions(model, dimensions);
01172   scalefactor = 2.0 / 
01173     glmAbs(glmMax(glmMax(dimensions[0], dimensions[1]), dimensions[2]));
01174 
01175   /* do the calculations */
01176   for(i = 1; i <= model->numvertices; i++) {
01177     x = model->vertices[3 * i + 0] * scalefactor;
01178     y = model->vertices[3 * i + 2] * scalefactor;
01179     model->texcoords[2 * i + 0] = (x + 1.0) / 2.0;
01180     model->texcoords[2 * i + 1] = (y + 1.0) / 2.0;
01181   }
01182   
01183   /* go through and put texture coordinate indices in all the triangles */
01184   group = model->groups;
01185   while(group) {
01186     for(i = 0; i < group->numtriangles; i++) {
01187       T(group->triangles[i]).tindices[0] = T(group->triangles[i]).vindices[0];
01188       T(group->triangles[i]).tindices[1] = T(group->triangles[i]).vindices[1];
01189       T(group->triangles[i]).tindices[2] = T(group->triangles[i]).vindices[2];
01190     }    
01191     group = group->next;
01192   }
01193 
01194 #if 0
01195   printf("glmLinearTexture(): generated %d linear texture coordinates\n",
01196           model->numtexcoords);
01197 #endif
01198 }
01199 
01200 /* glmSpheremapTexture: Generates texture coordinates according to a
01201  * spherical projection of the texture map.  Sometimes referred to as
01202  * spheremap, or reflection map texture coordinates.  It generates
01203  * these by using the normal to calculate where that vertex would map
01204  * onto a sphere.  Since it is impossible to map something flat
01205  * perfectly onto something spherical, there is distortion at the
01206  * poles.  This particular implementation causes the poles along the X
01207  * axis to be distorted.
01208  *
01209  * model - pointer to initialized GLMmodel structure
01210  */
01211 GLvoid
01212 glmSpheremapTexture(GLMmodel* model)
01213 {
01214   GLMgroup* group;
01215   GLfloat theta, phi, rho, x, y, z, r;
01216   GLuint i;
01217   
01218   assert(model);
01219   assert(model->normals);
01220 
01221   if (model->texcoords)
01222     free(model->texcoords);
01223   model->numtexcoords = model->numnormals;
01224   model->texcoords=(GLfloat*)malloc(sizeof(GLfloat)*2*(model->numtexcoords+1));
01225      
01226   for (i = 1; i <= model->numnormals; i++) {
01227     z = model->normals[3 * i + 0];      /* re-arrange for pole distortion */
01228     y = model->normals[3 * i + 1];
01229     x = model->normals[3 * i + 2];
01230     r = sqrt((x * x) + (y * y));
01231     rho = sqrt((r * r) + (z * z));
01232       
01233     if(r == 0.0) {
01234         theta = 0.0;
01235         phi = 0.0;
01236     } else {
01237       if(z == 0.0)
01238         phi = 3.14159265 / 2.0;
01239       else
01240         phi = acos(z / rho);
01241 
01242       if(y == 0.0)
01243         theta = 3.141592365 / 2.0;
01244       else
01245         theta = asin(y / r) + (3.14159265 / 2.0);
01246     }
01247     
01248     model->texcoords[2 * i + 0] = theta / 3.14159265;
01249     model->texcoords[2 * i + 1] = phi / 3.14159265;
01250   }
01251   
01252   /* go through and put texcoord indices in all the triangles */
01253   group = model->groups;
01254   while(group) {
01255     for (i = 0; i < group->numtriangles; i++) {
01256       T(group->triangles[i]).tindices[0] = T(group->triangles[i]).nindices[0];
01257       T(group->triangles[i]).tindices[1] = T(group->triangles[i]).nindices[1];
01258       T(group->triangles[i]).tindices[2] = T(group->triangles[i]).nindices[2];
01259     }
01260     group = group->next;
01261   }
01262 }
01263 
01264 /* glmDelete: Deletes a GLMmodel structure.
01265  *
01266  * model - initialized GLMmodel structure
01267  */
01268 GLvoid
01269 glmDelete(GLMmodel* model)
01270 {
01271   GLMgroup* group;
01272   GLuint i;
01273 
01274   assert(model);
01275 
01276   if (model->pathname)   free(model->pathname);
01277   if (model->mtllibname) free(model->mtllibname);
01278   if (model->vertices)   free(model->vertices);
01279   if (model->normals)    free(model->normals);
01280   if (model->texcoords)  free(model->texcoords);
01281   if (model->facetnorms) free(model->facetnorms);
01282   if (model->triangles)  free(model->triangles);
01283   if (model->materials) {
01284     for (i = 0; i < model->nummaterials; i++)
01285       free(model->materials[i].name);
01286   }
01287   free(model->materials);
01288   while(model->groups) {
01289     group = model->groups;
01290     model->groups = model->groups->next;
01291     free(group->name);
01292     free(group->triangles);
01293     free(group);
01294   }
01295 
01296   free(model);
01297 }
01298 
01299 /* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
01300  * Returns a pointer to the created object which should be free'd with
01301  * glmDelete().
01302  *
01303  * filename - name of the file containing the Wavefront .OBJ format data.  
01304  */
01305 GLMmodel* 
01306 glmReadOBJ(const char* filename)
01307 {
01308   GLMmodel* model;
01309   FILE*     file;
01310 
01311   /* open the file */
01312   file = fopen(filename, "r");
01313   if (!file) {
01314     fprintf(stderr, "glmReadOBJ() failed: can't open data file \"%s\".\n",
01315             filename);
01316     exit(1);
01317   }
01318 
01319   /* allocate a new model */
01320   model = (GLMmodel*)malloc(sizeof(GLMmodel));
01321   model->pathname      = strdup(filename);
01322   model->mtllibname    = NULL;
01323   model->numvertices   = 0;
01324   model->vertices      = NULL;
01325   model->numnormals    = 0;
01326   model->normals       = NULL;
01327   model->numtexcoords  = 0;
01328   model->texcoords     = NULL;
01329   model->numfacetnorms = 0;
01330   model->facetnorms    = NULL;
01331   model->numtriangles  = 0;
01332   model->triangles     = NULL;
01333   model->nummaterials  = 0;
01334   model->materials     = NULL;
01335   model->numgroups     = 0;
01336   model->groups        = NULL;
01337   model->position[0]   = 0.0;
01338   model->position[1]   = 0.0;
01339   model->position[2]   = 0.0;
01340 
01341   /* make a first pass through the file to get a count of the number
01342      of vertices, normals, texcoords & triangles */
01343   glmFirstPass(model, file);
01344 
01345   /* allocate memory */
01346   model->vertices = (GLfloat*)malloc(sizeof(GLfloat) *
01347                                      3 * (model->numvertices + 1));
01348   model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) *
01349                                           model->numtriangles);
01350   if (model->numnormals) {
01351     model->normals = (GLfloat*)malloc(sizeof(GLfloat) *
01352                                       3 * (model->numnormals + 1));
01353   }
01354   if (model->numtexcoords) {
01355     model->texcoords = (GLfloat*)malloc(sizeof(GLfloat) *
01356                                         2 * (model->numtexcoords + 1));
01357   }
01358 
01359   /* rewind to beginning of file and read in the data this pass */
01360   rewind(file);
01361 
01362   glmSecondPass(model, file);
01363 
01364   /* close the file */
01365   fclose(file);
01366 
01367   return model;
01368 }
01369 
01370 /* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
01371  * a file.
01372  *
01373  * model    - initialized GLMmodel structure
01374  * filename - name of the file to write the Wavefront .OBJ format data to
01375  * mode     - a bitwise or of values describing what is written to the file
01376  *            GLM_NONE     -  render with only vertices
01377  *            GLM_FLAT     -  render with facet normals
01378  *            GLM_SMOOTH   -  render with vertex normals
01379  *            GLM_TEXTURE  -  render with texture coords
01380  *            GLM_COLOR    -  render with colors (color material)
01381  *            GLM_MATERIAL -  render with materials
01382  *            GLM_COLOR and GLM_MATERIAL should not both be specified.  
01383  *            GLM_FLAT and GLM_SMOOTH should not both be specified.  
01384  */
01385 GLvoid
01386 glmWriteOBJ(GLMmodel* model, char* filename, GLuint mode)
01387 {
01388   GLuint    i;
01389   FILE*     file;
01390   GLMgroup* group;
01391 
01392   assert(model);
01393 
01394   /* do a bit of warning */
01395   if (mode & GLM_FLAT && !model->facetnorms) {
01396     printf("glmWriteOBJ() warning: flat normal output requested "
01397            "with no facet normals defined.\n");
01398     mode &= ~GLM_FLAT;
01399   }
01400   if (mode & GLM_SMOOTH && !model->normals) {
01401     printf("glmWriteOBJ() warning: smooth normal output requested "
01402            "with no normals defined.\n");
01403     mode &= ~GLM_SMOOTH;
01404   }
01405   if (mode & GLM_TEXTURE && !model->texcoords) {
01406     printf("glmWriteOBJ() warning: texture coordinate output requested "
01407            "with no texture coordinates defined.\n");
01408     mode &= ~GLM_TEXTURE;
01409   }
01410   if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
01411     printf("glmWriteOBJ() warning: flat normal output requested "
01412            "and smooth normal output requested (using smooth).\n");
01413     mode &= ~GLM_FLAT;
01414   }
01415   if (mode & GLM_COLOR && !model->materials) {
01416     printf("glmWriteOBJ() warning: color output requested "
01417            "with no colors (materials) defined.\n");
01418     mode &= ~GLM_COLOR;
01419   }
01420   if (mode & GLM_MATERIAL && !model->materials) {
01421     printf("glmWriteOBJ() warning: material output requested "
01422            "with no materials defined.\n");
01423     mode &= ~GLM_MATERIAL;
01424   }
01425   if (mode & GLM_COLOR && mode & GLM_MATERIAL) {
01426     printf("glmWriteOBJ() warning: color and material output requested "
01427            "outputting only materials.\n");
01428     mode &= ~GLM_COLOR;
01429   }
01430 
01431 
01432   /* open the file */
01433   file = fopen(filename, "w");
01434   if (!file) {
01435     fprintf(stderr, "glmWriteOBJ() failed: can't open file \"%s\" to write.\n",
01436             filename);
01437     exit(1);
01438   }
01439 
01440   /* spit out a header */
01441   fprintf(file, "#  \n");
01442   fprintf(file, "#  Wavefront OBJ generated by GLM library\n");
01443   fprintf(file, "#  \n");
01444   fprintf(file, "#  GLM library\n");
01445   fprintf(file, "#  Nate Robins\n");
01446   fprintf(file, "#  ndr@pobox.com\n");
01447   fprintf(file, "#  http://www.pobox.com/~ndr\n");
01448   fprintf(file, "#  \n");
01449 
01450   if (mode & GLM_MATERIAL && model->mtllibname) {
01451     fprintf(file, "\nmtllib %s\n\n", model->mtllibname);
01452     glmWriteMTL(model, filename, model->mtllibname);
01453   }
01454 
01455   /* spit out the vertices */
01456   fprintf(file, "\n");
01457   fprintf(file, "# %d vertices\n", model->numvertices);
01458   for (i = 1; i <= model->numvertices; i++) {
01459     fprintf(file, "v %f %f %f\n", 
01460             model->vertices[3 * i + 0],
01461             model->vertices[3 * i + 1],
01462             model->vertices[3 * i + 2]);
01463   }
01464 
01465   /* spit out the smooth/flat normals */
01466   if (mode & GLM_SMOOTH) {
01467     fprintf(file, "\n");
01468     fprintf(file, "# %d normals\n", model->numnormals);
01469     for (i = 1; i <= model->numnormals; i++) {
01470       fprintf(file, "vn %f %f %f\n", 
01471               model->normals[3 * i + 0],
01472               model->normals[3 * i + 1],
01473               model->normals[3 * i + 2]);
01474     }
01475   } else if (mode & GLM_FLAT) {
01476     fprintf(file, "\n");
01477     fprintf(file, "# %d normals\n", model->numfacetnorms);
01478     for (i = 1; i <= model->numnormals; i++) {
01479       fprintf(file, "vn %f %f %f\n", 
01480               model->facetnorms[3 * i + 0],
01481               model->facetnorms[3 * i + 1],
01482               model->facetnorms[3 * i + 2]);
01483     }
01484   }
01485 
01486   /* spit out the texture coordinates */
01487   if (mode & GLM_TEXTURE) {
01488     fprintf(file, "\n");
01489     fprintf(file, "# %d texcoords\n", model->texcoords);
01490     for (i = 1; i <= model->numtexcoords; i++) {
01491       fprintf(file, "vt %f %f\n", 
01492               model->texcoords[2 * i + 0],
01493               model->texcoords[2 * i + 1]);
01494     }
01495   }
01496 
01497   fprintf(file, "\n");
01498   fprintf(file, "# %d groups\n", model->numgroups);
01499   fprintf(file, "# %d faces (triangles)\n", model->numtriangles);
01500   fprintf(file, "\n");
01501 
01502   group = model->groups;
01503   while(group) {
01504     fprintf(file, "g %s\n", group->name);
01505     if (mode & GLM_MATERIAL)
01506       fprintf(file, "usemtl %s\n", model->materials[group->material].name);
01507     for (i = 0; i < group->numtriangles; i++) {
01508       if (mode & GLM_SMOOTH && mode & GLM_TEXTURE) {
01509         fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
01510                 T(group->triangles[i]).vindices[0], 
01511                 T(group->triangles[i]).nindices[0], 
01512                 T(group->triangles[i]).tindices[0],
01513                 T(group->triangles[i]).vindices[1],
01514                 T(group->triangles[i]).nindices[1],
01515                 T(group->triangles[i]).tindices[1],
01516                 T(group->triangles[i]).vindices[2],
01517                 T(group->triangles[i]).nindices[2],
01518                 T(group->triangles[i]).tindices[2]);
01519       } else if (mode & GLM_FLAT && mode & GLM_TEXTURE) {
01520         fprintf(file, "f %d/%d %d/%d %d/%d\n",
01521                 T(group->triangles[i]).vindices[0],
01522                 T(group->triangles[i]).findex,
01523                 T(group->triangles[i]).vindices[1],
01524                 T(group->triangles[i]).findex,
01525                 T(group->triangles[i]).vindices[2],
01526                 T(group->triangles[i]).findex);
01527       } else if (mode & GLM_TEXTURE) {
01528         fprintf(file, "f %d/%d %d/%d %d/%d\n",
01529                 T(group->triangles[i]).vindices[0],
01530                 T(group->triangles[i]).tindices[0],
01531                 T(group->triangles[i]).vindices[1],
01532                 T(group->triangles[i]).tindices[1],
01533                 T(group->triangles[i]).vindices[2],
01534                 T(group->triangles[i]).tindices[2]);
01535       } else if (mode & GLM_SMOOTH) {
01536         fprintf(file, "f %d//%d %d//%d %d//%d\n",
01537                 T(group->triangles[i]).vindices[0],
01538                 T(group->triangles[i]).nindices[0],
01539                 T(group->triangles[i]).vindices[1],
01540                 T(group->triangles[i]).nindices[1],
01541                 T(group->triangles[i]).vindices[2], 
01542                 T(group->triangles[i]).nindices[2]);
01543       } else if (mode & GLM_FLAT) {
01544         fprintf(file, "f %d//%d %d//%d %d//%d\n",
01545                 T(group->triangles[i]).vindices[0], 
01546                 T(group->triangles[i]).findex,
01547                 T(group->triangles[i]).vindices[1],
01548                 T(group->triangles[i]).findex,
01549                 T(group->triangles[i]).vindices[2],
01550                 T(group->triangles[i]).findex);
01551       } else {
01552         fprintf(file, "f %d %d %d\n",
01553                 T(group->triangles[i]).vindices[0],
01554                 T(group->triangles[i]).vindices[1],
01555                 T(group->triangles[i]).vindices[2]);
01556       }
01557     }
01558     fprintf(file, "\n");
01559     group = group->next;
01560   }
01561 
01562   fclose(file);
01563 }
01564 
01565 /* glmDraw: Renders the model to the current OpenGL context using the
01566  * mode specified.
01567  *
01568  * model    - initialized GLMmodel structure
01569  * mode     - a bitwise OR of values describing what is to be rendered.
01570  *            GLM_NONE     -  render with only vertices
01571  *            GLM_FLAT     -  render with facet normals
01572  *            GLM_SMOOTH   -  render with vertex normals
01573  *            GLM_TEXTURE  -  render with texture coords
01574  *            GLM_COLOR    -  render with colors (color material)
01575  *            GLM_MATERIAL -  render with materials
01576  *            GLM_COLOR and GLM_MATERIAL should not both be specified.  
01577  *            GLM_FLAT and GLM_SMOOTH should not both be specified.  
01578  */
01579 GLvoid
01580 glmDraw(GLMmodel* model, GLuint mode)
01581 {
01582   static GLuint i;
01583   static GLMgroup* group;
01584   static GLMtriangle* triangle;
01585   static GLMmaterial* material;
01586 
01587   assert(model);
01588   assert(model->vertices);
01589 
01590   /* do a bit of warning */
01591   if (mode & GLM_FLAT && !model->facetnorms) {
01592     printf("glmDraw() warning: flat render mode requested "
01593            "with no facet normals defined.\n");
01594     mode &= ~GLM_FLAT;
01595   }
01596   if (mode & GLM_SMOOTH && !model->normals) {
01597     printf("glmDraw() warning: smooth render mode requested "
01598            "with no normals defined.\n");
01599     mode &= ~GLM_SMOOTH;
01600   }
01601   if (mode & GLM_TEXTURE && !model->texcoords) {
01602     printf("glmDraw() warning: texture render mode requested "
01603            "with no texture coordinates defined.\n");
01604     mode &= ~GLM_TEXTURE;
01605   }
01606   if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
01607     printf("glmDraw() warning: flat render mode requested "
01608            "and smooth render mode requested (using smooth).\n");
01609     mode &= ~GLM_FLAT;
01610   }
01611   if (mode & GLM_COLOR && !model->materials) {
01612     printf("glmDraw() warning: color render mode requested "
01613            "with no materials defined.\n");
01614     mode &= ~GLM_COLOR;
01615   }
01616   if (mode & GLM_MATERIAL && !model->materials) {
01617     printf("glmDraw() warning: material render mode requested "
01618            "with no materials defined.\n");
01619     mode &= ~GLM_MATERIAL;
01620   }
01621   if (mode & GLM_COLOR && mode & GLM_MATERIAL) {
01622     printf("glmDraw() warning: color and material render mode requested "
01623            "using only material mode.\n");
01624     mode &= ~GLM_COLOR;
01625   }
01626   if (mode & GLM_COLOR)
01627     glEnable(GL_COLOR_MATERIAL);
01628   else if (mode & GLM_MATERIAL)
01629     glDisable(GL_COLOR_MATERIAL);
01630 
01631   /* perhaps this loop should be unrolled into material, color, flat,
01632      smooth, etc. loops?  since most cpu's have good branch prediction
01633      schemes (and these branches will always go one way), probably
01634      wouldn't gain too much?  */
01635 
01636   group = model->groups;
01637   while (group) {
01638     if (mode & GLM_MATERIAL) {
01639       material = &model->materials[group->material];
01640       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material->ambient);
01641       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material->diffuse);
01642       glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material->specular);
01643       glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material->shininess);
01644     }
01645 
01646     if (mode & GLM_COLOR) {
01647       glColor3fv(material->diffuse);
01648     }
01649 
01650     glBegin(GL_TRIANGLES);
01651     for (i = 0; i < group->numtriangles; i++) {
01652       triangle = &T(group->triangles[i]);
01653 
01654       if (mode & GLM_FLAT)
01655         glNormal3fv(&model->facetnorms[3 * triangle->findex]);
01656       
01657       if (mode & GLM_SMOOTH)
01658         glNormal3fv(&model->normals[3 * triangle->nindices[0]]);
01659       if (mode & GLM_TEXTURE)
01660         glTexCoord2fv(&model->texcoords[2 * triangle->tindices[0]]);
01661       glVertex3fv(&model->vertices[3 * triangle->vindices[0]]);
01662       
01663       if (mode & GLM_SMOOTH)
01664         glNormal3fv(&model->normals[3 * triangle->nindices[1]]);
01665       if (mode & GLM_TEXTURE)
01666         glTexCoord2fv(&model->texcoords[2 * triangle->tindices[1]]);
01667       glVertex3fv(&model->vertices[3 * triangle->vindices[1]]);
01668       
01669       if (mode & GLM_SMOOTH)
01670         glNormal3fv(&model->normals[3 * triangle->nindices[2]]);
01671       if (mode & GLM_TEXTURE)
01672         glTexCoord2fv(&model->texcoords[2 * triangle->tindices[2]]);
01673       glVertex3fv(&model->vertices[3 * triangle->vindices[2]]);
01674       
01675     }
01676     glEnd();
01677 
01678     group = group->next;
01679   }
01680 }
01681 
01682 /* glmList: Generates and returns a display list for the model using
01683  * the mode specified.
01684  *
01685  * model    - initialized GLMmodel structure
01686  * mode     - a bitwise OR of values describing what is to be rendered.
01687  *            GLM_NONE     -  render with only vertices
01688  *            GLM_FLAT     -  render with facet normals
01689  *            GLM_SMOOTH   -  render with vertex normals
01690  *            GLM_TEXTURE  -  render with texture coords
01691  *            GLM_COLOR    -  render with colors (color material)
01692  *            GLM_MATERIAL -  render with materials
01693  *            GLM_COLOR and GLM_MATERIAL should not both be specified.  
01694  * GLM_FLAT and GLM_SMOOTH should not both be specified.  */
01695 GLuint
01696 glmList(GLMmodel* model, GLuint mode)
01697 {
01698   GLuint list;
01699 
01700   list = glGenLists(1);
01701   glNewList(list, GL_COMPILE);
01702   glmDraw(model, mode);
01703   glEndList();
01704 
01705   return list;
01706 }
01707 
01708 /* glmWeld: eliminate (weld) vectors that are within an epsilon of
01709  * each other.
01710  *
01711  * model      - initialized GLMmodel structure
01712  * epsilon    - maximum difference between vertices
01713  *              ( 0.00001 is a good start for a unitized model)
01714  *
01715  */
01716 GLvoid
01717 glmWeld(GLMmodel* model, GLfloat epsilon)
01718 {
01719   GLfloat* vectors;
01720   GLfloat* copies;
01721   GLuint   numvectors;
01722   GLuint   i;
01723 
01724   /* vertices */
01725   numvectors = model->numvertices;
01726   vectors    = model->vertices;
01727   copies = glmWeldVectors(vectors, &numvectors, epsilon);
01728 
01729 #if 0
01730   printf("glmWeld(): %d redundant vertices.\n", 
01731          model->numvertices - numvectors - 1);
01732 #endif
01733 
01734   for (i = 0; i < model->numtriangles; i++) {
01735     T(i).vindices[0] = (GLuint)vectors[3 * T(i).vindices[0] + 0];
01736     T(i).vindices[1] = (GLuint)vectors[3 * T(i).vindices[1] + 0];
01737     T(i).vindices[2] = (GLuint)vectors[3 * T(i).vindices[2] + 0];
01738   }
01739 
01740   /* free space for old vertices */
01741   free(vectors);
01742 
01743   /* allocate space for the new vertices */
01744   model->numvertices = numvectors;
01745   model->vertices = (GLfloat*)malloc(sizeof(GLfloat) * 
01746                                      3 * (model->numvertices + 1));
01747 
01748   /* copy the optimized vertices into the actual vertex list */
01749   for (i = 1; i <= model->numvertices; i++) {
01750     model->vertices[3 * i + 0] = copies[3 * i + 0];
01751     model->vertices[3 * i + 1] = copies[3 * i + 1];
01752     model->vertices[3 * i + 2] = copies[3 * i + 2];
01753   }
01754 
01755   free(copies);
01756 }
01757 
01758 
01759 #if 0
01760   /* normals */
01761   if (model->numnormals) {
01762   numvectors = model->numnormals;
01763   vectors    = model->normals;
01764   copies = glmOptimizeVectors(vectors, &numvectors);
01765 
01766   printf("glmOptimize(): %d redundant normals.\n", 
01767          model->numnormals - numvectors);
01768 
01769   for (i = 0; i < model->numtriangles; i++) {
01770     T(i).nindices[0] = (GLuint)vectors[3 * T(i).nindices[0] + 0];
01771     T(i).nindices[1] = (GLuint)vectors[3 * T(i).nindices[1] + 0];
01772     T(i).nindices[2] = (GLuint)vectors[3 * T(i).nindices[2] + 0];
01773   }
01774 
01775   /* free space for old normals */
01776   free(vectors);
01777 
01778   /* allocate space for the new normals */
01779   model->numnormals = numvectors;
01780   model->normals = (GLfloat*)malloc(sizeof(GLfloat) * 
01781                                     3 * (model->numnormals + 1));
01782 
01783   /* copy the optimized vertices into the actual vertex list */
01784   for (i = 1; i <= model->numnormals; i++) {
01785     model->normals[3 * i + 0] = copies[3 * i + 0];
01786     model->normals[3 * i + 1] = copies[3 * i + 1];
01787     model->normals[3 * i + 2] = copies[3 * i + 2];
01788   }
01789 
01790   free(copies);
01791   }
01792 
01793   /* texcoords */
01794   if (model->numtexcoords) {
01795   numvectors = model->numtexcoords;
01796   vectors    = model->texcoords;
01797   copies = glmOptimizeVectors(vectors, &numvectors);
01798 
01799   printf("glmOptimize(): %d redundant texcoords.\n", 
01800          model->numtexcoords - numvectors);
01801 
01802   for (i = 0; i < model->numtriangles; i++) {
01803     for (j = 0; j < 3; j++) {
01804       T(i).tindices[j] = (GLuint)vectors[3 * T(i).tindices[j] + 0];
01805     }
01806   }
01807 
01808   /* free space for old texcoords */
01809   free(vectors);
01810 
01811   /* allocate space for the new texcoords */
01812   model->numtexcoords = numvectors;
01813   model->texcoords = (GLfloat*)malloc(sizeof(GLfloat) * 
01814                                       2 * (model->numtexcoords + 1));
01815 
01816   /* copy the optimized vertices into the actual vertex list */
01817   for (i = 1; i <= model->numtexcoords; i++) {
01818     model->texcoords[2 * i + 0] = copies[2 * i + 0];
01819     model->texcoords[2 * i + 1] = copies[2 * i + 1];
01820   }
01821 
01822   free(copies);
01823   }
01824 #endif
01825 
01826 #if 0
01827   /* look for unused vertices */
01828   /* look for unused normals */
01829   /* look for unused texcoords */
01830   for (i = 1; i <= model->numvertices; i++) {
01831     for (j = 0; j < model->numtriangles; i++) {
01832       if (T(j).vindices[0] == i || 
01833           T(j).vindices[1] == i || 
01834           T(j).vindices[1] == i)
01835         break;
01836     }
01837   }
01838 #endif

Generated on Wed Jun 28 12:24:30 2006 for esg by  doxygen 1.4.6