#include "shape.h" #include #include #include #include "../util/util.h" #define NUM_ARRAY_ELEMENTS(a) sizeof(a) / sizeof(*a) shape_t *Shape_CreateFromRawData(vertex_t* vertices, GLsizeiptr vertexBuffersize, GLushort* indices, GLsizeiptr indexBuffersize) { shape_t *shape = malloc( sizeof(shape_t) ); shape->num_indices = ( indexBuffersize / sizeof(GLushort) ); glGenVertexArrays(1, &shape->vao); glGenBuffers(1, &shape->vbo); glGenBuffers(1, &shape->ebo); glBindVertexArray(shape->vao); glBindBuffer(GL_ARRAY_BUFFER, shape->vbo); glBufferData(GL_ARRAY_BUFFER, vertexBuffersize, vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, shape->ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBuffersize, indices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (const void*)offsetof(vertex_t, position) ); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (const void*)offsetof(vertex_t, texCoord) ); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (const void*)offsetof(vertex_t, normal) ); glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (const void*)offsetof(vertex_t, tangent) ); glBindVertexArray(0); return shape; } shape_t* Shape_MakeSkyBox(float size) { vec3_t positions[] = { {-size, size, -size}, {-size, -size, -size}, {+size, -size, -size}, {+size, -size, -size}, {+size, +size, -size}, {-size, +size, -size}, {-size, -size, +size}, {-size, -size, -size}, {-size, +size, -size}, {-size, +size, -size}, {-size, +size, +size}, {-size, -size, +size}, {+size, -size, -size}, {+size, -size, +size}, {+size, +size, +size}, {+size, +size, +size}, {+size, +size, -size}, {+size, -size, -size}, {-size, -size, +size}, {-size, +size, +size}, {+size, +size, +size}, {+size, +size, +size}, {+size, -size, +size}, {-size, -size, +size}, {-size, +size, -size}, {+size, +size, -size}, {+size, +size, +size}, {+size, +size, +size}, {-size, +size, +size}, {-size, +size, -size}, {-size, -size, -size}, {-size, -size, +size}, {+size, -size, -size}, {+size, -size, -size}, {-size, -size, +size}, {+size, -size, +size} }; shape_t *shape = malloc( sizeof(shape_t) ); shape->num_indices = 0; glGenVertexArrays(1, &shape->vao); glGenBuffers(1, &shape->vbo); shape->ebo = 0; glBindVertexArray(shape->vao); glBindBuffer(GL_ARRAY_BUFFER, shape->vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0 ); glBindVertexArray(0); return shape; } static const int BUFFER_size = 128; typedef struct { vertex_t *data; GLushort *indices; vec3_t *positions; vec2_t *textures; vec3_t *normals; vec3_t *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_t *v0 = &mesh->positions[i0]; vec3_t *v1 = &mesh->positions[i1]; vec3_t *v2 = &mesh->positions[i2]; vec2_t *uv0 = &mesh->textures[i0]; vec2_t *uv1 = &mesh->textures[i1]; vec2_t *uv2 = &mesh->textures[i2]; vec3_t deltaPos1 = vec3_sub(v1, v0); vec3_t deltaPos2 = vec3_sub(v2, v0); vec2_t deltaUV1 = vec2_sub(uv1, uv0); vec2_t 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_t tangent = vec3_sub(&deltaPos1, &deltaPos2); tangent = vec3_scalar_mul(&tangent, r); mesh->tangents[i2] = tangent; mesh->tangents[i1] = tangent; mesh->tangents[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; } shape_t* Shape_LoadOBJ(const char* path) { OBJ_Mesh mesh; memset( &mesh, 0, sizeof(OBJ_Mesh) ); vec3_t *positions = NULL; vec3_t *normals = NULL; vec2_t *textures = NULL; vertex_t current_vertex; unsigned int positions_count = 0, normals_count = 0, textures_count = 0; 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') { textures = (vec2_t*) realloc(textures, sizeof(vec2_t) * (++textures_count) ); count = sscanf(buffer, "vt %f %f\n", &textures[textures_count - 1].x, &textures[textures_count - 1].y); if(count != 2) Util_FatalError("Bad texture coordinates on .obj file"); } else if(buffer[1] == 'n') { normals = (vec3_t*) realloc(normals, sizeof(vec3_t) * (++normals_count) ); count = sscanf(buffer, "vn %f %f %f\n", &normals[normals_count - 1].x, &normals[normals_count - 1].y, &normals[normals_count - 1].z); if(count != 3) Util_FatalError("Bad normals data on .obj file"); } else { positions = (vec3_t*) realloc(positions, sizeof(vec3_t) * (++positions_count) ); count = sscanf(buffer, "v %f %f %f\n", &positions[positions_count - 1].x, &positions[positions_count - 1].y, &positions[positions_count - 1].z); if(count != 3) Util_FatalError("Bad vertices data on .obj file"); } break; case 'f': mesh.index_count += 3; mesh.positions = realloc(mesh.positions, mesh.index_count * sizeof(vec3_t) ); mesh.textures = realloc(mesh.textures, mesh.index_count * sizeof(vec2_t) ); mesh.normals = realloc(mesh.normals, mesh.index_count * sizeof(vec3_t) ); 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"); mesh.positions[mesh.index_count - 3] = positions[ verts[0] - 1 ]; mesh.textures[mesh.index_count - 3] = textures[ texture[0] - 1 ]; mesh.normals[mesh.index_count - 3] = normals[ normal[0] - 1 ]; mesh.positions[mesh.index_count - 2] = positions[ verts[1] - 1 ]; mesh.textures[mesh.index_count - 2] = textures[ texture[1] - 1 ]; mesh.normals[mesh.index_count - 2] = normals[ normal[1] - 1 ]; mesh.positions[mesh.index_count - 1] = positions[ verts[2] - 1 ]; mesh.textures[mesh.index_count - 1] = textures[ texture[2] - 1 ]; mesh.normals[mesh.index_count - 1] = normals[ normal[2] - 1 ]; break; default: break; } } mesh.indices = malloc( mesh.index_count * sizeof(GLushort) ); mesh.tangents = malloc( mesh.index_count * sizeof(vec3_t) ); calculate_tangents(&mesh); for(i = 0; i < mesh.index_count; i++) { current_vertex = (vertex_t){.position = mesh.positions[i], .texCoord = mesh.textures[i], .normal = mesh.normals[i], .tangent = mesh.tangents[i] }; parse_obj_index(&mesh, ¤t_vertex); } for(i = 0; i < mesh.vertex_count; i++) { mesh.data[i].tangent = vec3_normalize(&mesh.data[i].tangent); } free(mesh.positions); free(mesh.normals); free(mesh.textures); free(mesh.tangents); free(positions); free(textures); free(normals); fclose(file); GLsizeiptr vertexBuffersize = mesh.vertex_count * sizeof(vertex_t); GLsizeiptr indexBuffersize = mesh.index_count * sizeof(GLushort); shape_t* shape = Shape_CreateFromRawData(mesh.data, vertexBuffersize, mesh.indices, indexBuffersize); free(mesh.data); free(mesh.indices); return shape; } void Shape_Free(shape_t* shape) { if(shape) { if(shape->vbo) glDeleteBuffers(1, &shape->vbo); if(shape->ebo) glDeleteBuffers(1, &shape->ebo); if(shape->vao) glDeleteVertexArrays(1, &shape->vao); free(shape); } }