aboutsummaryrefslogtreecommitdiff
#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,
                                              &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;
}

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, &current_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);
    }
}