feat(shaders_lab): visual node editor (imgui-node-editor) + multi-source

- cpp/vendor/imgui-node-editor: vendorized thedmd/imgui-node-editor v0.9.4
- cpp/functions/gfx/dag_node_editor: new visual pipeline editor replacing dag_panel
- DagStep: source_ids[4] + editor_pos + editor_uid (multi-input support)
- DagNodeDef: num_inputs explicit; circle reclassified as Op
- dag_compile: N inputs per node, topological ordering preserved
- main: use node editor; destroy on shutdown
- patch imgui_extra_math.inl: guard operator*(float, ImVec2) for imgui >= 18955

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-24 21:55:43 +02:00
parent bf5011de93
commit 88fca7b128
89 changed files with 22289 additions and 41 deletions
+33 -26
View File
@@ -22,57 +22,64 @@ std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline) {
return out.str();
}
// Emit per-node functions with signatures based on num_inputs
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";
}
int ni = def->num_inputs;
out << "vec4 node_" << i << "(";
if (ni >= 1) out << "vec4 a";
if (ni >= 2) out << ", vec4 b";
if (ni >= 3) out << ", vec4 c";
if (ni >= 4) out << ", vec4 d";
if (ni > 0) out << ", ";
out << "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";
if (i == 0) {
out << " vec4 out_" << i << " = vec4(0.0, 0.0, 0.0, 1.0);\n";
} else {
out << " vec4 out_" << i << " = out_" << (i - 1) << ";\n";
}
continue;
}
std::string prev = (i == 0) ? "vec4(0.0, 0.0, 0.0, 1.0)" : "out_" + std::to_string(i - 1);
int ni = def->num_inputs;
if (def->kind == DagKind::Blend) {
int src_idx = -1;
if (!step.source_id.empty()) {
// Resolve each input slot
// For slot k: look for source_ids[k] in pipeline[0..i-1]; fallback = prev output
auto resolve = [&](int k) -> std::string {
const std::string& sid = step.source_ids[static_cast<size_t>(k)];
if (!sid.empty()) {
for (int j = 0; j < i; ++j) {
if (pipeline[static_cast<size_t>(j)].id == step.source_id) {
src_idx = j;
break;
if (pipeline[static_cast<size_t>(j)].id == sid) {
return "out_" + std::to_string(j);
}
}
}
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";
// fallback
if (i == 0) return "vec4(0.0, 0.0, 0.0, 1.0)";
return "out_" + std::to_string(i - 1);
};
out << " vec4 out_" << i << " = node_" << i << "(";
for (int k = 0; k < ni; ++k) {
if (k > 0) out << ", ";
out << resolve(k);
}
if (ni > 0) out << ", ";
out << "uv);\n";
}
out << " fragColor = out_" << (n - 1) << ";\n";