source: projectionDesigner/trunk/projdesignerplugins/defaultplugin/glm.c @ 210

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