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