plugins/crazychat/glm.c

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

mercurial