A 3d fps game made in OpenGL
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.
 
 
 
 
Symmetry/src/shader.c

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