summaryrefslogtreecommitdiffstats
path: root/src/video/model.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/video/model.c')
-rw-r--r--src/video/model.c391
1 files changed, 391 insertions, 0 deletions
diff --git a/src/video/model.c b/src/video/model.c
new file mode 100644
index 0000000..675820e
--- /dev/null
+++ b/src/video/model.c
@@ -0,0 +1,391 @@
+// include a few datastructures & constants
+
+#include "video/model.h"
+#include "video/video.h"
+
+#include "math.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "zlib.h"
+
+extern void readMaterialLibrary(char *buf, Mesh *pMesh);
+extern void setMaterial(char *buf, Mesh *pMesh, int *iGroup);
+
+void readVector(char *buf, vec3 *pVertex ) {
+ if( sscanf(buf, " %f %f %f ",
+ pVertex->v, pVertex->v + 1, pVertex->v + 2) != 3) {
+ fprintf(stderr, "*** failed reading Vector from %s\n", buf);
+ }
+}
+
+void readTriFace(char *buf, face *pFace, int *iFace, int iGroup) {
+ // assumes model in "f %d//%d %d//%d %d//%d\n" format
+ if(
+ sscanf(buf, "f %d//%d %d//%d %d//%d ",
+ pFace[ *iFace ].vertex + 0, pFace[ *iFace ].normal + 0,
+ pFace[ *iFace ].vertex + 1, pFace[ *iFace ].normal + 1,
+ pFace[ *iFace ].vertex + 2, pFace[ *iFace ].normal + 2) == 6) {
+ pFace[ *iFace ].material = iGroup;
+ (*iFace)++;
+ } else {
+ fprintf(stderr, "*** failed parsing face %s\n", buf);
+ }
+}
+
+void readQuadFace(char *buf, quadFace *pFace, int *iFace, int iGroup) {
+// assumes model in "f %d//%d %d//%d %d//%d %d//%d\n" format
+ if(
+ sscanf(buf, "f %d//%d %d//%d %d//%d %d//%d",
+ pFace[ *iFace ].vertex + 0, pFace[ *iFace ].normal + 0,
+ pFace[ *iFace ].vertex + 1, pFace[ *iFace ].normal + 1,
+ pFace[ *iFace ].vertex + 2, pFace[ *iFace ].normal + 2,
+ pFace[ *iFace ].vertex + 3, pFace[ *iFace ].normal + 3) == 8) {
+ pFace[ *iFace ].material = iGroup;
+ (*iFace)++;
+ } else {
+ fprintf(stderr, "*** failed parsing face %s\n", buf);
+ }
+}
+
+Mesh* readMeshFromFile(const char *filename, MeshType iType) {
+ // allocate some buffers
+ // vertices, normals
+
+ vec3 *pVertices = malloc( sizeof(vec3) * MAX_VERTICES );
+ vec3 *pNormals = malloc( sizeof(vec3) * MAX_NORMALS );
+
+ quadFace *pqFaces = NULL;
+ face *pFaces = NULL;
+ int iFaceSize = 0;
+
+ Mesh *pMesh = malloc( sizeof(Mesh) );
+ int iGroup = 0;
+ int iVertex = 0, iNormal = 0, iFace = 0;
+
+ gzFile f;
+ char buf[BUF_SIZE];
+
+ int i, j, k;
+
+
+ switch(iType) {
+ case TRI_MESH:
+ pFaces = malloc( sizeof(face) * MAX_FACES );
+ iFaceSize = 3;
+ break;
+ case QUAD_MESH:
+ pqFaces = malloc( sizeof(quadFace) * MAX_FACES );
+ iFaceSize = 4;
+ break;
+ default:
+ fprintf(stderr, "[fatal]: illegal mesh type\n");
+ exit(1);
+ }
+
+ if((f = gzopen(filename, "r")) == 0) {
+ fprintf(stderr, "*** could not open file '%s'\n", filename);
+ return NULL;
+ }
+
+ while( gzgets(f, buf, sizeof(buf)) ) {
+ switch(buf[0]) {
+ case 'm': // material library
+ readMaterialLibrary(buf, pMesh);
+ break;
+ case 'u': // material name
+ setMaterial(buf, pMesh, &iGroup);
+ break;
+ case 'v': // vertex, normal, texture coordinate
+ switch(buf[1]) {
+ case ' ':
+ readVector(buf + 1, pVertices + iVertex);
+ iVertex++;
+ break;
+ case 'n': // normal
+ readVector(buf + 2, pNormals + iNormal);
+ iNormal++;
+ break;
+ case 't': // texture
+ break; // ignore textures;
+ }
+ break;
+ case 'f': // face (can produce multiple faces)
+ switch(iType) {
+ case TRI_MESH:
+ readTriFace(buf, pFaces, &iFace, iGroup);
+ break;
+ case QUAD_MESH:
+ readQuadFace(buf, pqFaces, &iFace, iGroup);
+ break;
+ }
+ break;
+ }
+ }
+
+ gzclose(f);
+
+ // printf("vertices: %d, normals: %d, faces: %d\n", iVertex, iNormal, iFace);
+
+ // count each material
+ pMesh->pnFaces = malloc( sizeof(int) * pMesh->nMaterials );
+ for(i = 0; i < pMesh->nMaterials; i++) {
+ pMesh->pnFaces[ i ] = 0;
+ }
+
+ switch(iType) {
+ case TRI_MESH:
+ for(i = 0; i < iFace; i++) {
+ pMesh->pnFaces[ pFaces[i].material ] += 1;
+ }
+ break;
+ case QUAD_MESH:
+ for(i = 0; i < iFace; i++) {
+ pMesh->pnFaces[ pqFaces[i].material ] += 1;
+ }
+ break;
+ }
+
+
+ // combine vectors & normals for each vertex, doubling where necessary
+
+ // initialize lookup[ vertex ][ normal ] table
+ {
+ int nVertices = 0;
+ int **lookup = malloc( sizeof(int*) * iVertex );
+
+ for(i = 0; i < iVertex; i++) {
+ lookup[i] = malloc( sizeof(int) * iNormal );
+ for(j = 0; j < iNormal; j++) {
+ lookup[i][j] = -1;
+ }
+ }
+
+ switch(iType) {
+ case TRI_MESH:
+ for(i = 0; i < iFace; i++) {
+ for(j = 0; j < iFaceSize; j++) {
+ int vertex = pFaces[i].vertex[j] - 1;
+ int normal = pFaces[i].normal[j] - 1;
+ if( lookup[ vertex ][ normal ] == -1 ) {
+ lookup[ vertex ][ normal ] = nVertices;
+ nVertices++;
+ }
+ }
+ }
+ break;
+ case QUAD_MESH:
+ for(i = 0; i < iFace; i++) {
+ for(j = 0; j < iFaceSize; j++) {
+ int vertex = pqFaces[i].vertex[j] - 1;
+ int normal = pqFaces[i].normal[j] - 1;
+ if( lookup[ vertex ][ normal ] == -1 ) {
+ lookup[ vertex ][ normal ] = nVertices;
+ nVertices++;
+ }
+ }
+ }
+ break;
+ }
+
+ // now that we know everything, build vertexarray based mesh
+ // copy normals & vertices indexed by lookup-table
+ pMesh->nVertices = nVertices;
+ pMesh->pVertices = malloc( sizeof(GLfloat) * 3 * nVertices );
+ pMesh->pNormals = malloc( sizeof(GLfloat) * 3 * nVertices );
+ for(i = 0; i < iVertex; i++) {
+ for(j = 0; j < iNormal; j++) {
+ int vertex = lookup[ i ][ j ];
+ if(vertex != -1 ) {
+ for(k = 0; k < 3; k++) {
+ *(pMesh->pVertices + 3 * vertex + k) = pVertices[ i ].v[k];
+ *(pMesh->pNormals + 3 * vertex + k) = pNormals[ j ].v[k];
+ }
+ }
+ }
+ }
+
+ // build indices (per Material)
+ {
+ int *face;
+ face = malloc( sizeof(int) * pMesh->nMaterials );
+ pMesh->ppIndices = malloc( sizeof(GLshort*) * pMesh->nMaterials );
+ for(i = 0; i < pMesh->nMaterials; i++) {
+ pMesh->ppIndices[i] =
+ malloc( sizeof(GLshort) * iFaceSize * pMesh->pnFaces[i] );
+ face[i] = 0;
+ }
+ switch(iType) {
+ case TRI_MESH:
+ for(i = 0; i < iFace; i++) {
+ int material = pFaces[i].material;
+ for(j = 0; j < iFaceSize; j++) {
+ int vertex = pFaces[i].vertex[j] - 1;
+ int normal = pFaces[i].normal[j] - 1;
+ pMesh->ppIndices[ material ][ iFaceSize * face[ material ] + j ] =
+ lookup[ vertex ][ normal ];
+ }
+ face[ material ] = face[ material] + 1;
+ }
+ break;
+ case QUAD_MESH:
+ for(i = 0; i < iFace; i++) {
+ int material = pqFaces[i].material;
+ for(j = 0; j < iFaceSize; j++) {
+ int vertex = pqFaces[i].vertex[j] - 1;
+ int normal = pqFaces[i].normal[j] - 1;
+ pMesh->ppIndices[ material ][ iFaceSize * face[ material ] + j ] =
+ lookup[ vertex ][ normal ];
+ }
+ face[ material ] = face[ material] + 1;
+ }
+ break;
+ }
+ free(face);
+ }
+ // printf("[scenegraph] vertices: %d, faces: %d\n", nVertices, iFace);
+
+ free(lookup);
+ free(pVertices);
+ free(pNormals);
+ switch(iType) {
+ case TRI_MESH:
+ free(pFaces);
+ break;
+ case QUAD_MESH:
+ free(pqFaces);
+ break;
+ }
+ }
+ computeBBox(pMesh);
+
+ return pMesh;
+}
+
+void drawModel(Mesh *pMesh, MeshType iType) {
+ int i;
+ int iFaceSize = 0;
+ GLenum primitive = GL_TRIANGLES;
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, pMesh->pVertices);
+ glNormalPointer(GL_FLOAT, 0, pMesh->pNormals);
+
+ switch(iType) {
+ case TRI_MESH:
+ primitive = GL_TRIANGLES;
+ iFaceSize = 3;
+ break;
+ case QUAD_MESH:
+ primitive = GL_QUADS;
+ iFaceSize = 4;
+ break;
+ default:
+ fprintf(stderr, "[fatal]: illegal mesh type\n");
+ exit(1);
+ }
+
+ for(i = 0; i < pMesh->nMaterials; i++) {
+ glMaterialfv(GL_FRONT, GL_AMBIENT,
+ pMesh->pMaterials[i].ambient);
+ glMaterialfv(GL_FRONT, GL_DIFFUSE,
+ pMesh->pMaterials[i].diffuse);
+ glMaterialfv(GL_FRONT, GL_SPECULAR,
+ pMesh->pMaterials[i].specular);
+ glMaterialf(GL_FRONT, GL_SHININESS,
+ pMesh->pMaterials[i].shininess);
+
+ glDrawElements(primitive, iFaceSize * pMesh->pnFaces[i],
+ GL_UNSIGNED_SHORT, pMesh->ppIndices[i]);
+
+ polycount += pMesh->pnFaces[i];
+ }
+
+}
+
+void drawModelExplosion(Mesh *pMesh, float fRadius) {
+ int i, j, k;
+#define EXP_VECTORS 10
+ float vectors[][3] = {
+ { 0.03f, -0.06f, -0.07f },
+ { 0.04f, 0.08f, -0.03f },
+ { 0.10f, -0.04f, -0.07f },
+ { 0.06f, -0.09f, -0.10f },
+ { -0.03f, -0.05f, 0.02f },
+ { 0.07f, 0.08f, -0.00f },
+ { 0.01f, -0.04f, 0.10f },
+ { -0.01f, -0.07f, 0.09f },
+ { 0.01f, -0.01f, -0.09f },
+ { -0.04f, 0.04f, 0.02f }
+ };
+
+ for(i = 0; i < pMesh->nMaterials; i++) {
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,
+ pMesh->pMaterials[i].ambient);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,
+ pMesh->pMaterials[i].diffuse);
+ glMaterialfv(GL_FRONT, GL_SPECULAR,
+ pMesh->pMaterials[i].specular);
+ glMaterialf(GL_FRONT, GL_SHININESS,
+ pMesh->pMaterials[i].shininess);
+
+ for(j = 0; j < pMesh->pnFaces[i]; j++) {
+
+ float *normal, *vertex;
+
+ normal = pMesh->pNormals + 3 * pMesh->ppIndices[i][3 * j];
+
+ glPushMatrix();
+ glTranslatef(fRadius * (*(normal + 0) + vectors[j % EXP_VECTORS][0]),
+ fRadius * (*(normal + 1) + vectors[j % EXP_VECTORS][1]),
+ fabsf(fRadius * (*(normal + 2) + vectors[j % EXP_VECTORS][2]) ));
+ glBegin(GL_TRIANGLES);
+ for(k = 0; k < 3; k++) {
+ normal = pMesh->pNormals + 3 * pMesh->ppIndices[i][3 * j + k];
+ vertex = pMesh->pVertices + 3 * pMesh->ppIndices[i][3 * j + k];
+
+ glNormal3fv(normal);
+ glVertex3fv(vertex);
+ }
+ glEnd();
+ glPopMatrix();
+ }
+ polycount += pMesh->pnFaces[i];
+ }
+}
+
+void computeBBox(Mesh *pMesh) {
+ int i, j;
+ vec3 vMin, vMax, vSize;
+
+ vcopy(pMesh->pVertices, vMin.v);
+ vcopy(pMesh->pVertices, vMax.v);
+
+ for(i = 0; i < pMesh->nVertices; i++) {
+ for(j = 0; j < 3; j++) {
+ if(vMin.v[j] > pMesh->pVertices[3 * i + j])
+ vMin.v[j] = pMesh->pVertices[3 * i + j];
+ if(vMax.v[j] < pMesh->pVertices[3 * i + j])
+ vMax.v[j] = pMesh->pVertices[3 * i + j];
+ }
+ /*
+ if(
+ vMin.v[0] <= pMesh->pVertices[3 * i + 0] &&
+ vMin.v[1] <= pMesh->pVertices[3 * i + 1] &&
+ vMin.v[2] <= pMesh->pVertices[3 * i + 2]
+ )
+ vcopy(pMesh->pVertices + 3 * i, vMin.v);
+ if(
+ vMax.v[0] >= pMesh->pVertices[3 * i + 0] &&
+ vMax.v[1] >= pMesh->pVertices[3 * i + 1] &&
+ vMax.v[2] >= pMesh->pVertices[3 * i + 2]
+ )
+ vcopy(pMesh->pVertices + 3 * i, vMax.v);
+ */
+ }
+
+ vsub(vMax.v, vMin.v, vSize.v);
+ vcopy(vMin.v, pMesh->BBox.vMin.v);
+ vcopy(vSize.v, pMesh->BBox.vSize.v);
+ pMesh->BBox.fRadius=length(pMesh->BBox.vSize.v)/10;
+}