diff options
| author | onelin <oscar@nelin.dk> | 2025-10-31 23:55:42 +0000 |
|---|---|---|
| committer | onelin <oscar@nelin.dk> | 2025-11-02 22:07:17 +0000 |
| commit | d38deeef3af2316a666f8fc0173940bd769b748e (patch) | |
| tree | 6e30d4a9eea18daa5705c894f28cd99ff047e8f9 /src/gl.c | |
| parent | 6c077751982ea2c7bd2d9262b01b9f8602f80dc8 (diff) | |
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.
Diffstat (limited to 'src/gl.c')
| -rw-r--r-- | src/gl.c | 281 |
1 files changed, 281 insertions, 0 deletions
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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <glad/gl.h> + +#include <daw/types.h> +#include <daw/logging.h> + +#include <daw/rendering.h> +#include <daw/platform.h> +#include <daw/daw.h> + +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; +} |
