| 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 |
|