feat(shaders_lab): DAG pipeline mode with node catalog
- cpp/functions/gfx/dag_types: DagStep, DagNodeDef, DagControl (header-only) - cpp/functions/gfx/dag_catalog: 10 hardcoded nodes (4 gen, 3 op, 3 blend) ported from shader-dag-blends.jsx - cpp/functions/gfx/dag_compile: pipeline → GLSL 330 core with fan-in via source_id - cpp/functions/gfx/dag_uniforms: upload u_params[16] via glUniform4fv - cpp/functions/gfx/dag_panel: ImGui pipeline editor (add/remove/reorder/controls) - main.cpp: Code/DAG mode toggle, per-mode compile path and uniforms - gl_loader: +glUniform4fv - rebuild Windows .exe Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,10 @@ add_imgui_app(shaders_lab
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/shader_canvas.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/uniform_parser.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/uniform_panel.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/dag_catalog.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/dag_compile.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/dag_uniforms.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/dag_panel.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/core/fps_overlay.cpp
|
||||
)
|
||||
target_include_directories(shaders_lab PRIVATE
|
||||
|
||||
+146
-45
@@ -5,6 +5,10 @@
|
||||
#include "gfx/gl_shader.h"
|
||||
#include "gfx/uniform_parser.h"
|
||||
#include "gfx/uniform_panel.h"
|
||||
#include "gfx/dag_catalog.h"
|
||||
#include "gfx/dag_compile.h"
|
||||
#include "gfx/dag_uniforms.h"
|
||||
#include "gfx/dag_panel.h"
|
||||
#include "core/fps_overlay.h"
|
||||
#include "seed_shaders.h"
|
||||
|
||||
@@ -12,6 +16,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
enum class AppMode { Code, Dag };
|
||||
|
||||
static AppMode g_mode = AppMode::Code;
|
||||
static fn::gfx::ShaderCanvas g_canvas;
|
||||
static std::string g_source = PLASMA;
|
||||
static std::string g_last_err;
|
||||
@@ -20,18 +27,35 @@ static std::chrono::steady_clock::time_point g_last_edit;
|
||||
static bool g_dirty = true;
|
||||
static std::vector<fn::gfx::UniformDescriptor> g_descs;
|
||||
static fn::gfx::UniformStore g_store;
|
||||
static std::vector<fn::gfx::DagStep> g_pipeline;
|
||||
static std::string g_dag_glsl;
|
||||
|
||||
static void try_compile() {
|
||||
auto r = fn::gfx::compile_fragment(g_source);
|
||||
if (r.ok) {
|
||||
g_descs = fn::gfx::parse_uniforms(g_source);
|
||||
fn::gfx::uniforms_sync(g_store, g_descs);
|
||||
fn::gfx::canvas_set_program(g_canvas, r.program);
|
||||
g_last_err.clear();
|
||||
g_last_err_line = -1;
|
||||
if (g_mode == AppMode::Code) {
|
||||
auto r = fn::gfx::compile_fragment(g_source);
|
||||
if (r.ok) {
|
||||
g_descs = fn::gfx::parse_uniforms(g_source);
|
||||
fn::gfx::uniforms_sync(g_store, g_descs);
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
g_last_err = r.err_msg;
|
||||
g_last_err_line = r.err_line;
|
||||
g_dag_glsl = fn::gfx::compile_dag_to_glsl(g_pipeline);
|
||||
auto r = fn::gfx::compile_fragment(g_dag_glsl);
|
||||
if (r.ok) {
|
||||
g_descs.clear();
|
||||
g_store.values.clear();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +69,20 @@ static void load_preset(const char* src) {
|
||||
mark_dirty();
|
||||
}
|
||||
|
||||
static void ensure_dag_default() {
|
||||
if (g_pipeline.empty()) {
|
||||
const fn::gfx::DagNodeDef* def = fn::gfx::dag_find("plasma");
|
||||
if (def) {
|
||||
fn::gfx::DagStep step;
|
||||
step.id = "n_default";
|
||||
step.name = def->name;
|
||||
step.params = def->param_defaults;
|
||||
g_pipeline.push_back(step);
|
||||
mark_dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void render() {
|
||||
if (!g_canvas.initialized) fn::gfx::canvas_init(g_canvas);
|
||||
|
||||
@@ -59,38 +97,82 @@ static void render() {
|
||||
|
||||
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()) {
|
||||
// --- Left panel: Code or DAG ---
|
||||
const char* panel_title = (g_mode == AppMode::Code) ? "Code" : "DAG";
|
||||
if (ImGui::Begin(panel_title)) {
|
||||
// Mode toggle topbar
|
||||
{
|
||||
bool code_active = (g_mode == AppMode::Code);
|
||||
bool dag_active = (g_mode == AppMode::Dag);
|
||||
if (code_active) ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.5f, 0.8f, 1.0f));
|
||||
if (ImGui::Button("Code")) {
|
||||
if (g_mode != AppMode::Code) {
|
||||
g_mode = AppMode::Code;
|
||||
mark_dirty();
|
||||
}
|
||||
}
|
||||
if (code_active) ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
if (dag_active) ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.5f, 0.8f, 1.0f));
|
||||
if (ImGui::Button("DAG")) {
|
||||
if (g_mode != AppMode::Dag) {
|
||||
g_mode = AppMode::Dag;
|
||||
ensure_dag_default();
|
||||
mark_dirty();
|
||||
}
|
||||
}
|
||||
if (dag_active) ImGui::PopStyleColor();
|
||||
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());
|
||||
}
|
||||
|
||||
if (g_mode == AppMode::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);
|
||||
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());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// DAG mode
|
||||
bool topo_changed = fn::gfx::dag_panel(g_pipeline);
|
||||
if (topo_changed) {
|
||||
mark_dirty();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,16 +180,35 @@ static void render() {
|
||||
|
||||
// --- Canvas panel ---
|
||||
if (ImGui::Begin("Canvas")) {
|
||||
fn::gfx::canvas_render(g_canvas, static_cast<float>(ImGui::GetTime()),
|
||||
[](unsigned int program) {
|
||||
fn::gfx::uniforms_apply(g_store, g_descs, program);
|
||||
});
|
||||
if (g_mode == AppMode::Code) {
|
||||
fn::gfx::canvas_render(g_canvas, static_cast<float>(ImGui::GetTime()),
|
||||
[](unsigned int program) {
|
||||
fn::gfx::uniforms_apply(g_store, g_descs, program);
|
||||
});
|
||||
} else {
|
||||
fn::gfx::canvas_render(g_canvas, static_cast<float>(ImGui::GetTime()),
|
||||
[](unsigned int program) {
|
||||
fn::gfx::dag_uniforms_apply(g_pipeline, program);
|
||||
});
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
// --- Controls panel ---
|
||||
if (ImGui::Begin("Controls")) {
|
||||
fn::gfx::uniforms_panel(g_store, g_descs);
|
||||
if (g_mode == AppMode::Code) {
|
||||
fn::gfx::uniforms_panel(g_store, g_descs);
|
||||
} else {
|
||||
// Show generated GLSL read-only
|
||||
if (ImGui::CollapsingHeader("Generated GLSL")) {
|
||||
ImVec2 avail = ImGui::GetContentRegionAvail();
|
||||
ImGui::InputTextMultiline("##dag_glsl",
|
||||
const_cast<char*>(g_dag_glsl.c_str()),
|
||||
g_dag_glsl.size() + 1,
|
||||
ImVec2(avail.x, std::min(avail.y, 400.0f)),
|
||||
ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
fps_overlay();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
#include "gfx/dag_catalog.h"
|
||||
#include <string>
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
static const std::vector<DagNodeDef>& build_catalog() {
|
||||
static std::vector<DagNodeDef> catalog = []() {
|
||||
std::vector<DagNodeDef> v;
|
||||
|
||||
// ── Gen: solid ────────────────────────────────────────────────
|
||||
{
|
||||
DagNodeDef n;
|
||||
n.name = "solid";
|
||||
n.label = "solid";
|
||||
n.desc = "color constante";
|
||||
n.kind = DagKind::Gen;
|
||||
n.param_names = {"r", "g", "b", ""};
|
||||
n.param_defaults = {0.35f, 0.25f, 0.55f, 0.0f};
|
||||
n.controls = {
|
||||
{ DagControl::Kind::Color, "color", {0, 1, 2}, 0.0f, 1.0f, 0.0f },
|
||||
};
|
||||
n.body_glsl = [](int idx) -> std::string {
|
||||
std::string i = std::to_string(idx);
|
||||
return " vec4 p = u_params[" + i + "];\n"
|
||||
" return vec4(p.x, p.y, p.z, 1.0);";
|
||||
};
|
||||
v.push_back(std::move(n));
|
||||
}
|
||||
|
||||
// ── Gen: gradient ─────────────────────────────────────────────
|
||||
{
|
||||
DagNodeDef n;
|
||||
n.name = "gradient";
|
||||
n.label = "gradient";
|
||||
n.desc = "gradiente direccional";
|
||||
n.kind = DagKind::Gen;
|
||||
n.param_names = {"angle", "hue", "", ""};
|
||||
n.param_defaults = {0.8f, 0.5f, 0.0f, 0.0f};
|
||||
n.controls = {
|
||||
{ DagControl::Kind::Slider, "angulo", {0, -1, -1}, 0.0f, 6.2832f, 0.01f },
|
||||
{ DagControl::Kind::Slider, "tono", {1, -1, -1}, 0.0f, 1.0f, 0.01f },
|
||||
};
|
||||
n.body_glsl = [](int idx) -> std::string {
|
||||
std::string i = std::to_string(idx);
|
||||
return " vec4 p = u_params[" + i + "];\n"
|
||||
" vec2 dir = vec2(cos(p.x), sin(p.x));\n"
|
||||
" float t = dot(uv - 0.5, dir) + 0.5;\n"
|
||||
" vec3 col = 0.5 + 0.5 * cos(6.28318 * (p.y + vec3(0.0, 0.33, 0.67) + t));\n"
|
||||
" return vec4(col, 1.0);";
|
||||
};
|
||||
v.push_back(std::move(n));
|
||||
}
|
||||
|
||||
// ── Gen: plasma ───────────────────────────────────────────────
|
||||
{
|
||||
DagNodeDef n;
|
||||
n.name = "plasma";
|
||||
n.label = "plasma";
|
||||
n.desc = "onda trigonometrica";
|
||||
n.kind = DagKind::Gen;
|
||||
n.param_names = {"speed", "scale", "", ""};
|
||||
n.param_defaults = {1.0f, 2.0f, 0.0f, 0.0f};
|
||||
n.controls = {
|
||||
{ DagControl::Kind::Slider, "velocidad", {0, -1, -1}, 0.0f, 3.0f, 0.01f },
|
||||
{ DagControl::Kind::Slider, "escala", {1, -1, -1}, 0.5f, 10.0f, 0.1f },
|
||||
};
|
||||
n.body_glsl = [](int idx) -> std::string {
|
||||
std::string i = std::to_string(idx);
|
||||
return " vec4 p = u_params[" + i + "];\n"
|
||||
" vec3 col = 0.5 + 0.5 * cos(u_time * p.x + uv.xyx * p.y + vec3(0.0, 2.0, 4.0));\n"
|
||||
" return vec4(col, 1.0);";
|
||||
};
|
||||
v.push_back(std::move(n));
|
||||
}
|
||||
|
||||
// ── Gen: circle ───────────────────────────────────────────────
|
||||
{
|
||||
DagNodeDef n;
|
||||
n.name = "circle";
|
||||
n.label = "circle";
|
||||
n.desc = "sdf de circulo";
|
||||
n.kind = DagKind::Gen;
|
||||
n.param_names = {"cx", "cy", "radius", "soft"};
|
||||
n.param_defaults = {0.0f, 0.0f, 0.35f, 0.01f};
|
||||
n.controls = {
|
||||
{ DagControl::Kind::XY, "centro", {0, 1, -1}, -0.8f, 0.8f, 0.01f },
|
||||
{ DagControl::Kind::Slider, "radio", {2, -1, -1}, 0.0f, 1.0f, 0.01f },
|
||||
{ DagControl::Kind::Slider, "suavidad", {3, -1, -1}, 0.001f, 0.1f, 0.001f },
|
||||
};
|
||||
n.body_glsl = [](int idx) -> std::string {
|
||||
std::string i = std::to_string(idx);
|
||||
return " vec4 p = u_params[" + i + "];\n"
|
||||
" float aspect = u_resolution.x / u_resolution.y;\n"
|
||||
" vec2 pos = vec2((uv.x - 0.5) * aspect - p.x, uv.y - 0.5 - p.y);\n"
|
||||
" float d = length(pos) - p.z;\n"
|
||||
" float fill = smoothstep(p.w, -p.w, d);\n"
|
||||
" return mix(c, vec4(1.0), fill);";
|
||||
};
|
||||
v.push_back(std::move(n));
|
||||
}
|
||||
|
||||
// ── Op: invert ────────────────────────────────────────────────
|
||||
{
|
||||
DagNodeDef n;
|
||||
n.name = "invert";
|
||||
n.label = "invert";
|
||||
n.desc = "1 - rgb";
|
||||
n.kind = DagKind::Op;
|
||||
n.param_names = {"", "", "", ""};
|
||||
n.param_defaults = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
n.controls = {};
|
||||
n.body_glsl = [](int /*idx*/) -> std::string {
|
||||
return " return vec4(1.0 - c.rgb, c.a);";
|
||||
};
|
||||
v.push_back(std::move(n));
|
||||
}
|
||||
|
||||
// ── Op: gamma ─────────────────────────────────────────────────
|
||||
{
|
||||
DagNodeDef n;
|
||||
n.name = "gamma";
|
||||
n.label = "gamma";
|
||||
n.desc = "pow(rgb, gamma)";
|
||||
n.kind = DagKind::Op;
|
||||
n.param_names = {"gamma", "", "", ""};
|
||||
n.param_defaults = {1.0f, 0.0f, 0.0f, 0.0f};
|
||||
n.controls = {
|
||||
{ DagControl::Kind::Slider, "gamma", {0, -1, -1}, 0.1f, 4.0f, 0.01f },
|
||||
};
|
||||
n.body_glsl = [](int idx) -> std::string {
|
||||
std::string i = std::to_string(idx);
|
||||
return " vec4 p = u_params[" + i + "];\n"
|
||||
" return vec4(pow(c.rgb, vec3(1.0 / max(p.x, 0.001))), c.a);";
|
||||
};
|
||||
v.push_back(std::move(n));
|
||||
}
|
||||
|
||||
// ── Op: hueShift ──────────────────────────────────────────────
|
||||
{
|
||||
DagNodeDef n;
|
||||
n.name = "hueShift";
|
||||
n.label = "hue shift";
|
||||
n.desc = "rotar matiz";
|
||||
n.kind = DagKind::Op;
|
||||
n.param_names = {"h", "", "", ""};
|
||||
n.param_defaults = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
n.controls = {
|
||||
{ DagControl::Kind::Slider, "h", {0, -1, -1}, 0.0f, 1.0f, 0.01f },
|
||||
};
|
||||
n.body_glsl = [](int idx) -> std::string {
|
||||
std::string i = std::to_string(idx);
|
||||
return " vec4 p = u_params[" + i + "];\n"
|
||||
" float a = 6.28318 * p.x;\n"
|
||||
" float ca = cos(a), sa = sin(a);\n"
|
||||
" mat3 hueMat = mat3(\n"
|
||||
" vec3(0.299 + 0.701 * ca + 0.168 * sa, 0.587 - 0.587 * ca + 0.330 * sa, 0.114 - 0.114 * ca - 0.497 * sa),\n"
|
||||
" vec3(0.299 - 0.299 * ca - 0.328 * sa, 0.587 + 0.413 * ca + 0.035 * sa, 0.114 - 0.114 * ca + 0.292 * sa),\n"
|
||||
" vec3(0.299 - 0.300 * ca + 1.250 * sa, 0.587 - 0.588 * ca - 1.050 * sa, 0.114 + 0.886 * ca - 0.203 * sa)\n"
|
||||
" );\n"
|
||||
" return vec4(clamp(hueMat * c.rgb, 0.0, 1.0), c.a);";
|
||||
};
|
||||
v.push_back(std::move(n));
|
||||
}
|
||||
|
||||
// ── Blend: mix ────────────────────────────────────────────────
|
||||
{
|
||||
DagNodeDef n;
|
||||
n.name = "blend_mix";
|
||||
n.label = "mix";
|
||||
n.desc = "interpolacion mix(a, b, t)";
|
||||
n.kind = DagKind::Blend;
|
||||
n.param_names = {"t", "", "", ""};
|
||||
n.param_defaults = {0.5f, 0.0f, 0.0f, 0.0f};
|
||||
n.controls = {
|
||||
{ DagControl::Kind::Slider, "t", {0, -1, -1}, 0.0f, 1.0f, 0.01f },
|
||||
};
|
||||
n.body_glsl = [](int idx) -> std::string {
|
||||
std::string i = std::to_string(idx);
|
||||
return " vec4 p = u_params[" + i + "];\n"
|
||||
" return mix(a, b, p.x);";
|
||||
};
|
||||
v.push_back(std::move(n));
|
||||
}
|
||||
|
||||
// ── Blend: multiply ───────────────────────────────────────────
|
||||
{
|
||||
DagNodeDef n;
|
||||
n.name = "blend_multiply";
|
||||
n.label = "multiply";
|
||||
n.desc = "a * b";
|
||||
n.kind = DagKind::Blend;
|
||||
n.param_names = {"", "", "", ""};
|
||||
n.param_defaults = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
n.controls = {};
|
||||
n.body_glsl = [](int /*idx*/) -> std::string {
|
||||
return " return vec4(a.rgb * b.rgb, a.a);";
|
||||
};
|
||||
v.push_back(std::move(n));
|
||||
}
|
||||
|
||||
// ── Blend: screen ─────────────────────────────────────────────
|
||||
{
|
||||
DagNodeDef n;
|
||||
n.name = "blend_screen";
|
||||
n.label = "screen";
|
||||
n.desc = "1 - (1-a)(1-b)";
|
||||
n.kind = DagKind::Blend;
|
||||
n.param_names = {"", "", "", ""};
|
||||
n.param_defaults = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
n.controls = {};
|
||||
n.body_glsl = [](int /*idx*/) -> std::string {
|
||||
return " return vec4(1.0 - (1.0 - a.rgb) * (1.0 - b.rgb), a.a);";
|
||||
};
|
||||
v.push_back(std::move(n));
|
||||
}
|
||||
|
||||
return v;
|
||||
}();
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const std::vector<DagNodeDef>& dag_catalog() {
|
||||
return build_catalog();
|
||||
}
|
||||
|
||||
const DagNodeDef* dag_find(const std::string& name) {
|
||||
for (const auto& n : dag_catalog()) {
|
||||
if (n.name == name) return &n;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "gfx/dag_types.h"
|
||||
#include <vector>
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
const std::vector<DagNodeDef>& dag_catalog();
|
||||
|
||||
const DagNodeDef* dag_find(const std::string& name);
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
name: dag_catalog
|
||||
kind: function
|
||||
lang: cpp
|
||||
domain: gfx
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "const std::vector<DagNodeDef>& dag_catalog(); const DagNodeDef* dag_find(const std::string& name)"
|
||||
description: "Catalogo global de nodos DAG para el pipeline de shaders. dag_catalog() devuelve referencia estable a los 10 nodos hardcoded (4 gen, 3 op, 3 blend) portados desde shader-dag-blends.jsx. dag_find() busca por nombre."
|
||||
tags: [dag, shader, catalog, nodes, gfx, pipeline]
|
||||
uses_functions: []
|
||||
uses_types:
|
||||
- dag_types_cpp_gfx
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [dag_types, string, vector]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/gfx/dag_catalog.cpp"
|
||||
params: []
|
||||
output: "dag_catalog(): referencia const estable al vector de DagNodeDef (instancia estatica, no se invalida). dag_find(name): puntero al nodo con ese nombre o nullptr si no existe."
|
||||
---
|
||||
|
||||
## Nodos incluidos
|
||||
|
||||
### Generadores (4)
|
||||
- **solid**: color constante RGB
|
||||
- **gradient**: gradiente direccional con tono
|
||||
- **plasma**: onda trigonometrica animada
|
||||
- **circle**: SDF de circulo con suavizado
|
||||
|
||||
### Operadores (3)
|
||||
- **invert**: 1 - rgb
|
||||
- **gamma**: correccion gamma pow(rgb, 1/g)
|
||||
- **hueShift**: rotacion de matiz via matriz YIQ
|
||||
|
||||
### Blends (3)
|
||||
- **blend_mix**: interpolacion mix(a, b, t)
|
||||
- **blend_multiply**: a.rgb * b.rgb
|
||||
- **blend_screen**: 1 - (1-a)(1-b)
|
||||
|
||||
## Notas
|
||||
|
||||
Los cuerpos GLSL omiten las declaraciones de u_time, u_resolution, u_params — las proporciona el preamble de gl_shader::compile_fragment o compile_dag_to_glsl. El indice idx que recibe body_glsl es la posicion en el pipeline (para indexar u_params[idx]).
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "gfx/dag_compile.h"
|
||||
#include "gfx/dag_catalog.h"
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
static constexpr int MAX_NODES = 16;
|
||||
|
||||
std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline) {
|
||||
const int n = static_cast<int>(std::min(pipeline.size(), static_cast<size_t>(MAX_NODES)));
|
||||
std::ostringstream out;
|
||||
|
||||
out << "uniform vec4 u_params[16];\n\n";
|
||||
|
||||
if (n == 0) {
|
||||
out << "void main() {\n";
|
||||
out << " vec2 uv = gl_FragCoord.xy / u_resolution;\n";
|
||||
out << " (void)uv;\n";
|
||||
out << " fragColor = vec4(0.04, 0.04, 0.06, 1.0);\n";
|
||||
out << "}\n";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const DagStep& step = pipeline[static_cast<size_t>(i)];
|
||||
const DagNodeDef* def = dag_find(step.name);
|
||||
if (!def) continue;
|
||||
|
||||
if (def->kind == DagKind::Blend) {
|
||||
out << "vec4 node_" << i << "(vec4 a, vec4 b, vec2 uv) {\n";
|
||||
} else {
|
||||
out << "vec4 node_" << i << "(vec4 c, vec2 uv) {\n";
|
||||
}
|
||||
out << def->body_glsl(i) << "\n";
|
||||
out << "}\n\n";
|
||||
}
|
||||
|
||||
out << "void main() {\n";
|
||||
out << " vec2 uv = gl_FragCoord.xy / u_resolution;\n";
|
||||
out << " vec4 c = vec4(0.04, 0.04, 0.06, 1.0);\n";
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const DagStep& step = pipeline[static_cast<size_t>(i)];
|
||||
const DagNodeDef* def = dag_find(step.name);
|
||||
if (!def) {
|
||||
out << " vec4 out_" << i << " = (i > 0 ? out_" << (i-1) << " : c);\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string prev = (i == 0) ? "vec4(0.0, 0.0, 0.0, 1.0)" : "out_" + std::to_string(i - 1);
|
||||
|
||||
if (def->kind == DagKind::Blend) {
|
||||
int src_idx = -1;
|
||||
if (!step.source_id.empty()) {
|
||||
for (int j = 0; j < i; ++j) {
|
||||
if (pipeline[static_cast<size_t>(j)].id == step.source_id) {
|
||||
src_idx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (src_idx < 0 || src_idx >= i) {
|
||||
src_idx = std::max(0, i - 2);
|
||||
}
|
||||
std::string src;
|
||||
if (i == 0) {
|
||||
src = prev;
|
||||
} else {
|
||||
src = "out_" + std::to_string(std::min(src_idx, i - 1));
|
||||
}
|
||||
out << " vec4 out_" << i << " = node_" << i << "(" << prev << ", " << src << ", uv);\n";
|
||||
} else {
|
||||
out << " vec4 out_" << i << " = node_" << i << "(" << prev << ", uv);\n";
|
||||
}
|
||||
}
|
||||
|
||||
out << " fragColor = out_" << (n - 1) << ";\n";
|
||||
out << "}\n";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "gfx/dag_types.h"
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
// Compila un pipeline DAG a GLSL 330 core completo (listo para gl_shader::compile_fragment).
|
||||
// El preamble de gl_shader ya declara #version, fragColor, u_time, u_resolution, u_mouse.
|
||||
// Este compilador emite uniform vec4 u_params[16], las funciones node_<i> y void main().
|
||||
// Si el pipeline esta vacio, emite un fragment que pinta gris oscuro.
|
||||
std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline);
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: dag_compile
|
||||
kind: function
|
||||
lang: cpp
|
||||
domain: gfx
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline)"
|
||||
description: "Compila un pipeline DAG a GLSL 330 core listo para pasarle a gl_shader::compile_fragment. Emite uniform vec4 u_params[16], una funcion node_<i> por paso y void main() que encadena los outputs. Blends usan source_id para fan-in estable ante reorders."
|
||||
tags: [dag, shader, glsl, compiler, gfx, pipeline]
|
||||
uses_functions:
|
||||
- dag_catalog_cpp_gfx
|
||||
uses_types:
|
||||
- dag_types_cpp_gfx
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [dag_compile, dag_catalog, dag_types, string, vector, sstream, algorithm]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/gfx/dag_compile.cpp"
|
||||
params:
|
||||
- name: pipeline
|
||||
desc: "Vector de DagStep. Cada paso tiene un nombre de nodo del catalogo, params array<float,4> y source_id para blends."
|
||||
output: "String GLSL que se pega tras el preamble de gl_shader (que ya declara #version 330 core, fragColor, u_time, u_resolution, u_mouse). Incluye uniform vec4 u_params[16], funciones node_<i> y void main()."
|
||||
---
|
||||
|
||||
## Estructura del GLSL emitido
|
||||
|
||||
```glsl
|
||||
uniform vec4 u_params[16];
|
||||
|
||||
vec4 node_0(vec4 c, vec2 uv) { ... }
|
||||
vec4 node_1(vec4 a, vec4 b, vec2 uv) { ... } // blend
|
||||
|
||||
void main() {
|
||||
vec2 uv = gl_FragCoord.xy / u_resolution;
|
||||
vec4 c = vec4(0.04, 0.04, 0.06, 1.0);
|
||||
vec4 out_0 = node_0(vec4(0.0, 0.0, 0.0, 1.0), uv);
|
||||
vec4 out_1 = node_1(out_0, out_0, uv);
|
||||
fragColor = out_1;
|
||||
}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- El preamble de gl_shader::compile_fragment ya declara los 3 uniforms basicos. compile_dag_to_glsl NO los redeclara.
|
||||
- Si el pipeline esta vacio, emite void main() que pinta gris oscuro (0.04, 0.04, 0.06).
|
||||
- MAX_NODES = 16. Pipelines mas largos se truncan silenciosamente.
|
||||
- source_id fallback: si el id no se encuentra o apunta a un indice >= idx, usa max(0, idx-2).
|
||||
@@ -0,0 +1,228 @@
|
||||
#include "gfx/dag_panel.h"
|
||||
#include "gfx/dag_catalog.h"
|
||||
#include "imgui.h"
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
static constexpr int MAX_NODES = 16;
|
||||
|
||||
static uint64_t s_next_id = 1;
|
||||
|
||||
static std::string make_id() {
|
||||
return "n" + std::to_string(s_next_id++);
|
||||
}
|
||||
|
||||
static ImVec4 kind_color(DagKind kind) {
|
||||
switch (kind) {
|
||||
case DagKind::Gen: return ImVec4(0.25f, 0.55f, 0.90f, 1.0f);
|
||||
case DagKind::Op: return ImVec4(0.65f, 0.40f, 0.90f, 1.0f);
|
||||
case DagKind::Blend: return ImVec4(0.90f, 0.65f, 0.15f, 1.0f);
|
||||
}
|
||||
return ImVec4(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
static const char* kind_label(DagKind kind) {
|
||||
switch (kind) {
|
||||
case DagKind::Gen: return "Gen";
|
||||
case DagKind::Op: return "Op";
|
||||
case DagKind::Blend: return "Blend";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
bool dag_panel(std::vector<DagStep>& pipeline) {
|
||||
bool changed = false;
|
||||
|
||||
// Toolbar
|
||||
int sz = static_cast<int>(pipeline.size());
|
||||
if (ImGui::Button("+ Add Node")) {
|
||||
ImGui::OpenPopup("dag_add_popup");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%d/%d nodes", sz, MAX_NODES);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear") && !pipeline.empty()) {
|
||||
ImGui::OpenPopup("dag_clear_confirm");
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupModal("dag_clear_confirm", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Vaciar el pipeline?");
|
||||
if (ImGui::Button("Si")) {
|
||||
pipeline.clear();
|
||||
changed = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("No")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("dag_add_popup")) {
|
||||
const char* kind_names[] = { "Gen", "Op", "Blend" };
|
||||
DagKind kinds[] = { DagKind::Gen, DagKind::Op, DagKind::Blend };
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
if (ImGui::BeginMenu(kind_names[k])) {
|
||||
for (const auto& def : dag_catalog()) {
|
||||
if (def.kind != kinds[k]) continue;
|
||||
if (ImGui::MenuItem(def.label.c_str())) {
|
||||
if (static_cast<int>(pipeline.size()) < MAX_NODES) {
|
||||
DagStep step;
|
||||
step.id = make_id();
|
||||
step.name = def.name;
|
||||
step.params = def.param_defaults;
|
||||
if (def.kind == DagKind::Blend && !pipeline.empty()) {
|
||||
int src = std::max(0, static_cast<int>(pipeline.size()) - 2);
|
||||
step.source_id = pipeline[static_cast<size_t>(src)].id;
|
||||
}
|
||||
pipeline.push_back(step);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Pipeline list
|
||||
int delete_idx = -1;
|
||||
int move_up_idx = -1;
|
||||
int move_down_idx = -1;
|
||||
|
||||
ImGui::BeginChild("dag_pipeline_scroll", ImVec2(0, 0), false);
|
||||
|
||||
for (int i = 0; i < static_cast<int>(pipeline.size()); ++i) {
|
||||
DagStep& step = pipeline[static_cast<size_t>(i)];
|
||||
const DagNodeDef* def = dag_find(step.name);
|
||||
if (!def) continue;
|
||||
|
||||
ImVec4 col = kind_color(def->kind);
|
||||
ImGui::PushID(i);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(col.x*0.4f, col.y*0.4f, col.z*0.4f, 0.8f));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(col.x*0.5f, col.y*0.5f, col.z*0.5f, 0.9f));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(col.x*0.6f, col.y*0.6f, col.z*0.6f, 1.0f));
|
||||
|
||||
std::string header = std::string("#") + std::to_string(i) + " " + def->label + " [" + kind_label(def->kind) + "]";
|
||||
bool open = ImGui::CollapsingHeader(header.c_str());
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (open) {
|
||||
ImGui::Indent(8.0f);
|
||||
|
||||
// Source selector for blends
|
||||
if (def->kind == DagKind::Blend) {
|
||||
std::vector<std::string> prev_labels;
|
||||
std::vector<int> prev_indices;
|
||||
for (int j = 0; j < i; ++j) {
|
||||
const DagNodeDef* pdef = dag_find(pipeline[static_cast<size_t>(j)].name);
|
||||
std::string lbl = "out_" + std::to_string(j);
|
||||
if (pdef) lbl += " (" + pdef->label + ")";
|
||||
prev_labels.push_back(lbl);
|
||||
prev_indices.push_back(j);
|
||||
}
|
||||
|
||||
int current_src = std::max(0, i - 2);
|
||||
if (!step.source_id.empty()) {
|
||||
for (int j = 0; j < i; ++j) {
|
||||
if (pipeline[static_cast<size_t>(j)].id == step.source_id) {
|
||||
current_src = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!prev_labels.empty()) {
|
||||
std::vector<const char*> items;
|
||||
for (const auto& l : prev_labels) items.push_back(l.c_str());
|
||||
int sel = current_src;
|
||||
if (ImGui::Combo("Source", &sel, items.data(), static_cast<int>(items.size()))) {
|
||||
if (sel >= 0 && sel < i) {
|
||||
step.source_id = pipeline[static_cast<size_t>(sel)].id;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImGui::TextDisabled("(requiere al menos un nodo antes)");
|
||||
}
|
||||
}
|
||||
|
||||
// Controls
|
||||
for (const auto& ctrl : def->controls) {
|
||||
std::string uid_label = ctrl.label + "##" + step.id + std::to_string(&ctrl - def->controls.data());
|
||||
if (ctrl.kind == DagControl::Kind::Slider) {
|
||||
int pidx = ctrl.param_idx[0];
|
||||
if (pidx >= 0 && pidx < 4) {
|
||||
ImGui::SliderFloat(uid_label.c_str(), &step.params[static_cast<size_t>(pidx)], ctrl.min, ctrl.max);
|
||||
}
|
||||
} else if (ctrl.kind == DagControl::Kind::XY) {
|
||||
int px = ctrl.param_idx[0];
|
||||
int py = ctrl.param_idx[1];
|
||||
if (px >= 0 && px < 4 && py >= 0 && py < 4 && py == px + 1) {
|
||||
ImGui::SliderFloat2(uid_label.c_str(), &step.params[static_cast<size_t>(px)], ctrl.min, ctrl.max);
|
||||
}
|
||||
} else if (ctrl.kind == DagControl::Kind::Color) {
|
||||
int pr = ctrl.param_idx[0];
|
||||
if (pr >= 0 && pr + 2 < 4) {
|
||||
ImGui::ColorEdit3(uid_label.c_str(), &step.params[static_cast<size_t>(pr)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Action buttons
|
||||
ImGui::Spacing();
|
||||
ImGui::BeginDisabled(i == 0);
|
||||
if (ImGui::SmallButton("Move Up")) {
|
||||
move_up_idx = i;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(i == static_cast<int>(pipeline.size()) - 1);
|
||||
if (ImGui::SmallButton("Move Down")) {
|
||||
move_down_idx = i;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.7f, 0.15f, 0.15f, 0.8f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.2f, 0.2f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0.3f, 0.3f, 1.0f));
|
||||
if (ImGui::SmallButton("Delete")) {
|
||||
delete_idx = i;
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImGui::Unindent(8.0f);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
// Apply deferred mutations
|
||||
if (delete_idx >= 0) {
|
||||
pipeline.erase(pipeline.begin() + delete_idx);
|
||||
changed = true;
|
||||
}
|
||||
if (move_up_idx > 0) {
|
||||
std::swap(pipeline[static_cast<size_t>(move_up_idx)], pipeline[static_cast<size_t>(move_up_idx - 1)]);
|
||||
changed = true;
|
||||
}
|
||||
if (move_down_idx >= 0 && move_down_idx < static_cast<int>(pipeline.size()) - 1) {
|
||||
std::swap(pipeline[static_cast<size_t>(move_down_idx)], pipeline[static_cast<size_t>(move_down_idx + 1)]);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "gfx/dag_types.h"
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
// Renderiza el panel del pipeline dentro del ImGui::Begin actual.
|
||||
// Modifica pipeline in-place. Devuelve true si la topologia cambio
|
||||
// (anadir/borrar/reordenar/cambiar source_id).
|
||||
// Cambios de param (sliders/color/xy) no cuentan como topology change.
|
||||
bool dag_panel(std::vector<DagStep>& pipeline);
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
name: dag_panel
|
||||
kind: component
|
||||
lang: cpp
|
||||
domain: gfx
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "bool dag_panel(std::vector<DagStep>& pipeline)"
|
||||
description: "Panel ImGui para editar un pipeline DAG de shaders. Toolbar con Add Node (popup por kind) y Clear. Lista scrollable de pasos con CollapsingHeader coloreado por kind, selector de source para blends, widgets de control (Slider/XY/Color) y botones Move Up/Down/Delete. Devuelve true si la topologia cambio."
|
||||
tags: [dag, shader, imgui, pipeline, editor, gfx, component]
|
||||
uses_functions:
|
||||
- dag_catalog_cpp_gfx
|
||||
uses_types:
|
||||
- dag_types_cpp_gfx
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [dag_panel, dag_catalog, dag_types, imgui, algorithm, string, vector]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/gfx/dag_panel.cpp"
|
||||
framework: imgui
|
||||
params:
|
||||
- name: pipeline
|
||||
desc: "Vector de DagStep modificado in-place. Add/Delete/Move cambian la topologia. Los sliders solo cambian params."
|
||||
output: "true si la topologia cambio (add/delete/move/source_id change). false si solo cambiaron valores de params (sliders). El caller usa este flag para decidir si recompilar el shader."
|
||||
---
|
||||
|
||||
## Estructura del panel
|
||||
|
||||
```
|
||||
[+ Add Node] [N/16 nodes] [Clear]
|
||||
─────────────────────────────────────
|
||||
▼ #0 plasma [Gen]
|
||||
velocidad [slider]
|
||||
escala [slider]
|
||||
[Move Up(disabled)] [Move Down] [Delete]
|
||||
|
||||
▶ #1 blend_mix [Blend]
|
||||
...
|
||||
```
|
||||
|
||||
## Colores por kind
|
||||
|
||||
- Gen: azul (0.25, 0.55, 0.90)
|
||||
- Op: violeta (0.65, 0.40, 0.90)
|
||||
- Blend: ambar (0.90, 0.65, 0.15)
|
||||
|
||||
## Notas
|
||||
|
||||
Los IDs de paso se generan con contador estatico `s_next_id`. Unicos dentro de la sesion. Al anadir un Blend, asigna automaticamente el source_id al paso de hace dos (o el primero si el pipeline tiene menos de 2 pasos). Las mutaciones (delete/move) se aplican al final del loop para no invalidar iteradores.
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
enum class DagKind { Gen, Op, Blend };
|
||||
|
||||
struct DagControl {
|
||||
enum class Kind { Slider, XY, Color };
|
||||
Kind kind;
|
||||
std::string label;
|
||||
std::array<int, 3> param_idx{-1, -1, -1};
|
||||
float min = 0.0f;
|
||||
float max = 1.0f;
|
||||
float step = 0.0f;
|
||||
};
|
||||
|
||||
struct DagNodeDef {
|
||||
std::string name;
|
||||
std::string label;
|
||||
std::string desc;
|
||||
DagKind kind = DagKind::Gen;
|
||||
std::array<std::string, 4> param_names{"", "", "", ""};
|
||||
std::array<float, 4> param_defaults{0, 0, 0, 0};
|
||||
std::vector<DagControl> controls;
|
||||
std::function<std::string(int idx)> body_glsl;
|
||||
};
|
||||
|
||||
struct DagStep {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::array<float, 4> params{0, 0, 0, 0};
|
||||
std::string source_id;
|
||||
};
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "gfx/dag_uniforms.h"
|
||||
#include "gfx/gl_loader.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
static constexpr int MAX_NODES = 16;
|
||||
|
||||
void dag_uniforms_apply(const std::vector<DagStep>& pipeline, unsigned int program) {
|
||||
float data[MAX_NODES * 4];
|
||||
std::memset(data, 0, sizeof(data));
|
||||
|
||||
const int n = static_cast<int>(std::min(pipeline.size(), static_cast<size_t>(MAX_NODES)));
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const auto& step = pipeline[static_cast<size_t>(i)];
|
||||
data[i * 4 + 0] = step.params[0];
|
||||
data[i * 4 + 1] = step.params[1];
|
||||
data[i * 4 + 2] = step.params[2];
|
||||
data[i * 4 + 3] = step.params[3];
|
||||
}
|
||||
|
||||
GLint loc = glGetUniformLocation(program, "u_params");
|
||||
if (loc >= 0) glUniform4fv(loc, MAX_NODES, data);
|
||||
}
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "gfx/dag_types.h"
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
// Sube los params del pipeline al shader via glUniform4fv(u_params, 16, data).
|
||||
// El programa GL debe estar activo (glUseProgram'd) por el caller.
|
||||
void dag_uniforms_apply(const std::vector<DagStep>& pipeline, unsigned int program);
|
||||
|
||||
} // namespace fn::gfx
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: dag_uniforms
|
||||
kind: function
|
||||
lang: cpp
|
||||
domain: gfx
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "void dag_uniforms_apply(const std::vector<DagStep>& pipeline, unsigned int program)"
|
||||
description: "Sube los params del pipeline al shader activo via glUniform4fv. Construye un float[64] con los params de cada DagStep (hasta 16 pasos) y llama glUniform4fv(u_params, 16, data)."
|
||||
tags: [dag, shader, uniforms, opengl, gfx, pipeline]
|
||||
uses_functions:
|
||||
- gl_loader_cpp_gfx
|
||||
uses_types:
|
||||
- dag_types_cpp_gfx
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [dag_uniforms, gl_loader, dag_types, vector, algorithm, cstring]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/gfx/dag_uniforms.cpp"
|
||||
params:
|
||||
- name: pipeline
|
||||
desc: "Vector de DagStep. params[0..3] de cada step se copian en data[i*4..i*4+3]. Steps mas alla de MAX_NODES=16 se ignoran."
|
||||
- name: program
|
||||
desc: "ID del programa GL activo. Debe haber sido activado con glUseProgram antes de llamar."
|
||||
output: "Efecto lateral: actualiza el uniform u_params[16] en el programa GL activo para el frame actual."
|
||||
---
|
||||
|
||||
## Notas
|
||||
|
||||
El array data[64] se inicializa a 0 antes de copiar, por lo que steps no usados quedan en cero. El caller es responsable de activar el programa antes de llamar.
|
||||
@@ -30,6 +30,7 @@ PFNGLUNIFORM1IPROC fn_glUniform1i = nullptr;
|
||||
PFNGLUNIFORM2FPROC fn_glUniform2f = nullptr;
|
||||
PFNGLUNIFORM3FPROC fn_glUniform3f = nullptr;
|
||||
PFNGLUNIFORM4FPROC fn_glUniform4f = nullptr;
|
||||
PFNGLUNIFORM4FVPROC fn_glUniform4fv = nullptr;
|
||||
PFNGLUSEPROGRAMPROC fn_glUseProgram = nullptr;
|
||||
|
||||
namespace fn::gfx {
|
||||
@@ -67,6 +68,7 @@ bool gl_loader_init() {
|
||||
LOAD(glUniform2f);
|
||||
LOAD(glUniform3f);
|
||||
LOAD(glUniform4f);
|
||||
LOAD(glUniform4fv);
|
||||
LOAD(glUseProgram);
|
||||
|
||||
#undef LOAD
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
extern PFNGLUNIFORM2FPROC fn_glUniform2f;
|
||||
extern PFNGLUNIFORM3FPROC fn_glUniform3f;
|
||||
extern PFNGLUNIFORM4FPROC fn_glUniform4f;
|
||||
extern PFNGLUNIFORM4FVPROC fn_glUniform4fv;
|
||||
extern PFNGLUSEPROGRAMPROC fn_glUseProgram;
|
||||
|
||||
#define glAttachShader fn_glAttachShader
|
||||
@@ -66,6 +67,7 @@
|
||||
#define glUniform2f fn_glUniform2f
|
||||
#define glUniform3f fn_glUniform3f
|
||||
#define glUniform4f fn_glUniform4f
|
||||
#define glUniform4fv fn_glUniform4fv
|
||||
#define glUseProgram fn_glUseProgram
|
||||
#else
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
|
||||
Reference in New Issue
Block a user