diff options
Diffstat (limited to '09-september/tomcat/renderer/model.c')
| -rw-r--r-- | 09-september/tomcat/renderer/model.c | 314 |
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, + ¤t_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", ¤t_texture.x, ¤t_texture.y); + + array_append(textures, ¤t_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", ¤t_normal.x, + ¤t_normal.y, + ¤t_normal.z); + array_append(normals, ¤t_normal); + + if(count != 3) + Util_FatalError("Bad normals data on .obj file"); + } + else + { + count = sscanf(buffer, "v %f %f %f\n", ¤t_position.x, + ¤t_position.y, + ¤t_position.z); + + array_append(positions, ¤t_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, ¤t_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); +} |
