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
);
427 /* _glmWriteMTL: write a wavefront material library file
429 * model - properly initialized GLMmodel structure
430 * modelpath - pathname of the model being written
431 * mtllibname - name of the material library to be written
434 _glmWriteMTL(GLMmodel
* model
, char* modelpath
, char* mtllibname
)
439 GLMmaterial
* material
;
442 dir
= _glmDirName(modelpath
);
443 filename
= (char*)malloc(sizeof(char) * (strlen(dir
) + strlen(mtllibname
)));
444 strcpy(filename
, dir
);
445 strcat(filename
, mtllibname
);
449 file
= fopen(filename
, "w");
451 fprintf(stderr
, "_glmWriteMTL() failed: can't open file \"%s\".\n",
457 /* spit out a header */
458 fprintf(file
, "# \n");
459 fprintf(file
, "# Wavefront MTL generated by GLM library\n");
460 fprintf(file
, "# \n");
461 fprintf(file
, "# GLM library copyright (C) 1997 by Nate Robins\n");
462 fprintf(file
, "# email: ndr@pobox.com\n");
463 fprintf(file
, "# www: http://www.pobox.com/~ndr\n");
464 fprintf(file
, "# \n\n");
466 for (i
= 0; i
< model
->nummaterials
; i
++) {
467 material
= &model
->materials
[i
];
468 fprintf(file
, "newmtl %s\n", material
->name
);
469 fprintf(file
, "Ka %f %f %f\n",
470 material
->ambient
[0], material
->ambient
[1], material
->ambient
[2]);
471 fprintf(file
, "Kd %f %f %f\n",
472 material
->diffuse
[0], material
->diffuse
[1], material
->diffuse
[2]);
473 fprintf(file
, "Ks %f %f %f\n",
474 material
->specular
[0],material
->specular
[1],material
->specular
[2]);
475 fprintf(file
, "Ns %f\n", material
->shininess
);
481 /* _glmFirstPass: first pass at a Wavefront OBJ file that gets all the
482 * statistics of the model (such as #vertices, #normals, etc)
484 * model - properly initialized GLMmodel structure
485 * file - (fopen'd) file descriptor
488 _glmFirstPass(GLMmodel
* model
, FILE* file
)
490 uint numvertices
; /* number of vertices in model */
491 uint numnormals
; /* number of normals in model */
492 uint numtexcoords
; /* number of texcoords in model */
493 uint numtriangles
; /* number of triangles in model */
494 GLMgroup
* group
; /* current group */
498 /* make a default group */
499 group
= _glmAddGroup(model
, "default");
501 numvertices
= numnormals
= numtexcoords
= numtriangles
= 0;
502 while(fscanf(file
, "%s", buf
) != EOF
) {
504 case '#': /* comment */
505 /* eat up rest of line */
506 fgets(buf
, sizeof(buf
), file
);
508 case 'v': /* v, vn, vt */
510 case '\0': /* vertex */
511 /* eat up rest of line */
512 fgets(buf
, sizeof(buf
), file
);
515 case 'n': /* normal */
516 /* eat up rest of line */
517 fgets(buf
, sizeof(buf
), file
);
520 case 't': /* texcoord */
521 /* eat up rest of line */
522 fgets(buf
, sizeof(buf
), file
);
526 printf("_glmFirstPass(): Unknown token \"%s\".\n", buf
);
532 fgets(buf
, sizeof(buf
), file
);
533 sscanf(buf
, "%s %s", buf
, buf
);
534 model
->mtllibname
= stralloc(buf
);
535 _glmReadMTL(model
, buf
);
538 /* eat up rest of line */
539 fgets(buf
, sizeof(buf
), file
);
541 case 'g': /* group */
542 /* eat up rest of line */
543 fgets(buf
, sizeof(buf
), file
);
544 sscanf(buf
, "%s", buf
);
545 group
= _glmAddGroup(model
, buf
);
549 fscanf(file
, "%s", buf
);
550 /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
551 if (strstr(buf
, "//")) {
553 sscanf(buf
, "%u//%u", &v
, &n
);
554 fscanf(file
, "%u//%u", &v
, &n
);
555 fscanf(file
, "%u//%u", &v
, &n
);
557 group
->numtriangles
++;
558 while(fscanf(file
, "%u//%u", &v
, &n
) > 0) {
560 group
->numtriangles
++;
562 } else if (sscanf(buf
, "%u/%u/%u", &v
, &t
, &n
) == 3) {
564 fscanf(file
, "%u/%u/%u", &v
, &t
, &n
);
565 fscanf(file
, "%u/%u/%u", &v
, &t
, &n
);
567 group
->numtriangles
++;
568 while(fscanf(file
, "%u/%u/%u", &v
, &t
, &n
) > 0) {
570 group
->numtriangles
++;
572 } else if (sscanf(buf
, "%u/%u", &v
, &t
) == 2) {
574 fscanf(file
, "%u/%u", &v
, &t
);
575 fscanf(file
, "%u/%u", &v
, &t
);
577 group
->numtriangles
++;
578 while(fscanf(file
, "%u/%u", &v
, &t
) > 0) {
580 group
->numtriangles
++;
584 fscanf(file
, "%u", &v
);
585 fscanf(file
, "%u", &v
);
587 group
->numtriangles
++;
588 while(fscanf(file
, "%u", &v
) > 0) {
590 group
->numtriangles
++;
596 /* eat up rest of line */
597 fgets(buf
, sizeof(buf
), file
);
603 /* announce the model statistics */
604 printf(" Vertices: %d\n", numvertices
);
605 printf(" Normals: %d\n", numnormals
);
606 printf(" Texcoords: %d\n", numtexcoords
);
607 printf(" Triangles: %d\n", numtriangles
);
608 printf(" Groups: %d\n", model
->numgroups
);
611 /* set the stats in the model structure */
612 model
->numvertices
= numvertices
;
613 model
->numnormals
= numnormals
;
614 model
->numtexcoords
= numtexcoords
;
615 model
->numtriangles
= numtriangles
;
617 /* allocate memory for the triangles in each group */
618 group
= model
->groups
;
620 group
->triangles
= (uint
*)malloc(sizeof(uint
) * group
->numtriangles
);
621 group
->numtriangles
= 0;
626 /* _glmSecondPass: second pass at a Wavefront OBJ file that gets all
629 * model - properly initialized GLMmodel structure
630 * file - (fopen'd) file descriptor
633 _glmSecondPass(GLMmodel
* model
, FILE* file
)
635 uint numvertices
; /* number of vertices in model */
636 uint numnormals
; /* number of normals in model */
637 uint numtexcoords
; /* number of texcoords in model */
638 uint numtriangles
; /* number of triangles in model */
639 float* vertices
; /* array of vertices */
640 float* normals
; /* array of normals */
641 float* texcoords
; /* array of texture coordinates */
642 GLMgroup
* group
; /* current group pointer */
643 uint material
; /* current material */
647 /* set the pointer shortcuts */
648 vertices
= model
->vertices
;
649 normals
= model
->normals
;
650 texcoords
= model
->texcoords
;
651 group
= model
->groups
;
653 /* on the second pass through the file, read all the data into the
655 numvertices
= numnormals
= numtexcoords
= 1;
658 while(fscanf(file
, "%s", buf
) != EOF
) {
660 case '#': /* comment */
661 /* eat up rest of line */
662 fgets(buf
, sizeof(buf
), file
);
664 case 'v': /* v, vn, vt */
666 case '\0': /* vertex */
667 fscanf(file
, "%f %f %f",
668 &vertices
[3 * numvertices
+ X
],
669 &vertices
[3 * numvertices
+ Y
],
670 &vertices
[3 * numvertices
+ Z
]);
673 case 'n': /* normal */
674 fscanf(file
, "%f %f %f",
675 &normals
[3 * numnormals
+ X
],
676 &normals
[3 * numnormals
+ Y
],
677 &normals
[3 * numnormals
+ Z
]);
680 case 't': /* texcoord */
681 fscanf(file
, "%f %f",
682 &texcoords
[2 * numtexcoords
+ X
],
683 &texcoords
[2 * numtexcoords
+ Y
]);
689 fgets(buf
, sizeof(buf
), file
);
690 sscanf(buf
, "%s %s", buf
, buf
);
691 material
= _glmFindMaterial(model
, buf
);
692 if (!group
->material
)
693 group
->material
= material
;
694 /*printf("material %s = %u\n", buf, material);*/
696 case 'g': /* group */
697 /* eat up rest of line */
698 fgets(buf
, sizeof(buf
), file
);
699 sscanf(buf
, "%s", buf
);
700 group
= _glmFindGroup(model
, buf
);
701 group
->material
= material
;
702 /*printf("GROUP %s material %u\n", buf, material);*/
706 fscanf(file
, "%s", buf
);
707 /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
708 if (strstr(buf
, "//")) {
710 sscanf(buf
, "%u//%u", &v
, &n
);
711 T(numtriangles
).vindices
[0] = v
;
712 T(numtriangles
).nindices
[0] = n
;
713 fscanf(file
, "%u//%u", &v
, &n
);
714 T(numtriangles
).vindices
[1] = v
;
715 T(numtriangles
).nindices
[1] = n
;
716 fscanf(file
, "%u//%u", &v
, &n
);
717 T(numtriangles
).vindices
[2] = v
;
718 T(numtriangles
).nindices
[2] = n
;
719 group
->triangles
[group
->numtriangles
++] = numtriangles
;
721 while(fscanf(file
, "%u//%u", &v
, &n
) > 0) {
722 T(numtriangles
).vindices
[0] = T(numtriangles
-1).vindices
[0];
723 T(numtriangles
).nindices
[0] = T(numtriangles
-1).nindices
[0];
724 T(numtriangles
).vindices
[1] = T(numtriangles
-1).vindices
[2];
725 T(numtriangles
).nindices
[1] = T(numtriangles
-1).nindices
[2];
726 T(numtriangles
).vindices
[2] = v
;
727 T(numtriangles
).nindices
[2] = n
;
728 group
->triangles
[group
->numtriangles
++] = numtriangles
;
731 } else if (sscanf(buf
, "%u/%u/%u", &v
, &t
, &n
) == 3) {
733 T(numtriangles
).vindices
[0] = v
;
734 T(numtriangles
).tindices
[0] = t
;
735 T(numtriangles
).nindices
[0] = n
;
736 fscanf(file
, "%u/%u/%u", &v
, &t
, &n
);
737 T(numtriangles
).vindices
[1] = v
;
738 T(numtriangles
).tindices
[1] = t
;
739 T(numtriangles
).nindices
[1] = n
;
740 fscanf(file
, "%u/%u/%u", &v
, &t
, &n
);
741 T(numtriangles
).vindices
[2] = v
;
742 T(numtriangles
).tindices
[2] = t
;
743 T(numtriangles
).nindices
[2] = n
;
744 group
->triangles
[group
->numtriangles
++] = numtriangles
;
746 while(fscanf(file
, "%u/%u/%u", &v
, &t
, &n
) > 0) {
747 T(numtriangles
).vindices
[0] = T(numtriangles
-1).vindices
[0];
748 T(numtriangles
).tindices
[0] = T(numtriangles
-1).tindices
[0];
749 T(numtriangles
).nindices
[0] = T(numtriangles
-1).nindices
[0];
750 T(numtriangles
).vindices
[1] = T(numtriangles
-1).vindices
[2];
751 T(numtriangles
).tindices
[1] = T(numtriangles
-1).tindices
[2];
752 T(numtriangles
).nindices
[1] = T(numtriangles
-1).nindices
[2];
753 T(numtriangles
).vindices
[2] = v
;
754 T(numtriangles
).tindices
[2] = t
;
755 T(numtriangles
).nindices
[2] = n
;
756 group
->triangles
[group
->numtriangles
++] = numtriangles
;
759 } else if (sscanf(buf
, "%u/%u", &v
, &t
) == 2) {
761 T(numtriangles
).vindices
[0] = v
;
762 T(numtriangles
).tindices
[0] = t
;
763 fscanf(file
, "%u/%u", &v
, &t
);
764 T(numtriangles
).vindices
[1] = v
;
765 T(numtriangles
).tindices
[1] = t
;
766 fscanf(file
, "%u/%u", &v
, &t
);
767 T(numtriangles
).vindices
[2] = v
;
768 T(numtriangles
).tindices
[2] = t
;
769 group
->triangles
[group
->numtriangles
++] = numtriangles
;
771 while(fscanf(file
, "%u/%u", &v
, &t
) > 0) {
772 T(numtriangles
).vindices
[0] = T(numtriangles
-1).vindices
[0];
773 T(numtriangles
).tindices
[0] = T(numtriangles
-1).tindices
[0];
774 T(numtriangles
).vindices
[1] = T(numtriangles
-1).vindices
[2];
775 T(numtriangles
).tindices
[1] = T(numtriangles
-1).tindices
[2];
776 T(numtriangles
).vindices
[2] = v
;
777 T(numtriangles
).tindices
[2] = t
;
778 group
->triangles
[group
->numtriangles
++] = numtriangles
;
783 sscanf(buf
, "%u", &v
);
784 T(numtriangles
).vindices
[0] = v
;
785 fscanf(file
, "%u", &v
);
786 T(numtriangles
).vindices
[1] = v
;
787 fscanf(file
, "%u", &v
);
788 T(numtriangles
).vindices
[2] = v
;
789 group
->triangles
[group
->numtriangles
++] = numtriangles
;
791 while(fscanf(file
, "%u", &v
) > 0) {
792 T(numtriangles
).vindices
[0] = T(numtriangles
-1).vindices
[0];
793 T(numtriangles
).vindices
[1] = T(numtriangles
-1).vindices
[2];
794 T(numtriangles
).vindices
[2] = v
;
795 group
->triangles
[group
->numtriangles
++] = numtriangles
;
802 /* eat up rest of line */
803 fgets(buf
, sizeof(buf
), file
);
809 /* announce the memory requirements */
810 printf(" Memory: %d bytes\n",
811 numvertices
* 3*sizeof(float) +
812 numnormals
* 3*sizeof(float) * (numnormals
? 1 : 0) +
813 numtexcoords
* 3*sizeof(float) * (numtexcoords
? 1 : 0) +
814 numtriangles
* sizeof(GLMtriangle
));
821 /* public functions */
823 /* glmUnitize: "unitize" a model by translating it to the origin and
824 * scaling it to fit in a unit cube around the origin. Returns the
827 * model - properly initialized GLMmodel structure
830 glmUnitize(GLMmodel
* model
)
833 float maxx
, minx
, maxy
, miny
, maxz
, minz
;
834 float cx
, cy
, cz
, w
, h
, d
;
838 assert(model
->vertices
);
840 /* get the max/mins */
841 maxx
= minx
= model
->vertices
[3 + X
];
842 maxy
= miny
= model
->vertices
[3 + Y
];
843 maxz
= minz
= model
->vertices
[3 + Z
];
844 for (i
= 1; i
<= model
->numvertices
; i
++) {
845 if (maxx
< model
->vertices
[3 * i
+ X
])
846 maxx
= model
->vertices
[3 * i
+ X
];
847 if (minx
> model
->vertices
[3 * i
+ X
])
848 minx
= model
->vertices
[3 * i
+ X
];
850 if (maxy
< model
->vertices
[3 * i
+ Y
])
851 maxy
= model
->vertices
[3 * i
+ Y
];
852 if (miny
> model
->vertices
[3 * i
+ Y
])
853 miny
= model
->vertices
[3 * i
+ Y
];
855 if (maxz
< model
->vertices
[3 * i
+ Z
])
856 maxz
= model
->vertices
[3 * i
+ Z
];
857 if (minz
> model
->vertices
[3 * i
+ Z
])
858 minz
= model
->vertices
[3 * i
+ Z
];
861 /* calculate model width, height, and depth */
862 w
= _glmAbs(maxx
) + _glmAbs(minx
);
863 h
= _glmAbs(maxy
) + _glmAbs(miny
);
864 d
= _glmAbs(maxz
) + _glmAbs(minz
);
866 /* calculate center of the model */
867 cx
= (maxx
+ minx
) / 2.0;
868 cy
= (maxy
+ miny
) / 2.0;
869 cz
= (maxz
+ minz
) / 2.0;
871 /* calculate unitizing scale factor */
872 scale
= 2.0 / _glmMax(_glmMax(w
, h
), d
);
874 /* translate around center then scale */
875 for (i
= 1; i
<= model
->numvertices
; i
++) {
876 model
->vertices
[3 * i
+ X
] -= cx
;
877 model
->vertices
[3 * i
+ Y
] -= cy
;
878 model
->vertices
[3 * i
+ Z
] -= cz
;
879 model
->vertices
[3 * i
+ X
] *= scale
;
880 model
->vertices
[3 * i
+ Y
] *= scale
;
881 model
->vertices
[3 * i
+ Z
] *= scale
;
887 /* glmDimensions: Calculates the dimensions (width, height, depth) of
890 * model - initialized GLMmodel structure
891 * dimensions - array of 3 floats (float dimensions[3])
894 glmDimensions(GLMmodel
* model
, float* dimensions
)
897 float maxx
, minx
, maxy
, miny
, maxz
, minz
;
900 assert(model
->vertices
);
903 /* get the max/mins */
904 maxx
= minx
= model
->vertices
[3 + X
];
905 maxy
= miny
= model
->vertices
[3 + Y
];
906 maxz
= minz
= model
->vertices
[3 + Z
];
907 for (i
= 1; i
<= model
->numvertices
; i
++) {
908 if (maxx
< model
->vertices
[3 * i
+ X
])
909 maxx
= model
->vertices
[3 * i
+ X
];
910 if (minx
> model
->vertices
[3 * i
+ X
])
911 minx
= model
->vertices
[3 * i
+ X
];
913 if (maxy
< model
->vertices
[3 * i
+ Y
])
914 maxy
= model
->vertices
[3 * i
+ Y
];
915 if (miny
> model
->vertices
[3 * i
+ Y
])
916 miny
= model
->vertices
[3 * i
+ Y
];
918 if (maxz
< model
->vertices
[3 * i
+ Z
])
919 maxz
= model
->vertices
[3 * i
+ Z
];
920 if (minz
> model
->vertices
[3 * i
+ Z
])
921 minz
= model
->vertices
[3 * i
+ Z
];
924 /* calculate model width, height, and depth */
925 dimensions
[X
] = _glmAbs(maxx
) + _glmAbs(minx
);
926 dimensions
[Y
] = _glmAbs(maxy
) + _glmAbs(miny
);
927 dimensions
[Z
] = _glmAbs(maxz
) + _glmAbs(minz
);
930 /* glmScale: Scales a model by a given amount.
932 * model - properly initialized GLMmodel structure
933 * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
936 glmScale(GLMmodel
* model
, float scale
)
940 for (i
= 1; i
<= model
->numvertices
; i
++) {
941 model
->vertices
[3 * i
+ X
] *= scale
;
942 model
->vertices
[3 * i
+ Y
] *= scale
;
943 model
->vertices
[3 * i
+ Z
] *= scale
;
947 /* glmReverseWinding: Reverse the polygon winding for all polygons in
948 * this model. Default winding is counter-clockwise. Also changes
949 * the direction of the normals.
951 * model - properly initialized GLMmodel structure
954 glmReverseWinding(GLMmodel
* model
)
960 for (i
= 0; i
< model
->numtriangles
; i
++) {
961 swap
= T(i
).vindices
[0];
962 T(i
).vindices
[0] = T(i
).vindices
[2];
963 T(i
).vindices
[2] = swap
;
965 if (model
->numnormals
) {
966 swap
= T(i
).nindices
[0];
967 T(i
).nindices
[0] = T(i
).nindices
[2];
968 T(i
).nindices
[2] = swap
;
971 if (model
->numtexcoords
) {
972 swap
= T(i
).tindices
[0];
973 T(i
).tindices
[0] = T(i
).tindices
[2];
974 T(i
).tindices
[2] = swap
;
978 /* reverse facet normals */
979 for (i
= 1; i
<= model
->numfacetnorms
; i
++) {
980 model
->facetnorms
[3 * i
+ X
] = -model
->facetnorms
[3 * i
+ X
];
981 model
->facetnorms
[3 * i
+ Y
] = -model
->facetnorms
[3 * i
+ Y
];
982 model
->facetnorms
[3 * i
+ Z
] = -model
->facetnorms
[3 * i
+ Z
];
985 /* reverse vertex normals */
986 for (i
= 1; i
<= model
->numnormals
; i
++) {
987 model
->normals
[3 * i
+ X
] = -model
->normals
[3 * i
+ X
];
988 model
->normals
[3 * i
+ Y
] = -model
->normals
[3 * i
+ Y
];
989 model
->normals
[3 * i
+ Z
] = -model
->normals
[3 * i
+ Z
];
993 /* glmFacetNormals: Generates facet normals for a model (by taking the
994 * cross product of the two vectors derived from the sides of each
995 * triangle). Assumes a counter-clockwise winding.
997 * model - initialized GLMmodel structure
1000 glmFacetNormals(GLMmodel
* model
)
1007 assert(model
->vertices
);
1009 /* clobber any old facetnormals */
1010 if (model
->facetnorms
)
1011 free(model
->facetnorms
);
1013 /* allocate memory for the new facet normals */
1014 model
->numfacetnorms
= model
->numtriangles
;
1015 model
->facetnorms
= (float*)malloc(sizeof(float) *
1016 3 * (model
->numfacetnorms
+ 1));
1018 for (i
= 0; i
< model
->numtriangles
; i
++) {
1019 model
->triangles
[i
].findex
= i
+1;
1021 u
[X
] = model
->vertices
[3 * T(i
).vindices
[1] + X
] -
1022 model
->vertices
[3 * T(i
).vindices
[0] + X
];
1023 u
[Y
] = model
->vertices
[3 * T(i
).vindices
[1] + Y
] -
1024 model
->vertices
[3 * T(i
).vindices
[0] + Y
];
1025 u
[Z
] = model
->vertices
[3 * T(i
).vindices
[1] + Z
] -
1026 model
->vertices
[3 * T(i
).vindices
[0] + Z
];
1028 v
[X
] = model
->vertices
[3 * T(i
).vindices
[2] + X
] -
1029 model
->vertices
[3 * T(i
).vindices
[0] + X
];
1030 v
[Y
] = model
->vertices
[3 * T(i
).vindices
[2] + Y
] -
1031 model
->vertices
[3 * T(i
).vindices
[0] + Y
];
1032 v
[Z
] = model
->vertices
[3 * T(i
).vindices
[2] + Z
] -
1033 model
->vertices
[3 * T(i
).vindices
[0] + Z
];
1035 _glmCross(u
, v
, &model
->facetnorms
[3 * (i
+1)]);
1036 _glmNormalize(&model
->facetnorms
[3 * (i
+1)]);
1040 /* glmVertexNormals: Generates smooth vertex normals for a model.
1041 * First builds a list of all the triangles each vertex is in. Then
1042 * loops through each vertex in the the list averaging all the facet
1043 * normals of the triangles each vertex is in. Finally, sets the
1044 * normal index in the triangle for the vertex to the generated smooth
1045 * normal. If the dot product of a facet normal and the facet normal
1046 * associated with the first triangle in the list of triangles the
1047 * current vertex is in is greater than the cosine of the angle
1048 * parameter to the function, that facet normal is not added into the
1049 * average normal calculation and the corresponding vertex is given
1050 * the facet normal. This tends to preserve hard edges. The angle to
1051 * use depends on the model, but 90 degrees is usually a good start.
1053 * model - initialized GLMmodel structure
1054 * angle - maximum angle (in degrees) to smooth across
1057 glmVertexNormals(GLMmodel
* model
, float angle
)
1065 float dot
, cos_angle
;
1069 assert(model
->facetnorms
);
1071 /* calculate the cosine of the angle (in degrees) */
1072 cos_angle
= cos(angle
* M_PI
/ 180.0);
1074 /* nuke any previous normals */
1076 free(model
->normals
);
1078 /* allocate space for new normals */
1079 model
->numnormals
= model
->numtriangles
* 3; /* 3 normals per triangle */
1080 model
->normals
= (float*)malloc(sizeof(float)* 3* (model
->numnormals
+1));
1082 /* allocate a structure that will hold a linked list of triangle
1083 indices for each vertex */
1084 members
= (GLMnode
**)malloc(sizeof(GLMnode
*) * (model
->numvertices
+ 1));
1085 for (i
= 1; i
<= model
->numvertices
; i
++)
1088 /* for every triangle, create a node for each vertex in it */
1089 for (i
= 0; i
< model
->numtriangles
; i
++) {
1090 node
= (GLMnode
*)malloc(sizeof(GLMnode
));
1092 node
->next
= members
[T(i
).vindices
[0]];
1093 members
[T(i
).vindices
[0]] = node
;
1095 node
= (GLMnode
*)malloc(sizeof(GLMnode
));
1097 node
->next
= members
[T(i
).vindices
[1]];
1098 members
[T(i
).vindices
[1]] = node
;
1100 node
= (GLMnode
*)malloc(sizeof(GLMnode
));
1102 node
->next
= members
[T(i
).vindices
[2]];
1103 members
[T(i
).vindices
[2]] = node
;
1106 /* calculate the average normal for each vertex */
1108 for (i
= 1; i
<= model
->numvertices
; i
++) {
1109 /* calculate an average normal for this vertex by averaging the
1110 facet normal of every triangle this vertex is in */
1113 fprintf(stderr
, "glmVertexNormals(): vertex w/o a triangle\n");
1114 average
[0] = 0.0; average
[1] = 0.0; average
[2] = 0.0;
1117 /* only average if the dot product of the angle between the two
1118 facet normals is greater than the cosine of the threshold
1119 angle -- or, said another way, the angle between the two
1120 facet normals is less than (or equal to) the threshold angle */
1121 dot
= _glmDot(&model
->facetnorms
[3 * T(node
->index
).findex
],
1122 &model
->facetnorms
[3 * T(members
[i
]->index
).findex
]);
1123 if (dot
> cos_angle
) {
1124 node
->averaged
= TRUE
;
1125 average
[0] += model
->facetnorms
[3 * T(node
->index
).findex
+ 0];
1126 average
[1] += model
->facetnorms
[3 * T(node
->index
).findex
+ 1];
1127 average
[2] += model
->facetnorms
[3 * T(node
->index
).findex
+ 2];
1128 avg
= 1; /* we averaged at least one normal! */
1130 node
->averaged
= FALSE
;
1136 /* normalize the averaged normal */
1137 _glmNormalize(average
);
1139 /* add the normal to the vertex normals list */
1140 model
->normals
[3 * numnormals
+ 0] = average
[0];
1141 model
->normals
[3 * numnormals
+ 1] = average
[1];
1142 model
->normals
[3 * numnormals
+ 2] = average
[2];
1147 /* set the normal of this vertex in each triangle it is in */
1150 if (node
->averaged
) {
1151 /* if this node was averaged, use the average normal */
1152 if (T(node
->index
).vindices
[0] == i
)
1153 T(node
->index
).nindices
[0] = avg
;
1154 else if (T(node
->index
).vindices
[1] == i
)
1155 T(node
->index
).nindices
[1] = avg
;
1156 else if (T(node
->index
).vindices
[2] == i
)
1157 T(node
->index
).nindices
[2] = avg
;
1159 /* if this node wasn't averaged, use the facet normal */
1160 model
->normals
[3 * numnormals
+ 0] =
1161 model
->facetnorms
[3 * T(node
->index
).findex
+ 0];
1162 model
->normals
[3 * numnormals
+ 1] =
1163 model
->facetnorms
[3 * T(node
->index
).findex
+ 1];
1164 model
->normals
[3 * numnormals
+ 2] =
1165 model
->facetnorms
[3 * T(node
->index
).findex
+ 2];
1166 if (T(node
->index
).vindices
[0] == i
)
1167 T(node
->index
).nindices
[0] = numnormals
;
1168 else if (T(node
->index
).vindices
[1] == i
)
1169 T(node
->index
).nindices
[1] = numnormals
;
1170 else if (T(node
->index
).vindices
[2] == i
)
1171 T(node
->index
).nindices
[2] = numnormals
;
1178 model
->numnormals
= numnormals
- 1;
1180 /* free the member information */
1181 for (i
= 1; i
<= model
->numvertices
; i
++) {
1191 /* pack the normals array (we previously allocated the maximum
1192 number of normals that could possibly be created (numtriangles *
1193 3), so get rid of some of them (usually alot unless none of the
1194 facet normals were averaged)) */
1195 normals
= model
->normals
;
1196 model
->normals
= (float*)malloc(sizeof(float)* 3* (model
->numnormals
+1));
1197 for (i
= 1; i
<= model
->numnormals
; i
++) {
1198 model
->normals
[3 * i
+ 0] = normals
[3 * i
+ 0];
1199 model
->normals
[3 * i
+ 1] = normals
[3 * i
+ 1];
1200 model
->normals
[3 * i
+ 2] = normals
[3 * i
+ 2];
1204 printf("glmVertexNormals(): %d normals generated\n", model
->numnormals
);
1208 /* glmLinearTexture: Generates texture coordinates according to a
1209 * linear projection of the texture map. It generates these by
1210 * linearly mapping the vertices onto a square.
1212 * model - pointer to initialized GLMmodel structure
1215 glmLinearTexture(GLMmodel
* model
)
1218 float dimensions
[3];
1219 float x
, y
, scalefactor
;
1224 if (model
->texcoords
)
1225 free(model
->texcoords
);
1226 model
->numtexcoords
= model
->numvertices
;
1227 model
->texcoords
=(float*)malloc(sizeof(float)*2*(model
->numtexcoords
+1));
1229 glmDimensions(model
, dimensions
);
1231 _glmAbs(_glmMax(_glmMax(dimensions
[0], dimensions
[1]), dimensions
[2]));
1233 /* do the calculations */
1234 for(i
= 1; i
<= model
->numvertices
; i
++) {
1235 x
= model
->vertices
[3 * i
+ 0] * scalefactor
;
1236 y
= model
->vertices
[3 * i
+ 2] * scalefactor
;
1237 model
->texcoords
[2 * i
+ 0] = (x
+ 1.0) / 2.0;
1238 model
->texcoords
[2 * i
+ 1] = (y
+ 1.0) / 2.0;
1241 /* go through and put texture coordinate indices in all the triangles */
1242 group
= model
->groups
;
1244 for(i
= 0; i
< group
->numtriangles
; i
++) {
1245 T(group
->triangles
[i
]).tindices
[0] = T(group
->triangles
[i
]).vindices
[0];
1246 T(group
->triangles
[i
]).tindices
[1] = T(group
->triangles
[i
]).vindices
[1];
1247 T(group
->triangles
[i
]).tindices
[2] = T(group
->triangles
[i
]).vindices
[2];
1249 group
= group
->next
;
1253 printf("glmLinearTexture(): generated %d linear texture coordinates\n",
1254 model
->numtexcoords
);
1258 /* glmSpheremapTexture: Generates texture coordinates according to a
1259 * spherical projection of the texture map. Sometimes referred to as
1260 * spheremap, or reflection map texture coordinates. It generates
1261 * these by using the normal to calculate where that vertex would map
1262 * onto a sphere. Since it is impossible to map something flat
1263 * perfectly onto something spherical, there is distortion at the
1264 * poles. This particular implementation causes the poles along the X
1265 * axis to be distorted.
1267 * model - pointer to initialized GLMmodel structure
1270 glmSpheremapTexture(GLMmodel
* model
)
1273 float theta
, phi
, rho
, x
, y
, z
, r
;
1277 assert(model
->normals
);
1279 if (model
->texcoords
)
1280 free(model
->texcoords
);
1281 model
->numtexcoords
= model
->numnormals
;
1282 model
->texcoords
=(float*)malloc(sizeof(float)*2*(model
->numtexcoords
+1));
1284 /* do the calculations */
1285 for (i
= 1; i
<= model
->numnormals
; i
++) {
1286 z
= model
->normals
[3 * i
+ 0]; /* re-arrange for pole distortion */
1287 y
= model
->normals
[3 * i
+ 1];
1288 x
= model
->normals
[3 * i
+ 2];
1289 r
= sqrt((x
* x
) + (y
* y
));
1290 rho
= sqrt((r
* r
) + (z
* z
));
1299 phi
= acos(z
/ rho
);
1301 #if WE_DONT_NEED_THIS_CODE
1303 theta
= M_PI
/ 2.0; /* asin(y / r); */
1305 theta
= acos(x
/ r
);
1309 theta
= M_PI
/ 2.0; /* acos(x / r); */
1311 theta
= asin(y
/ r
) + (M_PI
/ 2.0);
1314 model
->texcoords
[2 * i
+ 0] = theta
/ M_PI
;
1315 model
->texcoords
[2 * i
+ 1] = phi
/ M_PI
;
1318 /* go through and put texcoord indices in all the triangles */
1319 group
= model
->groups
;
1321 for (i
= 0; i
< group
->numtriangles
; i
++) {
1322 T(group
->triangles
[i
]).tindices
[0] = T(group
->triangles
[i
]).nindices
[0];
1323 T(group
->triangles
[i
]).tindices
[1] = T(group
->triangles
[i
]).nindices
[1];
1324 T(group
->triangles
[i
]).tindices
[2] = T(group
->triangles
[i
]).nindices
[2];
1326 group
= group
->next
;
1330 printf("glmSpheremapTexture(): generated %d spheremap texture coordinates\n",
1331 model
->numtexcoords
);
1335 /* glmDelete: Deletes a GLMmodel structure.
1337 * model - initialized GLMmodel structure
1340 glmDelete(GLMmodel
* model
)
1347 if (model
->pathname
) free(model
->pathname
);
1348 if (model
->mtllibname
) free(model
->mtllibname
);
1349 if (model
->vertices
) free(model
->vertices
);
1350 if (model
->normals
) free(model
->normals
);
1351 if (model
->texcoords
) free(model
->texcoords
);
1352 if (model
->facetnorms
) free(model
->facetnorms
);
1353 if (model
->triangles
) free(model
->triangles
);
1354 if (model
->materials
) {
1355 for (i
= 0; i
< model
->nummaterials
; i
++)
1356 free(model
->materials
[i
].name
);
1358 free(model
->materials
);
1359 while(model
->groups
) {
1360 group
= model
->groups
;
1361 model
->groups
= model
->groups
->next
;
1363 free(group
->triangles
);
1370 static GLMmaterial
*
1371 glmDefaultMaterial(void)
1373 GLMmaterial
*m
= (GLMmaterial
*) calloc(1, sizeof(GLMmaterial
));
1375 m
->diffuse
[0] = 0.75;
1376 m
->diffuse
[1] = 0.75;
1377 m
->diffuse
[2] = 0.75;
1378 m
->diffuse
[3] = 1.0;
1380 m
->specular
[0] = 1.0;
1381 m
->specular
[1] = 1.0;
1382 m
->specular
[2] = 1.0;
1383 m
->specular
[3] = 1.0;
1391 /* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
1392 * Returns a pointer to the created object which should be free'd with
1395 * filename - name of the file containing the Wavefront .OBJ format data.
1398 glmReadOBJ(char* filename
)
1404 file
= fopen(filename
, "r");
1406 fprintf(stderr
, "glmReadOBJ() failed: can't open data file \"%s\".\n",
1412 /* announce the model name */
1413 printf("Model: %s\n", filename
);
1416 /* allocate a new model */
1417 model
= (GLMmodel
*)malloc(sizeof(GLMmodel
));
1418 model
->pathname
= stralloc(filename
);
1419 model
->mtllibname
= NULL
;
1420 model
->numvertices
= 0;
1421 model
->vertices
= NULL
;
1422 model
->numnormals
= 0;
1423 model
->normals
= NULL
;
1424 model
->numtexcoords
= 0;
1425 model
->texcoords
= NULL
;
1426 model
->numfacetnorms
= 0;
1427 model
->facetnorms
= NULL
;
1428 model
->numtriangles
= 0;
1429 model
->triangles
= NULL
;
1430 model
->nummaterials
= 0;
1431 model
->materials
= NULL
;
1432 model
->numgroups
= 0;
1433 model
->groups
= NULL
;
1434 model
->position
[0] = 0.0;
1435 model
->position
[1] = 0.0;
1436 model
->position
[2] = 0.0;
1439 /* make a first pass through the file to get a count of the number
1440 of vertices, normals, texcoords & triangles */
1441 _glmFirstPass(model
, file
);
1443 /* allocate memory */
1444 model
->vertices
= (float*)malloc(sizeof(float) *
1445 3 * (model
->numvertices
+ 1));
1446 model
->triangles
= (GLMtriangle
*)malloc(sizeof(GLMtriangle
) *
1447 model
->numtriangles
);
1448 if (model
->numnormals
) {
1449 model
->normals
= (float*)malloc(sizeof(float) *
1450 3 * (model
->numnormals
+ 1));
1452 if (model
->numtexcoords
) {
1453 model
->texcoords
= (float*)malloc(sizeof(float) *
1454 2 * (model
->numtexcoords
+ 1));
1457 /* rewind to beginning of file and read in the data this pass */
1460 _glmSecondPass(model
, file
);
1462 /* close the file */
1465 if (!model
->materials
) {
1466 model
->materials
= glmDefaultMaterial();
1467 model
->nummaterials
= 1;
1473 /* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
1476 * model - initialized GLMmodel structure
1477 * filename - name of the file to write the Wavefront .OBJ format data to
1478 * mode - a bitwise or of values describing what is written to the file
1479 * GLM_NONE - render with only vertices
1480 * GLM_FLAT - render with facet normals
1481 * GLM_SMOOTH - render with vertex normals
1482 * GLM_TEXTURE - render with texture coords
1483 * GLM_COLOR - render with colors (color material)
1484 * GLM_MATERIAL - render with materials
1485 * GLM_COLOR and GLM_MATERIAL should not both be specified.
1486 * GLM_FLAT and GLM_SMOOTH should not both be specified.
1489 glmWriteOBJ(GLMmodel
* model
, char* filename
, uint mode
)
1497 /* do a bit of warning */
1498 if (mode
& GLM_FLAT
&& !model
->facetnorms
) {
1499 printf("glmWriteOBJ() warning: flat normal output requested "
1500 "with no facet normals defined.\n");
1503 if (mode
& GLM_SMOOTH
&& !model
->normals
) {
1504 printf("glmWriteOBJ() warning: smooth normal output requested "
1505 "with no normals defined.\n");
1506 mode
&= ~GLM_SMOOTH
;
1508 if (mode
& GLM_TEXTURE
&& !model
->texcoords
) {
1509 printf("glmWriteOBJ() warning: texture coordinate output requested "
1510 "with no texture coordinates defined.\n");
1511 mode
&= ~GLM_TEXTURE
;
1513 if (mode
& GLM_FLAT
&& mode
& GLM_SMOOTH
) {
1514 printf("glmWriteOBJ() warning: flat normal output requested "
1515 "and smooth normal output requested (using smooth).\n");
1520 file
= fopen(filename
, "w");
1522 fprintf(stderr
, "glmWriteOBJ() failed: can't open file \"%s\" to write.\n",
1527 /* spit out a header */
1528 fprintf(file
, "# \n");
1529 fprintf(file
, "# Wavefront OBJ generated by GLM library\n");
1530 fprintf(file
, "# \n");
1531 fprintf(file
, "# GLM library copyright (C) 1997 by Nate Robins\n");
1532 fprintf(file
, "# email: ndr@pobox.com\n");
1533 fprintf(file
, "# www: http://www.pobox.com/~ndr\n");
1534 fprintf(file
, "# \n");
1536 if (mode
& GLM_MATERIAL
&& model
->mtllibname
) {
1537 fprintf(file
, "\nmtllib %s\n\n", model
->mtllibname
);
1538 _glmWriteMTL(model
, filename
, model
->mtllibname
);
1541 /* spit out the vertices */
1542 fprintf(file
, "\n");
1543 fprintf(file
, "# %d vertices\n", model
->numvertices
);
1544 for (i
= 1; i
<= model
->numvertices
; i
++) {
1545 fprintf(file
, "v %f %f %f\n",
1546 model
->vertices
[3 * i
+ 0],
1547 model
->vertices
[3 * i
+ 1],
1548 model
->vertices
[3 * i
+ 2]);
1551 /* spit out the smooth/flat normals */
1552 if (mode
& GLM_SMOOTH
) {
1553 fprintf(file
, "\n");
1554 fprintf(file
, "# %d normals\n", model
->numnormals
);
1555 for (i
= 1; i
<= model
->numnormals
; i
++) {
1556 fprintf(file
, "vn %f %f %f\n",
1557 model
->normals
[3 * i
+ 0],
1558 model
->normals
[3 * i
+ 1],
1559 model
->normals
[3 * i
+ 2]);
1561 } else if (mode
& GLM_FLAT
) {
1562 fprintf(file
, "\n");
1563 fprintf(file
, "# %d normals\n", model
->numfacetnorms
);
1564 for (i
= 1; i
<= model
->numnormals
; i
++) {
1565 fprintf(file
, "vn %f %f %f\n",
1566 model
->facetnorms
[3 * i
+ 0],
1567 model
->facetnorms
[3 * i
+ 1],
1568 model
->facetnorms
[3 * i
+ 2]);
1572 /* spit out the texture coordinates */
1573 if (mode
& GLM_TEXTURE
) {
1574 fprintf(file
, "\n");
1575 fprintf(file
, "# %d texcoords\n", model
->numtexcoords
);
1576 for (i
= 1; i
<= model
->numtexcoords
; i
++) {
1577 fprintf(file
, "vt %f %f\n",
1578 model
->texcoords
[2 * i
+ 0],
1579 model
->texcoords
[2 * i
+ 1]);
1583 fprintf(file
, "\n");
1584 fprintf(file
, "# %d groups\n", model
->numgroups
);
1585 fprintf(file
, "# %d faces (triangles)\n", model
->numtriangles
);
1586 fprintf(file
, "\n");
1588 group
= model
->groups
;
1590 fprintf(file
, "g %s\n", group
->name
);
1591 if (mode
& GLM_MATERIAL
)
1592 fprintf(file
, "usemtl %s\n", model
->materials
[group
->material
].name
);
1593 for (i
= 0; i
< group
->numtriangles
; i
++) {
1594 if (mode
& GLM_SMOOTH
&& mode
& GLM_TEXTURE
) {
1595 fprintf(file
, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
1596 T(group
->triangles
[i
]).vindices
[0],
1597 T(group
->triangles
[i
]).nindices
[0],
1598 T(group
->triangles
[i
]).tindices
[0],
1599 T(group
->triangles
[i
]).vindices
[1],
1600 T(group
->triangles
[i
]).nindices
[1],
1601 T(group
->triangles
[i
]).tindices
[1],
1602 T(group
->triangles
[i
]).vindices
[2],
1603 T(group
->triangles
[i
]).nindices
[2],
1604 T(group
->triangles
[i
]).tindices
[2]);
1605 } else if (mode
& GLM_FLAT
&& mode
& GLM_TEXTURE
) {
1606 fprintf(file
, "f %d/%d %d/%d %d/%d\n",
1607 T(group
->triangles
[i
]).vindices
[0],
1608 T(group
->triangles
[i
]).findex
,
1609 T(group
->triangles
[i
]).vindices
[1],
1610 T(group
->triangles
[i
]).findex
,
1611 T(group
->triangles
[i
]).vindices
[2],
1612 T(group
->triangles
[i
]).findex
);
1613 } else if (mode
& GLM_TEXTURE
) {
1614 fprintf(file
, "f %d/%d %d/%d %d/%d\n",
1615 T(group
->triangles
[i
]).vindices
[0],
1616 T(group
->triangles
[i
]).tindices
[0],
1617 T(group
->triangles
[i
]).vindices
[1],
1618 T(group
->triangles
[i
]).tindices
[1],
1619 T(group
->triangles
[i
]).vindices
[2],
1620 T(group
->triangles
[i
]).tindices
[2]);
1621 } else if (mode
& GLM_SMOOTH
) {
1622 fprintf(file
, "f %d//%d %d//%d %d//%d\n",
1623 T(group
->triangles
[i
]).vindices
[0],
1624 T(group
->triangles
[i
]).nindices
[0],
1625 T(group
->triangles
[i
]).vindices
[1],
1626 T(group
->triangles
[i
]).nindices
[1],
1627 T(group
->triangles
[i
]).vindices
[2],
1628 T(group
->triangles
[i
]).nindices
[2]);
1629 } else if (mode
& GLM_FLAT
) {
1630 fprintf(file
, "f %d//%d %d//%d %d//%d\n",
1631 T(group
->triangles
[i
]).vindices
[0],
1632 T(group
->triangles
[i
]).findex
,
1633 T(group
->triangles
[i
]).vindices
[1],
1634 T(group
->triangles
[i
]).findex
,
1635 T(group
->triangles
[i
]).vindices
[2],
1636 T(group
->triangles
[i
]).findex
);
1638 fprintf(file
, "f %d %d %d\n",
1639 T(group
->triangles
[i
]).vindices
[0],
1640 T(group
->triangles
[i
]).vindices
[1],
1641 T(group
->triangles
[i
]).vindices
[2]);
1644 fprintf(file
, "\n");
1645 group
= group
->next
;
1651 /* glmWeld: eliminate (weld) vectors that are within an epsilon of
1654 * model - initialized GLMmodel structure
1655 * epsilon - maximum difference between vertices
1656 * ( 0.00001 is a good start for a unitized model)
1660 glmWeld(GLMmodel
* model
, float epsilon
)
1668 numvectors
= model
->numvertices
;
1669 vectors
= model
->vertices
;
1670 copies
= _glmWeldVectors(vectors
, &numvectors
, epsilon
);
1672 printf("glmWeld(): %d redundant vertices.\n",
1673 model
->numvertices
- numvectors
- 1);
1675 for (i
= 0; i
< model
->numtriangles
; i
++) {
1676 T(i
).vindices
[0] = (uint
)vectors
[3 * T(i
).vindices
[0] + 0];
1677 T(i
).vindices
[1] = (uint
)vectors
[3 * T(i
).vindices
[1] + 0];
1678 T(i
).vindices
[2] = (uint
)vectors
[3 * T(i
).vindices
[2] + 0];
1681 /* free space for old vertices */
1684 /* allocate space for the new vertices */
1685 model
->numvertices
= numvectors
;
1686 model
->vertices
= (float*)malloc(sizeof(float) *
1687 3 * (model
->numvertices
+ 1));
1689 /* copy the optimized vertices into the actual vertex list */
1690 for (i
= 1; i
<= model
->numvertices
; i
++) {
1691 model
->vertices
[3 * i
+ 0] = copies
[3 * i
+ 0];
1692 model
->vertices
[3 * i
+ 1] = copies
[3 * i
+ 1];
1693 model
->vertices
[3 * i
+ 2] = copies
[3 * i
+ 2];
1701 glmReIndex(GLMmodel
*model
)
1705 float *newNormals
= NULL
;
1706 float *newTexcoords
= NULL
;
1707 const uint numv
= model
->numvertices
;
1709 if (model
->numnormals
> 0)
1710 newNormals
= (float *) malloc((numv
+ 1) * 3 * sizeof(float));
1712 if (model
->numtexcoords
> 0)
1713 newTexcoords
= (float *) malloc((numv
+ 1) * 2 * sizeof(float));
1715 for (group
= model
->groups
; group
; group
= group
->next
) {
1717 n
= group
->numtriangles
;
1719 group
->triIndexes
= (uint
*) malloc(n
* 3 * sizeof(uint
));
1721 group
->minIndex
= 10000000;
1722 group
->maxIndex
= 0;
1724 for (i
= 0; i
< n
; i
++) {
1726 for (j
= 0; j
< 3; j
++) {
1727 uint vindex
= T(group
->triangles
[i
]).vindices
[j
];
1728 uint nindex
= T(group
->triangles
[i
]).nindices
[j
];
1729 uint tindex
= T(group
->triangles
[i
]).tindices
[j
];
1731 float *nrm
= &model
->normals
[nindex
* 3];
1732 float *tex
= &model
->texcoords
[tindex
* 2];
1735 assert(vindex
* 3 + 2 < (numv
+ 1) * 3);
1736 newNormals
[vindex
* 3 + 0] = nrm
[0];
1737 newNormals
[vindex
* 3 + 1] = nrm
[1];
1738 newNormals
[vindex
* 3 + 2] = nrm
[2];
1741 newTexcoords
[vindex
* 2 + 0] = tex
[0];
1742 newTexcoords
[vindex
* 2 + 1] = tex
[1];
1745 T(group
->triangles
[i
]).nindices
[j
] = vindex
;
1746 T(group
->triangles
[i
]).tindices
[j
] = vindex
;
1748 group
->triIndexes
[i
* 3 + j
] = vindex
;
1750 if (vindex
> group
->maxIndex
)
1751 group
->maxIndex
= vindex
;
1752 if (vindex
< group
->minIndex
)
1753 group
->minIndex
= vindex
;
1759 free(model
->normals
);
1760 model
->normals
= newNormals
;
1761 model
->numnormals
= model
->numvertices
;
1765 free(model
->texcoords
);
1766 model
->texcoords
= newTexcoords
;
1767 model
->numtexcoords
= model
->numvertices
;
1774 glmPrint(const GLMmodel
*model
)
1782 printf("%u vertices\n", model
->numvertices
);
1783 printf("%u normals\n", model
->numnormals
);
1784 printf("%u texcoords\n", model
->numtexcoords
);
1786 for (group
= model
->groups
; group
; group
= group
->next
, grp
++) {
1787 printf("Group %u:\n", grp
);
1788 printf(" Min index %u, max index %u\n", group
->minIndex
, group
->maxIndex
);
1791 if (mode
& GLM_MATERIAL
) {
1792 glMaterialfv(GL_FRONT_AND_BACK
, GL_AMBIENT
,
1793 model
->materials
[group
->material
].ambient
);
1794 glMaterialfv(GL_FRONT_AND_BACK
, GL_DIFFUSE
,
1795 model
->materials
[group
->material
].diffuse
);
1796 glMaterialfv(GL_FRONT_AND_BACK
, GL_SPECULAR
,
1797 model
->materials
[group
->material
].specular
);
1798 glMaterialf(GL_FRONT_AND_BACK
, GL_SHININESS
,
1799 model
->materials
[group
->material
].shininess
);
1802 if (mode
& GLM_COLOR
) {
1803 glColor3fv(model
->materials
[group
->material
].diffuse
);
1806 totalTris
+= group
->numtriangles
;
1808 printf(" %u triangles\n", group
->numtriangles
);
1809 n
= group
->numtriangles
;
1813 for (i
= 0; i
< n
; i
++) {
1815 printf(" %u: vert ", i
);
1816 for (j
= 0; j
< 3; j
++) {
1817 printf("%u ", T(group
->triangles
[i
]).vindices
[j
]);
1821 for (j
= 0; j
< 3; j
++) {
1822 printf("%u ", T(group
->triangles
[i
]).nindices
[j
]);
1826 for (j
= 0; j
< 3; j
++) {
1827 printf("%u ", T(group
->triangles
[i
]).tindices
[j
]);
1833 printf("Total tris: %u\n", totalTris
);
1840 if (model
->numnormals
) {
1841 numvectors
= model
->numnormals
;
1842 vectors
= model
->normals
;
1843 copies
= _glmOptimizeVectors(vectors
, &numvectors
);
1845 printf("glmOptimize(): %d redundant normals.\n",
1846 model
->numnormals
- numvectors
);
1848 for (i
= 0; i
< model
->numtriangles
; i
++) {
1849 T(i
).nindices
[0] = (uint
)vectors
[3 * T(i
).nindices
[0] + 0];
1850 T(i
).nindices
[1] = (uint
)vectors
[3 * T(i
).nindices
[1] + 0];
1851 T(i
).nindices
[2] = (uint
)vectors
[3 * T(i
).nindices
[2] + 0];
1854 /* free space for old normals */
1857 /* allocate space for the new normals */
1858 model
->numnormals
= numvectors
;
1859 model
->normals
= (float*)malloc(sizeof(float) *
1860 3 * (model
->numnormals
+ 1));
1862 /* copy the optimized vertices into the actual vertex list */
1863 for (i
= 1; i
<= model
->numnormals
; i
++) {
1864 model
->normals
[3 * i
+ 0] = copies
[3 * i
+ 0];
1865 model
->normals
[3 * i
+ 1] = copies
[3 * i
+ 1];
1866 model
->normals
[3 * i
+ 2] = copies
[3 * i
+ 2];
1873 if (model
->numtexcoords
) {
1874 numvectors
= model
->numtexcoords
;
1875 vectors
= model
->texcoords
;
1876 copies
= _glmOptimizeVectors(vectors
, &numvectors
);
1878 printf("glmOptimize(): %d redundant texcoords.\n",
1879 model
->numtexcoords
- numvectors
);
1881 for (i
= 0; i
< model
->numtriangles
; i
++) {
1882 for (j
= 0; j
< 3; j
++) {
1883 T(i
).tindices
[j
] = (uint
)vectors
[3 * T(i
).tindices
[j
] + 0];
1887 /* free space for old texcoords */
1890 /* allocate space for the new texcoords */
1891 model
->numtexcoords
= numvectors
;
1892 model
->texcoords
= (float*)malloc(sizeof(float) *
1893 2 * (model
->numtexcoords
+ 1));
1895 /* copy the optimized vertices into the actual vertex list */
1896 for (i
= 1; i
<= model
->numtexcoords
; i
++) {
1897 model
->texcoords
[2 * i
+ 0] = copies
[2 * i
+ 0];
1898 model
->texcoords
[2 * i
+ 1] = copies
[2 * i
+ 1];
1906 /* look for unused vertices */
1907 /* look for unused normals */
1908 /* look for unused texcoords */
1909 for (i
= 1; i
<= model
->numvertices
; i
++) {
1910 for (j
= 0; j
< model
->numtriangles
; i
++) {
1911 if (T(j
).vindices
[0] == i
||
1912 T(j
).vindices
[1] == i
||
1913 T(j
).vindices
[1] == i
)