Files
fn_registry/cpp/functions/gfx/gl_shader.cpp
T
egutierrez 4a95407d0e feat(shaders_lab): add gl_loader + Windows cross-compile
- cpp/functions/gfx/gl_loader.{h,cpp,md}: mini loader para OpenGL 2.0+
  (Linux no-op via GL_GLEXT_PROTOTYPES, Windows wglGetProcAddress)
- Portar gl_shader/gl_framebuffer/fullscreen_quad/shader_canvas al loader
- CMakeLists: WIN32_EXECUTABLE para lanzar sin consola en Windows
- apps/shaders_lab/shaders_lab.exe: binario PE32+ precompilado

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:52:37 +02:00

117 lines
3.3 KiB
C++

#include "gfx/gl_loader.h"
#include "gfx/gl_shader.h"
#include <cstdio>
#include <cstring>
#include <regex>
#include <string>
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:<line>:" format
std::regex re1(R"(ERROR:\s*\d+:(\d+):)");
// Try "0(<line>)" 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<size_t>(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<size_t>(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