You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
352 lines
8.6 KiB
352 lines
8.6 KiB
#include "shader.h"
|
|
#include "file_io.h"
|
|
#include "array.h"
|
|
#include "num_types.h"
|
|
#include "string_utils.h"
|
|
#include "log.h"
|
|
#include "renderer.h"
|
|
#include "texture.h"
|
|
#include "gl_load.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
// Constants for locations of attributes inside all shaders
|
|
const int POSITION_LOC = 0;
|
|
const int NORMAL_LOC = 1;
|
|
const int UV_LOC = 2;
|
|
const int COLOR_LOC = 3;
|
|
|
|
static uint* shader_list;
|
|
static int* empty_indices;
|
|
|
|
void debug_print_shader(const char* shaderText)
|
|
{
|
|
size_t len = strlen(shaderText);
|
|
int line_count = 1;
|
|
printf("%d. ", line_count);
|
|
for(uint i = 0; i < len; i++)
|
|
{
|
|
if(shaderText[i] != '\n')
|
|
printf("%c", shaderText[i]);
|
|
else
|
|
printf("\n%d. ", ++line_count);
|
|
}
|
|
printf("\n END_DEBUG_PRINT\n\n");
|
|
}
|
|
|
|
char* run_preprocessor(char* shader_text)
|
|
{
|
|
char* include_loc = strstr(shader_text, "//include");
|
|
if(include_loc)
|
|
{
|
|
char* line_end = strchr(include_loc, '\n');
|
|
int line_size = line_end - include_loc;
|
|
char* inc_line = malloc((sizeof(char) * line_size) + 1);
|
|
strncpy(inc_line, include_loc, line_size);
|
|
inc_line[line_size] = '\0';
|
|
|
|
char* filename = strtok(inc_line, " ");
|
|
while(filename)
|
|
{
|
|
filename = strtok(NULL, " ");
|
|
if(filename)
|
|
{
|
|
char* path = str_new("shaders/");
|
|
path = str_concat(path, filename);
|
|
char* file = io_file_read(path);
|
|
char* shader_copy = str_new(shader_text);
|
|
char* temp = realloc(shader_text, (strlen(shader_text) + strlen(file) + 2));
|
|
if(temp)
|
|
{
|
|
shader_text = temp;
|
|
strcpy(shader_text, file);
|
|
strcat(shader_text, shader_copy);
|
|
}
|
|
else
|
|
{
|
|
log_warning("Realloc failed in Shader::run_preprocessor");
|
|
}
|
|
|
|
free(path);
|
|
free(shader_copy);
|
|
free(file);
|
|
}
|
|
}
|
|
free(inc_line);
|
|
}
|
|
return shader_text;
|
|
}
|
|
|
|
void shader_init(void)
|
|
{
|
|
shader_list = array_new(uint);
|
|
empty_indices = array_new(int);
|
|
}
|
|
|
|
int shader_create(const char* vert_shader_name, const char* frag_shader_name)
|
|
{
|
|
char* vs_path = str_new("shaders/");
|
|
vs_path = str_concat(vs_path, vert_shader_name);
|
|
char* fs_path = str_new("shaders/");
|
|
fs_path = str_concat(fs_path, frag_shader_name);
|
|
|
|
GLuint vert_shader = glCreateShader(GL_VERTEX_SHADER);
|
|
GLuint frag_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
char* vert_source = io_file_read(vs_path);
|
|
char* frag_source = io_file_read(fs_path);
|
|
|
|
assert(vert_source != NULL);
|
|
assert(frag_source != NULL);
|
|
|
|
vert_source = run_preprocessor(vert_source);
|
|
frag_source = run_preprocessor(frag_source);
|
|
|
|
GLint v_size = (GLint)strlen(vert_source);
|
|
GLint f_size = (GLint)strlen(frag_source);
|
|
|
|
const char* vert_sourcePtr = vert_source;
|
|
const char* frag_sourcePtr = frag_source;
|
|
|
|
const GLint* vert_size = &v_size;
|
|
const GLint* frag_size = &f_size;
|
|
|
|
glShaderSource(vert_shader, 1, &vert_sourcePtr, vert_size);
|
|
glShaderSource(frag_shader, 1, &frag_sourcePtr, frag_size);
|
|
|
|
glCompileShader(vert_shader);
|
|
glCompileShader(frag_shader);
|
|
|
|
GLint is_vert_compiled = 0;
|
|
GLint is_frag_compiled = 0;
|
|
glGetShaderiv(vert_shader, GL_COMPILE_STATUS, &is_vert_compiled);
|
|
glGetShaderiv(frag_shader, GL_COMPILE_STATUS, &is_frag_compiled);
|
|
|
|
if(!is_vert_compiled)
|
|
{
|
|
GLint log_size = 0;
|
|
glGetShaderiv(vert_shader, GL_INFO_LOG_LENGTH, &log_size);
|
|
char* message = (char *)malloc(sizeof(char) * log_size);
|
|
glGetShaderInfoLog(vert_shader, log_size, NULL, message);
|
|
|
|
log_error("shader:create", "COMPILING VS %s : %s", vert_shader_name, message);
|
|
debug_print_shader(vert_source);
|
|
free(message);
|
|
}
|
|
|
|
if(!is_frag_compiled)
|
|
{
|
|
GLint log_size = 0;
|
|
glGetShaderiv(frag_shader, GL_INFO_LOG_LENGTH, &log_size);
|
|
char* message = (char *)malloc(sizeof(char) * log_size);
|
|
glGetShaderInfoLog(frag_shader, log_size, NULL, message);
|
|
|
|
log_error("shader:create", "COMPILING FS %s : %s", frag_shader_name, message);
|
|
debug_print_shader(frag_source);
|
|
free(message);
|
|
}
|
|
|
|
free(vert_source);
|
|
free(frag_source);
|
|
|
|
if(!is_vert_compiled || !is_frag_compiled)
|
|
{
|
|
glDeleteShader(vert_shader);
|
|
glDeleteShader(frag_shader);
|
|
return -1;
|
|
}
|
|
|
|
GLuint program = glCreateProgram();
|
|
glAttachShader(program, vert_shader);
|
|
glAttachShader(program, frag_shader);
|
|
|
|
// Bind attribute locations
|
|
glBindAttribLocation(program, POSITION_LOC, "vPosition");
|
|
glBindAttribLocation(program, NORMAL_LOC, "vNormal");
|
|
glBindAttribLocation(program, UV_LOC, "vUV");
|
|
glBindAttribLocation(program, COLOR_LOC, "vColor");
|
|
renderer_check_glerror("shader:create");
|
|
glLinkProgram(program);
|
|
|
|
GLint is_linked = 0;
|
|
glGetProgramiv(program, GL_LINK_STATUS, &is_linked);
|
|
if(!is_linked)
|
|
{
|
|
GLint log_size = 0;
|
|
glGetProgramiv(program, GL_LINK_STATUS, &log_size);
|
|
char* message = (char *)malloc(sizeof(char) * log_size);
|
|
glGetProgramInfoLog(program, log_size, NULL, message);
|
|
log_error("shader:create", "LINK SHADER : %s", message);
|
|
free(message);
|
|
|
|
glDeleteProgram(program);
|
|
glDeleteShader(vert_shader);
|
|
glDeleteShader(frag_shader);
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Safe to delete shaders now */
|
|
glDeleteShader(vert_shader);
|
|
glDeleteShader(frag_shader);
|
|
|
|
/* add new object or overwrite existing one */
|
|
uint* new_shader = 0;
|
|
int index = -1;
|
|
int empty_len = array_len(empty_indices);
|
|
if(empty_len != 0)
|
|
{
|
|
index = empty_indices[empty_len - 1];
|
|
array_pop(empty_indices);
|
|
new_shader = &shader_list[index];
|
|
}
|
|
else
|
|
{
|
|
new_shader = array_grow(shader_list, uint);
|
|
index = array_len(shader_list) - 1;
|
|
}
|
|
assert(new_shader);
|
|
*new_shader = program;
|
|
|
|
log_message("%s, %s compiled into shader program", vert_shader_name, frag_shader_name);
|
|
free(vs_path);
|
|
free(fs_path);
|
|
|
|
return index;
|
|
}
|
|
|
|
void shader_bind(const int shader_index)
|
|
{
|
|
glUseProgram(shader_list[shader_index]);
|
|
}
|
|
|
|
void shader_unbind(void)
|
|
{
|
|
glUseProgram(0);
|
|
}
|
|
|
|
int shader_get_uniform_location(const int shader_index, const char* name)
|
|
{
|
|
GLint handle = glGetUniformLocation(shader_list[shader_index], name);
|
|
if(handle == -1)
|
|
log_error("shader:get_uniform_location", "Invalid uniform %s", name);
|
|
|
|
return handle;
|
|
}
|
|
|
|
void shader_set_uniform_int(const int shader_index, const char* name, const int value)
|
|
{
|
|
GLint location = shader_get_uniform_location(shader_index, name);
|
|
if(location >= 0)
|
|
glUniform1i(location, value);
|
|
}
|
|
|
|
void shader_set_uniform_float(const int shader_index, const char* name, const float value)
|
|
{
|
|
GLint location = shader_get_uniform_location(shader_index, name);
|
|
if(location >= 0)
|
|
glUniform1f(location, value);
|
|
}
|
|
|
|
void shader_set_uniform_vec2(const int shader_index, const char* name, const vec2* value)
|
|
{
|
|
GLint location = shader_get_uniform_location(shader_index, name);
|
|
if(location >= 0)
|
|
glUniform2fv(location, 1, &value->x);
|
|
}
|
|
|
|
void shader_set_uniform_vec3(const int shader_index, const char* name, const vec3* value)
|
|
{
|
|
GLint location = shader_get_uniform_location(shader_index, name);
|
|
if(location >= 0)
|
|
glUniform3fv(location, 1, &value->x);
|
|
}
|
|
|
|
void shader_set_uniform_vec4(const int shader_index, const char* name, const vec4* value)
|
|
{
|
|
GLint location = shader_get_uniform_location(shader_index, name);
|
|
if(location >= 0)
|
|
glUniform4fv(location, 1, &value->x);
|
|
}
|
|
|
|
void shader_set_uniform_mat4(const int shader_index, const char* name, const mat4* value)
|
|
{
|
|
GLint location = shader_get_uniform_location(shader_index, name);
|
|
if(location >= 0)
|
|
glUniformMatrix4fv(location, 1, GL_FALSE, &value->mat[0]);
|
|
}
|
|
|
|
void shader_remove(const int shader_index)
|
|
{
|
|
uint shader = shader_list[shader_index];
|
|
if(shader == 0) return; /* shader is already deleted or invalid */
|
|
int curr_program = 0;
|
|
glGetIntegerv(GL_CURRENT_PROGRAM, &curr_program);
|
|
if((uint)curr_program == shader)
|
|
glUseProgram(0);
|
|
glDeleteProgram(shader);
|
|
shader_list[shader_index] = 0;
|
|
array_push(empty_indices, shader_index, int);
|
|
}
|
|
|
|
void shader_cleanup(void)
|
|
{
|
|
for(int i = 0; i < array_len(shader_list); i++)
|
|
shader_remove(i);
|
|
|
|
array_free(shader_list);
|
|
array_free(empty_indices);
|
|
}
|
|
|
|
void shader_set_uniform(const int uniform_type, const int uniform_loc, void* value)
|
|
{
|
|
assert(value);
|
|
switch(uniform_type)
|
|
{
|
|
case UT_INT:
|
|
{
|
|
glUniform1i(uniform_loc, *((int*)value));
|
|
break;
|
|
}
|
|
case UT_FLOAT:
|
|
{
|
|
glUniform1f(uniform_loc, *((float*)value));
|
|
break;
|
|
}
|
|
case UT_VEC2:
|
|
{
|
|
vec2* vector = (vec2*)value;
|
|
glUniform2fv(uniform_loc, 1, &vector->x);
|
|
break;
|
|
}
|
|
case UT_VEC3:
|
|
{
|
|
vec3* vector = (vec3*)value;
|
|
glUniform3fv(uniform_loc, 1, &vector->x);
|
|
break;
|
|
}
|
|
case UT_VEC4:
|
|
{
|
|
vec4* vector = (vec4*)value;
|
|
glUniform4fv(uniform_loc, 1, &vector->x);
|
|
break;
|
|
}
|
|
case UT_MAT4:
|
|
{
|
|
mat4* mat = (mat4*)value;
|
|
glUniformMatrix4fv(uniform_loc, 1, GL_FALSE, &mat->mat[0]);
|
|
break;
|
|
}
|
|
case UT_TEX:
|
|
{
|
|
int texture_index = *((int*)value);
|
|
int texture_unit = texture_get_textureunit(texture_index);
|
|
glUniform1i(uniform_loc, (GL_TEXTURE0 + texture_unit) - GL_TEXTURE0);
|
|
texture_bind(texture_index);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|