| /* |
| * GLM library. Wavefront .obj file format reader/writer/manipulator. |
| * |
| * Written by Nate Robins, 1997. |
| * email: ndr@pobox.com |
| * www: http://www.pobox.com/~ndr |
| */ |
| |
| /* includes */ |
| #include <math.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <assert.h> |
| #include "glm.h" |
| #include "readtex.h" |
| |
| |
| typedef unsigned char boolean; |
| #define TRUE 1 |
| #define FALSE 0 |
| |
| |
| /* Some <math.h> files do not define M_PI... */ |
| #ifndef M_PI |
| #define M_PI 3.14159265358979323846 |
| #endif |
| |
| /* defines */ |
| #define T(x) model->triangles[(x)] |
| |
| |
| /* enums */ |
| enum { X, Y, Z, W }; /* elements of a vertex */ |
| |
| |
| /* typedefs */ |
| |
| /* _GLMnode: general purpose node |
| */ |
| typedef struct _GLMnode { |
| uint index; |
| boolean averaged; |
| struct _GLMnode* next; |
| } GLMnode; |
| |
| /* strdup is actually not a standard ANSI C or POSIX routine |
| so implement a private one. OpenVMS does not have a strdup; Linux's |
| standard libc doesn't declare strdup by default (unless BSD or SVID |
| interfaces are requested). */ |
| static char * |
| stralloc(const char *string) |
| { |
| char *copy; |
| |
| copy = malloc(strlen(string) + 1); |
| if (copy == NULL) |
| return NULL; |
| strcpy(copy, string); |
| return copy; |
| } |
| |
| /* private functions */ |
| |
| /* _glmMax: returns the maximum of two floats */ |
| static float |
| _glmMax(float a, float b) |
| { |
| if (a > b) |
| return a; |
| return b; |
| } |
| |
| /* _glmAbs: returns the absolute value of a float */ |
| static float |
| _glmAbs(float f) |
| { |
| if (f < 0) |
| return -f; |
| return f; |
| } |
| |
| /* _glmDot: compute the dot product of two vectors |
| * |
| * u - array of 3 floats (float u[3]) |
| * v - array of 3 floats (float v[3]) |
| */ |
| static float |
| _glmDot(float* u, float* v) |
| { |
| assert(u); |
| assert(v); |
| |
| /* compute the dot product */ |
| return u[X] * v[X] + u[Y] * v[Y] + u[Z] * v[Z]; |
| } |
| |
| /* _glmCross: compute the cross product of two vectors |
| * |
| * u - array of 3 floats (float u[3]) |
| * v - array of 3 floats (float v[3]) |
| * n - array of 3 floats (float n[3]) to return the cross product in |
| */ |
| static void |
| _glmCross(float* u, float* v, float* n) |
| { |
| assert(u); |
| assert(v); |
| assert(n); |
| |
| /* compute the cross product (u x v for right-handed [ccw]) */ |
| n[X] = u[Y] * v[Z] - u[Z] * v[Y]; |
| n[Y] = u[Z] * v[X] - u[X] * v[Z]; |
| n[Z] = u[X] * v[Y] - u[Y] * v[X]; |
| } |
| |
| /* _glmNormalize: normalize a vector |
| * |
| * n - array of 3 floats (float n[3]) to be normalized |
| */ |
| static void |
| _glmNormalize(float* n) |
| { |
| float l; |
| |
| assert(n); |
| |
| /* normalize */ |
| l = (float)sqrt(n[X] * n[X] + n[Y] * n[Y] + n[Z] * n[Z]); |
| n[0] /= l; |
| n[1] /= l; |
| n[2] /= l; |
| } |
| |
| /* _glmEqual: compares two vectors and returns TRUE if they are |
| * equal (within a certain threshold) or FALSE if not. An epsilon |
| * that works fairly well is 0.000001. |
| * |
| * u - array of 3 floats (float u[3]) |
| * v - array of 3 floats (float v[3]) |
| */ |
| static boolean |
| _glmEqual(float* u, float* v, float epsilon) |
| { |
| if (_glmAbs(u[0] - v[0]) < epsilon && |
| _glmAbs(u[1] - v[1]) < epsilon && |
| _glmAbs(u[2] - v[2]) < epsilon) |
| { |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| /* _glmWeldVectors: eliminate (weld) vectors that are within an |
| * epsilon of each other. |
| * |
| * vectors - array of float[3]'s to be welded |
| * numvectors - number of float[3]'s in vectors |
| * epsilon - maximum difference between vectors |
| * |
| */ |
| static float* |
| _glmWeldVectors(float* vectors, uint* numvectors, float epsilon) |
| { |
| float* copies; |
| uint copied; |
| uint i, j; |
| |
| copies = (float*)malloc(sizeof(float) * 3 * (*numvectors + 1)); |
| memcpy(copies, vectors, (sizeof(float) * 3 * (*numvectors + 1))); |
| |
| copied = 1; |
| for (i = 1; i <= *numvectors; i++) { |
| for (j = 1; j <= copied; j++) { |
| if (_glmEqual(&vectors[3 * i], &copies[3 * j], epsilon)) { |
| goto duplicate; |
| } |
| } |
| |
| /* must not be any duplicates -- add to the copies array */ |
| copies[3 * copied + 0] = vectors[3 * i + 0]; |
| copies[3 * copied + 1] = vectors[3 * i + 1]; |
| copies[3 * copied + 2] = vectors[3 * i + 2]; |
| j = copied; /* pass this along for below */ |
| copied++; |
| |
| duplicate: |
| /* set the first component of this vector to point at the correct |
| index into the new copies array */ |
| vectors[3 * i + 0] = (float)j; |
| } |
| |
| *numvectors = copied-1; |
| return copies; |
| } |
| |
| /* _glmFindGroup: Find a group in the model |
| */ |
| static GLMgroup* |
| _glmFindGroup(GLMmodel* model, char* name) |
| { |
| GLMgroup* group; |
| |
| assert(model); |
| |
| group = model->groups; |
| while(group) { |
| if (!strcmp(name, group->name)) |
| break; |
| group = group->next; |
| } |
| |
| return group; |
| } |
| |
| /* _glmAddGroup: Add a group to the model |
| */ |
| static GLMgroup* |
| _glmAddGroup(GLMmodel* model, char* name) |
| { |
| GLMgroup* group; |
| |
| group = _glmFindGroup(model, name); |
| if (!group) { |
| group = (GLMgroup*)malloc(sizeof(GLMgroup)); |
| group->name = stralloc(name); |
| group->material = 0; |
| group->numtriangles = 0; |
| group->triangles = NULL; |
| group->next = model->groups; |
| model->groups = group; |
| model->numgroups++; |
| } |
| |
| return group; |
| } |
| |
| /* _glmFindGroup: Find a material in the model |
| */ |
| static uint |
| _glmFindMaterial(GLMmodel* model, char* name) |
| { |
| uint i; |
| |
| for (i = 0; i < model->nummaterials; i++) { |
| if (!strcmp(model->materials[i].name, name)) |
| goto found; |
| } |
| |
| /* didn't find the name, so set it as the default material */ |
| printf("_glmFindMaterial(): can't find material \"%s\".\n", name); |
| i = 0; |
| |
| found: |
| return i; |
| } |
| |
| |
| /* _glmDirName: return the directory given a path |
| * |
| * path - filesystem path |
| * |
| * The return value should be free'd. |
| */ |
| static char* |
| _glmDirName(char* path) |
| { |
| char* dir; |
| char* s; |
| |
| dir = stralloc(path); |
| |
| s = strrchr(dir, '/'); |
| if (s) |
| s[1] = '\0'; |
| else |
| dir[0] = '\0'; |
| |
| return dir; |
| } |
| |
| |
| /* _glmReadMTL: read a wavefront material library file |
| * |
| * model - properly initialized GLMmodel structure |
| * name - name of the material library |
| */ |
| static void |
| _glmReadMTL(GLMmodel* model, char* name) |
| { |
| FILE* file; |
| char* dir; |
| char* filename; |
| char buf[128], buf2[128]; |
| uint nummaterials, i; |
| GLMmaterial *mat; |
| |
| dir = _glmDirName(model->pathname); |
| filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(name) + 1)); |
| strcpy(filename, dir); |
| strcat(filename, name); |
| free(dir); |
| |
| /* open the file */ |
| file = fopen(filename, "r"); |
| if (!file) { |
| fprintf(stderr, "_glmReadMTL() failed: can't open material file \"%s\".\n", |
| filename); |
| exit(1); |
| } |
| free(filename); |
| |
| /* count the number of materials in the file */ |
| nummaterials = 1; |
| while(fscanf(file, "%s", buf) != EOF) { |
| switch(buf[0]) { |
| case '#': /* comment */ |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| break; |
| case 'n': /* newmtl */ |
| fgets(buf, sizeof(buf), file); |
| nummaterials++; |
| sscanf(buf, "%s %s", buf, buf); |
| break; |
| default: |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| break; |
| } |
| } |
| |
| rewind(file); |
| |
| /* allocate memory for the materials */ |
| model->materials = (GLMmaterial*)calloc(nummaterials, sizeof(GLMmaterial)); |
| model->nummaterials = nummaterials; |
| |
| /* set the default material */ |
| for (i = 0; i < nummaterials; i++) { |
| model->materials[i].name = NULL; |
| model->materials[i].shininess = 0; |
| model->materials[i].diffuse[0] = 0.8; |
| model->materials[i].diffuse[1] = 0.8; |
| model->materials[i].diffuse[2] = 0.8; |
| model->materials[i].diffuse[3] = 1.0; |
| model->materials[i].ambient[0] = 0.2; |
| model->materials[i].ambient[1] = 0.2; |
| model->materials[i].ambient[2] = 0.2; |
| model->materials[i].ambient[3] = 0.0; |
| model->materials[i].specular[0] = 0.0; |
| model->materials[i].specular[1] = 0.0; |
| model->materials[i].specular[2] = 0.0; |
| model->materials[i].specular[3] = 0.0; |
| } |
| model->materials[0].name = stralloc("default"); |
| |
| /* now, read in the data */ |
| nummaterials = 0; |
| |
| mat = &model->materials[nummaterials]; |
| |
| while(fscanf(file, "%s", buf) != EOF) { |
| switch(buf[0]) { |
| case '#': /* comment */ |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| break; |
| case 'n': /* newmtl */ |
| fgets(buf, sizeof(buf), file); |
| sscanf(buf, "%s %s", buf, buf); |
| nummaterials++; |
| model->materials[nummaterials].name = stralloc(buf); |
| break; |
| case 'N': |
| fscanf(file, "%f", &model->materials[nummaterials].shininess); |
| /* wavefront shininess is from [0, 1000], so scale for OpenGL */ |
| model->materials[nummaterials].shininess /= 1000.0; |
| model->materials[nummaterials].shininess *= 128.0; |
| mat = &model->materials[nummaterials]; |
| break; |
| case 'K': |
| switch(buf[1]) { |
| case 'd': |
| fscanf(file, "%f %f %f", |
| &model->materials[nummaterials].diffuse[0], |
| &model->materials[nummaterials].diffuse[1], |
| &model->materials[nummaterials].diffuse[2]); |
| break; |
| case 's': |
| fscanf(file, "%f %f %f", |
| &model->materials[nummaterials].specular[0], |
| &model->materials[nummaterials].specular[1], |
| &model->materials[nummaterials].specular[2]); |
| break; |
| case 'a': |
| fscanf(file, "%f %f %f", |
| &model->materials[nummaterials].ambient[0], |
| &model->materials[nummaterials].ambient[1], |
| &model->materials[nummaterials].ambient[2]); |
| break; |
| default: |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| break; |
| } |
| break; |
| case 'd': /* alpha? */ |
| fscanf(file, "%f", |
| &model->materials[nummaterials].diffuse[3]); |
| break; |
| case 'm': /* texture map */ |
| fscanf(file, "%s", buf2); |
| /*printf("map %s\n", buf2);*/ |
| mat->map_kd = strdup(buf2); |
| break; |
| |
| default: |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| break; |
| } |
| } |
| } |
| |
| |
| /* _glmWriteMTL: write a wavefront material library file |
| * |
| * model - properly initialized GLMmodel structure |
| * modelpath - pathname of the model being written |
| * mtllibname - name of the material library to be written |
| */ |
| static void |
| _glmWriteMTL(GLMmodel* model, char* modelpath, char* mtllibname) |
| { |
| FILE* file; |
| char* dir; |
| char* filename; |
| GLMmaterial* material; |
| uint i; |
| |
| dir = _glmDirName(modelpath); |
| filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(mtllibname))); |
| strcpy(filename, dir); |
| strcat(filename, mtllibname); |
| free(dir); |
| |
| /* open the file */ |
| file = fopen(filename, "w"); |
| if (!file) { |
| fprintf(stderr, "_glmWriteMTL() failed: can't open file \"%s\".\n", |
| filename); |
| exit(1); |
| } |
| free(filename); |
| |
| /* spit out a header */ |
| fprintf(file, "# \n"); |
| fprintf(file, "# Wavefront MTL generated by GLM library\n"); |
| fprintf(file, "# \n"); |
| fprintf(file, "# GLM library copyright (C) 1997 by Nate Robins\n"); |
| fprintf(file, "# email: ndr@pobox.com\n"); |
| fprintf(file, "# www: http://www.pobox.com/~ndr\n"); |
| fprintf(file, "# \n\n"); |
| |
| for (i = 0; i < model->nummaterials; i++) { |
| material = &model->materials[i]; |
| fprintf(file, "newmtl %s\n", material->name); |
| fprintf(file, "Ka %f %f %f\n", |
| material->ambient[0], material->ambient[1], material->ambient[2]); |
| fprintf(file, "Kd %f %f %f\n", |
| material->diffuse[0], material->diffuse[1], material->diffuse[2]); |
| fprintf(file, "Ks %f %f %f\n", |
| material->specular[0],material->specular[1],material->specular[2]); |
| fprintf(file, "Ns %f\n", material->shininess); |
| fprintf(file, "\n"); |
| } |
| } |
| |
| |
| /* _glmFirstPass: first pass at a Wavefront OBJ file that gets all the |
| * statistics of the model (such as #vertices, #normals, etc) |
| * |
| * model - properly initialized GLMmodel structure |
| * file - (fopen'd) file descriptor |
| */ |
| static void |
| _glmFirstPass(GLMmodel* model, FILE* file) |
| { |
| uint numvertices; /* number of vertices in model */ |
| uint numnormals; /* number of normals in model */ |
| uint numtexcoords; /* number of texcoords in model */ |
| uint numtriangles; /* number of triangles in model */ |
| GLMgroup* group; /* current group */ |
| unsigned v, n, t; |
| char buf[128]; |
| |
| /* make a default group */ |
| group = _glmAddGroup(model, "default"); |
| |
| numvertices = numnormals = numtexcoords = numtriangles = 0; |
| while(fscanf(file, "%s", buf) != EOF) { |
| switch(buf[0]) { |
| case '#': /* comment */ |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| break; |
| case 'v': /* v, vn, vt */ |
| switch(buf[1]) { |
| case '\0': /* vertex */ |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| numvertices++; |
| break; |
| case 'n': /* normal */ |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| numnormals++; |
| break; |
| case 't': /* texcoord */ |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| numtexcoords++; |
| break; |
| default: |
| printf("_glmFirstPass(): Unknown token \"%s\".\n", buf); |
| exit(1); |
| break; |
| } |
| break; |
| case 'm': |
| fgets(buf, sizeof(buf), file); |
| sscanf(buf, "%s %s", buf, buf); |
| model->mtllibname = stralloc(buf); |
| _glmReadMTL(model, buf); |
| break; |
| case 'u': |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| break; |
| case 'g': /* group */ |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| sscanf(buf, "%s", buf); |
| group = _glmAddGroup(model, buf); |
| break; |
| case 'f': /* face */ |
| v = n = t = 0; |
| fscanf(file, "%s", buf); |
| /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */ |
| if (strstr(buf, "//")) { |
| /* v//n */ |
| sscanf(buf, "%u//%u", &v, &n); |
| fscanf(file, "%u//%u", &v, &n); |
| fscanf(file, "%u//%u", &v, &n); |
| numtriangles++; |
| group->numtriangles++; |
| while(fscanf(file, "%u//%u", &v, &n) > 0) { |
| numtriangles++; |
| group->numtriangles++; |
| } |
| } else if (sscanf(buf, "%u/%u/%u", &v, &t, &n) == 3) { |
| /* v/t/n */ |
| fscanf(file, "%u/%u/%u", &v, &t, &n); |
| fscanf(file, "%u/%u/%u", &v, &t, &n); |
| numtriangles++; |
| group->numtriangles++; |
| while(fscanf(file, "%u/%u/%u", &v, &t, &n) > 0) { |
| numtriangles++; |
| group->numtriangles++; |
| } |
| } else if (sscanf(buf, "%u/%u", &v, &t) == 2) { |
| /* v/t */ |
| fscanf(file, "%u/%u", &v, &t); |
| fscanf(file, "%u/%u", &v, &t); |
| numtriangles++; |
| group->numtriangles++; |
| while(fscanf(file, "%u/%u", &v, &t) > 0) { |
| numtriangles++; |
| group->numtriangles++; |
| } |
| } else { |
| /* v */ |
| fscanf(file, "%u", &v); |
| fscanf(file, "%u", &v); |
| numtriangles++; |
| group->numtriangles++; |
| while(fscanf(file, "%u", &v) > 0) { |
| numtriangles++; |
| group->numtriangles++; |
| } |
| } |
| break; |
| |
| default: |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| break; |
| } |
| } |
| |
| #if 0 |
| /* announce the model statistics */ |
| printf(" Vertices: %d\n", numvertices); |
| printf(" Normals: %d\n", numnormals); |
| printf(" Texcoords: %d\n", numtexcoords); |
| printf(" Triangles: %d\n", numtriangles); |
| printf(" Groups: %d\n", model->numgroups); |
| #endif |
| |
| /* set the stats in the model structure */ |
| model->numvertices = numvertices; |
| model->numnormals = numnormals; |
| model->numtexcoords = numtexcoords; |
| model->numtriangles = numtriangles; |
| |
| /* allocate memory for the triangles in each group */ |
| group = model->groups; |
| while(group) { |
| group->triangles = (uint*)malloc(sizeof(uint) * group->numtriangles); |
| group->numtriangles = 0; |
| group = group->next; |
| } |
| } |
| |
| /* _glmSecondPass: second pass at a Wavefront OBJ file that gets all |
| * the data. |
| * |
| * model - properly initialized GLMmodel structure |
| * file - (fopen'd) file descriptor |
| */ |
| static void |
| _glmSecondPass(GLMmodel* model, FILE* file) |
| { |
| uint numvertices; /* number of vertices in model */ |
| uint numnormals; /* number of normals in model */ |
| uint numtexcoords; /* number of texcoords in model */ |
| uint numtriangles; /* number of triangles in model */ |
| float* vertices; /* array of vertices */ |
| float* normals; /* array of normals */ |
| float* texcoords; /* array of texture coordinates */ |
| GLMgroup* group; /* current group pointer */ |
| uint material; /* current material */ |
| uint v, n, t; |
| char buf[128]; |
| |
| /* set the pointer shortcuts */ |
| vertices = model->vertices; |
| normals = model->normals; |
| texcoords = model->texcoords; |
| group = model->groups; |
| |
| /* on the second pass through the file, read all the data into the |
| allocated arrays */ |
| numvertices = numnormals = numtexcoords = 1; |
| numtriangles = 0; |
| material = 0; |
| while(fscanf(file, "%s", buf) != EOF) { |
| switch(buf[0]) { |
| case '#': /* comment */ |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| break; |
| case 'v': /* v, vn, vt */ |
| switch(buf[1]) { |
| case '\0': /* vertex */ |
| fscanf(file, "%f %f %f", |
| &vertices[3 * numvertices + X], |
| &vertices[3 * numvertices + Y], |
| &vertices[3 * numvertices + Z]); |
| numvertices++; |
| break; |
| case 'n': /* normal */ |
| fscanf(file, "%f %f %f", |
| &normals[3 * numnormals + X], |
| &normals[3 * numnormals + Y], |
| &normals[3 * numnormals + Z]); |
| numnormals++; |
| break; |
| case 't': /* texcoord */ |
| fscanf(file, "%f %f", |
| &texcoords[2 * numtexcoords + X], |
| &texcoords[2 * numtexcoords + Y]); |
| numtexcoords++; |
| break; |
| } |
| break; |
| case 'u': |
| fgets(buf, sizeof(buf), file); |
| sscanf(buf, "%s %s", buf, buf); |
| material = _glmFindMaterial(model, buf); |
| if (!group->material) |
| group->material = material; |
| /*printf("material %s = %u\n", buf, material);*/ |
| break; |
| case 'g': /* group */ |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| sscanf(buf, "%s", buf); |
| group = _glmFindGroup(model, buf); |
| group->material = material; |
| /*printf("GROUP %s material %u\n", buf, material);*/ |
| break; |
| case 'f': /* face */ |
| v = n = t = 0; |
| fscanf(file, "%s", buf); |
| /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */ |
| if (strstr(buf, "//")) { |
| /* v//n */ |
| sscanf(buf, "%u//%u", &v, &n); |
| T(numtriangles).vindices[0] = v; |
| T(numtriangles).nindices[0] = n; |
| fscanf(file, "%u//%u", &v, &n); |
| T(numtriangles).vindices[1] = v; |
| T(numtriangles).nindices[1] = n; |
| fscanf(file, "%u//%u", &v, &n); |
| T(numtriangles).vindices[2] = v; |
| T(numtriangles).nindices[2] = n; |
| group->triangles[group->numtriangles++] = numtriangles; |
| numtriangles++; |
| while(fscanf(file, "%u//%u", &v, &n) > 0) { |
| T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0]; |
| T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0]; |
| T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2]; |
| T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2]; |
| T(numtriangles).vindices[2] = v; |
| T(numtriangles).nindices[2] = n; |
| group->triangles[group->numtriangles++] = numtriangles; |
| numtriangles++; |
| } |
| } else if (sscanf(buf, "%u/%u/%u", &v, &t, &n) == 3) { |
| /* v/t/n */ |
| T(numtriangles).vindices[0] = v; |
| T(numtriangles).tindices[0] = t; |
| T(numtriangles).nindices[0] = n; |
| fscanf(file, "%u/%u/%u", &v, &t, &n); |
| T(numtriangles).vindices[1] = v; |
| T(numtriangles).tindices[1] = t; |
| T(numtriangles).nindices[1] = n; |
| fscanf(file, "%u/%u/%u", &v, &t, &n); |
| T(numtriangles).vindices[2] = v; |
| T(numtriangles).tindices[2] = t; |
| T(numtriangles).nindices[2] = n; |
| group->triangles[group->numtriangles++] = numtriangles; |
| numtriangles++; |
| while(fscanf(file, "%u/%u/%u", &v, &t, &n) > 0) { |
| T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0]; |
| T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0]; |
| T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0]; |
| T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2]; |
| T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2]; |
| T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2]; |
| T(numtriangles).vindices[2] = v; |
| T(numtriangles).tindices[2] = t; |
| T(numtriangles).nindices[2] = n; |
| group->triangles[group->numtriangles++] = numtriangles; |
| numtriangles++; |
| } |
| } else if (sscanf(buf, "%u/%u", &v, &t) == 2) { |
| /* v/t */ |
| T(numtriangles).vindices[0] = v; |
| T(numtriangles).tindices[0] = t; |
| fscanf(file, "%u/%u", &v, &t); |
| T(numtriangles).vindices[1] = v; |
| T(numtriangles).tindices[1] = t; |
| fscanf(file, "%u/%u", &v, &t); |
| T(numtriangles).vindices[2] = v; |
| T(numtriangles).tindices[2] = t; |
| group->triangles[group->numtriangles++] = numtriangles; |
| numtriangles++; |
| while(fscanf(file, "%u/%u", &v, &t) > 0) { |
| T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0]; |
| T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0]; |
| T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2]; |
| T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2]; |
| T(numtriangles).vindices[2] = v; |
| T(numtriangles).tindices[2] = t; |
| group->triangles[group->numtriangles++] = numtriangles; |
| numtriangles++; |
| } |
| } else { |
| /* v */ |
| sscanf(buf, "%u", &v); |
| T(numtriangles).vindices[0] = v; |
| fscanf(file, "%u", &v); |
| T(numtriangles).vindices[1] = v; |
| fscanf(file, "%u", &v); |
| T(numtriangles).vindices[2] = v; |
| group->triangles[group->numtriangles++] = numtriangles; |
| numtriangles++; |
| while(fscanf(file, "%u", &v) > 0) { |
| T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0]; |
| T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2]; |
| T(numtriangles).vindices[2] = v; |
| group->triangles[group->numtriangles++] = numtriangles; |
| numtriangles++; |
| } |
| } |
| break; |
| |
| default: |
| /* eat up rest of line */ |
| fgets(buf, sizeof(buf), file); |
| break; |
| } |
| } |
| |
| #if 0 |
| /* announce the memory requirements */ |
| printf(" Memory: %d bytes\n", |
| numvertices * 3*sizeof(float) + |
| numnormals * 3*sizeof(float) * (numnormals ? 1 : 0) + |
| numtexcoords * 3*sizeof(float) * (numtexcoords ? 1 : 0) + |
| numtriangles * sizeof(GLMtriangle)); |
| #endif |
| } |
| |
| |
| |
| |
| /* public functions */ |
| |
| /* glmUnitize: "unitize" a model by translating it to the origin and |
| * scaling it to fit in a unit cube around the origin. Returns the |
| * scalefactor used. |
| * |
| * model - properly initialized GLMmodel structure |
| */ |
| float |
| glmUnitize(GLMmodel* model) |
| { |
| uint i; |
| float maxx, minx, maxy, miny, maxz, minz; |
| float cx, cy, cz, w, h, d; |
| float scale; |
| |
| assert(model); |
| assert(model->vertices); |
| |
| /* get the max/mins */ |
| maxx = minx = model->vertices[3 + X]; |
| maxy = miny = model->vertices[3 + Y]; |
| maxz = minz = model->vertices[3 + Z]; |
| for (i = 1; i <= model->numvertices; i++) { |
| if (maxx < model->vertices[3 * i + X]) |
| maxx = model->vertices[3 * i + X]; |
| if (minx > model->vertices[3 * i + X]) |
| minx = model->vertices[3 * i + X]; |
| |
| if (maxy < model->vertices[3 * i + Y]) |
| maxy = model->vertices[3 * i + Y]; |
| if (miny > model->vertices[3 * i + Y]) |
| miny = model->vertices[3 * i + Y]; |
| |
| if (maxz < model->vertices[3 * i + Z]) |
| maxz = model->vertices[3 * i + Z]; |
| if (minz > model->vertices[3 * i + Z]) |
| minz = model->vertices[3 * i + Z]; |
| } |
| |
| /* calculate model width, height, and depth */ |
| w = _glmAbs(maxx) + _glmAbs(minx); |
| h = _glmAbs(maxy) + _glmAbs(miny); |
| d = _glmAbs(maxz) + _glmAbs(minz); |
| |
| /* calculate center of the model */ |
| cx = (maxx + minx) / 2.0; |
| cy = (maxy + miny) / 2.0; |
| cz = (maxz + minz) / 2.0; |
| |
| /* calculate unitizing scale factor */ |
| scale = 2.0 / _glmMax(_glmMax(w, h), d); |
| |
| /* translate around center then scale */ |
| for (i = 1; i <= model->numvertices; i++) { |
| model->vertices[3 * i + X] -= cx; |
| model->vertices[3 * i + Y] -= cy; |
| model->vertices[3 * i + Z] -= cz; |
| model->vertices[3 * i + X] *= scale; |
| model->vertices[3 * i + Y] *= scale; |
| model->vertices[3 * i + Z] *= scale; |
| } |
| |
| return scale; |
| } |
| |
| /* glmDimensions: Calculates the dimensions (width, height, depth) of |
| * a model. |
| * |
| * model - initialized GLMmodel structure |
| * dimensions - array of 3 floats (float dimensions[3]) |
| */ |
| void |
| glmDimensions(GLMmodel* model, float* dimensions) |
| { |
| uint i; |
| float maxx, minx, maxy, miny, maxz, minz; |
| |
| assert(model); |
| assert(model->vertices); |
| assert(dimensions); |
| |
| /* get the max/mins */ |
| maxx = minx = model->vertices[3 + X]; |
| maxy = miny = model->vertices[3 + Y]; |
| maxz = minz = model->vertices[3 + Z]; |
| for (i = 1; i <= model->numvertices; i++) { |
| if (maxx < model->vertices[3 * i + X]) |
| maxx = model->vertices[3 * i + X]; |
| if (minx > model->vertices[3 * i + X]) |
| minx = model->vertices[3 * i + X]; |
| |
| if (maxy < model->vertices[3 * i + Y]) |
| maxy = model->vertices[3 * i + Y]; |
| if (miny > model->vertices[3 * i + Y]) |
| miny = model->vertices[3 * i + Y]; |
| |
| if (maxz < model->vertices[3 * i + Z]) |
| maxz = model->vertices[3 * i + Z]; |
| if (minz > model->vertices[3 * i + Z]) |
| minz = model->vertices[3 * i + Z]; |
| } |
| |
| /* calculate model width, height, and depth */ |
| dimensions[X] = _glmAbs(maxx) + _glmAbs(minx); |
| dimensions[Y] = _glmAbs(maxy) + _glmAbs(miny); |
| dimensions[Z] = _glmAbs(maxz) + _glmAbs(minz); |
| } |
| |
| /* glmScale: Scales a model by a given amount. |
| * |
| * model - properly initialized GLMmodel structure |
| * scale - scalefactor (0.5 = half as large, 2.0 = twice as large) |
| */ |
| void |
| glmScale(GLMmodel* model, float scale) |
| { |
| uint i; |
| |
| for (i = 1; i <= model->numvertices; i++) { |
| model->vertices[3 * i + X] *= scale; |
| model->vertices[3 * i + Y] *= scale; |
| model->vertices[3 * i + Z] *= scale; |
| } |
| } |
| |
| /* glmReverseWinding: Reverse the polygon winding for all polygons in |
| * this model. Default winding is counter-clockwise. Also changes |
| * the direction of the normals. |
| * |
| * model - properly initialized GLMmodel structure |
| */ |
| void |
| glmReverseWinding(GLMmodel* model) |
| { |
| uint i, swap; |
| |
| assert(model); |
| |
| for (i = 0; i < model->numtriangles; i++) { |
| swap = T(i).vindices[0]; |
| T(i).vindices[0] = T(i).vindices[2]; |
| T(i).vindices[2] = swap; |
| |
| if (model->numnormals) { |
| swap = T(i).nindices[0]; |
| T(i).nindices[0] = T(i).nindices[2]; |
| T(i).nindices[2] = swap; |
| } |
| |
| if (model->numtexcoords) { |
| swap = T(i).tindices[0]; |
| T(i).tindices[0] = T(i).tindices[2]; |
| T(i).tindices[2] = swap; |
| } |
| } |
| |
| /* reverse facet normals */ |
| for (i = 1; i <= model->numfacetnorms; i++) { |
| model->facetnorms[3 * i + X] = -model->facetnorms[3 * i + X]; |
| model->facetnorms[3 * i + Y] = -model->facetnorms[3 * i + Y]; |
| model->facetnorms[3 * i + Z] = -model->facetnorms[3 * i + Z]; |
| } |
| |
| /* reverse vertex normals */ |
| for (i = 1; i <= model->numnormals; i++) { |
| model->normals[3 * i + X] = -model->normals[3 * i + X]; |
| model->normals[3 * i + Y] = -model->normals[3 * i + Y]; |
| model->normals[3 * i + Z] = -model->normals[3 * i + Z]; |
| } |
| } |
| |
| /* glmFacetNormals: Generates facet normals for a model (by taking the |
| * cross product of the two vectors derived from the sides of each |
| * triangle). Assumes a counter-clockwise winding. |
| * |
| * model - initialized GLMmodel structure |
| */ |
| void |
| glmFacetNormals(GLMmodel* model) |
| { |
| uint i; |
| float u[3]; |
| float v[3]; |
| |
| assert(model); |
| assert(model->vertices); |
| |
| /* clobber any old facetnormals */ |
| if (model->facetnorms) |
| free(model->facetnorms); |
| |
| /* allocate memory for the new facet normals */ |
| model->numfacetnorms = model->numtriangles; |
| model->facetnorms = (float*)malloc(sizeof(float) * |
| 3 * (model->numfacetnorms + 1)); |
| |
| for (i = 0; i < model->numtriangles; i++) { |
| model->triangles[i].findex = i+1; |
| |
| u[X] = model->vertices[3 * T(i).vindices[1] + X] - |
| model->vertices[3 * T(i).vindices[0] + X]; |
| u[Y] = model->vertices[3 * T(i).vindices[1] + Y] - |
| model->vertices[3 * T(i).vindices[0] + Y]; |
| u[Z] = model->vertices[3 * T(i).vindices[1] + Z] - |
| model->vertices[3 * T(i).vindices[0] + Z]; |
| |
| v[X] = model->vertices[3 * T(i).vindices[2] + X] - |
| model->vertices[3 * T(i).vindices[0] + X]; |
| v[Y] = model->vertices[3 * T(i).vindices[2] + Y] - |
| model->vertices[3 * T(i).vindices[0] + Y]; |
| v[Z] = model->vertices[3 * T(i).vindices[2] + Z] - |
| model->vertices[3 * T(i).vindices[0] + Z]; |
| |
| _glmCross(u, v, &model->facetnorms[3 * (i+1)]); |
| _glmNormalize(&model->facetnorms[3 * (i+1)]); |
| } |
| } |
| |
| /* glmVertexNormals: Generates smooth vertex normals for a model. |
| * First builds a list of all the triangles each vertex is in. Then |
| * loops through each vertex in the the list averaging all the facet |
| * normals of the triangles each vertex is in. Finally, sets the |
| * normal index in the triangle for the vertex to the generated smooth |
| * normal. If the dot product of a facet normal and the facet normal |
| * associated with the first triangle in the list of triangles the |
| * current vertex is in is greater than the cosine of the angle |
| * parameter to the function, that facet normal is not added into the |
| * average normal calculation and the corresponding vertex is given |
| * the facet normal. This tends to preserve hard edges. The angle to |
| * use depends on the model, but 90 degrees is usually a good start. |
| * |
| * model - initialized GLMmodel structure |
| * angle - maximum angle (in degrees) to smooth across |
| */ |
| void |
| glmVertexNormals(GLMmodel* model, float angle) |
| { |
| GLMnode* node; |
| GLMnode* tail; |
| GLMnode** members; |
| float* normals; |
| uint numnormals; |
| float average[3]; |
| float dot, cos_angle; |
| uint i, avg; |
| |
| assert(model); |
| assert(model->facetnorms); |
| |
| /* calculate the cosine of the angle (in degrees) */ |
| cos_angle = cos(angle * M_PI / 180.0); |
| |
| /* nuke any previous normals */ |
| if (model->normals) |
| free(model->normals); |
| |
| /* allocate space for new normals */ |
| model->numnormals = model->numtriangles * 3; /* 3 normals per triangle */ |
| model->normals = (float*)malloc(sizeof(float)* 3* (model->numnormals+1)); |
| |
| /* allocate a structure that will hold a linked list of triangle |
| indices for each vertex */ |
| members = (GLMnode**)malloc(sizeof(GLMnode*) * (model->numvertices + 1)); |
| for (i = 1; i <= model->numvertices; i++) |
| members[i] = NULL; |
| |
| /* for every triangle, create a node for each vertex in it */ |
| for (i = 0; i < model->numtriangles; i++) { |
| node = (GLMnode*)malloc(sizeof(GLMnode)); |
| node->index = i; |
| node->next = members[T(i).vindices[0]]; |
| members[T(i).vindices[0]] = node; |
| |
| node = (GLMnode*)malloc(sizeof(GLMnode)); |
| node->index = i; |
| node->next = members[T(i).vindices[1]]; |
| members[T(i).vindices[1]] = node; |
| |
| node = (GLMnode*)malloc(sizeof(GLMnode)); |
| node->index = i; |
| node->next = members[T(i).vindices[2]]; |
| members[T(i).vindices[2]] = node; |
| } |
| |
| /* calculate the average normal for each vertex */ |
| numnormals = 1; |
| for (i = 1; i <= model->numvertices; i++) { |
| /* calculate an average normal for this vertex by averaging the |
| facet normal of every triangle this vertex is in */ |
| node = members[i]; |
| if (!node) |
| fprintf(stderr, "glmVertexNormals(): vertex w/o a triangle\n"); |
| average[0] = 0.0; average[1] = 0.0; average[2] = 0.0; |
| avg = 0; |
| while (node) { |
| /* only average if the dot product of the angle between the two |
| facet normals is greater than the cosine of the threshold |
| angle -- or, said another way, the angle between the two |
| facet normals is less than (or equal to) the threshold angle */ |
| dot = _glmDot(&model->facetnorms[3 * T(node->index).findex], |
| &model->facetnorms[3 * T(members[i]->index).findex]); |
| if (dot > cos_angle) { |
| node->averaged = TRUE; |
| average[0] += model->facetnorms[3 * T(node->index).findex + 0]; |
| average[1] += model->facetnorms[3 * T(node->index).findex + 1]; |
| average[2] += model->facetnorms[3 * T(node->index).findex + 2]; |
| avg = 1; /* we averaged at least one normal! */ |
| } else { |
| node->averaged = FALSE; |
| } |
| node = node->next; |
| } |
| |
| if (avg) { |
| /* normalize the averaged normal */ |
| _glmNormalize(average); |
| |
| /* add the normal to the vertex normals list */ |
| model->normals[3 * numnormals + 0] = average[0]; |
| model->normals[3 * numnormals + 1] = average[1]; |
| model->normals[3 * numnormals + 2] = average[2]; |
| avg = numnormals; |
| numnormals++; |
| } |
| |
| /* set the normal of this vertex in each triangle it is in */ |
| node = members[i]; |
| while (node) { |
| if (node->averaged) { |
| /* if this node was averaged, use the average normal */ |
| if (T(node->index).vindices[0] == i) |
| T(node->index).nindices[0] = avg; |
| else if (T(node->index).vindices[1] == i) |
| T(node->index).nindices[1] = avg; |
| else if (T(node->index).vindices[2] == i) |
| T(node->index).nindices[2] = avg; |
| } else { |
| /* if this node wasn't averaged, use the facet normal */ |
| model->normals[3 * numnormals + 0] = |
| model->facetnorms[3 * T(node->index).findex + 0]; |
| model->normals[3 * numnormals + 1] = |
| model->facetnorms[3 * T(node->index).findex + 1]; |
| model->normals[3 * numnormals + 2] = |
| model->facetnorms[3 * T(node->index).findex + 2]; |
| if (T(node->index).vindices[0] == i) |
| T(node->index).nindices[0] = numnormals; |
| else if (T(node->index).vindices[1] == i) |
| T(node->index).nindices[1] = numnormals; |
| else if (T(node->index).vindices[2] == i) |
| T(node->index).nindices[2] = numnormals; |
| numnormals++; |
| } |
| node = node->next; |
| } |
| } |
| |
| model->numnormals = numnormals - 1; |
| |
| /* free the member information */ |
| for (i = 1; i <= model->numvertices; i++) { |
| node = members[i]; |
| while (node) { |
| tail = node; |
| node = node->next; |
| free(tail); |
| } |
| } |
| free(members); |
| |
| /* pack the normals array (we previously allocated the maximum |
| number of normals that could possibly be created (numtriangles * |
| 3), so get rid of some of them (usually alot unless none of the |
| facet normals were averaged)) */ |
| normals = model->normals; |
| model->normals = (float*)malloc(sizeof(float)* 3* (model->numnormals+1)); |
| for (i = 1; i <= model->numnormals; i++) { |
| model->normals[3 * i + 0] = normals[3 * i + 0]; |
| model->normals[3 * i + 1] = normals[3 * i + 1]; |
| model->normals[3 * i + 2] = normals[3 * i + 2]; |
| } |
| free(normals); |
| |
| printf("glmVertexNormals(): %d normals generated\n", model->numnormals); |
| } |
| |
| |
| /* glmLinearTexture: Generates texture coordinates according to a |
| * linear projection of the texture map. It generates these by |
| * linearly mapping the vertices onto a square. |
| * |
| * model - pointer to initialized GLMmodel structure |
| */ |
| void |
| glmLinearTexture(GLMmodel* model) |
| { |
| GLMgroup *group; |
| float dimensions[3]; |
| float x, y, scalefactor; |
| uint i; |
| |
| assert(model); |
| |
| if (model->texcoords) |
| free(model->texcoords); |
| model->numtexcoords = model->numvertices; |
| model->texcoords=(float*)malloc(sizeof(float)*2*(model->numtexcoords+1)); |
| |
| glmDimensions(model, dimensions); |
| scalefactor = 2.0 / |
| _glmAbs(_glmMax(_glmMax(dimensions[0], dimensions[1]), dimensions[2])); |
| |
| /* do the calculations */ |
| for(i = 1; i <= model->numvertices; i++) { |
| x = model->vertices[3 * i + 0] * scalefactor; |
| y = model->vertices[3 * i + 2] * scalefactor; |
| model->texcoords[2 * i + 0] = (x + 1.0) / 2.0; |
| model->texcoords[2 * i + 1] = (y + 1.0) / 2.0; |
| } |
| |
| /* go through and put texture coordinate indices in all the triangles */ |
| group = model->groups; |
| while(group) { |
| for(i = 0; i < group->numtriangles; i++) { |
| T(group->triangles[i]).tindices[0] = T(group->triangles[i]).vindices[0]; |
| T(group->triangles[i]).tindices[1] = T(group->triangles[i]).vindices[1]; |
| T(group->triangles[i]).tindices[2] = T(group->triangles[i]).vindices[2]; |
| } |
| group = group->next; |
| } |
| |
| #if 0 |
| printf("glmLinearTexture(): generated %d linear texture coordinates\n", |
| model->numtexcoords); |
| #endif |
| } |
| |
| /* glmSpheremapTexture: Generates texture coordinates according to a |
| * spherical projection of the texture map. Sometimes referred to as |
| * spheremap, or reflection map texture coordinates. It generates |
| * these by using the normal to calculate where that vertex would map |
| * onto a sphere. Since it is impossible to map something flat |
| * perfectly onto something spherical, there is distortion at the |
| * poles. This particular implementation causes the poles along the X |
| * axis to be distorted. |
| * |
| * model - pointer to initialized GLMmodel structure |
| */ |
| void |
| glmSpheremapTexture(GLMmodel* model) |
| { |
| GLMgroup* group; |
| float theta, phi, rho, x, y, z, r; |
| uint i; |
| |
| assert(model); |
| assert(model->normals); |
| |
| if (model->texcoords) |
| free(model->texcoords); |
| model->numtexcoords = model->numnormals; |
| model->texcoords=(float*)malloc(sizeof(float)*2*(model->numtexcoords+1)); |
| |
| /* do the calculations */ |
| for (i = 1; i <= model->numnormals; i++) { |
| z = model->normals[3 * i + 0]; /* re-arrange for pole distortion */ |
| y = model->normals[3 * i + 1]; |
| x = model->normals[3 * i + 2]; |
| r = sqrt((x * x) + (y * y)); |
| rho = sqrt((r * r) + (z * z)); |
| |
| if(r == 0.0) { |
| theta = 0.0; |
| phi = 0.0; |
| } else { |
| if(z == 0.0) |
| phi = M_PI / 2.0; |
| else |
| phi = acos(z / rho); |
| |
| #if WE_DONT_NEED_THIS_CODE |
| if(x == 0.0) |
| theta = M_PI / 2.0; /* asin(y / r); */ |
| else |
| theta = acos(x / r); |
| #endif |
| |
| if(y == 0.0) |
| theta = M_PI / 2.0; /* acos(x / r); */ |
| else |
| theta = asin(y / r) + (M_PI / 2.0); |
| } |
| |
| model->texcoords[2 * i + 0] = theta / M_PI; |
| model->texcoords[2 * i + 1] = phi / M_PI; |
| } |
| |
| /* go through and put texcoord indices in all the triangles */ |
| group = model->groups; |
| while(group) { |
| for (i = 0; i < group->numtriangles; i++) { |
| T(group->triangles[i]).tindices[0] = T(group->triangles[i]).nindices[0]; |
| T(group->triangles[i]).tindices[1] = T(group->triangles[i]).nindices[1]; |
| T(group->triangles[i]).tindices[2] = T(group->triangles[i]).nindices[2]; |
| } |
| group = group->next; |
| } |
| |
| #if 0 |
| printf("glmSpheremapTexture(): generated %d spheremap texture coordinates\n", |
| model->numtexcoords); |
| #endif |
| } |
| |
| /* glmDelete: Deletes a GLMmodel structure. |
| * |
| * model - initialized GLMmodel structure |
| */ |
| void |
| glmDelete(GLMmodel* model) |
| { |
| GLMgroup* group; |
| uint i; |
| |
| assert(model); |
| |
| if (model->pathname) free(model->pathname); |
| if (model->mtllibname) free(model->mtllibname); |
| if (model->vertices) free(model->vertices); |
| if (model->normals) free(model->normals); |
| if (model->texcoords) free(model->texcoords); |
| if (model->facetnorms) free(model->facetnorms); |
| if (model->triangles) free(model->triangles); |
| if (model->materials) { |
| for (i = 0; i < model->nummaterials; i++) |
| free(model->materials[i].name); |
| } |
| free(model->materials); |
| while(model->groups) { |
| group = model->groups; |
| model->groups = model->groups->next; |
| free(group->name); |
| free(group->triangles); |
| free(group); |
| } |
| |
| free(model); |
| } |
| |
| static GLMmaterial * |
| glmDefaultMaterial(void) |
| { |
| GLMmaterial *m = (GLMmaterial *) calloc(1, sizeof(GLMmaterial)); |
| |
| m->diffuse[0] = 0.75; |
| m->diffuse[1] = 0.75; |
| m->diffuse[2] = 0.75; |
| m->diffuse[3] = 1.0; |
| |
| m->specular[0] = 1.0; |
| m->specular[1] = 1.0; |
| m->specular[2] = 1.0; |
| m->specular[3] = 1.0; |
| |
| m->shininess = 5; |
| |
| return m; |
| } |
| |
| |
| /* glmReadOBJ: Reads a model description from a Wavefront .OBJ file. |
| * Returns a pointer to the created object which should be free'd with |
| * glmDelete(). |
| * |
| * filename - name of the file containing the Wavefront .OBJ format data. |
| */ |
| GLMmodel* |
| glmReadOBJ(char* filename) |
| { |
| GLMmodel* model; |
| FILE* file; |
| |
| /* open the file */ |
| file = fopen(filename, "r"); |
| if (!file) { |
| fprintf(stderr, "glmReadOBJ() failed: can't open data file \"%s\".\n", |
| filename); |
| exit(1); |
| } |
| |
| #if 0 |
| /* announce the model name */ |
| printf("Model: %s\n", filename); |
| #endif |
| |
| /* allocate a new model */ |
| model = (GLMmodel*)malloc(sizeof(GLMmodel)); |
| model->pathname = stralloc(filename); |
| model->mtllibname = NULL; |
| model->numvertices = 0; |
| model->vertices = NULL; |
| model->numnormals = 0; |
| model->normals = NULL; |
| model->numtexcoords = 0; |
| model->texcoords = NULL; |
| model->numfacetnorms = 0; |
| model->facetnorms = NULL; |
| model->numtriangles = 0; |
| model->triangles = NULL; |
| model->nummaterials = 0; |
| model->materials = NULL; |
| model->numgroups = 0; |
| model->groups = NULL; |
| model->position[0] = 0.0; |
| model->position[1] = 0.0; |
| model->position[2] = 0.0; |
| model->scale = 1.0; |
| |
| /* make a first pass through the file to get a count of the number |
| of vertices, normals, texcoords & triangles */ |
| _glmFirstPass(model, file); |
| |
| /* allocate memory */ |
| model->vertices = (float*)malloc(sizeof(float) * |
| 3 * (model->numvertices + 1)); |
| model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) * |
| model->numtriangles); |
| if (model->numnormals) { |
| model->normals = (float*)malloc(sizeof(float) * |
| 3 * (model->numnormals + 1)); |
| } |
| if (model->numtexcoords) { |
| model->texcoords = (float*)malloc(sizeof(float) * |
| 2 * (model->numtexcoords + 1)); |
| } |
| |
| /* rewind to beginning of file and read in the data this pass */ |
| rewind(file); |
| |
| _glmSecondPass(model, file); |
| |
| /* close the file */ |
| fclose(file); |
| |
| if (!model->materials) { |
| model->materials = glmDefaultMaterial(); |
| model->nummaterials = 1; |
| } |
| |
| return model; |
| } |
| |
| /* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to |
| * a file. |
| * |
| * model - initialized GLMmodel structure |
| * filename - name of the file to write the Wavefront .OBJ format data to |
| * mode - a bitwise or of values describing what is written to the file |
| * GLM_NONE - render with only vertices |
| * GLM_FLAT - render with facet normals |
| * GLM_SMOOTH - render with vertex normals |
| * GLM_TEXTURE - render with texture coords |
| * GLM_COLOR - render with colors (color material) |
| * GLM_MATERIAL - render with materials |
| * GLM_COLOR and GLM_MATERIAL should not both be specified. |
| * GLM_FLAT and GLM_SMOOTH should not both be specified. |
| */ |
| void |
| glmWriteOBJ(GLMmodel* model, char* filename, uint mode) |
| { |
| uint i; |
| FILE* file; |
| GLMgroup* group; |
| |
| assert(model); |
| |
| /* do a bit of warning */ |
| if (mode & GLM_FLAT && !model->facetnorms) { |
| printf("glmWriteOBJ() warning: flat normal output requested " |
| "with no facet normals defined.\n"); |
| mode &= ~GLM_FLAT; |
| } |
| if (mode & GLM_SMOOTH && !model->normals) { |
| printf("glmWriteOBJ() warning: smooth normal output requested " |
| "with no normals defined.\n"); |
| mode &= ~GLM_SMOOTH; |
| } |
| if (mode & GLM_TEXTURE && !model->texcoords) { |
| printf("glmWriteOBJ() warning: texture coordinate output requested " |
| "with no texture coordinates defined.\n"); |
| mode &= ~GLM_TEXTURE; |
| } |
| if (mode & GLM_FLAT && mode & GLM_SMOOTH) { |
| printf("glmWriteOBJ() warning: flat normal output requested " |
| "and smooth normal output requested (using smooth).\n"); |
| mode &= ~GLM_FLAT; |
| } |
| |
| /* open the file */ |
| file = fopen(filename, "w"); |
| if (!file) { |
| fprintf(stderr, "glmWriteOBJ() failed: can't open file \"%s\" to write.\n", |
| filename); |
| exit(1); |
| } |
| |
| /* spit out a header */ |
| fprintf(file, "# \n"); |
| fprintf(file, "# Wavefront OBJ generated by GLM library\n"); |
| fprintf(file, "# \n"); |
| fprintf(file, "# GLM library copyright (C) 1997 by Nate Robins\n"); |
| fprintf(file, "# email: ndr@pobox.com\n"); |
| fprintf(file, "# www: http://www.pobox.com/~ndr\n"); |
| fprintf(file, "# \n"); |
| |
| if (mode & GLM_MATERIAL && model->mtllibname) { |
| fprintf(file, "\nmtllib %s\n\n", model->mtllibname); |
| _glmWriteMTL(model, filename, model->mtllibname); |
| } |
| |
| /* spit out the vertices */ |
| fprintf(file, "\n"); |
| fprintf(file, "# %d vertices\n", model->numvertices); |
| for (i = 1; i <= model->numvertices; i++) { |
| fprintf(file, "v %f %f %f\n", |
| model->vertices[3 * i + 0], |
| model->vertices[3 * i + 1], |
| model->vertices[3 * i + 2]); |
| } |
| |
| /* spit out the smooth/flat normals */ |
| if (mode & GLM_SMOOTH) { |
| fprintf(file, "\n"); |
| fprintf(file, "# %d normals\n", model->numnormals); |
| for (i = 1; i <= model->numnormals; i++) { |
| fprintf(file, "vn %f %f %f\n", |
| model->normals[3 * i + 0], |
| model->normals[3 * i + 1], |
| model->normals[3 * i + 2]); |
| } |
| } else if (mode & GLM_FLAT) { |
| fprintf(file, "\n"); |
| fprintf(file, "# %d normals\n", model->numfacetnorms); |
| for (i = 1; i <= model->numnormals; i++) { |
| fprintf(file, "vn %f %f %f\n", |
| model->facetnorms[3 * i + 0], |
| model->facetnorms[3 * i + 1], |
| model->facetnorms[3 * i + 2]); |
| } |
| } |
| |
| /* spit out the texture coordinates */ |
| if (mode & GLM_TEXTURE) { |
| fprintf(file, "\n"); |
| fprintf(file, "# %d texcoords\n", model->numtexcoords); |
| for (i = 1; i <= model->numtexcoords; i++) { |
| fprintf(file, "vt %f %f\n", |
| model->texcoords[2 * i + 0], |
| model->texcoords[2 * i + 1]); |
| } |
| } |
| |
| fprintf(file, "\n"); |
| fprintf(file, "# %d groups\n", model->numgroups); |
| fprintf(file, "# %d faces (triangles)\n", model->numtriangles); |
| fprintf(file, "\n"); |
| |
| group = model->groups; |
| while(group) { |
| fprintf(file, "g %s\n", group->name); |
| if (mode & GLM_MATERIAL) |
| fprintf(file, "usemtl %s\n", model->materials[group->material].name); |
| for (i = 0; i < group->numtriangles; i++) { |
| if (mode & GLM_SMOOTH && mode & GLM_TEXTURE) { |
| fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", |
| T(group->triangles[i]).vindices[0], |
| T(group->triangles[i]).nindices[0], |
| T(group->triangles[i]).tindices[0], |
| T(group->triangles[i]).vindices[1], |
| T(group->triangles[i]).nindices[1], |
| T(group->triangles[i]).tindices[1], |
| T(group->triangles[i]).vindices[2], |
| T(group->triangles[i]).nindices[2], |
| T(group->triangles[i]).tindices[2]); |
| } else if (mode & GLM_FLAT && mode & GLM_TEXTURE) { |
| fprintf(file, "f %d/%d %d/%d %d/%d\n", |
| T(group->triangles[i]).vindices[0], |
| T(group->triangles[i]).findex, |
| T(group->triangles[i]).vindices[1], |
| T(group->triangles[i]).findex, |
| T(group->triangles[i]).vindices[2], |
| T(group->triangles[i]).findex); |
| } else if (mode & GLM_TEXTURE) { |
| fprintf(file, "f %d/%d %d/%d %d/%d\n", |
| T(group->triangles[i]).vindices[0], |
| T(group->triangles[i]).tindices[0], |
| T(group->triangles[i]).vindices[1], |
| T(group->triangles[i]).tindices[1], |
| T(group->triangles[i]).vindices[2], |
| T(group->triangles[i]).tindices[2]); |
| } else if (mode & GLM_SMOOTH) { |
| fprintf(file, "f %d//%d %d//%d %d//%d\n", |
| T(group->triangles[i]).vindices[0], |
| T(group->triangles[i]).nindices[0], |
| T(group->triangles[i]).vindices[1], |
| T(group->triangles[i]).nindices[1], |
| T(group->triangles[i]).vindices[2], |
| T(group->triangles[i]).nindices[2]); |
| } else if (mode & GLM_FLAT) { |
| fprintf(file, "f %d//%d %d//%d %d//%d\n", |
| T(group->triangles[i]).vindices[0], |
| T(group->triangles[i]).findex, |
| T(group->triangles[i]).vindices[1], |
| T(group->triangles[i]).findex, |
| T(group->triangles[i]).vindices[2], |
| T(group->triangles[i]).findex); |
| } else { |
| fprintf(file, "f %d %d %d\n", |
| T(group->triangles[i]).vindices[0], |
| T(group->triangles[i]).vindices[1], |
| T(group->triangles[i]).vindices[2]); |
| } |
| } |
| fprintf(file, "\n"); |
| group = group->next; |
| } |
| |
| fclose(file); |
| } |
| |
| /* glmWeld: eliminate (weld) vectors that are within an epsilon of |
| * each other. |
| * |
| * model - initialized GLMmodel structure |
| * epsilon - maximum difference between vertices |
| * ( 0.00001 is a good start for a unitized model) |
| * |
| */ |
| void |
| glmWeld(GLMmodel* model, float epsilon) |
| { |
| float* vectors; |
| float* copies; |
| uint numvectors; |
| uint i; |
| |
| /* vertices */ |
| numvectors = model->numvertices; |
| vectors = model->vertices; |
| copies = _glmWeldVectors(vectors, &numvectors, epsilon); |
| |
| printf("glmWeld(): %d redundant vertices.\n", |
| model->numvertices - numvectors - 1); |
| |
| for (i = 0; i < model->numtriangles; i++) { |
| T(i).vindices[0] = (uint)vectors[3 * T(i).vindices[0] + 0]; |
| T(i).vindices[1] = (uint)vectors[3 * T(i).vindices[1] + 0]; |
| T(i).vindices[2] = (uint)vectors[3 * T(i).vindices[2] + 0]; |
| } |
| |
| /* free space for old vertices */ |
| free(vectors); |
| |
| /* allocate space for the new vertices */ |
| model->numvertices = numvectors; |
| model->vertices = (float*)malloc(sizeof(float) * |
| 3 * (model->numvertices + 1)); |
| |
| /* copy the optimized vertices into the actual vertex list */ |
| for (i = 1; i <= model->numvertices; i++) { |
| model->vertices[3 * i + 0] = copies[3 * i + 0]; |
| model->vertices[3 * i + 1] = copies[3 * i + 1]; |
| model->vertices[3 * i + 2] = copies[3 * i + 2]; |
| } |
| |
| free(copies); |
| } |
| |
| |
| void |
| glmReIndex(GLMmodel *model) |
| { |
| uint i, j, n; |
| GLMgroup* group; |
| float *newNormals = NULL; |
| float *newTexcoords = NULL; |
| const uint numv = model->numvertices; |
| |
| if (model->numnormals > 0) |
| newNormals = (float *) malloc((numv + 1) * 3 * sizeof(float)); |
| |
| if (model->numtexcoords > 0) |
| newTexcoords = (float *) malloc((numv + 1) * 2 * sizeof(float)); |
| |
| for (group = model->groups; group; group = group->next) { |
| |
| n = group->numtriangles; |
| |
| group->triIndexes = (uint *) malloc(n * 3 * sizeof(uint)); |
| |
| group->minIndex = 10000000; |
| group->maxIndex = 0; |
| |
| for (i = 0; i < n; i++) { |
| |
| for (j = 0; j < 3; j++) { |
| uint vindex = T(group->triangles[i]).vindices[j]; |
| uint nindex = T(group->triangles[i]).nindices[j]; |
| uint tindex = T(group->triangles[i]).tindices[j]; |
| |
| float *nrm = &model->normals[nindex * 3]; |
| float *tex = &model->texcoords[tindex * 2]; |
| |
| if (newNormals) { |
| assert(vindex * 3 + 2 < (numv + 1) * 3); |
| newNormals[vindex * 3 + 0] = nrm[0]; |
| newNormals[vindex * 3 + 1] = nrm[1]; |
| newNormals[vindex * 3 + 2] = nrm[2]; |
| } |
| if (newTexcoords) { |
| newTexcoords[vindex * 2 + 0] = tex[0]; |
| newTexcoords[vindex * 2 + 1] = tex[1]; |
| } |
| |
| T(group->triangles[i]).nindices[j] = vindex; |
| T(group->triangles[i]).tindices[j] = vindex; |
| |
| group->triIndexes[i * 3 + j] = vindex; |
| |
| if (vindex > group->maxIndex) |
| group->maxIndex = vindex; |
| if (vindex < group->minIndex) |
| group->minIndex = vindex; |
| } |
| } |
| } |
| |
| if (newNormals) { |
| free(model->normals); |
| model->normals = newNormals; |
| model->numnormals = model->numvertices; |
| } |
| |
| if (newTexcoords) { |
| free(model->texcoords); |
| model->texcoords = newTexcoords; |
| model->numtexcoords = model->numvertices; |
| } |
| } |
| |
| |
| |
| void |
| glmPrint(const GLMmodel *model) |
| { |
| uint i, j, grp, n; |
| GLMgroup* group; |
| uint totalTris = 0; |
| |
| grp = 0; |
| |
| printf("%u vertices\n", model->numvertices); |
| printf("%u normals\n", model->numnormals); |
| printf("%u texcoords\n", model->numtexcoords); |
| |
| for (group = model->groups; group; group = group->next, grp++) { |
| printf("Group %u:\n", grp); |
| printf(" Min index %u, max index %u\n", group->minIndex, group->maxIndex); |
| |
| #if 0 |
| if (mode & GLM_MATERIAL) { |
| glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, |
| model->materials[group->material].ambient); |
| glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, |
| model->materials[group->material].diffuse); |
| glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, |
| model->materials[group->material].specular); |
| glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, |
| model->materials[group->material].shininess); |
| } |
| |
| if (mode & GLM_COLOR) { |
| glColor3fv(model->materials[group->material].diffuse); |
| } |
| #endif |
| totalTris += group->numtriangles; |
| |
| printf(" %u triangles\n", group->numtriangles); |
| n = group->numtriangles; |
| if (n > 10) |
| n = 10; |
| |
| for (i = 0; i < n; i++) { |
| |
| printf(" %u: vert ", i); |
| for (j = 0; j < 3; j++) { |
| printf("%u ", T(group->triangles[i]).vindices[j]); |
| } |
| |
| printf(" normal "); |
| for (j = 0; j < 3; j++) { |
| printf("%u ", T(group->triangles[i]).nindices[j]); |
| } |
| |
| printf(" tex "); |
| for (j = 0; j < 3; j++) { |
| printf("%u ", T(group->triangles[i]).tindices[j]); |
| } |
| |
| printf("\n"); |
| } |
| } |
| printf("Total tris: %u\n", totalTris); |
| } |
| |
| |
| |
| #if 0 |
| /* normals */ |
| if (model->numnormals) { |
| numvectors = model->numnormals; |
| vectors = model->normals; |
| copies = _glmOptimizeVectors(vectors, &numvectors); |
| |
| printf("glmOptimize(): %d redundant normals.\n", |
| model->numnormals - numvectors); |
| |
| for (i = 0; i < model->numtriangles; i++) { |
| T(i).nindices[0] = (uint)vectors[3 * T(i).nindices[0] + 0]; |
| T(i).nindices[1] = (uint)vectors[3 * T(i).nindices[1] + 0]; |
| T(i).nindices[2] = (uint)vectors[3 * T(i).nindices[2] + 0]; |
| } |
| |
| /* free space for old normals */ |
| free(vectors); |
| |
| /* allocate space for the new normals */ |
| model->numnormals = numvectors; |
| model->normals = (float*)malloc(sizeof(float) * |
| 3 * (model->numnormals + 1)); |
| |
| /* copy the optimized vertices into the actual vertex list */ |
| for (i = 1; i <= model->numnormals; i++) { |
| model->normals[3 * i + 0] = copies[3 * i + 0]; |
| model->normals[3 * i + 1] = copies[3 * i + 1]; |
| model->normals[3 * i + 2] = copies[3 * i + 2]; |
| } |
| |
| free(copies); |
| } |
| |
| /* texcoords */ |
| if (model->numtexcoords) { |
| numvectors = model->numtexcoords; |
| vectors = model->texcoords; |
| copies = _glmOptimizeVectors(vectors, &numvectors); |
| |
| printf("glmOptimize(): %d redundant texcoords.\n", |
| model->numtexcoords - numvectors); |
| |
| for (i = 0; i < model->numtriangles; i++) { |
| for (j = 0; j < 3; j++) { |
| T(i).tindices[j] = (uint)vectors[3 * T(i).tindices[j] + 0]; |
| } |
| } |
| |
| /* free space for old texcoords */ |
| free(vectors); |
| |
| /* allocate space for the new texcoords */ |
| model->numtexcoords = numvectors; |
| model->texcoords = (float*)malloc(sizeof(float) * |
| 2 * (model->numtexcoords + 1)); |
| |
| /* copy the optimized vertices into the actual vertex list */ |
| for (i = 1; i <= model->numtexcoords; i++) { |
| model->texcoords[2 * i + 0] = copies[2 * i + 0]; |
| model->texcoords[2 * i + 1] = copies[2 * i + 1]; |
| } |
| |
| free(copies); |
| } |
| #endif |
| |
| #if 0 |
| /* look for unused vertices */ |
| /* look for unused normals */ |
| /* look for unused texcoords */ |
| for (i = 1; i <= model->numvertices; i++) { |
| for (j = 0; j < model->numtriangles; i++) { |
| if (T(j).vindices[0] == i || |
| T(j).vindices[1] == i || |
| T(j).vindices[1] == i) |
| break; |
| } |
| } |
| #endif |