aboutsummaryrefslogtreecommitdiff
#include "renderer.h"
#include "../util/util.h"

#include <stdlib.h>
#include <string.h>

#define MAX_HASH_SHADER   16
static Shader *shader_hash_table[MAX_HASH_SHADER];

static void CompileShader(const char *source, GLuint shaderID)
{
    glShaderSource(shaderID, 1, &source, 0);
    glCompileShader(shaderID);
    GLint error;
    glGetShaderiv(shaderID, GL_COMPILE_STATUS, &error);
    if(error != GL_TRUE)
    {
        GLint logLenth;
        glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &logLenth);
        GLchar buffer[logLenth];
        glGetShaderInfoLog(shaderID, logLenth, &logLenth, buffer);
        glDeleteShader(shaderID);
        Util_FatalError("Some shader failed to compile:\n%s", buffer);
    }
}

Shader *shader_new(const char *name, const char *vertexShaderPath, const char *fragShaderPath)
{
    if(strlen(name) >= MAX_PATH_LENGTH)
        Util_FatalError("File following shader name is too long: %s", name);

    Shader *s;
    s = shader_get(name);
    if(s != NULL)
        return s;

    char *vertexShaderSource = Util_LoadFile(vertexShaderPath);
    char *fragmentShaderSource = Util_LoadFile(fragShaderPath);

    GLuint vs = 0, fs = 0, program;
    vs = glCreateShader(GL_VERTEX_SHADER);
    fs = glCreateShader(GL_FRAGMENT_SHADER);

    if(vs == 0 || fs == 0)
        Util_FatalError("Shaders could not be created\n");

    program = glCreateProgram();

    CompileShader(vertexShaderSource, vs);
    CompileShader(fragmentShaderSource, fs);

    glAttachShader(program, vs);
    glAttachShader(program, fs);

    glLinkProgram(program);

    GLint error;
    glGetProgramiv(program, GL_LINK_STATUS, &error);

    if(error != GL_TRUE)
    {
        GLint logLength;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);

        GLchar buffer[logLength];
        glGetProgramInfoLog(program, logLength, &logLength, buffer);

        glDeleteProgram(program);
        glDeleteShader(vs);
        glDeleteShader(fs);

        Util_FatalError("Shader program failed to link!:\n%s", buffer);
    }
    /** Free some usless resources **/
    glDetachShader(program, vs);
    glDetachShader(program, fs);
    glDeleteShader(vs);
    glDeleteShader(fs);

    free(vertexShaderSource);
    free(fragmentShaderSource);

    /** Alloc the new texture **/
    s = malloc( sizeof(Shader) );
    memset(s, 0, sizeof(Shader) );
    s->id = program;

    /** Register inside the resource manager **/
    unsigned int hash_ = Util_Hash( name );
    hash_ %= MAX_HASH_SHADER;

    render.shaders[render.num_shaders] = s;
    render.num_shaders += 1;

    strcpy(s->name, name);
    s->_hash_next = shader_hash_table[hash_];
    shader_hash_table[hash_] = s;

    s->_hash = hash_;

    /** Return the final result **/
    return s;
}

Shader *shader_get(const char *name)
{
    Shader *s;

    unsigned int hash_ = Util_Hash( name );
    hash_ %= MAX_HASH_SHADER;

    if(shader_hash_table[hash_] != NULL)
    {
        for(s = shader_hash_table[hash_]; s; s = s->_hash_next)
        {
            if( s->_hash == hash_ )
                return s;
        }
    }
    return NULL;
}

void shader_purge(Shader *shader)
{
    /** Purge the opengl data **/
    if(shader->id != 0)
    {
        glUseProgram(0);
        glDeleteProgram(shader->id);
        shader->id = 0;
    }
}

GLint shader_get_uniform_location( Shader *s, const char *uniformName )
{
    GLint u = glGetUniformLocation(s->id, uniformName);
    if(u == GL_INVALID_INDEX)
        Util_FatalError("Uniform \"%s\" could not be found!", uniformName);
    else
        return u;

    return 0;
}

GLint shader_get_attrib_location( Shader *s, const char *attributeName )
{
    GLint attrLocation = glGetAttribLocation(s->id, attributeName);
    if(attrLocation < 0)
        Util_FatalError("Attribute \"%s\" could not be found!\n", attributeName);
    return attrLocation;
}

void shader_set_uniform_mat4( Shader *s, const char *name, const float matrix[16] )
{
    GLint location = shader_get_uniform_location(s, name);
    glUniformMatrix4fv(location, 1, GL_FALSE, matrix);
}

void shader_set_uniform_float( Shader *s, const char *name, const float val )
{
    GLint location = shader_get_uniform_location(s, name);
    glUniform1f(location, val);
}

void shader_set_uniform_vec2( Shader *s, const char *name, const float vec[2] )
{
    GLint location = shader_get_uniform_location(s, name);
    glUniform2fv(location, 1, vec);
}

void shader_set_uniform_vec3( Shader *s, const char *name, const float vec[3] )
{
    GLint location = shader_get_uniform_location(s, name);
    glUniform3fv(location, 1, vec);
}

void shader_set_uniform_vec4( Shader *s, const char *name, const float vec[4] )
{
    GLint location = shader_get_uniform_location(s, name);
    glUniform4fv(location, 1, vec);
}

void shader_set_uniform_int( Shader *s, const char *name, const int val )
{
    GLint location = shader_get_uniform_location(s, name);
    glUniform1i(location, val);
}