Files
fn_registry/cpp/apps/shaders_lab/main.cpp
T
egutierrez 042bb43b37 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>
2026-04-24 20:33:36 +02:00

116 lines
3.3 KiB
C++

#include "app_base.h"
#include "imgui.h"
#include "gfx/shader_canvas.h"
#include "gfx/gl_shader.h"
#include "core/fps_overlay.h"
#include "seed_shaders.h"
#include <chrono>
#include <string>
static fn::gfx::ShaderCanvas g_canvas;
static std::string g_source = PLASMA;
static std::string g_last_err;
static int g_last_err_line = -1;
static std::chrono::steady_clock::time_point g_last_edit;
static bool g_dirty = true;
static void try_compile() {
auto r = fn::gfx::compile_fragment(g_source);
if (r.ok) {
fn::gfx::canvas_set_program(g_canvas, r.program);
g_last_err.clear();
g_last_err_line = -1;
} else {
g_last_err = r.err_msg;
g_last_err_line = r.err_line;
}
}
static void mark_dirty() {
g_last_edit = std::chrono::steady_clock::now();
g_dirty = true;
}
static void load_preset(const char* src) {
g_source = src;
mark_dirty();
}
static void render() {
if (!g_canvas.initialized) fn::gfx::canvas_init(g_canvas);
if (g_dirty) {
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - g_last_edit).count();
if (elapsed > 250) {
try_compile();
g_dirty = false;
}
}
ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());
// --- Code panel ---
if (ImGui::Begin("Code")) {
if (ImGui::Button("Plasma")) { load_preset(PLASMA); }
ImGui::SameLine();
if (ImGui::Button("Circle")) { load_preset(CIRCLE); }
ImGui::SameLine();
if (ImGui::Button("Checker")) { load_preset(CHECKER); }
ImVec2 avail = ImGui::GetContentRegionAvail();
float footer_height = g_last_err.empty() ? 0.0f : ImGui::GetTextLineHeightWithSpacing() + 8.0f;
ImVec2 editor_size(avail.x, avail.y - footer_height);
char buf[1 << 16];
size_t copy_len = g_source.size() < sizeof(buf) - 1 ? g_source.size() : sizeof(buf) - 1;
memcpy(buf, g_source.c_str(), copy_len);
buf[copy_len] = '\0';
ImGui::PushFont(nullptr); // use default monospace-ish font
if (ImGui::InputTextMultiline("##code", buf, sizeof(buf), editor_size,
ImGuiInputTextFlags_AllowTabInput)) {
g_source = buf;
mark_dirty();
}
ImGui::PopFont();
if (!g_last_err.empty()) {
ImGui::Separator();
if (g_last_err_line > 0) {
ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), "line %d: %s",
g_last_err_line, g_last_err.c_str());
} else {
ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), "%s", g_last_err.c_str());
}
}
}
ImGui::End();
// --- Canvas panel ---
if (ImGui::Begin("Canvas")) {
fn::gfx::canvas_render(g_canvas, static_cast<float>(ImGui::GetTime()));
}
ImGui::End();
// --- Controls panel ---
if (ImGui::Begin("Controls")) {
ImGui::TextDisabled("Controls (fase 2)");
ImGui::Spacing();
fps_overlay();
}
ImGui::End();
}
int main() {
fn::AppConfig cfg;
cfg.title = "shaders_lab";
cfg.width = 1400;
cfg.height = 860;
int rc = fn::run_app(cfg, render);
fn::gfx::canvas_destroy(g_canvas);
return rc;
}