#include "model.h" #include "renderer.h" #include "../util/array.h" #include "../util/util.h" #include #include #include #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); }