aboutsummaryrefslogtreecommitdiff
path: root/09-september/tomcat/renderer/model.c
diff options
context:
space:
mode:
Diffstat (limited to '09-september/tomcat/renderer/model.c')
-rw-r--r--09-september/tomcat/renderer/model.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/09-september/tomcat/renderer/model.c b/09-september/tomcat/renderer/model.c
new file mode 100644
index 0000000..3c65ad3
--- /dev/null
+++ b/09-september/tomcat/renderer/model.c
@@ -0,0 +1,314 @@
+#include "model.h"
+#include "renderer.h"
+#include "../util/array.h"
+#include "../util/util.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define MAX_HASH_MODELS 1024
+static Model *model_hash_table[MAX_HASH_MODELS];
+
+static const int BUFFER_SIZE = 128;
+
+static Model *_model_alloc(const char *name)
+{
+ if(strlen(name) > MAX_PATH_LENGTH)
+ Util_FatalError("File following model name is too long: %s", name);
+
+ if(render.num_models >= MAX_MODELS)
+ return NULL;
+
+ Model *model;
+
+ unsigned int hash_ = Util_Hash( name );
+ hash_ %= MAX_HASH_MODELS;
+
+ model = malloc( sizeof(Model) );
+ memset(model, 0, sizeof(Model) );
+
+ render.models[render.num_models] = model;
+ render.num_models += 1;
+
+ strcpy(model->_name, name);
+ model->_hash_next = model_hash_table[hash_];
+ model_hash_table[hash_] = model;
+
+ model->_hash = hash_;
+
+ return model;
+}
+
+Model *model_get(const char *name)
+{
+ Model *model;
+
+ unsigned int hash_ = Util_Hash( name );
+ hash_ %= MAX_HASH_MODELS;
+
+ if(model_hash_table[hash_] != NULL)
+ {
+ for(model = model_hash_table[hash_]; model; model = model->_hash_next)
+ {
+ if( model->_hash == hash_ )
+ return model;
+ }
+ }
+
+ return NULL;
+}
+
+typedef struct
+{
+ vertex_t *data;
+ GLushort *indices;
+
+ Array *positions;
+ Array *textures;
+ Array *normals;
+ Array *tangents;
+
+ unsigned int vertex_count, index_count;
+ unsigned int index_pointer;
+
+ GLubyte hasTextCoords, hasNormals;
+} OBJ_Mesh;
+
+static vertex_t *search_index(vertex_t *pkey, vertex_t *pelem, unsigned int vertex_count)
+{
+ int i;
+ for(i = 0; i < vertex_count; i++)
+ {
+ if(pelem[i].position.x == pkey->position.x &&
+ pelem[i].position.y == pkey->position.y &&
+ pelem[i].position.z == pkey->position.z &&
+ pelem[i].texCoord.x == pkey->texCoord.x &&
+ pelem[i].texCoord.y == pkey->texCoord.y &&
+ pelem[i].normal.x == pkey->normal.x &&
+ pelem[i].normal.y == pkey->normal.y &&
+ pelem[i].normal.z == pkey->normal.z)
+ {
+ return &pelem[i];
+ }
+ }
+ return NULL;
+}
+
+static void calculate_tangents(OBJ_Mesh *mesh)
+{
+ int i;
+ for(i = 0; i < mesh->index_count; i += 3)
+ {
+ int i0 = i;
+ int i1 = i + 1;
+ int i2 = i + 2;
+
+ Vec3 *v0 = ( (Vec3 *)mesh->positions->data) + i0;
+ Vec3 *v1 = ( (Vec3 *)mesh->positions->data) + i1;
+ Vec3 *v2 = ( (Vec3 *)mesh->positions->data) + i2;
+
+ Vec2 *uv0 = ( (Vec2 *)mesh->textures->data) + i0;
+ Vec2 *uv1 = ( (Vec2 *)mesh->textures->data) + i1;
+ Vec2 *uv2 = ( (Vec2 *)mesh->textures->data) + i2;
+
+ Vec3 deltaPos1 = vec3_sub(v1, v0);
+ Vec3 deltaPos2 = vec3_sub(v2, v0);
+
+ Vec2 deltaUV1 = vec2_sub(uv1, uv0);
+ Vec2 deltaUV2 = vec2_sub(uv2, uv0);
+
+ GLfloat r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
+
+ deltaPos1 = vec3_scalar_mul(&deltaPos1, deltaUV2.y);
+ deltaPos2 = vec3_scalar_mul(&deltaPos2, deltaUV1.y);
+
+ Vec3 tangent = vec3_sub(&deltaPos1, &deltaPos2);
+ tangent = vec3_scalar_mul(&tangent, r);
+
+ ( (Vec3 *)mesh->tangents->data)[i2] = tangent;
+ ( (Vec3 *)mesh->tangents->data)[i1] = tangent;
+ ( (Vec3 *)mesh->tangents->data)[i0] = tangent;
+ }
+ /*
+ We normalize the tangents at the end of the parse_obj_index loop
+ for(i = 0; i < mesh.index_count; i++)
+ {
+ mesh.tangents[i] = vec3_normalize(&mesh.tangents[i]);
+ }
+ */
+}
+
+static void parse_obj_index(OBJ_Mesh *mesh, vertex_t *current_vertex)
+{
+ vertex_t *indexOnArray = search_index(current_vertex, mesh->data, mesh->vertex_count);
+
+ /* We check if the vertex was already loaded, so the index points to the created vertex instead of repeating data*/
+ if(indexOnArray == NULL)
+ {
+ mesh->data = (vertex_t*) realloc( mesh->data, sizeof(vertex_t) * (++mesh->vertex_count) );
+
+ /* We make the index point to the last vertex added */
+ mesh->indices[mesh->index_pointer] = mesh->vertex_count - 1;
+
+ mesh->data[mesh->vertex_count - 1] = *current_vertex;
+ }
+ else
+ {
+ GLushort index = (GLushort)(indexOnArray - mesh->data);
+ mesh->data[index].tangent = vec3_add( &mesh->data[index].tangent,
+ &current_vertex->tangent );
+
+ /* We make the index point to the previus vertex added instead of creating a new one */
+ mesh->indices[mesh->index_pointer] = index;
+ }
+
+ mesh->index_pointer += 1;
+}
+
+Model *model_obj_new(const char *path)
+{
+ Model *model;
+ model = model_get(path);
+
+ if(model)
+ return model;
+
+ OBJ_Mesh obj_mesh;
+ memset( &obj_mesh, 0, sizeof(OBJ_Mesh) );
+
+ obj_mesh.positions = array_create( sizeof(Vec3) );
+ obj_mesh.normals = array_create( sizeof(Vec3) );
+ obj_mesh.textures = array_create( sizeof(Vec2) );
+
+ Array *positions = array_create( sizeof(Vec3) );
+ Array *normals = array_create( sizeof(Vec3) );
+ Array *textures = array_create( sizeof(Vec2) );
+
+ vertex_t current_vertex;
+
+ Vec3 current_position;
+ Vec3 current_normal;
+ Vec2 current_texture;
+
+ int count = 0, i;
+ int texture[3], normal[3], verts[3];
+
+ FILE *file = fopen(path, "r");
+ if(file == NULL)
+ Util_FatalError("%s file could not be loaded!", path);
+
+ char buffer[BUFFER_SIZE];
+ while( !feof(file) )
+ {
+ fgets(buffer, BUFFER_SIZE, file);
+ switch(buffer[0])
+ {
+ case 'v':
+ if(buffer[1] == 't')
+ {
+ count = sscanf(buffer, "vt %f %f\n", &current_texture.x, &current_texture.y);
+
+ array_append(textures, &current_texture);
+
+ if(count != 2)
+ Util_FatalError("Bad texture coordinates on .obj file");
+ }
+ else if(buffer[1] == 'n')
+ {
+ count = sscanf(buffer, "vn %f %f %f\n", &current_normal.x,
+ &current_normal.y,
+ &current_normal.z);
+ array_append(normals, &current_normal);
+
+ if(count != 3)
+ Util_FatalError("Bad normals data on .obj file");
+ }
+ else
+ {
+ count = sscanf(buffer, "v %f %f %f\n", &current_position.x,
+ &current_position.y,
+ &current_position.z);
+
+ array_append(positions, &current_position);
+
+ if(count != 3)
+ Util_FatalError("Bad vertices data on .obj file");
+ }
+ break;
+ case 'f':
+ obj_mesh.index_count += 3;
+
+ count = sscanf(buffer, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
+ &verts[0], &texture[0], &normal[0],
+ &verts[1], &texture[1], &normal[1],
+ &verts[2], &texture[2], &normal[2]);
+
+ if(count != 9)
+ Util_FatalError("Bad face data on .obj file");
+
+ array_append(obj_mesh.positions, &( (Vec3 *)positions->data )[ verts[0] -1 ] );
+ array_append(obj_mesh.positions, &( (Vec3 *)positions->data )[ verts[1] -1 ] );
+ array_append(obj_mesh.positions, &( (Vec3 *)positions->data )[ verts[2] -1 ] );
+
+ array_append(obj_mesh.normals, &( (Vec3 *)normals->data )[ normal[0] - 1 ] );
+ array_append(obj_mesh.normals, &( (Vec3 *)normals->data )[ normal[1] - 1 ] );
+ array_append(obj_mesh.normals, &( (Vec3 *)normals->data )[ normal[2] - 1 ] );
+
+ array_append(obj_mesh.textures, &( (Vec2 *)textures->data )[ texture[0] - 1 ] );
+ array_append(obj_mesh.textures, &( (Vec2 *)textures->data )[ texture[1] - 1 ] );
+ array_append(obj_mesh.textures, &( (Vec2 *)textures->data )[ texture[2] - 1 ] );
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ obj_mesh.indices = malloc( obj_mesh.index_count * sizeof(GLushort) );
+ obj_mesh.tangents = array_create_by_size( sizeof(Vec3), obj_mesh.index_count );
+ calculate_tangents(&obj_mesh);
+
+ for(i = 0; i < obj_mesh.index_count; i++)
+ {
+ current_vertex.position = ( (Vec3 *)obj_mesh.positions->data )[i];
+ current_vertex.texCoord = ( (Vec2 *)obj_mesh.textures->data )[i];
+ current_vertex.normal = ( (Vec3 *)obj_mesh.normals->data )[i];
+ current_vertex.tangent = ( (Vec3 *)obj_mesh.tangents->data )[i];
+
+ parse_obj_index(&obj_mesh, &current_vertex);
+ }
+
+ for(i = 0; i < obj_mesh.vertex_count; i++)
+ {
+ obj_mesh.data[i].tangent = vec3_normalize(&obj_mesh.data[i].tangent);
+ }
+
+ array_free(obj_mesh.positions);
+ array_free(obj_mesh.normals);
+ array_free(obj_mesh.textures);
+ array_free(obj_mesh.tangents);
+
+ array_free(positions);
+ array_free(textures);
+ array_free(normals);
+
+ fclose(file);
+
+ GLsizeiptr vertexBuffersize = obj_mesh.vertex_count * sizeof(vertex_t);
+ GLsizeiptr indexBuffersize = obj_mesh.index_count * sizeof(GLushort);
+
+ model = _model_alloc(path);
+
+ model->mesh = mesh_new(obj_mesh.data, vertexBuffersize, obj_mesh.indices, indexBuffersize);
+
+ free(obj_mesh.data);
+ free(obj_mesh.indices);
+
+ return model;
+}
+
+void model_purge(Model *model)
+{
+ mesh_purge(model->mesh);
+}