#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* source, const ShaderType shader_type) { const GladGLContext* gl = GLOBAL_PLATFORM->window->context; i32 Result = GL_FALSE; i32 infolog_len; GLenum shadertype = GL_INVALID_ENUM; switch (shader_type) { case Shader_Vertex: shadertype = GL_VERTEX_SHADER; break; case Shader_Fragment: shadertype = GL_FRAGMENT_SHADER; break; default: UNIMPLEMENTED; break; } u32 shaderID = gl->CreateShader(shadertype); LOG("CREATED SHADER ID %d", shaderID); 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); } return (Shader){.program = shaderID, .type = shader_type}; } Shader compile_shader_f(const char* file_path, const ShaderType shader_type) { Shader s; char* source = NULL; FILE* file = NULL; if (file_path == NULL) { WARN("Empty path to shader"); return (Shader){.program = 0, .type = Shader_Error}; } file = fopen(file_path, "r"); 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); s = compile_shader(source, shader_type); free(source); return s; } // 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_f(vertex_file_path, Shader_Vertex); const Shader fragmentShader = compile_shader_f(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 & 7) { // Stored in the first 3 bits 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->CreateVertexArrays(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; gl->CreateBuffers(1, &(buffers[i].buffername)); gl->NamedBufferData(buffers[i].buffername, (isize)sz, buffers[i].data, ShaderBuffer_get_gl_accesstype(buffers[i].buffertype)); } o.shader = *shader; o.texture = texture; o.texture_len = 1; o.buffer = buffers; o.buffer_len = num_buffers; o.mvp = gl->GetUniformLocation(o.shader.program, "MVP"); o.model_position = gl->GetUniformLocation(o.shader.program, "modelPosition"); // 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; }