Files
fn_registry/cpp/functions/gfx/dag_panel.cpp
T
egutierrez 5d83c11169 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>
2026-04-24 21:15:21 +02:00

229 lines
8.3 KiB
C++

#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