2 * GLM library. Wavefront .obj file format reader/writer/manipulator.
4 * Written by Nate Robins, 1997.
6 * www: http://www.pobox.com/~ndr
19 typedef unsigned char boolean
;
24 /* Some <math.h> files do not define M_PI... */
26 #define M_PI 3.14159265358979323846
30 #define T(x) model->triangles[(x)]
34 enum { X
, Y
, Z
, W
}; /* elements of a vertex */
39 /* _GLMnode: general purpose node
41 typedef struct _GLMnode
{
44 struct _GLMnode
* next
;
47 /* strdup is actually not a standard ANSI C or POSIX routine
48 so implement a private one. OpenVMS does not have a strdup; Linux's
49 standard libc doesn't declare strdup by default (unless BSD or SVID
50 interfaces are requested). */
52 stralloc(const char *string
)
56 copy
= malloc(strlen(string
) + 1);
63 /* private functions */
65 /* _glmMax: returns the maximum of two floats */
67 _glmMax(float a
, float b
)
74 /* _glmAbs: returns the absolute value of a float */
83 /* _glmDot: compute the dot product of two vectors
85 * u - array of 3 floats (float u[3])
86 * v - array of 3 floats (float v[3])
89 _glmDot(float* u
, float* v
)
94 /* compute the dot product */
95 return u
[X
] * v
[X
] + u
[Y
] * v
[Y
] + u
[Z
] * v
[Z
];
98 /* _glmCross: compute the cross product of two vectors
100 * u - array of 3 floats (float u[3])
101 * v - array of 3 floats (float v[3])
102 * n - array of 3 floats (float n[3]) to return the cross product in
105 _glmCross(float* u
, float* v
, float* n
)
111 /* compute the cross product (u x v for right-handed [ccw]) */
112 n
[X
] = u
[Y
] * v
[Z
] - u
[Z
] * v
[Y
];
113 n
[Y
] = u
[Z
] * v
[X
] - u
[X
] * v
[Z
];
114 n
[Z
] = u
[X
] * v
[Y
] - u
[Y
] * v
[X
];
117 /* _glmNormalize: normalize a vector
119 * n - array of 3 floats (float n[3]) to be normalized
122 _glmNormalize(float* n
)
129 l
= (float)sqrt(n
[X
] * n
[X
] + n
[Y
] * n
[Y
] + n
[Z
] * n
[Z
]);
135 /* _glmEqual: compares two vectors and returns TRUE if they are
136 * equal (within a certain threshold) or FALSE if not. An epsilon
137 * that works fairly well is 0.000001.
139 * u - array of 3 floats (float u[3])
140 * v - array of 3 floats (float v[3])
143 _glmEqual(float* u
, float* v
, float epsilon
)
145 if (_glmAbs(u
[0] - v
[0]) < epsilon
&&
146 _glmAbs(u
[1] - v
[1]) < epsilon
&&
147 _glmAbs(u
[2] - v
[2]) < epsilon
)
154 /* _glmWeldVectors: eliminate (weld) vectors that are within an
155 * epsilon of each other.
157 * vectors - array of float[3]'s to be welded
158 * numvectors - number of float[3]'s in vectors
159 * epsilon - maximum difference between vectors
163 _glmWeldVectors(float* vectors
, uint
* numvectors
, float epsilon
)
169 copies
= (float*)malloc(sizeof(float) * 3 * (*numvectors
+ 1));
170 memcpy(copies
, vectors
, (sizeof(float) * 3 * (*numvectors
+ 1)));
173 for (i
= 1; i
<= *numvectors
; i
++) {
174 for (j
= 1; j
<= copied
; j
++) {
175 if (_glmEqual(&vectors
[3 * i
], &copies
[3 * j
], epsilon
)) {
180 /* must not be any duplicates -- add to the copies array */
181 copies
[3 * copied
+ 0] = vectors
[3 * i
+ 0];
182 copies
[3 * copied
+ 1] = vectors
[3 * i
+ 1];
183 copies
[3 * copied
+ 2] = vectors
[3 * i
+ 2];
184 j
= copied
; /* pass this along for below */
188 /* set the first component of this vector to point at the correct
189 index into the new copies array */
190 vectors
[3 * i
+ 0] = (float)j
;
193 *numvectors
= copied
-1;
197 /* _glmFindGroup: Find a group in the model
200 _glmFindGroup(GLMmodel
* model
, char* name
)
206 group
= model
->groups
;
208 if (!strcmp(name
, group
->name
))
216 /* _glmAddGroup: Add a group to the model
219 _glmAddGroup(GLMmodel
* model
, char* name
)
223 group
= _glmFindGroup(model
, name
);
225 group
= (GLMgroup
*)malloc(sizeof(GLMgroup
));
226 group
->name
= stralloc(name
);
228 group
->numtriangles
= 0;
229 group
->triangles
= NULL
;
230 group
->next
= model
->groups
;
231 model
->groups
= group
;
238 /* _glmFindGroup: Find a material in the model
241 _glmFindMaterial(GLMmodel
* model
, char* name
)
245 for (i
= 0; i
< model
->nummaterials
; i
++) {
246 if (!strcmp(model
->materials
[i
].name
, name
))
250 /* didn't find the name, so set it as the default material */
251 printf("_glmFindMaterial(): can't find material \"%s\".\n", name
);
259 /* _glmDirName: return the directory given a path
261 * path - filesystem path
263 * The return value should be free'd.
266 _glmDirName(char* path
)
271 dir
= stralloc(path
);
273 s
= strrchr(dir
, '/');
283 /* _glmReadMTL: read a wavefront material library file
285 * model - properly initialized GLMmodel structure
286 * name - name of the material library
289 _glmReadMTL(GLMmodel
* model
, char* name
)
294 char buf
[128], buf2
[128];
295 uint nummaterials
, i
;
298 dir
= _glmDirName(model
->pathname
);
299 filename
= (char*)malloc(sizeof(char) * (strlen(dir
) + strlen(name
) + 1));
300 strcpy(filename
, dir
);
301 strcat(filename
, name
);
305 file
= fopen(filename
, "r");
307 fprintf(stderr
, "_glmReadMTL() failed: can't open material file \"%s\".\n",
313 /* count the number of materials in the file */
315 while(fscanf(file
, "%s", buf
) != EOF
) {
317 case '#': /* comment */
318 /* eat up rest of line */
319 fgets(buf
, sizeof(buf
), file
);
321 case 'n': /* newmtl */
322 fgets(buf
, sizeof(buf
), file
);
324 sscanf(buf
, "%s %s", buf
, buf
);
327 /* eat up rest of line */
328 fgets(buf
, sizeof(buf
), file
);
335 /* allocate memory for the materials */
336 model
->materials
= (GLMmaterial
*)calloc(nummaterials
, sizeof(GLMmaterial
));
337 model
->nummaterials
= nummaterials
;
339 /* set the default material */
340 for (i
= 0; i
< nummaterials
; i
++) {
341 model
->materials
[i
].name
= NULL
;
342 model
->materials
[i
].shininess
= 0;
343 model
->materials
[i
].diffuse
[0] = 0.8;
344 model
->materials
[i
].diffuse
[1] = 0.8;
345 model
->materials
[i
].diffuse
[2] = 0.8;
346 model
->materials
[i
].diffuse
[3] = 1.0;
347 model
->materials
[i
].ambient
[0] = 0.2;
348 model
->materials
[i
].ambient
[1] = 0.2;
349 model
->materials
[i
].ambient
[2] = 0.2;
350 model
->materials
[i
].ambient
[3] = 0.0;
351 model
->materials
[i
].specular
[0] = 0.0;
352 model
->materials
[i
].specular
[1] = 0.0;
353 model
->materials
[i
].specular
[2] = 0.0;
354 model
->materials
[i
].specular
[3] = 0.0;
356 model
->materials
[0].name
= stralloc("default");
358 /* now, read in the data */
361 mat
= &model
->materials
[nummaterials
];
363 while(fscanf(file
, "%s", buf
) != EOF
) {
365 case '#': /* comment */
366 /* eat up rest of line */
367 fgets(buf
, sizeof(buf
), file
);
369 case 'n': /* newmtl */
370 fgets(buf
, sizeof(buf
), file
);
371 sscanf(buf
, "%s %s", buf
, buf
);
373 model
->materials
[nummaterials
].name
= stralloc(buf
);
376 fscanf(file
, "%f", &model
->materials
[nummaterials
].shininess
);
377 /* wavefront shininess is from [0, 1000], so scale for OpenGL */
378 model
->materials
[nummaterials
].shininess
/= 1000.0;
379 model
->materials
[nummaterials
].shininess
*= 128.0;
380 mat
= &model
->materials
[nummaterials
];
385 fscanf(file
, "%f %f %f",
386 &model
->materials
[nummaterials
].diffuse
[0],
387 &model
->materials
[nummaterials
].diffuse
[1],
388 &model
->materials
[nummaterials
].diffuse
[2]);
391 fscanf(file
, "%f %f %f",
392 &model
->materials
[nummaterials
].specular
[0],
393 &model
->materials
[nummaterials
].specular
[1],
394 &model
->materials
[nummaterials
].specular
[2]);
397 fscanf(file
, "%f %f %f",
398 &model
->materials
[nummaterials
].ambient
[0],
399 &model
->materials
[nummaterials
].ambient
[1],
400 &model
->materials
[nummaterials
].ambient
[2]);
403 /* eat up rest of line */
404 fgets(buf
, sizeof(buf
), file
);
408 case 'd': /* alpha? */
410 &model
->materials
[nummaterials
].diffuse
[3]);
412 case 'm': /* texture map */
413 fscanf(file
, "%s", buf2
);
414 /*printf("map %s\n", buf2);*/
415 mat
->map_kd
= strdup(buf2
);
419 /* eat up rest of line */
420 fgets(buf
, sizeof(buf
), file
);
428 /* _glmWriteMTL: write a wavefront material library file
430 * model - properly initialized GLMmodel structure
431 * modelpath - pathname of the model being written
432 * mtllibname - name of the material library to be written
435 _glmWriteMTL(GLMmodel
* model
, char* modelpath
, char* mtllibname
)
440 GLMmaterial
* material
;
443 dir
= _glmDirName(modelpath
);
444 filename
= (char*)malloc(sizeof(char) * (strlen(dir
) + strlen(mtllibname
)));
445 strcpy(filename
, dir
);
446 strcat(filename
, mtllibname
);
450 file
= fopen(filename
, "w");
452 fprintf(stderr
, "_glmWriteMTL() failed: can't open file \"%s\".\n",
458 /* spit out a header */
459 fprintf(file
, "# \n");
460 fprintf(file
, "# Wavefront MTL generated by GLM library\n");
461 fprintf(file
, "# \n");
462 fprintf(file
, "# GLM library copyright (C) 1997 by Nate Robins\n");
463 fprintf(file
, "# email: ndr@pobox.com\n");
464 fprintf(file
, "# www: http://www.pobox.com/~ndr\n");
465 fprintf(file
, "# \n\n");
467 for (i
= 0; i
< model
->nummaterials
; i
++) {
468 material
= &model
->materials
[i
];
469 fprintf(file
, "newmtl %s\n", material
->name
);
470 fprintf(file
, "Ka %f %f %f\n",
471 material
->ambient
[0], material
->ambient
[1], material
->ambient
[2]);
472 fprintf(file
, "Kd %f %f %f\n",
473 material
->diffuse
[0], material
->diffuse
[1], material
->diffuse
[2]);
474 fprintf(file
, "Ks %f %f %f\n",
475 material
->specular
[0],material
->specular
[1],material
->specular
[2]);
476 fprintf(file
, "Ns %f\n", material
->shininess
);
483 /* _glmFirstPass: first pass at a Wavefront OBJ file that gets all the
484 * statistics of the model (such as #vertices, #normals, etc)
486 * model - properly initialized GLMmodel structure
487 * file - (fopen'd) file descriptor
490 _glmFirstPass(GLMmodel
* model
, FILE* file
)
492 uint numvertices
; /* number of vertices in model */
493 uint numnormals
; /* number of normals in model */
494 uint numtexcoords
; /* number of texcoords in model */
495 uint numtriangles
; /* number of triangles in model */
496 GLMgroup
* group
; /* current group */
500 /* make a default group */
501 group
= _glmAddGroup(model
, "default");
503 numvertices
= numnormals
= numtexcoords
= numtriangles
= 0;
504 while(fscanf(file
, "%s", buf
) != EOF
) {
506 case '#': /* comment */
507 /* eat up rest of line */
508 fgets(buf
, sizeof(buf
), file
);
510 case 'v': /* v, vn, vt */
512 case '\0': /* vertex */
513 /* eat up rest of line */
514 fgets(buf
, sizeof(buf
), file
);
517 case 'n': /* normal */
518 /* eat up rest of line */
519 fgets(buf
, sizeof(buf
), file
);
522 case 't': /* texcoord */
523 /* eat up rest of line */
524 fgets(buf
, sizeof(buf
), file
);
528 printf("_glmFirstPass(): Unknown token \"%s\".\n", buf
);
534 fgets(buf
, sizeof(buf
), file
);
535 sscanf(buf
, "%s %s", buf
, buf
);
536 model
->mtllibname
= stralloc(buf
);
537 _glmReadMTL(model
, buf
);
540 /* eat up rest of line */
541 fgets(buf
, sizeof(buf
), file
);
543 case 'g': /* group */
544 /* eat up rest of line */
545 fgets(buf
, sizeof(buf
), file
);
546 sscanf(buf
, "%s", buf
);
547 group
= _glmAddGroup(model
, buf
);
551 fscanf(file
, "%s", buf
);
552 /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
553 if (strstr(buf
, "//")) {
555 sscanf(buf
, "%u//%u", &v
, &n
);
556 fscanf(file
, "%u//%u", &v
, &n
);
557 fscanf(file
, "%u//%u", &v
, &n
);
559 group
->numtriangles
++;
560 while(fscanf(file
, "%u//%u", &v
, &n
) > 0) {
562 group
->numtriangles
++;
564 } else if (sscanf(buf
, "%u/%u/%u", &v
, &t
, &n
) == 3) {
566 fscanf(file
, "%u/%u/%u", &v
, &t
, &n
);
567 fscanf(file
, "%u/%u/%u", &v
, &t
, &n
);
569 group
->numtriangles
++;
570 while(fscanf(file
, "%u/%u/%u", &v
, &t
, &n
) > 0) {
572 group
->numtriangles
++;
574 } else if (sscanf(buf
, "%u/%u", &v
, &t
) == 2) {
576 fscanf(file
, "%u/%u", &v
, &t
);
577 fscanf(file
, "%u/%u", &v
, &t
);
579 group
->numtriangles
++;
580 while(fscanf(file
, "%u/%u", &v
, &t
) > 0) {
582 group
->numtriangles
++;
586 fscanf(file
, "%u", &v
);
587 fscanf(file
, "%u", &v
);
589 group
->numtriangles
++;
590 while(fscanf(file
, "%u", &v
) > 0) {
592 group
->numtriangles
++;
598 /* eat up rest of line */
599 fgets(buf
, sizeof(buf
), file
);
605 /* announce the model statistics */
606 printf(" Vertices: %d\n", numvertices
);
607 printf(" Normals: %d\n", numnormals
);
608 printf(" Texcoords: %d\n", numtexcoords
);
609 printf(" Triangles: %d\n", numtriangles
);
610 printf(" Groups: %d\n", model
->numgroups
);
613 /* set the stats in the model structure */
614 model
->numvertices
= numvertices
;
615 model
->numnormals
= numnormals
;
616 model
->numtexcoords
= numtexcoords
;
617 model
->numtriangles
= numtriangles
;
619 /* allocate memory for the triangles in each group */
620 group
= model
->groups
;
622 group
->triangles
= (uint
*)malloc(sizeof(uint
) * group
->numtriangles
);
623 group
->numtriangles
= 0;
628 /* _glmSecondPass: second pass at a Wavefront OBJ file that gets all
631 * model - properly initialized GLMmodel structure
632 * file - (fopen'd) file descriptor
635 _glmSecondPass(GLMmodel
* model
, FILE* file
)
637 uint numvertices
; /* number of vertices in model */
638 uint numnormals
; /* number of normals in model */
639 uint numtexcoords
; /* number of texcoords in model */
640 uint numtriangles
; /* number of triangles in model */
641 float* vertices
; /* array of vertices */
642 float* normals
; /* array of normals */
643 float* texcoords
; /* array of texture coordinates */
644 GLMgroup
* group
; /* current group pointer */
645 uint material
; /* current material */
649 /* set the pointer shortcuts */
650 vertices
= model
->vertices
;
651 normals
= model
->normals
;
652 texcoords
= model
->texcoords
;
653 group
= model
->groups
;
655 /* on the second pass through the file, read all the data into the
657 numvertices
= numnormals
= numtexcoords
= 1;
660 while(fscanf(file
, "%s", buf
) != EOF
) {
662 case '#': /* comment */
663 /* eat up rest of line */
664 fgets(buf
, sizeof(buf
), file
);
666 case 'v': /* v, vn, vt */
668 case '\0': /* vertex */
669 fscanf(file
, "%f %f %f",
670 &vertices
[3 * numvertices
+ X
],
671 &vertices
[3 * numvertices
+ Y
],
672 &vertices
[3 * numvertices
+ Z
]);
675 case 'n': /* normal */
676 fscanf(file
, "%f %f %f",
677 &normals
[3 * numnormals
+ X
],
678 &normals
[3 * numnormals
+ Y
],
679 &normals
[3 * numnormals
+ Z
]);
682 case 't': /* texcoord */
683 fscanf(file
, "%f %f",
684 &texcoords
[2 * numtexcoords
+ X
],
685 &texcoords
[2 * numtexcoords
+ Y
]);
691 fgets(buf
, sizeof(buf
), file
);
692 sscanf(buf
, "%s %s", buf
, buf
);
693 material
= _glmFindMaterial(model
, buf
);
694 if (!group
->material
)
695 group
->material
= material
;
696 /*printf("material %s = %u\n", buf, material);*/
698 case 'g': /* group */
699 /* eat up rest of line */
700 fgets(buf
, sizeof(buf
), file
);
701 sscanf(buf
, "%s", buf
);
702 group
= _glmFindGroup(model
, buf
);
703 group
->material
= material
;
704 /*printf("GROUP %s material %u\n", buf, material);*/
708 fscanf(file
, "%s", buf
);
709 /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
710 if (strstr(buf
, "//")) {
712 sscanf(buf
, "%u//%u", &v
, &n
);
713 T(numtriangles
).vindices
[0] = v
;
714 T(numtriangles
).nindices
[0] = n
;
715 fscanf(file
, "%u//%u", &v
, &n
);
716 T(numtriangles
).vindices
[1] = v
;
717 T(numtriangles
).nindices
[1] = n
;
718 fscanf(file
, "%u//%u", &v
, &n
);
719 T(numtriangles
).vindices
[2] = v
;
720 T(numtriangles
).nindices
[2] = n
;
721 group
->triangles
[group
->numtriangles
++] = numtriangles
;
723 while(fscanf(file
, "%u//%u", &v
, &n
) > 0) {
724 T(numtriangles
).vindices
[0] = T(numtriangles
-1).vindices
[0];
725 T(numtriangles
).nindices
[0] = T(numtriangles
-1).nindices
[0];
726 T(numtriangles
).vindices
[1] = T(numtriangles
-1).vindices
[2];
727 T(numtriangles
).nindices
[1] = T(numtriangles
-1).nindices
[2];
728 T(numtriangles
).vindices
[2] = v
;
729 T(numtriangles
).nindices
[2] = n
;
730 group
->triangles
[group
->numtriangles
++] = numtriangles
;
733 } else if (sscanf(buf
, "%u/%u/%u", &v
, &t
, &n
) == 3) {
735 T(numtriangles
).vindices
[0] = v
;
736 T(numtriangles
).tindices
[0] = t
;
737 T(numtriangles
).nindices
[0] = n
;
738 fscanf(file
, "%u/%u/%u", &v
, &t
, &n
);
739 T(numtriangles
).vindices
[1] = v
;
740 T(numtriangles
).tindices
[1] = t
;
741 T(numtriangles
).nindices
[1] = n
;
742 fscanf(file
, "%u/%u/%u", &v
, &t
, &n
);
743 T(numtriangles
).vindices
[2] = v
;
744 T(numtriangles
).tindices
[2] = t
;
745 T(numtriangles
).nindices
[2] = n
;
746 group
->triangles
[group
->numtriangles
++] = numtriangles
;
748 while(fscanf(file
, "%u/%u/%u", &v
, &t
, &n
) > 0) {
749 T(numtriangles
).vindices
[0] = T(numtriangles
-1).vindices
[0];
750 T(numtriangles
).tindices
[0] = T(numtriangles
-1).tindices
[0];
751 T(numtriangles
).nindices
[0] = T(numtriangles
-1).nindices
[0];
752 T(numtriangles
).vindices
[1] = T(numtriangles
-1).vindices
[2];
753 T(numtriangles
).tindices
[1] = T(numtriangles
-1).tindices
[2];
754 T(numtriangles
).nindices
[1] = T(numtriangles
-1).nindices
[2];
755 T(numtriangles
).vindices
[2] = v
;
756 T(numtriangles
).tindices
[2] = t
;
757 T(numtriangles
).nindices
[2] = n
;
758 group
->triangles
[group
->numtriangles
++] = numtriangles
;
761 } else if (sscanf(buf
, "%u/%u", &v
, &t
) == 2) {
763 T(numtriangles
).vindices
[0] = v
;
764 T(numtriangles
).tindices
[0] = t
;
765 fscanf(file
, "%u/%u", &v
, &t
);
766 T(numtriangles
).vindices
[1] = v
;
767 T(numtriangles
).tindices
[1] = t
;
768 fscanf(file
, "%u/%u", &v
, &t
);
769 T(numtriangles
).vindices
[2] = v
;
770 T(numtriangles
).tindices
[2] = t
;
771 group
->triangles
[group
->numtriangles
++] = numtriangles
;
773 while(fscanf(file
, "%u/%u", &v
, &t
) > 0) {
774 T(numtriangles
).vindices
[0] = T(numtriangles
-1).vindices
[0];
775 T(numtriangles
).tindices
[0] = T(numtriangles
-1).tindices
[0];
776 T(numtriangles
).vindices
[1] = T(numtriangles
-1).vindices
[2];
777 T(numtriangles
).tindices
[1] = T(numtriangles
-1).tindices
[2];
778 T(numtriangles
).vindices
[2] = v
;
779 T(numtriangles
).tindices
[2] = t
;
780 group
->triangles
[group
->numtriangles
++] = numtriangles
;
785 sscanf(buf
, "%u", &v
);
786 T(numtriangles
).vindices
[0] = v
;
787 fscanf(file
, "%u", &v
);
788 T(numtriangles
).vindices
[1] = v
;
789 fscanf(file
, "%u", &v
);
790 T(numtriangles
).vindices
[2] = v
;
791 group
->triangles
[group
->numtriangles
++] = numtriangles
;
793 while(fscanf(file
, "%u", &v
) > 0) {
794 T(numtriangles
).vindices
[0] = T(numtriangles
-1).vindices
[0];
795 T(numtriangles
).vindices
[1] = T(numtriangles
-1).vindices
[2];
796 T(numtriangles
).vindices
[2] = v
;
797 group
->triangles
[group
->numtriangles
++] = numtriangles
;
804 /* eat up rest of line */
805 fgets(buf
, sizeof(buf
), file
);
811 /* announce the memory requirements */
812 printf(" Memory: %d bytes\n",
813 numvertices
* 3*sizeof(float) +
814 numnormals
* 3*sizeof(float) * (numnormals
? 1 : 0) +
815 numtexcoords
* 3*sizeof(float) * (numtexcoords
? 1 : 0) +
816 numtriangles
* sizeof(GLMtriangle
));
823 /* public functions */
825 /* glmUnitize: "unitize" a model by translating it to the origin and
826 * scaling it to fit in a unit cube around the origin. Returns the
829 * model - properly initialized GLMmodel structure
832 glmUnitize(GLMmodel
* model
)
835 float maxx
, minx
, maxy
, miny
, maxz
, minz
;
836 float cx
, cy
, cz
, w
, h
, d
;
840 assert(model
->vertices
);
842 /* get the max/mins */
843 maxx
= minx
= model
->vertices
[3 + X
];
844 maxy
= miny
= model
->vertices
[3 + Y
];
845 maxz
= minz
= model
->vertices
[3 + Z
];
846 for (i
= 1; i
<= model
->numvertices
; i
++) {
847 if (maxx
< model
->vertices
[3 * i
+ X
])
848 maxx
= model
->vertices
[3 * i
+ X
];
849 if (minx
> model
->vertices
[3 * i
+ X
])
850 minx
= model
->vertices
[3 * i
+ X
];
852 if (maxy
< model
->vertices
[3 * i
+ Y
])
853 maxy
= model
->vertices
[3 * i
+ Y
];
854 if (miny
> model
->vertices
[3 * i
+ Y
])
855 miny
= model
->vertices
[3 * i
+ Y
];
857 if (maxz
< model
->vertices
[3 * i
+ Z
])
858 maxz
= model
->vertices
[3 * i
+ Z
];
859 if (minz
> model
->vertices
[3 * i
+ Z
])
860 minz
= model
->vertices
[3 * i
+ Z
];
863 /* calculate model width, height, and depth */
864 w
= _glmAbs(maxx
) + _glmAbs(minx
);
865 h
= _glmAbs(maxy
) + _glmAbs(miny
);
866 d
= _glmAbs(maxz
) + _glmAbs(minz
);
868 /* calculate center of the model */
869 cx
= (maxx
+ minx
) / 2.0;
870 cy
= (maxy
+ miny
) / 2.0;
871 cz
= (maxz
+ minz
) / 2.0;
873 /* calculate unitizing scale factor */
874 scale
= 2.0 / _glmMax(_glmMax(w
, h
), d
);
876 /* translate around center then scale */
877 for (i
= 1; i
<= model
->numvertices
; i
++) {
878 model
->vertices
[3 * i
+ X
] -= cx
;
879 model
->vertices
[3 * i
+ Y
] -= cy
;
880 model
->vertices
[3 * i
+ Z
] -= cz
;
881 model
->vertices
[3 * i
+ X
] *= scale
;
882 model
->vertices
[3 * i
+ Y
] *= scale
;
883 model
->vertices
[3 * i
+ Z
] *= scale
;
889 /* glmDimensions: Calculates the dimensions (width, height, depth) of
892 * model - initialized GLMmodel structure
893 * dimensions - array of 3 floats (float dimensions[3])
896 glmDimensions(GLMmodel
* model
, float* dimensions
)
899 float maxx
, minx
, maxy
, miny
, maxz
, minz
;
902 assert(model
->vertices
);
905 /* get the max/mins */
906 maxx
= minx
= model
->vertices
[3 + X
];
907 maxy
= miny
= model
->vertices
[3 + Y
];
908 maxz
= minz
= model
->vertices
[3 + Z
];
909 for (i
= 1; i
<= model
->numvertices
; i
++) {
910 if (maxx
< model
->vertices
[3 * i
+ X
])
911 maxx
= model
->vertices
[3 * i
+ X
];
912 if (minx
> model
->vertices
[3 * i
+ X
])
913 minx
= model
->vertices
[3 * i
+ X
];
915 if (maxy
< model
->vertices
[3 * i
+ Y
])
916 maxy
= model
->vertices
[3 * i
+ Y
];
917 if (miny
> model
->vertices
[3 * i
+ Y
])
918 miny
= model
->vertices
[3 * i
+ Y
];
920 if (maxz
< model
->vertices
[3 * i
+ Z
])
921 maxz
= model
->vertices
[3 * i
+ Z
];
922 if (minz
> model
->vertices
[3 * i
+ Z
])
923 minz
= model
->vertices
[3 * i
+ Z
];
926 /* calculate model width, height, and depth */
927 dimensions
[X
] = _glmAbs(maxx
) + _glmAbs(minx
);
928 dimensions
[Y
] = _glmAbs(maxy
) + _glmAbs(miny
);
929 dimensions
[Z
] = _glmAbs(maxz
) + _glmAbs(minz
);
932 /* glmScale: Scales a model by a given amount.
934 * model - properly initialized GLMmodel structure
935 * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
938 glmScale(GLMmodel
* model
, float scale
)
942 for (i
= 1; i
<= model
->numvertices
; i
++) {
943 model
->vertices
[3 * i
+ X
] *= scale
;
944 model
->vertices
[3 * i
+ Y
] *= scale
;
945 model
->vertices
[3 * i
+ Z
] *= scale
;
949 /* glmReverseWinding: Reverse the polygon winding for all polygons in
950 * this model. Default winding is counter-clockwise. Also changes
951 * the direction of the normals.
953 * model - properly initialized GLMmodel structure
956 glmReverseWinding(GLMmodel
* model
)
962 for (i
= 0; i
< model
->numtriangles
; i
++) {
963 swap
= T(i
).vindices
[0];
964 T(i
).vindices
[0] = T(i
).vindices
[2];
965 T(i
).vindices
[2] = swap
;
967 if (model
->numnormals
) {
968 swap
= T(i
).nindices
[0];
969 T(i
).nindices
[0] = T(i
).nindices
[2];
970 T(i
).nindices
[2] = swap
;
973 if (model
->numtexcoords
) {
974 swap
= T(i
).tindices
[0];
975 T(i
).tindices
[0] = T(i
).tindices
[2];
976 T(i
).tindices
[2] = swap
;
980 /* reverse facet normals */
981 for (i
= 1; i
<= model
->numfacetnorms
; i
++) {
982 model
->facetnorms
[3 * i
+ X
] = -model
->facetnorms
[3 * i
+ X
];
983 model
->facetnorms
[3 * i
+ Y
] = -model
->facetnorms
[3 * i
+ Y
];
984 model
->facetnorms
[3 * i
+ Z
] = -model
->facetnorms
[3 * i
+ Z
];
987 /* reverse vertex normals */
988 for (i
= 1; i
<= model
->numnormals
; i
++) {
989 model
->normals
[3 * i
+ X
] = -model
->normals
[3 * i
+ X
];
990 model
->normals
[3 * i
+ Y
] = -model
->normals
[3 * i
+ Y
];
991 model
->normals
[3 * i
+ Z
] = -model
->normals
[3 * i
+ Z
];
995 /* glmFacetNormals: Generates facet normals for a model (by taking the
996 * cross product of the two vectors derived from the sides of each
997 * triangle). Assumes a counter-clockwise winding.
999 * model - initialized GLMmodel structure
1002 glmFacetNormals(GLMmodel
* model
)
1009 assert(model
->vertices
);
1011 /* clobber any old facetnormals */
1012 if (model
->facetnorms
)
1013 free(model
->facetnorms
);
1015 /* allocate memory for the new facet normals */
1016 model
->numfacetnorms
= model
->numtriangles
;
1017 model
->facetnorms
= (float*)malloc(sizeof(float) *
1018 3 * (model
->numfacetnorms
+ 1));
1020 for (i
= 0; i
< model
->numtriangles
; i
++) {
1021 model
->triangles
[i
].findex
= i
+1;
1023 u
[X
] = model
->vertices
[3 * T(i
).vindices
[1] + X
] -
1024 model
->vertices
[3 * T(i
).vindices
[0] + X
];
1025 u
[Y
] = model
->vertices
[3 * T(i
).vindices
[1] + Y
] -
1026 model
->vertices
[3 * T(i
).vindices
[0] + Y
];
1027 u
[Z
] = model
->vertices
[3 * T(i
).vindices
[1] + Z
] -
1028 model
->vertices
[3 * T(i
).vindices
[0] + Z
];
1030 v
[X
] = model
->vertices
[3 * T(i
).vindices
[2] + X
] -
1031 model
->vertices
[3 * T(i
).vindices
[0] + X
];
1032 v
[Y
] = model
->vertices
[3 * T(i
).vindices
[2] + Y
] -
1033 model
->vertices
[3 * T(i
).vindices
[0] + Y
];
1034 v
[Z
] = model
->vertices
[3 * T(i
).vindices
[2] + Z
] -
1035 model
->vertices
[3 * T(i
).vindices
[0] + Z
];
1037 _glmCross(u
, v
, &model
->facetnorms
[3 * (i
+1)]);
1038 _glmNormalize(&model
->facetnorms
[3 * (i
+1)]);
1042 /* glmVertexNormals: Generates smooth vertex normals for a model.
1043 * First builds a list of all the triangles each vertex is in. Then
1044 * loops through each vertex in the list averaging all the facet
1045 * normals of the triangles each vertex is in. Finally, sets the
1046 * normal index in the triangle for the vertex to the generated smooth
1047 * normal. If the dot product of a facet normal and the facet normal
1048 * associated with the first triangle in the list of triangles the
1049 * current vertex is in is greater than the cosine of the angle
1050 * parameter to the function, that facet normal is not added into the
1051 * average normal calculation and the corresponding vertex is given
1052 * the facet normal. This tends to preserve hard edges. The angle to
1053 * use depends on the model, but 90 degrees is usually a good start.
1055 * model - initialized GLMmodel structure
1056 * angle - maximum angle (in degrees) to smooth across
1059 glmVertexNormals(GLMmodel
* model
, float angle
)
1067 float dot
, cos_angle
;
1071 assert(model
->facetnorms
);
1073 /* calculate the cosine of the angle (in degrees) */
1074 cos_angle
= cos(angle
* M_PI
/ 180.0);
1076 /* nuke any previous normals */
1078 free(model
->normals
);
1080 /* allocate space for new normals */
1081 model
->numnormals
= model
->numtriangles
* 3; /* 3 normals per triangle */
1082 model
->normals
= (float*)malloc(sizeof(float)* 3* (model
->numnormals
+1));
1084 /* allocate a structure that will hold a linked list of triangle
1085 indices for each vertex */
1086 members
= (GLMnode
**)malloc(sizeof(GLMnode
*) * (model
->numvertices
+ 1));
1087 for (i
= 1; i
<= model
->numvertices
; i
++)
1090 /* for every triangle, create a node for each vertex in it */
1091 for (i
= 0; i
< model
->numtriangles
; i
++) {
1092 node
= (GLMnode
*)malloc(sizeof(GLMnode
));
1094 node
->next
= members
[T(i
).vindices
[0]];
1095 members
[T(i
).vindices
[0]] = node
;
1097 node
= (GLMnode
*)malloc(sizeof(GLMnode
));
1099 node
->next
= members
[T(i
).vindices
[1]];
1100 members
[T(i
).vindices
[1]] = node
;
1102 node
= (GLMnode
*)malloc(sizeof(GLMnode
));
1104 node
->next
= members
[T(i
).vindices
[2]];
1105 members
[T(i
).vindices
[2]] = node
;
1108 /* calculate the average normal for each vertex */
1110 for (i
= 1; i
<= model
->numvertices
; i
++) {
1111 /* calculate an average normal for this vertex by averaging the
1112 facet normal of every triangle this vertex is in */
1115 fprintf(stderr
, "glmVertexNormals(): vertex w/o a triangle\n");
1116 average
[0] = 0.0; average
[1] = 0.0; average
[2] = 0.0;
1119 /* only average if the dot product of the angle between the two
1120 facet normals is greater than the cosine of the threshold
1121 angle -- or, said another way, the angle between the two
1122 facet normals is less than (or equal to) the threshold angle */
1123 dot
= _glmDot(&model
->facetnorms
[3 * T(node
->index
).findex
],
1124 &model
->facetnorms
[3 * T(members
[i
]->index
).findex
]);
1125 if (dot
> cos_angle
) {
1126 node
->averaged
= TRUE
;
1127 average
[0] += model
->facetnorms
[3 * T(node
->index
).findex
+ 0];
1128 average
[1] += model
->facetnorms
[3 * T(node
->index
).findex
+ 1];
1129 average
[2] += model
->facetnorms
[3 * T(node
->index
).findex
+ 2];
1130 avg
= 1; /* we averaged at least one normal! */
1132 node
->averaged
= FALSE
;
1138 /* normalize the averaged normal */
1139 _glmNormalize(average
);
1141 /* add the normal to the vertex normals list */
1142 model
->normals
[3 * numnormals
+ 0] = average
[0];
1143 model
->normals
[3 * numnormals
+ 1] = average
[1];
1144 model
->normals
[3 * numnormals
+ 2] = average
[2];
1149 /* set the normal of this vertex in each triangle it is in */
1152 if (node
->averaged
) {
1153 /* if this node was averaged, use the average normal */
1154 if (T(node
->index
).vindices
[0] == i
)
1155 T(node
->index
).nindices
[0] = avg
;
1156 else if (T(node
->index
).vindices
[1] == i
)
1157 T(node
->index
).nindices
[1] = avg
;
1158 else if (T(node
->index
).vindices
[2] == i
)
1159 T(node
->index
).nindices
[2] = avg
;
1161 /* if this node wasn't averaged, use the facet normal */
1162 model
->normals
[3 * numnormals
+ 0] =
1163 model
->facetnorms
[3 * T(node
->index
).findex
+ 0];
1164 model
->normals
[3 * numnormals
+ 1] =
1165 model
->facetnorms
[3 * T(node
->index
).findex
+ 1];
1166 model
->normals
[3 * numnormals
+ 2] =
1167 model
->facetnorms
[3 * T(node
->index
).findex
+ 2];
1168 if (T(node
->index
).vindices
[0] == i
)
1169 T(node
->index
).nindices
[0] = numnormals
;
1170 else if (T(node
->index
).vindices
[1] == i
)
1171 T(node
->index
).nindices
[1] = numnormals
;
1172 else if (T(node
->index
).vindices
[2] == i
)
1173 T(node
->index
).nindices
[2] = numnormals
;
1180 model
->numnormals
= numnormals
- 1;
1182 /* free the member information */
1183 for (i
= 1; i
<= model
->numvertices
; i
++) {
1193 /* pack the normals array (we previously allocated the maximum
1194 number of normals that could possibly be created (numtriangles *
1195 3), so get rid of some of them (usually alot unless none of the
1196 facet normals were averaged)) */
1197 normals
= model
->normals
;
1198 model
->normals
= (float*)malloc(sizeof(float)* 3* (model
->numnormals
+1));
1199 for (i
= 1; i
<= model
->numnormals
; i
++) {
1200 model
->normals
[3 * i
+ 0] = normals
[3 * i
+ 0];
1201 model
->normals
[3 * i
+ 1] = normals
[3 * i
+ 1];
1202 model
->normals
[3 * i
+ 2] = normals
[3 * i
+ 2];
1206 printf("glmVertexNormals(): %d normals generated\n", model
->numnormals
);
1210 /* glmLinearTexture: Generates texture coordinates according to a
1211 * linear projection of the texture map. It generates these by
1212 * linearly mapping the vertices onto a square.
1214 * model - pointer to initialized GLMmodel structure
1217 glmLinearTexture(GLMmodel
* model
)
1220 float dimensions
[3];
1221 float x
, y
, scalefactor
;
1226 if (model
->texcoords
)
1227 free(model
->texcoords
);
1228 model
->numtexcoords
= model
->numvertices
;
1229 model
->texcoords
=(float*)malloc(sizeof(float)*2*(model
->numtexcoords
+1));
1231 glmDimensions(model
, dimensions
);
1233 _glmAbs(_glmMax(_glmMax(dimensions
[0], dimensions
[1]), dimensions
[2]));
1235 /* do the calculations */
1236 for(i
= 1; i
<= model
->numvertices
; i
++) {
1237 x
= model
->vertices
[3 * i
+ 0] * scalefactor
;
1238 y
= model
->vertices
[3 * i
+ 2] * scalefactor
;
1239 model
->texcoords
[2 * i
+ 0] = (x
+ 1.0) / 2.0;
1240 model
->texcoords
[2 * i
+ 1] = (y
+ 1.0) / 2.0;
1243 /* go through and put texture coordinate indices in all the triangles */
1244 group
= model
->groups
;
1246 for(i
= 0; i
< group
->numtriangles
; i
++) {
1247 T(group
->triangles
[i
]).tindices
[0] = T(group
->triangles
[i
]).vindices
[0];
1248 T(group
->triangles
[i
]).tindices
[1] = T(group
->triangles
[i
]).vindices
[1];
1249 T(group
->triangles
[i
]).tindices
[2] = T(group
->triangles
[i
]).vindices
[2];
1251 group
= group
->next
;
1255 printf("glmLinearTexture(): generated %d linear texture coordinates\n",
1256 model
->numtexcoords
);
1260 /* glmSpheremapTexture: Generates texture coordinates according to a
1261 * spherical projection of the texture map. Sometimes referred to as
1262 * spheremap, or reflection map texture coordinates. It generates
1263 * these by using the normal to calculate where that vertex would map
1264 * onto a sphere. Since it is impossible to map something flat
1265 * perfectly onto something spherical, there is distortion at the
1266 * poles. This particular implementation causes the poles along the X
1267 * axis to be distorted.
1269 * model - pointer to initialized GLMmodel structure
1272 glmSpheremapTexture(GLMmodel
* model
)
1275 float theta
, phi
, rho
, x
, y
, z
, r
;
1279 assert(model
->normals
);
1281 if (model
->texcoords
)
1282 free(model
->texcoords
);
1283 model
->numtexcoords
= model
->numnormals
;
1284 model
->texcoords
=(float*)malloc(sizeof(float)*2*(model
->numtexcoords
+1));
1286 /* do the calculations */
1287 for (i
= 1; i
<= model
->numnormals
; i
++) {
1288 z
= model
->normals
[3 * i
+ 0]; /* re-arrange for pole distortion */
1289 y
= model
->normals
[3 * i
+ 1];
1290 x
= model
->normals
[3 * i
+ 2];
1291 r
= sqrt((x
* x
) + (y
* y
));
1292 rho
= sqrt((r
* r
) + (z
* z
));
1301 phi
= acos(z
/ rho
);
1303 #if WE_DONT_NEED_THIS_CODE
1305 theta
= M_PI
/ 2.0; /* asin(y / r); */
1307 theta
= acos(x
/ r
);
1311 theta
= M_PI
/ 2.0; /* acos(x / r); */
1313 theta
= asin(y
/ r
) + (M_PI
/ 2.0);
1316 model
->texcoords
[2 * i
+ 0] = theta
/ M_PI
;
1317 model
->texcoords
[2 * i
+ 1] = phi
/ M_PI
;
1320 /* go through and put texcoord indices in all the triangles */
1321 group
= model
->groups
;
1323 for (i
= 0; i
< group
->numtriangles
; i
++) {
1324 T(group
->triangles
[i
]).tindices
[0] = T(group
->triangles
[i
]).nindices
[0];
1325 T(group
->triangles
[i
]).tindices
[1] = T(group
->triangles
[i
]).nindices
[1];
1326 T(group
->triangles
[i
]).tindices
[2] = T(group
->triangles
[i
]).nindices
[2];
1328 group
= group
->next
;
1332 printf("glmSpheremapTexture(): generated %d spheremap texture coordinates\n",
1333 model
->numtexcoords
);
1337 /* glmDelete: Deletes a GLMmodel structure.
1339 * model - initialized GLMmodel structure
1342 glmDelete(GLMmodel
* model
)
1349 if (model
->pathname
) free(model
->pathname
);
1350 if (model
->mtllibname
) free(model
->mtllibname
);
1351 if (model
->vertices
) free(model
->vertices
);
1352 if (model
->normals
) free(model
->normals
);
1353 if (model
->texcoords
) free(model
->texcoords
);
1354 if (model
->facetnorms
) free(model
->facetnorms
);
1355 if (model
->triangles
) free(model
->triangles
);
1356 if (model
->materials
) {
1357 for (i
= 0; i
< model
->nummaterials
; i
++)
1358 free(model
->materials
[i
].name
);
1360 free(model
->materials
);
1361 while(model
->groups
) {
1362 group
= model
->groups
;
1363 model
->groups
= model
->groups
->next
;
1365 free(group
->triangles
);
1372 static GLMmaterial
*
1373 glmDefaultMaterial(void)
1375 GLMmaterial
*m
= (GLMmaterial
*) calloc(1, sizeof(GLMmaterial
));
1377 m
->diffuse
[0] = 0.75;
1378 m
->diffuse
[1] = 0.75;
1379 m
->diffuse
[2] = 0.75;
1380 m
->diffuse
[3] = 1.0;
1382 m
->specular
[0] = 1.0;
1383 m
->specular
[1] = 1.0;
1384 m
->specular
[2] = 1.0;
1385 m
->specular
[3] = 1.0;
1393 /* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
1394 * Returns a pointer to the created object which should be free'd with
1397 * filename - name of the file containing the Wavefront .OBJ format data.
1400 glmReadOBJ(char* filename
)
1406 file
= fopen(filename
, "r");
1408 fprintf(stderr
, "glmReadOBJ() failed: can't open data file \"%s\".\n",
1414 /* announce the model name */
1415 printf("Model: %s\n", filename
);
1418 /* allocate a new model */
1419 model
= (GLMmodel
*)malloc(sizeof(GLMmodel
));
1420 model
->pathname
= stralloc(filename
);
1421 model
->mtllibname
= NULL
;
1422 model
->numvertices
= 0;
1423 model
->vertices
= NULL
;
1424 model
->numnormals
= 0;
1425 model
->normals
= NULL
;
1426 model
->numtexcoords
= 0;
1427 model
->texcoords
= NULL
;
1428 model
->numfacetnorms
= 0;
1429 model
->facetnorms
= NULL
;
1430 model
->numtriangles
= 0;
1431 model
->triangles
= NULL
;
1432 model
->nummaterials
= 0;
1433 model
->materials
= NULL
;
1434 model
->numgroups
= 0;
1435 model
->groups
= NULL
;
1436 model
->position
[0] = 0.0;
1437 model
->position
[1] = 0.0;
1438 model
->position
[2] = 0.0;
1441 /* make a first pass through the file to get a count of the number
1442 of vertices, normals, texcoords & triangles */
1443 _glmFirstPass(model
, file
);
1445 /* allocate memory */
1446 model
->vertices
= (float*)malloc(sizeof(float) *
1447 3 * (model
->numvertices
+ 1));
1448 model
->triangles
= (GLMtriangle
*)malloc(sizeof(GLMtriangle
) *
1449 model
->numtriangles
);
1450 if (model
->numnormals
) {
1451 model
->normals
= (float*)malloc(sizeof(float) *
1452 3 * (model
->numnormals
+ 1));
1454 if (model
->numtexcoords
) {
1455 model
->texcoords
= (float*)malloc(sizeof(float) *
1456 2 * (model
->numtexcoords
+ 1));
1459 /* rewind to beginning of file and read in the data this pass */
1462 _glmSecondPass(model
, file
);
1464 /* close the file */
1467 if (!model
->materials
) {
1468 model
->materials
= glmDefaultMaterial();
1469 model
->nummaterials
= 1;
1475 /* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
1478 * model - initialized GLMmodel structure
1479 * filename - name of the file to write the Wavefront .OBJ format data to
1480 * mode - a bitwise or of values describing what is written to the file
1481 * GLM_NONE - render with only vertices
1482 * GLM_FLAT - render with facet normals
1483 * GLM_SMOOTH - render with vertex normals
1484 * GLM_TEXTURE - render with texture coords
1485 * GLM_COLOR - render with colors (color material)
1486 * GLM_MATERIAL - render with materials
1487 * GLM_COLOR and GLM_MATERIAL should not both be specified.
1488 * GLM_FLAT and GLM_SMOOTH should not both be specified.
1491 glmWriteOBJ(GLMmodel
* model
, char* filename
, uint mode
)
1499 /* do a bit of warning */
1500 if (mode
& GLM_FLAT
&& !model
->facetnorms
) {
1501 printf("glmWriteOBJ() warning: flat normal output requested "
1502 "with no facet normals defined.\n");
1505 if (mode
& GLM_SMOOTH
&& !model
->normals
) {
1506 printf("glmWriteOBJ() warning: smooth normal output requested "
1507 "with no normals defined.\n");
1508 mode
&= ~GLM_SMOOTH
;
1510 if (mode
& GLM_TEXTURE
&& !model
->texcoords
) {
1511 printf("glmWriteOBJ() warning: texture coordinate output requested "
1512 "with no texture coordinates defined.\n");
1513 mode
&= ~GLM_TEXTURE
;
1515 if (mode
& GLM_FLAT
&& mode
& GLM_SMOOTH
) {
1516 printf("glmWriteOBJ() warning: flat normal output requested "
1517 "and smooth normal output requested (using smooth).\n");
1522 file
= fopen(filename
, "w");
1524 fprintf(stderr
, "glmWriteOBJ() failed: can't open file \"%s\" to write.\n",
1529 /* spit out a header */
1530 fprintf(file
, "# \n");
1531 fprintf(file
, "# Wavefront OBJ generated by GLM library\n");
1532 fprintf(file
, "# \n");
1533 fprintf(file
, "# GLM library copyright (C) 1997 by Nate Robins\n");
1534 fprintf(file
, "# email: ndr@pobox.com\n");
1535 fprintf(file
, "# www: http://www.pobox.com/~ndr\n");
1536 fprintf(file
, "# \n");
1538 if (mode
& GLM_MATERIAL
&& model
->mtllibname
) {
1539 fprintf(file
, "\nmtllib %s\n\n", model
->mtllibname
);
1540 _glmWriteMTL(model
, filename
, model
->mtllibname
);
1543 /* spit out the vertices */
1544 fprintf(file
, "\n");
1545 fprintf(file
, "# %d vertices\n", model
->numvertices
);
1546 for (i
= 1; i
<= model
->numvertices
; i
++) {
1547 fprintf(file
, "v %f %f %f\n",
1548 model
->vertices
[3 * i
+ 0],
1549 model
->vertices
[3 * i
+ 1],
1550 model
->vertices
[3 * i
+ 2]);
1553 /* spit out the smooth/flat normals */
1554 if (mode
& GLM_SMOOTH
) {
1555 fprintf(file
, "\n");
1556 fprintf(file
, "# %d normals\n", model
->numnormals
);
1557 for (i
= 1; i
<= model
->numnormals
; i
++) {
1558 fprintf(file
, "vn %f %f %f\n",
1559 model
->normals
[3 * i
+ 0],
1560 model
->normals
[3 * i
+ 1],
1561 model
->normals
[3 * i
+ 2]);
1563 } else if (mode
& GLM_FLAT
) {
1564 fprintf(file
, "\n");
1565 fprintf(file
, "# %d normals\n", model
->numfacetnorms
);
1566 for (i
= 1; i
<= model
->numnormals
; i
++) {
1567 fprintf(file
, "vn %f %f %f\n",
1568 model
->facetnorms
[3 * i
+ 0],
1569 model
->facetnorms
[3 * i
+ 1],
1570 model
->facetnorms
[3 * i
+ 2]);
1574 /* spit out the texture coordinates */
1575 if (mode
& GLM_TEXTURE
) {
1576 fprintf(file
, "\n");
1577 fprintf(file
, "# %d texcoords\n", model
->numtexcoords
);
1578 for (i
= 1; i
<= model
->numtexcoords
; i
++) {
1579 fprintf(file
, "vt %f %f\n",
1580 model
->texcoords
[2 * i
+ 0],
1581 model
->texcoords
[2 * i
+ 1]);
1585 fprintf(file
, "\n");
1586 fprintf(file
, "# %d groups\n", model
->numgroups
);
1587 fprintf(file
, "# %d faces (triangles)\n", model
->numtriangles
);
1588 fprintf(file
, "\n");
1590 group
= model
->groups
;
1592 fprintf(file
, "g %s\n", group
->name
);
1593 if (mode
& GLM_MATERIAL
)
1594 fprintf(file
, "usemtl %s\n", model
->materials
[group
->material
].name
);
1595 for (i
= 0; i
< group
->numtriangles
; i
++) {
1596 if (mode
& GLM_SMOOTH
&& mode
& GLM_TEXTURE
) {
1597 fprintf(file
, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
1598 T(group
->triangles
[i
]).vindices
[0],
1599 T(group
->triangles
[i
]).nindices
[0],
1600 T(group
->triangles
[i
]).tindices
[0],
1601 T(group
->triangles
[i
]).vindices
[1],
1602 T(group
->triangles
[i
]).nindices
[1],
1603 T(group
->triangles
[i
]).tindices
[1],
1604 T(group
->triangles
[i
]).vindices
[2],
1605 T(group
->triangles
[i
]).nindices
[2],
1606 T(group
->triangles
[i
]).tindices
[2]);
1607 } else if (mode
& GLM_FLAT
&& mode
& GLM_TEXTURE
) {
1608 fprintf(file
, "f %d/%d %d/%d %d/%d\n",
1609 T(group
->triangles
[i
]).vindices
[0],
1610 T(group
->triangles
[i
]).findex
,
1611 T(group
->triangles
[i
]).vindices
[1],
1612 T(group
->triangles
[i
]).findex
,
1613 T(group
->triangles
[i
]).vindices
[2],
1614 T(group
->triangles
[i
]).findex
);
1615 } else if (mode
& GLM_TEXTURE
) {
1616 fprintf(file
, "f %d/%d %d/%d %d/%d\n",
1617 T(group
->triangles
[i
]).vindices
[0],
1618 T(group
->triangles
[i
]).tindices
[0],
1619 T(group
->triangles
[i
]).vindices
[1],
1620 T(group
->triangles
[i
]).tindices
[1],
1621 T(group
->triangles
[i
]).vindices
[2],
1622 T(group
->triangles
[i
]).tindices
[2]);
1623 } else if (mode
& GLM_SMOOTH
) {
1624 fprintf(file
, "f %d//%d %d//%d %d//%d\n",
1625 T(group
->triangles
[i
]).vindices
[0],
1626 T(group
->triangles
[i
]).nindices
[0],
1627 T(group
->triangles
[i
]).vindices
[1],
1628 T(group
->triangles
[i
]).nindices
[1],
1629 T(group
->triangles
[i
]).vindices
[2],
1630 T(group
->triangles
[i
]).nindices
[2]);
1631 } else if (mode
& GLM_FLAT
) {
1632 fprintf(file
, "f %d//%d %d//%d %d//%d\n",
1633 T(group
->triangles
[i
]).vindices
[0],
1634 T(group
->triangles
[i
]).findex
,
1635 T(group
->triangles
[i
]).vindices
[1],
1636 T(group
->triangles
[i
]).findex
,
1637 T(group
->triangles
[i
]).vindices
[2],
1638 T(group
->triangles
[i
]).findex
);
1640 fprintf(file
, "f %d %d %d\n",
1641 T(group
->triangles
[i
]).vindices
[0],
1642 T(group
->triangles
[i
]).vindices
[1],
1643 T(group
->triangles
[i
]).vindices
[2]);
1646 fprintf(file
, "\n");
1647 group
= group
->next
;
1653 /* glmWeld: eliminate (weld) vectors that are within an epsilon of
1656 * model - initialized GLMmodel structure
1657 * epsilon - maximum difference between vertices
1658 * ( 0.00001 is a good start for a unitized model)
1662 glmWeld(GLMmodel
* model
, float epsilon
)
1670 numvectors
= model
->numvertices
;
1671 vectors
= model
->vertices
;
1672 copies
= _glmWeldVectors(vectors
, &numvectors
, epsilon
);
1674 printf("glmWeld(): %d redundant vertices.\n",
1675 model
->numvertices
- numvectors
- 1);
1677 for (i
= 0; i
< model
->numtriangles
; i
++) {
1678 T(i
).vindices
[0] = (uint
)vectors
[3 * T(i
).vindices
[0] + 0];
1679 T(i
).vindices
[1] = (uint
)vectors
[3 * T(i
).vindices
[1] + 0];
1680 T(i
).vindices
[2] = (uint
)vectors
[3 * T(i
).vindices
[2] + 0];
1683 /* free space for old vertices */
1686 /* allocate space for the new vertices */
1687 model
->numvertices
= numvectors
;
1688 model
->vertices
= (float*)malloc(sizeof(float) *
1689 3 * (model
->numvertices
+ 1));
1691 /* copy the optimized vertices into the actual vertex list */
1692 for (i
= 1; i
<= model
->numvertices
; i
++) {
1693 model
->vertices
[3 * i
+ 0] = copies
[3 * i
+ 0];
1694 model
->vertices
[3 * i
+ 1] = copies
[3 * i
+ 1];
1695 model
->vertices
[3 * i
+ 2] = copies
[3 * i
+ 2];
1703 glmReIndex(GLMmodel
*model
)
1707 float *newNormals
= NULL
;
1708 float *newTexcoords
= NULL
;
1709 const uint numv
= model
->numvertices
;
1711 if (model
->numnormals
> 0)
1712 newNormals
= (float *) malloc((numv
+ 1) * 3 * sizeof(float));
1714 if (model
->numtexcoords
> 0)
1715 newTexcoords
= (float *) malloc((numv
+ 1) * 2 * sizeof(float));
1717 for (group
= model
->groups
; group
; group
= group
->next
) {
1719 n
= group
->numtriangles
;
1721 group
->triIndexes
= (uint
*) malloc(n
* 3 * sizeof(uint
));
1723 group
->minIndex
= 10000000;
1724 group
->maxIndex
= 0;
1726 for (i
= 0; i
< n
; i
++) {
1728 for (j
= 0; j
< 3; j
++) {
1729 uint vindex
= T(group
->triangles
[i
]).vindices
[j
];
1730 uint nindex
= T(group
->triangles
[i
]).nindices
[j
];
1731 uint tindex
= T(group
->triangles
[i
]).tindices
[j
];
1733 float *nrm
= &model
->normals
[nindex
* 3];
1734 float *tex
= &model
->texcoords
[tindex
* 2];
1737 assert(vindex
* 3 + 2 < (numv
+ 1) * 3);
1738 newNormals
[vindex
* 3 + 0] = nrm
[0];
1739 newNormals
[vindex
* 3 + 1] = nrm
[1];
1740 newNormals
[vindex
* 3 + 2] = nrm
[2];
1743 newTexcoords
[vindex
* 2 + 0] = tex
[0];
1744 newTexcoords
[vindex
* 2 + 1] = tex
[1];
1747 T(group
->triangles
[i
]).nindices
[j
] = vindex
;
1748 T(group
->triangles
[i
]).tindices
[j
] = vindex
;
1750 group
->triIndexes
[i
* 3 + j
] = vindex
;
1752 if (vindex
> group
->maxIndex
)
1753 group
->maxIndex
= vindex
;
1754 if (vindex
< group
->minIndex
)
1755 group
->minIndex
= vindex
;
1761 free(model
->normals
);
1762 model
->normals
= newNormals
;
1763 model
->numnormals
= model
->numvertices
;
1767 free(model
->texcoords
);
1768 model
->texcoords
= newTexcoords
;
1769 model
->numtexcoords
= model
->numvertices
;
1776 glmPrint(const GLMmodel
*model
)
1784 printf("%u vertices\n", model
->numvertices
);
1785 printf("%u normals\n", model
->numnormals
);
1786 printf("%u texcoords\n", model
->numtexcoords
);
1788 for (group
= model
->groups
; group
; group
= group
->next
, grp
++) {
1789 printf("Group %u:\n", grp
);
1790 printf(" Min index %u, max index %u\n", group
->minIndex
, group
->maxIndex
);
1793 if (mode
& GLM_MATERIAL
) {
1794 glMaterialfv(GL_FRONT_AND_BACK
, GL_AMBIENT
,
1795 model
->materials
[group
->material
].ambient
);
1796 glMaterialfv(GL_FRONT_AND_BACK
, GL_DIFFUSE
,
1797 model
->materials
[group
->material
].diffuse
);
1798 glMaterialfv(GL_FRONT_AND_BACK
, GL_SPECULAR
,
1799 model
->materials
[group
->material
].specular
);
1800 glMaterialf(GL_FRONT_AND_BACK
, GL_SHININESS
,
1801 model
->materials
[group
->material
].shininess
);
1804 if (mode
& GLM_COLOR
) {
1805 glColor3fv(model
->materials
[group
->material
].diffuse
);
1808 totalTris
+= group
->numtriangles
;
1810 printf(" %u triangles\n", group
->numtriangles
);
1811 n
= group
->numtriangles
;
1815 for (i
= 0; i
< n
; i
++) {
1817 printf(" %u: vert ", i
);
1818 for (j
= 0; j
< 3; j
++) {
1819 printf("%u ", T(group
->triangles
[i
]).vindices
[j
]);
1823 for (j
= 0; j
< 3; j
++) {
1824 printf("%u ", T(group
->triangles
[i
]).nindices
[j
]);
1828 for (j
= 0; j
< 3; j
++) {
1829 printf("%u ", T(group
->triangles
[i
]).tindices
[j
]);
1835 printf("Total tris: %u\n", totalTris
);
1842 if (model
->numnormals
) {
1843 numvectors
= model
->numnormals
;
1844 vectors
= model
->normals
;
1845 copies
= _glmOptimizeVectors(vectors
, &numvectors
);
1847 printf("glmOptimize(): %d redundant normals.\n",
1848 model
->numnormals
- numvectors
);
1850 for (i
= 0; i
< model
->numtriangles
; i
++) {
1851 T(i
).nindices
[0] = (uint
)vectors
[3 * T(i
).nindices
[0] + 0];
1852 T(i
).nindices
[1] = (uint
)vectors
[3 * T(i
).nindices
[1] + 0];
1853 T(i
).nindices
[2] = (uint
)vectors
[3 * T(i
).nindices
[2] + 0];
1856 /* free space for old normals */
1859 /* allocate space for the new normals */
1860 model
->numnormals
= numvectors
;
1861 model
->normals
= (float*)malloc(sizeof(float) *
1862 3 * (model
->numnormals
+ 1));
1864 /* copy the optimized vertices into the actual vertex list */
1865 for (i
= 1; i
<= model
->numnormals
; i
++) {
1866 model
->normals
[3 * i
+ 0] = copies
[3 * i
+ 0];
1867 model
->normals
[3 * i
+ 1] = copies
[3 * i
+ 1];
1868 model
->normals
[3 * i
+ 2] = copies
[3 * i
+ 2];
1875 if (model
->numtexcoords
) {
1876 numvectors
= model
->numtexcoords
;
1877 vectors
= model
->texcoords
;
1878 copies
= _glmOptimizeVectors(vectors
, &numvectors
);
1880 printf("glmOptimize(): %d redundant texcoords.\n",
1881 model
->numtexcoords
- numvectors
);
1883 for (i
= 0; i
< model
->numtriangles
; i
++) {
1884 for (j
= 0; j
< 3; j
++) {
1885 T(i
).tindices
[j
] = (uint
)vectors
[3 * T(i
).tindices
[j
] + 0];
1889 /* free space for old texcoords */
1892 /* allocate space for the new texcoords */
1893 model
->numtexcoords
= numvectors
;
1894 model
->texcoords
= (float*)malloc(sizeof(float) *
1895 2 * (model
->numtexcoords
+ 1));
1897 /* copy the optimized vertices into the actual vertex list */
1898 for (i
= 1; i
<= model
->numtexcoords
; i
++) {
1899 model
->texcoords
[2 * i
+ 0] = copies
[2 * i
+ 0];
1900 model
->texcoords
[2 * i
+ 1] = copies
[2 * i
+ 1];
1908 /* look for unused vertices */
1909 /* look for unused normals */
1910 /* look for unused texcoords */
1911 for (i
= 1; i
<= model
->numvertices
; i
++) {
1912 for (j
= 0; j
< model
->numtriangles
; i
++) {
1913 if (T(j
).vindices
[0] == i
||
1914 T(j
).vindices
[1] == i
||
1915 T(j
).vindices
[1] == i
)