source: GTP/trunk/Lib/Geom/shared/GTGeometry/src/libs/vmi/src/glm.c @ 983

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