#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);
}