From bd57f345ed9dbed1d81683e48199626de2ea9044 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 27 Jun 2025 10:18:39 -0700 Subject: Restructure project --- src/core/shader_program.c | 291 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 src/core/shader_program.c (limited to 'src/core/shader_program.c') diff --git a/src/core/shader_program.c b/src/core/shader_program.c new file mode 100644 index 0000000..3cbe48d --- /dev/null +++ b/src/core/shader_program.c @@ -0,0 +1,291 @@ +#include "shader_program.h" + +#include "gl_util.h" +#include "shader.h" +#include "texture.h" +#include + +#include + +#include +#include + +/// Creates an OpenGL shader program. +/// Returns non-zero on success, 0 on failure. +static GLuint create_program(GLuint vertex_shader, GLuint fragment_shader) { + const GLuint prog = glCreateProgram(); + if (prog == 0) { + LOGE("Failed creating shader program"); + return 0; + } + glAttachShader(prog, vertex_shader); + glAttachShader(prog, fragment_shader); + glLinkProgram(prog); + GLint result; + glGetProgramiv(prog, GL_LINK_STATUS, &result); + if (result == GL_FALSE) { + GLint log_len; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + char* log = calloc(log_len, sizeof(char)); + glGetProgramInfoLog(prog, log_len, NULL, log); + LOGE("Failed creating shader program: %s", log); + free(log); + } else { + LOGE("Failed creating shader program"); + } + glDeleteProgram(prog); + return 0; + } + ASSERT_GL; + return prog; +} + +bool gfx_build_shader_program( + ShaderProgram* prog, const ShaderProgramDesc* desc) { + assert(prog); + assert(desc); + + prog->id = create_program(desc->vertex_shader->id, desc->fragment_shader->id); + return prog->id != 0; +} + +void gfx_del_shader_program(ShaderProgram* prog) { + assert(prog); + + if (prog->id) { + glDeleteProgram(prog->id); + prog->id = 0; + } + ASSERT_GL; +} + +void gfx_activate_shader_program(const ShaderProgram* prog) { + assert(prog); + glUseProgram(prog->id); + ASSERT_GL; +} + +void gfx_deactivate_shader_program(const ShaderProgram* prog) { + assert(prog); + glUseProgram(0); + ASSERT_GL; +} + +static void set_texture_uniform( + GLuint prog, const char* name, int texture_unit, const Texture* texture) { + assert(prog != 0); + assert(name); + assert(texture); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(texture->target, texture->id); + glUniform1i(location, texture_unit); + } +} + +static void set_mat4_uniform( + GLuint prog, const char* name, const mat4* mats, size_t count) { + assert(prog != 0); + assert(name); + assert(mats); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glUniformMatrix4fv(location, count, GL_FALSE, (const float*)mats); + } +} + +static void set_vec3_uniform(GLuint prog, const char* name, vec3 value) { + assert(prog != 0); + assert(name); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glUniform3f(location, value.x, value.y, value.z); + } +} + +static void set_vec4_uniform(GLuint prog, const char* name, vec4 value) { + assert(prog != 0); + assert(name); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glUniform4f(location, value.x, value.y, value.z, value.w); + } +} + +static void set_float_uniform(GLuint prog, const char* name, float value) { + assert(prog != 0); + assert(name); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glUniform1f(location, value); + } +} + +void gfx_apply_uniforms(const ShaderProgram* prog) { + assert(prog); + + int next_texture_unit = 0; + for (int i = 0; i < prog->num_uniforms; ++i) { + const ShaderUniform* uniform = &prog->uniforms[i]; + switch (uniform->type) { + case UniformTexture: + set_texture_uniform( + prog->id, uniform->name.str, next_texture_unit, + uniform->value.texture); + next_texture_unit++; + break; + case UniformMat4: + set_mat4_uniform(prog->id, uniform->name.str, &uniform->value.mat4, 1); + break; + case UniformVec3: + set_vec3_uniform(prog->id, uniform->name.str, uniform->value.vec3); + break; + case UniformVec4: + set_vec4_uniform(prog->id, uniform->name.str, uniform->value.vec4); + break; + case UniformFloat: + set_float_uniform(prog->id, uniform->name.str, uniform->value.scalar); + break; + case UniformMat4Array: + set_mat4_uniform( + prog->id, uniform->name.str, uniform->value.array.values, + uniform->value.array.count); + break; + } + } +} + +// Get the ShaderUniform object by name from the shader program if it already +// exists, or allocate a new one otherwise. +static ShaderUniform* get_or_allocate_uniform( + ShaderProgram* prog, const char* name) { + assert(prog); + assert(name); + + // First search for the uniform in the list. + for (int i = 0; i < prog->num_uniforms; ++i) { + ShaderUniform* uniform = &prog->uniforms[i]; + if (sstring_eq_cstr(uniform->name, name)) { + return uniform; + } + } + + // Create the uniform if it does not exist. + if (prog->num_uniforms == GFX_MAX_UNIFORMS_PER_SHADER) { + FAIL("Exceeded the maximum number of uniforms per shader. Please increase " + "this value."); + return 0; + } + ShaderUniform* uniform = &prog->uniforms[prog->num_uniforms]; + prog->num_uniforms++; + return uniform; +} + +// The functions below save the value of a uniform in the shader program. If the +// uniform does not even exist, then there is no need to store the value. + +void gfx_set_texture_uniform( + ShaderProgram* prog, const char* name, const Texture* texture) { + assert(prog); + assert(name); + assert(texture); + + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformTexture; + uniform->value.texture = texture; +} + +void gfx_set_mat4_uniform( + ShaderProgram* prog, const char* name, const mat4* mat) { + assert(prog); + assert(name); + assert(mat); + + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformMat4; + uniform->value.mat4 = *mat; +} + +void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) { + assert(prog); + assert(name); + + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformVec3; + uniform->value.vec3 = value; +} + +void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) { + assert(prog); + assert(name); + + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformVec4; + uniform->value.vec4 = value; +} + +void gfx_set_float_uniform(ShaderProgram* prog, const char* name, float value) { + assert(prog); + assert(name); + + // No need to store the uniform on our side if it does not exist in the + // program. + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformFloat; + uniform->value.scalar = value; +} + +void gfx_set_mat4_array_uniform( + ShaderProgram* prog, const char* name, const mat4* mats, size_t count) { + assert(prog); + assert(name); + assert(mats); + + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformMat4Array; + uniform->value.array.count = count; + uniform->value.array.values = mats; +} -- cgit v1.2.3