/*
glm.c
Nate Robins, 1997, 2000
nate@pobox.com, http://www.pobox.com/~nate
Wavefront OBJ model file format reader/writer/manipulator.
Includes routines for generating smooth normals with
preservation of edges, welding redundant vertices & texture
coordinate generation (spheremap and planar projections) + more.
Improved version of GLM - 08.05.2008 Tudor Carean
Added support for textures and loading callbacks
*/
#include <stdafx.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "glm.h"
//#define DebugVisibleSurfaces
#define total_textures 5
#define T(x) (model->triangles[(x)])
GLuint glmLoadTexture(char *filename, GLboolean alpha, GLboolean repeat, GLboolean filtering, GLboolean mipmaps, GLfloat *texcoordwidth, GLfloat *texcoordheight);
/* _GLMnode: general purpose node */
typedef struct _GLMnode {
GLuint index;
GLboolean averaged;
struct _GLMnode* next;
} GLMnode;
/* glmMax: returns the maximum of two floats */
static GLfloat
glmMax(GLfloat a, GLfloat b)
{
if (b > a)
return b;
return a;
}
/* glmAbs: returns the absolute value of a float */
static GLfloat
glmAbs(GLfloat f)
{
if (f < 0)
return -f;
return f;
}
/* glmDot: compute the dot product of two vectors
*
* u - array of 3 GLfloats (GLfloat u[3])
* v - array of 3 GLfloats (GLfloat v[3])
*/
GLfloat glmDot(GLfloat* u, GLfloat* v)
{
assert(u); assert(v);
return u[0]*v[0] + u[1]*v[1] + u[2]*v[2];
}
/* glmCross: compute the cross product of two vectors
*
* u - array of 3 GLfloats (GLfloat u[3])
* v - array of 3 GLfloats (GLfloat v[3])
* n - array of 3 GLfloats (GLfloat n[3]) to return the cross product in
*/
static GLvoid
glmCross(GLfloat* u, GLfloat* v, GLfloat* n)
{
assert(u); assert(v); assert(n);
n[0] = u[1]*v[2] - u[2]*v[1];
n[1] = u[2]*v[0] - u[0]*v[2];
n[2] = u[0]*v[1] - u[1]*v[0];
}
/* glmNormalize: normalize a vector
*
* v - array of 3 GLfloats (GLfloat v[3]) to be normalized
*/
static GLvoid
glmNormalize(GLfloat* v)
{
GLfloat l;
assert(v);
l = (GLfloat)sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
v[0] /= l;
v[1] /= l;
v[2] /= l;
}
/* glmEqual: compares two vectors and returns GL_TRUE if they are
* equal (within a certain threshold) or GL_FALSE if not. An epsilon
* that works fairly well is 0.000001.
*
* u - array of 3 GLfloats (GLfloat u[3])
* v - array of 3 GLfloats (GLfloat v[3])
*/
static GLboolean
glmEqual(GLfloat* u, GLfloat* v, GLfloat epsilon)
{
if (glmAbs(u[0] - v[0]) < epsilon &&
glmAbs(u[1] - v[1]) < epsilon &&
glmAbs(u[2] - v[2]) < epsilon)
{
return GL_TRUE;
}
return GL_FALSE;
}
/* glmWeldVectors: eliminate (weld) vectors that are within an
* epsilon of each other.
*
* vectors - array of GLfloat[3]'s to be welded
* numvectors - number of GLfloat[3]'s in vectors
* epsilon - maximum difference between vectors
*
*/
GLfloat*
glmWeldVectors(GLfloat* vectors, GLuint* numvectors, GLfloat epsilon)
{
GLfloat* copies;
GLuint copied;
GLuint i, j;
copies = (GLfloat*)malloc(sizeof(GLfloat) * 3 * (*numvectors + 1));
memcpy(copies, vectors, (sizeof(GLfloat) * 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] = (GLfloat)j;
}
*numvectors = copied-1;
return copies;
}
/* glmFindGroup: Find a group in the model */
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 */
GLMgroup*
glmAddGroup(GLMmodel* model, char* name)
{
GLMgroup* group;
group = glmFindGroup(model, name);
if (!group) {
group = (GLMgroup*)malloc(sizeof(GLMgroup));
group->name = strdup(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 */
GLuint
glmFindMaterial(GLMmodel* model, char* name)
{
GLuint i;
/* XXX doing a linear search on a string key'd list is pretty lame,
but it works and is fast enough for now. */
for (i = 0; i < model->nummaterials; i++) {
if (!strcmp(model->materials[i].name, name))
goto found;
}
/* didn't find the name, so print a warning and return the default
material (0). */
printf("glmFindMaterial(): can't find material \"%s\".\n", name);
i = 0;
found:
return i;
}
/* glmDirName: return the directory given a path
*
* path - filesystem path
*
* NOTE: the return value should be free'd.
*/
static char*
glmDirName(char* path)
{
char* dir;
char* s;
dir = strdup(path);
s = strrchr(dir, '/');
if (s)
s[1] = '\0';
else
dir[0] = '\0';
return dir;
}
int glmFindOrAddTexture(GLMmodel* model, char* name,mycallback *call)
{
GLuint i;
char *dir, *filename;
float width, height;
char *numefis = name;
while (*numefis==' ') numefis++;
for (i = 0; i < model->numtextures; i++) {
if (!strcmp(model->textures[i].name, numefis))
return i;
}
char afis[80];
sprintf(afis,"Loading Textures (%s )...",name);
int procent = ((float)((float)model->numtextures*30/total_textures)/100)*(call->end-call->start)+call->start;
if (call) call->loadcallback(procent,afis); // textures represent 30% from the model (just saying :))
if (strstr(name,":\\"))
{
filename = (char*)malloc(sizeof(char) * (strlen(name) + 1));
strcpy(filename,name);
}
else
{
dir = glmDirName(model->pathname);
filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(numefis) + 1));
strcpy(filename, dir);
//strcpy(filename, numefis);
strcat(filename, numefis);
free(dir);
}
int lung = strlen(filename);
if (filename[lung-1]<32) filename[lung-1]=0;
if (filename[lung-2]<32) filename[lung-2]=0;
model->numtextures++;
model->textures = (GLMtexture*)realloc(model->textures, sizeof(GLMtexture)*model->numtextures);
model->textures[model->numtextures-1].name = strdup(numefis);
model->textures[model->numtextures-1].id = glmLoadTexture(filename, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE, &width, &height);
model->textures[model->numtextures-1].width = width;
model->textures[model->numtextures-1].height = height;
free(filename);
return model->numtextures-1;
}
/* glmReadMTL: read a wavefront material library file
*
* model - properly initialized GLMmodel structure
* name - name of the material library
*/
static GLvoid
glmReadMTL(GLMmodel* model, char* name, mycallback *call)
{
FILE* file;