#include "shape.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#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);
}
}