e828af3ac1
- DagKind::Output (new enum): terminal sink; compiler wires fragColor to its source_ids[0] - dag_catalog: "output" node (1 input, red) - dag_compile: skips Output in node_<i> emission; final fragColor resolves from Output's connection - dag_node_editor: no more Add button; drops "DAG_NODE_TYPE" payloads at mouse canvas position; Output cannot be deleted; Output has no output pin - dag_palette (new fn): Functions window with grouped, draggable node cards - main.cpp: "Functions" window added; ensure_dag_default seeds plasma + connected Output
256 lines
9.5 KiB
C++
256 lines
9.5 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);
|
|
case DagKind::Output: return ImVec4(0.85f, 0.25f, 0.25f, 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";
|
|
case DagKind::Output: return "Output";
|
|
}
|
|
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_ids[1] = 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;
|
|
int drop_src_idx = -1;
|
|
int drop_target_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 (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) {
|
|
ImGui::SetDragDropPayload("DAG_STEP", &i, sizeof(int));
|
|
ImGui::Text("Move %s", def->label.c_str());
|
|
ImGui::EndDragDropSource();
|
|
}
|
|
if (ImGui::BeginDragDropTarget()) {
|
|
if (const ImGuiPayload* pl = ImGui::AcceptDragDropPayload("DAG_STEP")) {
|
|
drop_src_idx = *static_cast<const int*>(pl->Data);
|
|
drop_target_idx = i;
|
|
}
|
|
ImGui::EndDragDropTarget();
|
|
}
|
|
|
|
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_ids[1].empty()) {
|
|
for (int j = 0; j < i; ++j) {
|
|
if (pipeline[static_cast<size_t>(j)].id == step.source_ids[1]) {
|
|
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_ids[1] = 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;
|
|
}
|
|
if (drop_src_idx >= 0 && drop_target_idx >= 0 && drop_src_idx != drop_target_idx &&
|
|
drop_src_idx < static_cast<int>(pipeline.size()) &&
|
|
drop_target_idx < static_cast<int>(pipeline.size())) {
|
|
DagStep moved = pipeline[static_cast<size_t>(drop_src_idx)];
|
|
pipeline.erase(pipeline.begin() + drop_src_idx);
|
|
int target = drop_target_idx;
|
|
if (drop_src_idx < drop_target_idx) target--;
|
|
pipeline.insert(pipeline.begin() + target, moved);
|
|
changed = true;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
} // namespace fn::gfx
|