#define GL_GLEXT_PROTOTYPES #include #include #include "gfx/gl_shader.h" #include #include #include #include namespace fn::gfx { static const char* k_vert_src = R"glsl( #version 330 core const vec2 verts[6] = vec2[]( vec2(-1,-1), vec2(1,-1), vec2(-1,1), vec2(1,-1), vec2(1,1), vec2(-1,1) ); void main() { gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0); } )glsl"; static const char* k_frag_preamble = "#version 330 core\n" "out vec4 fragColor;\n" "uniform vec2 u_resolution;\n" "uniform float u_time;\n" "uniform vec2 u_mouse;\n"; static int parse_err_line(const char* log) { // Try "ERROR: 0::" format std::regex re1(R"(ERROR:\s*\d+:(\d+):)"); // Try "0()" format std::regex re2(R"(\d+\((\d+)\))"); std::cmatch m; if (std::regex_search(log, m, re1)) { return std::stoi(m[1].str()); } if (std::regex_search(log, m, re2)) { return std::stoi(m[1].str()); } return -1; } static unsigned int compile_shader(GLenum type, const char** srcs, int count, std::string& out_err, int& out_line) { unsigned int s = glCreateShader(type); glShaderSource(s, count, srcs, nullptr); glCompileShader(s); GLint ok = 0; glGetShaderiv(s, GL_COMPILE_STATUS, &ok); if (!ok) { GLint len = 0; glGetShaderiv(s, GL_INFO_LOG_LENGTH, &len); out_err.resize(static_cast(len)); glGetShaderInfoLog(s, len, nullptr, &out_err[0]); out_line = parse_err_line(out_err.c_str()); glDeleteShader(s); return 0; } return s; } CompileResult compile_fragment(const std::string& user_fragment_src) { CompileResult result; // Vertex shader (no preamble needed — it's a standalone complete shader) std::string vert_err; int vert_line = -1; unsigned int vert = compile_shader(GL_VERTEX_SHADER, &k_vert_src, 1, vert_err, vert_line); if (!vert) { result.err_msg = "vertex: " + vert_err; result.err_line = vert_line; return result; } // Fragment shader — prepend preamble, then user body const char* frag_srcs[2] = { k_frag_preamble, user_fragment_src.c_str() }; std::string frag_err; int frag_line = -1; unsigned int frag = compile_shader(GL_FRAGMENT_SHADER, frag_srcs, 2, frag_err, frag_line); if (!frag) { glDeleteShader(vert); result.err_msg = frag_err; // Adjust line: subtract preamble lines (4 lines) if (frag_line > 4) result.err_line = frag_line - 4; else result.err_line = frag_line; return result; } // Link unsigned int prog = glCreateProgram(); glAttachShader(prog, vert); glAttachShader(prog, frag); glLinkProgram(prog); glDeleteShader(vert); glDeleteShader(frag); GLint ok = 0; glGetProgramiv(prog, GL_LINK_STATUS, &ok); if (!ok) { GLint len = 0; glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len); result.err_msg.resize(static_cast(len)); glGetProgramInfoLog(prog, len, nullptr, &result.err_msg[0]); result.err_line = parse_err_line(result.err_msg.c_str()); glDeleteProgram(prog); return result; } result.program = prog; result.ok = true; return result; } void delete_program(unsigned int program) { if (program) glDeleteProgram(program); } } // namespace fn::gfx