From d38deeef3af2316a666f8fc0173940bd769b748e Mon Sep 17 00:00:00 2001 From: onelin Date: Sat, 1 Nov 2025 00:55:42 +0100 Subject: Flatten project structure This will make it easier to break up the code into smaller chunks again later. One would think doing this seems fun to me at this point. --- src/gl.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 src/gl.c (limited to 'src/gl.c') diff --git a/src/gl.c b/src/gl.c new file mode 100644 index 0000000..9dce5e5 --- /dev/null +++ b/src/gl.c @@ -0,0 +1,281 @@ +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +extern Instance* GLOBAL_PLATFORM; + +const char* ShaderType_str[] = { + [Shader_Error] = "Shader_Error", + [Shader_Program] = "Shader_Program", + [Shader_Vertex] = "Shader_Vertex", + [Shader_Tessellation] = "Shader_Tessellation", + [Shader_Geometry] = "Shader_Geometry", + [Shader_Fragment] = "Shader_Fragment", + [Shader_Compute] = "Shader_Compute", +}; + +Shader compile_shader(const char* file_path, const ShaderType shader_type) { + u32 shaderID = 0; + GLenum shadertype = GL_INVALID_ENUM; + + i32 Result = GL_FALSE; + i32 infolog_len; + + char* source = NULL; + FILE* file = NULL; + + const GladGLContext* gl = GLOBAL_PLATFORM->window->context; + + if (file_path == NULL) { + WARN("Empty path to shader"); + return (Shader){.program = 0, .type = Shader_Error}; + } + + switch (shader_type) { + case Shader_Vertex: + shadertype = GL_VERTEX_SHADER; + break; + case Shader_Fragment: + shadertype = GL_FRAGMENT_SHADER; + break; + default: break; + } + + file = fopen(file_path, "r"); + + shaderID = gl->CreateShader(shadertype); + LOG("CREATED SHADER ID %d", shaderID); + + if(file != NULL) { + const usize size = f_get_sz(file); + + source = calloc((usize)size + 1, sizeof(char)); + + // Assume the whole file is successfully read + fread(source, sizeof(char), (usize)size, file); + + fclose(file); + } else { + ERROR("Cannot open \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET"\".", file_path); + return (Shader){.program = 0, .type = Shader_Error}; + } + + // Compile shader + INFO("Compiling shader \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET"\".", file_path); + char const* src_ptr = source; + gl->ShaderSource(shaderID, 1, &src_ptr , NULL); + gl->CompileShader(shaderID); + + // Check shader + gl->GetShaderiv(shaderID, GL_COMPILE_STATUS, &Result); + gl->GetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &infolog_len); + if ( infolog_len > 0 ) { + char* msg = calloc((usize)infolog_len + 1, sizeof(char)); + gl->GetShaderInfoLog(shaderID, infolog_len, NULL, msg); + ERROR("Failed to compile shader: " TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET, msg); + free(msg); + } + + free(source); + + return (Shader){.program = shaderID, .type = shader_type}; +} + + +// http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/ +u32 load_shaders( + const GladGLContext* gl, + const char* vertex_file_path, + const char* fragment_file_path) { + + i32 Result = GL_FALSE; + i32 infolog_len; + + // Create the shaders + const Shader vertexShader = compile_shader(vertex_file_path, Shader_Vertex); + const Shader fragmentShader = compile_shader(fragment_file_path, Shader_Fragment); + + // Link the program + INFO("Linking program"); + u32 ProgramID = gl->CreateProgram(); + + if (vertex_file_path != NULL) gl->AttachShader(ProgramID, vertexShader.program); + if (fragment_file_path != NULL) gl->AttachShader(ProgramID, fragmentShader.program); + + gl->LinkProgram(ProgramID); + + // Check the program + gl->GetProgramiv(ProgramID, GL_LINK_STATUS, &Result); + gl->GetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &infolog_len); + if ( infolog_len > 0 ){ + char* msg = calloc((usize)infolog_len + 1, sizeof(char)); + gl->GetShaderInfoLog(ProgramID, infolog_len, NULL, msg); + ERROR("Compiling shader: " TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET, msg); + free(msg); + } + + gl->DetachShader(ProgramID, vertexShader.program); + gl->DetachShader(ProgramID, fragmentShader.program); + + //gl->DeleteShader(vertexShader.program); + //gl->DeleteShader(fragmentShader.program); + + return ProgramID; +} + +/* Returns a shader program */ +Shader compose_shader(Shader *shaders, usize shaders_len) { + const GladGLContext* gl = GLOBAL_PLATFORM->window->context; + i32 Result = GL_FALSE; + + if (shaders_len == 0) { + ERROR("No shaders provided!"); + return (Shader){.program = 0, .type = Shader_Error}; + } + + u32 prog = gl->CreateProgram(); + + if (prog == 0) { + ERROR("Failed to create program!"); + return (Shader){.program = 0, .type = Shader_Error}; + } + + for (usize i = 0; i < shaders_len; i++) { + gl->AttachShader(prog, shaders[i].program); + INFO("Attaching shader %d to %d", shaders[i].program, prog); + } + + gl->LinkProgram(prog); + + // Check the program + gl->GetProgramiv(prog, GL_LINK_STATUS, &Result); + if (Result != GL_TRUE) { + // Get the size of the log + i32 log_len = 0; + i32 msg_len = 0; + gl->GetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_len); + char* msg = calloc((usize)log_len + 1, sizeof(char)); + + // Copy the log message(s) + gl->GetProgramInfoLog(prog, log_len, &msg_len, msg); + + ERROR("(Compose) Compiling shader:\n" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET "\n", msg); + free(msg); + } + + for (usize i = 0; i < shaders_len; i++) { + gl->DetachShader(prog, shaders[i].program); + } + + return (Shader){.program = prog, .type = Shader_Program}; +} + +/* Free up resources associated with `shader` */ +void shaders_delete(Shader* shader, usize shader_len) { + const GladGLContext* gl = GLOBAL_PLATFORM->window->context; + + for (usize i = 0; i < shader_len; i++) { + gl->DeleteShader(shader[i].program); + } +} + +GLenum ShaderBuffer_get_gl_access(u64 flags) { + const ShaderBufferFlag access = ShaderBuffer_get_access_type(flags); + + switch(flags & 0b111) { + case ShaderBuffer_AccessFrequency_stream: + switch(access) { + case ShaderBuffer_AccessType_draw: return GL_STREAM_DRAW; + case ShaderBuffer_AccessType_read: return GL_STREAM_READ; + case ShaderBuffer_AccessType_copy: return GL_STREAM_COPY; + default: return GL_STREAM_DRAW; + } + case ShaderBuffer_AccessFrequency_static: + switch(access) { + case ShaderBuffer_AccessType_draw: return GL_STATIC_DRAW; + case ShaderBuffer_AccessType_read: return GL_STATIC_READ; + case ShaderBuffer_AccessType_copy: return GL_STATIC_COPY; + default: return GL_STATIC_DRAW; + } + case ShaderBuffer_AccessFrequency_dynamic: + switch(access) { + case ShaderBuffer_AccessType_draw: return GL_DYNAMIC_DRAW; + case ShaderBuffer_AccessType_read: return GL_DYNAMIC_READ; + case ShaderBuffer_AccessType_copy: return GL_DYNAMIC_COPY; + default: return GL_DYNAMIC_DRAW; + } + default: return GL_STATIC_DRAW; + } +} + +RenderObject RenderObject_new( + Shader* shader, + u32 texture, + ShaderBuffer *restrict buffers, usize num_buffers) { + + GladGLContext *gl = GLOBAL_PLATFORM->window->context; + RenderObject o; + + gl->GenVertexArrays(1, &(o.vao)); + gl->BindVertexArray(o.vao); + + /* For each buffer in the shader, */ + /* The shader should be generalied, */ + for (usize i = 0; i < num_buffers; i++) { + const usize sz = buffers[i].size_elem * buffers[i].count; + const u32 b_gl_type = ShaderBuffer_get_gl_type(buffers[i].buffertype); + + gl->GenBuffers(1, &(buffers[i].buffername)); + gl->BindBuffer(b_gl_type, buffers[i].buffername); + gl->BufferData(b_gl_type, (isize)sz, buffers[i].data, ShaderBuffer_get_gl_accesstype(buffers[i].buffertype)); + } + + o.shader = *shader; + o.texture = texture; + o.buffer = buffers; + o.buffer_len = num_buffers; + o.mvp = gl->GetUniformLocation(o.shader.program, "MVP"); + + // It is very much a non-issue if we don't find the model view projection in + // the shader. In fact, it is removed from a shader program if it is not used. + // TODO: Add common uniforms, should be a list of strings (uniform name) & + // their locations (i32), such as + // * mouse coords, + // * time, + // * delta time, + // * modelviewprojection, + // * window size. + // These should be added to the RenderObject, if found. + + // if (o.mvp == -1) { + // WARN("Unable to find \"MVP\" input in shader program"); + //} + + gl->BindVertexArray(0); + + + return o; +} + +ShaderType guess_shadertype_from_filename(const char *restrict fname) { + const usize path_len = strlen(fname); + + if (path_len <= 4) { + ERROR("Unable to determine shader type from suffix! (%s)", fname); + return Shader_Error; + } + + if (!strncmp(".vert", &fname[path_len - 5], 5)) { return Shader_Vertex; } + if (!strncmp(".frag", &fname[path_len - 5], 5)) { return Shader_Fragment; } + + return Shader_Error; +} -- cgit v1.3