comparison plugins/crazychat/glm.c @ 11218:ed017b9c532d

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