feat(shaders_lab): scaffold C++ app with GLSL live-reload canvas
- cpp/functions/gfx: gl_shader, gl_framebuffer, fullscreen_quad, shader_canvas - cpp/apps/shaders_lab: main + 3 seed shaders (plasma, circle, checker) - ImGui docking layout: Code | Canvas | Controls Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
|
||||
#include "gfx/shader_canvas.h"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
void canvas_init(ShaderCanvas& c) {
|
||||
if (c.initialized) return;
|
||||
fb_init(c.fb);
|
||||
quad_init(c.quad);
|
||||
c.initialized = true;
|
||||
}
|
||||
|
||||
void canvas_set_program(ShaderCanvas& c, unsigned int program) {
|
||||
if (c.program) delete_program(c.program);
|
||||
c.program = program;
|
||||
}
|
||||
|
||||
void canvas_render(ShaderCanvas& c, float time_seconds) {
|
||||
ImVec2 avail = ImGui::GetContentRegionAvail();
|
||||
int w = static_cast<int>(avail.x);
|
||||
int h = static_cast<int>(avail.y);
|
||||
if (w < 1) w = 1;
|
||||
if (h < 1) h = 1;
|
||||
|
||||
fb_resize(c.fb, w, h);
|
||||
|
||||
// Save GL state
|
||||
GLint prev_fbo = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
|
||||
GLint prev_vp[4];
|
||||
glGetIntegerv(GL_VIEWPORT, prev_vp);
|
||||
|
||||
// Render to FBO
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, c.fb.fbo);
|
||||
glViewport(0, 0, w, h);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (c.program) {
|
||||
glUseProgram(c.program);
|
||||
|
||||
GLint loc_res = glGetUniformLocation(c.program, "u_resolution");
|
||||
GLint loc_time = glGetUniformLocation(c.program, "u_time");
|
||||
GLint loc_mouse = glGetUniformLocation(c.program, "u_mouse");
|
||||
|
||||
if (loc_res >= 0) glUniform2f(loc_res, static_cast<float>(w), static_cast<float>(h));
|
||||
if (loc_time >= 0) glUniform1f(loc_time, time_seconds);
|
||||
if (loc_mouse >= 0) {
|
||||
ImVec2 mouse = ImGui::GetMousePos();
|
||||
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||
// cursor pos is AFTER content region starts
|
||||
float mx = mouse.x - canvas_pos.x;
|
||||
float my = static_cast<float>(h) - (mouse.y - canvas_pos.y);
|
||||
glUniform2f(loc_mouse, mx, my);
|
||||
}
|
||||
|
||||
quad_draw(c.quad);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
// Restore GL state
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prev_fbo));
|
||||
glViewport(prev_vp[0], prev_vp[1], prev_vp[2], prev_vp[3]);
|
||||
|
||||
// Draw texture in ImGui panel (flip V: OpenGL origin is bottom-left)
|
||||
ImGui::Image(
|
||||
(ImTextureID)(intptr_t)c.fb.tex,
|
||||
avail,
|
||||
ImVec2(0, 1), ImVec2(1, 0)
|
||||
);
|
||||
}
|
||||
|
||||
void canvas_destroy(ShaderCanvas& c) {
|
||||
if (!c.initialized) return;
|
||||
if (c.program) { delete_program(c.program); c.program = 0; }
|
||||
quad_destroy(c.quad);
|
||||
fb_destroy(c.fb);
|
||||
c.initialized = false;
|
||||
}
|
||||
|
||||
} // namespace fn::gfx
|
||||
Reference in New Issue
Block a user