finished refactoring dynamic node mess

This commit is contained in:
lachrymaLF 2024-06-23 04:57:34 -04:00
parent dec9cb0497
commit 68d06aa660
6 changed files with 241 additions and 169 deletions

View file

@ -91,8 +91,8 @@ namespace K {
state.current_frame = 0;
state.fps = 30.0f;
state.frame_max = 200;
state.plugboard.in = Plugboard::MakeInstance(Plugboard::K_P_CompositionIn);
state.plugboard.out = Plugboard::MakeInstance(Plugboard::K_P_CompositionOut);
// state.plugboard.in = Plugboard::K_P_CompositionIn{};
// state.plugboard.out = Plugboard::K_P_CompositionOut{};
K::UI::SetupViewport(state);
return true;

View file

@ -2,7 +2,7 @@
#include <Keishiki.h>
namespace K::Plugboard {
bool DetectCycle(const NodeInstance& n, Vector<NodeInstance*>& nodes) { // true if cycle found
bool DetectCycle(const NodeInstanceP n, Vector<NodeInstanceP> nodes) { // true if cycle found
return !DFS(n, [&nodes](ConnectInfo info){
if (std::find(nodes.begin(), nodes.end(), info.p) == nodes.end()) {
nodes.push_back(info.p);
@ -15,50 +15,50 @@ namespace K::Plugboard {
void CollectChains(ConnectInfo& in, Vector<ConnectInfo>& chains) {
chains.clear();
if (std::holds_alternative<Chain>(*in.p))
if (std::holds_alternative<Chain*>(in.p))
chains.push_back(in);
DFS(*in.p, [&chains](ConnectInfo info){
if (std::holds_alternative<Chain>(*info.p))
DFS(in.p, [&chains](ConnectInfo info){
if (std::holds_alternative<Chain*>(info.p))
chains.push_back(info);
return true;
});
}
void Connect(NodeInstance& from_instance, u8 input_index, NodeInstance& to_instance, u8 to_out_index) {
Vector<NodeInstance*> nodes{&from_instance};
void Connect(NodeInstanceP from_instance, u8 input_index, NodeInstanceP to_instance, u8 to_out_index) {
Vector<NodeInstanceP> nodes{from_instance};
if (!DetectCycle(to_instance, nodes)) // no cycle
std::visit([&](auto&& from, auto&& to) {
from.in[input_index].value = ConnectInfo{&to_instance, to_out_index};
to.out[to_out_index].outgoing.push_back({&from_instance, input_index});
from->in[input_index].value = ConnectInfo{to_instance, to_out_index};
to->out[to_out_index].outgoing.push_back({from_instance, input_index});
}, from_instance, to_instance);
else
Log("Connect failed -- Cycle detected");
}
void Disconnect(NodeInstance& from_instance, u8 input_index) {
void Disconnect(NodeInstanceP from_instance, u8 input_index) {
std::visit([&](auto&& from) {
std::visit([&](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, ConnectInfo>) {
std::visit([&](auto&& a) {
auto& v = a.out[arg.index].outgoing;
auto& v = a->out[arg.index].outgoing;
for (auto it = v.begin(); it != v.end(); it++) {
if (it->p == &from_instance) {
if (it->p == from_instance) {
v.erase(it);
break;
}
}
}, *arg.p);
from.in[input_index].value = ExpandVariant(from.in[input_index].type);
}, arg.p);
from->in[input_index].value = ExpandVariant(from->in[input_index].type);
}
else if constexpr (std::is_same_v<T, T_Map<Type::T_Count>::type>)
; // already disconnected
}, from.in[input_index].value);
}, from->in[input_index].value);
}, from_instance);
}
NodeInstance MakeInstance(K_P_Nodes i) {
/* NodeInstance MakeInstance(K_P_Nodes i) {
static NodeInstance table[] = {
Add{},
Negate{},
@ -86,7 +86,7 @@ namespace K::Plugboard {
};
static_assert(sizeof(table)/sizeof(table[0]) == K_P_Count);
return table[i];
}
}*/
T_Map<T_Count>::type ExpandVariant(u32 i) {
static T_Map<T_Count>::type table[] = { f32{}, i32{}, RGBA{}, XY{}, XYZ{}, String{} };
@ -297,7 +297,7 @@ namespace K::Plugboard {
}
T_Map<T_Count>::type Eval(const CompState& s, const ConnectInfo& info) {
return std::visit([&info, &s](auto&& arg) { return arg.fetch[info.index](s, arg); }, *info.p);
return std::visit([&info, &s](auto&& arg) { return arg->fetch[info.index](s, *arg); }, info.p);
}
T_Map<T_Count>::type ConvertValue(const T_Map<T_Count>::type& v, Type target, bool& good) {
@ -403,12 +403,11 @@ namespace K::Plugboard {
}
void Plugboard::RecollectChains() {
auto& node = std::get<CompositionOut>(out);
for (u32 i = 0; i < node.in.size(); i++) {
std::visit([i, &node](auto&& arg) {
for (u32 i = 0; i < out.in.size(); i++) {
std::visit([i, this](auto&& arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, ConnectInfo>)
CollectChains(arg, node.show_nodes[i]);
}, node.in[i].value);
CollectChains(arg, out.show_nodes[i]);
}, out.in[i].value);
}
}
}

View file

@ -187,7 +187,7 @@ namespace K::UI {
}
template <Plugboard::Node N>
void PlugboardNodeDrawSockets(CompState& s, N& n, Plugboard::NodeInstance& instance, ImGuiStyle& style,
void PlugboardNodeDrawSockets(CompState& s, N& n, ImGuiStyle& style,
bool& dragging_on_socket, ImVec2& source, Dict<Plugboard::ConnectInfo, Plugboard::LinksFromSource, Plugboard::ConnectInfoHasher>& link_pos,
bool dummy = true) {
i32 row = 1;
@ -208,14 +208,14 @@ namespace K::UI {
}
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
Plugboard::ConnectInfo d{ &instance, out };
Plugboard::ConnectInfo d{ &n, out };
ImGui::SetDragDropPayload("K_PLUG_OUT_TO_IN", &d, sizeof(d));
ImGui::EndDragDropSource();
}
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("K_PLUG_IN_TO_OUT")) {
Plugboard::ConnectInfo in = *(const Plugboard::ConnectInfo*)payload->Data;
Plugboard::Connect(*in.p, in.index, instance, out);
Plugboard::Connect(in.p, in.index, &n, out);
s.plugboard.RecollectChains();
}
ImGui::EndDragDropTarget();
@ -224,7 +224,7 @@ namespace K::UI {
// Update link info if needed
if (!n.out[out].outgoing.empty()) {\
link_pos[{&instance, out}].source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2,
link_pos[{&n, out}].source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2,
-ImGui::GetFrameHeight() / 2 -
style.ItemInnerSpacing.y};
}
@ -236,14 +236,14 @@ namespace K::UI {
ImGui::RadioButton("##In", false);
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
Plugboard::ConnectInfo d{ &instance, in };
Plugboard::ConnectInfo d{ &n, in };
ImGui::SetDragDropPayload("K_PLUG_IN_TO_OUT", &d, sizeof(d));
ImGui::EndDragDropSource();
}
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("K_PLUG_OUT_TO_IN")) {
Plugboard::ConnectInfo out = *(const Plugboard::ConnectInfo*)payload->Data;
Plugboard::Connect(instance, in, *out.p, out.index);
Plugboard::Connect(&n, in, out.p, out.index);
s.plugboard.RecollectChains();
}
ImGui::EndDragDropTarget();
@ -279,15 +279,15 @@ namespace K::UI {
}
template <Plugboard::Node N>
bool PlugboardDrawNode(CompState& s, N& n, Plugboard::NodeInstance& instance, ImVec2& view_pos, i32 id, ImGuiIO& io, ImGuiStyle& style,
bool& dragging_on_socket, ImVec2& source, Dict<Plugboard::ConnectInfo, Plugboard::LinksFromSource, Plugboard::ConnectInfoHasher>& link_pos, bool selected) {
bool PlugboardDrawNode(CompState& s, N& n, ImVec2& view_pos, ImGuiIO& io, ImGuiStyle& style,
bool& dragging_on_socket, ImVec2& source, Dict<Plugboard::ConnectInfo, Plugboard::LinksFromSource, Plugboard::ConnectInfoHasher>& link_pos) {
bool stat = false;
const auto& pos = n.pos;
const auto& name = n.name;
ImGui::SetCursorPos(view_pos + pos);
ImGui::PushID(id);
ImGui::PushStyleColor(ImGuiCol_ChildBg, selected ? 0xAA202040 : 0xAA080813);
ImGui::PushID(&n);
ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xAA080813);
static ImVec2 old_pos{};
if (ImGui::BeginChild(name, {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border)) {
if (ImGui::BeginTable(name, 3)) {
@ -304,7 +304,7 @@ namespace K::UI {
}
ImGui::TableNextColumn();
if constexpr (std::is_same_v<N, Plugboard::Interpolation>) {
if constexpr (requires (N node) { { node.Draw() } -> std::same_as<void>; }) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
@ -312,7 +312,7 @@ namespace K::UI {
ImGui::TableNextColumn();
}
PlugboardNodeDrawSockets<N>(s, n, instance, style, dragging_on_socket, source, link_pos);
PlugboardNodeDrawSockets<N>(s, n, style, dragging_on_socket, source, link_pos);
ImGui::EndTable();
}
@ -428,16 +428,14 @@ namespace K::UI {
static u32 node_current{};
if (ImGui::Button("Add")) {
auto n = Plugboard::MakeInstance(Plugboard::K_P_Nodes(node_current));
s.plugboard.nodes.push_back(std::move(n));
s.plugboard.nodes.Store(static_cast<Plugboard::K_P_Nodes>(node_current));
}
ImGui::SameLine();
ImGui::SetNextItemWidth(150.0f);
std::visit([](auto&& arg) {
if (ImGui::BeginCombo("##Node", arg.name, ImGuiComboFlags_None)) {
if (ImGui::BeginCombo("##Node", s.plugboard.nodes.GetName(static_cast<Plugboard::K_P_Nodes>(node_current)), ImGuiComboFlags_None)) {
for (u32 n = 0; n < Plugboard::K_P_Count; n++) {
const bool is_selected = (node_current == n);
if (ImGui::Selectable(std::visit([](auto&& a) { return a.name; }, Plugboard::MakeInstance(Plugboard::K_P_Nodes(node_current))), is_selected))
if (ImGui::Selectable(s.plugboard.nodes.GetName(static_cast<Plugboard::K_P_Nodes>(n)), is_selected))
node_current = n;
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
@ -446,7 +444,6 @@ namespace K::UI {
}
ImGui::EndCombo();
}
}, Plugboard::MakeInstance(Plugboard::K_P_Nodes(node_current))); // COPIES!!!
ImGui::SameLine();
if (ImGui::Button(playing ? "Pause" : "Play"))
@ -509,8 +506,6 @@ namespace K::UI {
ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF100500);
ImGui::SetNextWindowSizeConstraints({ImGui::GetContentRegionAvail().x * .1f, -1}, { ImGui::GetContentRegionAvail().x * .8f, -1 });
if (ImGui::BeginChild("Nodes", {ImGui::GetContentRegionAvail().x * .4f, ImGui::GetContentRegionAvail().y}, ImGuiChildFlags_ResizeX, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar)) {
static u32 selected;
ImGuiWindow *window = ImGui::GetCurrentWindow();
ImVec2 window_pos = ImGui::GetWindowPos();
nodes_begin = window_pos;
@ -518,7 +513,7 @@ namespace K::UI {
if (ImGui::BeginDragDropTargetCustom(window->ContentRegionRect, window->ID)) {
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_PLUG_IN_TO_OUT")) {
Plugboard::ConnectInfo in = *(const Plugboard::ConnectInfo *) payload->Data;
Plugboard::Disconnect(*in.p, in.index);
Plugboard::Disconnect(in.p, in.index);
}
ImGui::EndDragDropTarget();
}
@ -540,14 +535,15 @@ namespace K::UI {
view_pos = old_view_pos + ImGui::GetMouseDragDelta();
}
u32 i = 0;
for (auto it = s.plugboard.nodes.begin(); it != s.plugboard.nodes.end(); it++, i++)
std::visit([&it, &i, &io, &style, &dragging_on_socket, &drag_source, &s](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if (PlugboardDrawNode<T>(s, arg, *it, view_pos, static_cast<i32>(i), io, style, dragging_on_socket,
drag_source, s.plugboard.links_pos, selected == i))
selected = i;
}, *it);
std::apply([&](auto&&... args) {
(([&](auto&& arg){
for (auto& node : arg) {
PlugboardDrawNode<std::decay_t<decltype(node)>>(s, node, view_pos, io, style, dragging_on_socket,
drag_source, s.plugboard.links_pos);
}
}(args)), ...);
}, s.plugboard.nodes.nodes);
ImGui::SetCursorPos({});
ImGui::PushStyleColor(ImGuiCol_ChildBg, 0x33FFFFFF);
ImGui::BeginChild("Composition In", {},
@ -555,7 +551,7 @@ namespace K::UI {
ImGuiChildFlags_AutoResizeY);
ImGui::PopStyleColor();
if (ImGui::BeginTable("Composition In", 3)) {
PlugboardNodeDrawSockets<Plugboard::CompositionIn>(s, std::get<Plugboard::CompositionIn>(s.plugboard.in), s.plugboard.in, style, dragging_on_socket, drag_source,
PlugboardNodeDrawSockets(s, s.plugboard.in, style, dragging_on_socket, drag_source,
s.plugboard.links_pos, false);
ImGui::EndTable();
}
@ -582,7 +578,7 @@ namespace K::UI {
f32 view_amt = view_right - view_left, mouse_tl_x;
f32 fr_step;
static Vector<Plugboard::NodeInstance*> selected_chains{};
static Vector<Plugboard::NodeInstanceP> selected_chains{};
// Mouse Handlers
// Drag playhead/pan
@ -950,13 +946,13 @@ namespace K::UI {
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_PLUG_OUT_TO_IN")) {
auto out = *(const Plugboard::ConnectInfo *) payload->Data;
Plugboard::Connect(*u.connection.p, u.connection.index, *out.p, out.index);
Plugboard::Connect(u.connection.p, u.connection.index, out.p, out.index);
s.plugboard.RecollectChains();
}
ImGui::EndDragDropTarget();
}
auto& connected_v = std::visit([&u](auto&& arg) -> auto& { return arg.in[u.connection.index].value; }, *u.connection.p);
auto& connected_v = std::visit([&u](auto&& arg) -> auto& { return arg->in[u.connection.index].value; }, u.connection.p);
std::visit([&u, &dragging_on_socket, &drag_source, &s, &style, &io](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
@ -971,13 +967,12 @@ namespace K::UI {
}
else if (ImGui::IsItemActive()) {
if (ImGui::IsKeyPressed(ImGuiKey_LeftShift)) {
s.plugboard.nodes.push_back(Plugboard::MakeInstance(Plugboard::K_P_Chain));
auto it = s.plugboard.nodes.rbegin();
Plugboard::Connect(*u.connection.p, u.connection.index, *it, 0);
Plugboard::Connect(*it, 0, s.plugboard.in, 0);
auto& nodes = std::get<Plugboard::CompositionOut>(*u.connection.p).show_nodes[u.connection.index];
auto& c = s.plugboard.nodes.Store(Plugboard::Chain{});
Plugboard::Connect(u.connection.p, u.connection.index, &c, 0);
Plugboard::Connect(&c, 0, &s.plugboard.in, 0);
auto& nodes = std::get<Plugboard::CompositionOut*>(u.connection.p)->show_nodes[u.connection.index];
nodes.clear();
nodes.emplace_back(&*it, 0);
nodes.emplace_back(&c, 0);
}
else {
dragging_on_socket = true;
@ -1057,7 +1052,7 @@ namespace K::UI {
draw_tl_bg_drag_area();
if (uniform_open && connected_v.index() == 1) {
for (const auto& info: std::get<Plugboard::CompositionOut>(*u.connection.p).show_nodes[u.connection.index]) {
for (const auto& info: std::get<Plugboard::CompositionOut*>(u.connection.p)->show_nodes[u.connection.index]) {
ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height);
ImGui::TableSetColumnIndex(0);
ImGui::TableSetColumnIndex(1);
@ -1068,7 +1063,7 @@ namespace K::UI {
auto chain_sel_it = std::find(selected_chains.begin(), selected_chains.end(), info.p);
if (chain_sel_it != selected_chains.end())
n_flags |= ImGuiTreeNodeFlags_Selected;
ImGui::TreeNodeEx(std::visit([](auto&& arg){ return arg.name; }, *info.p), n_flags);
ImGui::TreeNodeEx(std::visit([](auto&& arg){ return arg->name; }, info.p), n_flags);
if (ImGui::IsItemClicked()) {
if (chain_sel_it != selected_chains.end())
selected_chains.erase(chain_sel_it);
@ -1077,14 +1072,14 @@ namespace K::UI {
}
ImGui::TableSetColumnIndex(2);
auto& [chain, chain_copy] = std::get<Plugboard::Chain>(*info.p).extra;
auto& [chain, chain_copy] = std::get<Plugboard::Chain*>(info.p)->extra;
if (tl_clear_selection_request)
chain.selected.clear();
bool m_empty = chain.segments.empty(), m_has_before = !m_empty && chain.segments.begin()->frame >= s.current_frame;
bool m_empty = chain.segments.empty(), m_has_before = !m_empty && chain.segments.begin()->frame < s.current_frame;
ImGui::BeginDisabled(m_empty || m_has_before);
ImGui::BeginDisabled(m_empty || !m_has_before);
if (ImGui::Button("<")) {
s.current_frame = std::prev(std::lower_bound(chain.segments.begin(), chain.segments.end(),
s.current_frame,
@ -1191,7 +1186,7 @@ namespace K::UI {
if constexpr (std::is_same_v<T, Plugboard::ConnectInfo>)
s.plugboard.links_pos[arg].sinks.push_back(
{table_left, ImGui::GetCursorScreenPos().y - row_height / 2.0f});
}, std::visit([&u](auto&& arg) -> auto& { return arg.in[u.connection.index].value; }, *u.connection.p));
}, std::visit([&u](auto&& arg) -> auto& { return arg->in[u.connection.index].value; }, u.connection.p));
ImGui::PopStyleVar(); // FramePadding
@ -1282,7 +1277,7 @@ namespace K::UI {
u32 keys = 0;
for (const auto& n : selected_chains) {
Plugboard::ConnectInfo i {n, 0}; // it's a chain -- 0 is out
auto& [chain, chain_copy] = std::get<Plugboard::Chain>(*n).extra;
auto& [chain, chain_copy] = std::get<Plugboard::Chain*>(n)->extra;
if (started_dragging)
chain_copy = chain;
@ -1304,14 +1299,14 @@ namespace K::UI {
i32 frame = k;
f64 v = val;
i32 drag_off_x; // data coords
f64 drag_off_x; // data coords
f64 drag_off_y; // data coords
ImPlotPoint mouse_pos = ImPlot::PixelsToPlot(io.MousePos),
mouse_clicked_pos = ImPlot::PixelsToPlot(io.MouseClickedPos[ImGuiMouseButton_Left]);
drag_off_x = static_cast<i32>(std::round(mouse_pos.x - mouse_clicked_pos.x));
drag_off_x = mouse_pos.x - mouse_clicked_pos.x;
drag_off_y = mouse_pos.y - mouse_clicked_pos.y;
if (dragging && is_sel) {
frame += drag_off_x;
frame += static_cast<i32>(std::round(drag_off_x));
v += drag_off_y;
}
@ -1333,7 +1328,7 @@ namespace K::UI {
i32 frame_nxt = k_nxt;
f64 v_nxt = val_nxt;
if (dragging && is_nxt_sel) {
frame_nxt += drag_off_x;
frame_nxt += static_cast<i32>(std::round(drag_off_x));
v_nxt += drag_off_y;
}
@ -1518,7 +1513,7 @@ namespace K::UI {
ImGui::TableHeadersRow();
i32 i = 0;
for (auto it = s.layers[s.active].track.uniforms.begin(); it != s.layers[s.active].track.uniforms.end();) {
if (it->connection.p == nullptr)
if (std::visit([](auto&& arg){ return arg == nullptr; }, it->connection.p))
continue;
ImGui::TableNextRow();
@ -1535,9 +1530,9 @@ namespace K::UI {
if constexpr (std::is_same_v<T, Plugboard::T_Map<Plugboard::Type::T_Count>::type>)
DrawPlugboardVariableEditWidget(arg);
else
ImGui::Text("Plugged into %s!", std::visit([](auto&& d) { return d.name; }, *arg.p));
}, n.in[it->connection.index].value);
}, *it->connection.p);
ImGui::Text("Plugged into %s!", std::visit([](auto&& d) { return d->name; }, arg.p));
}, n->in[it->connection.index].value);
}, it->connection.p);
ImGui::TableNextColumn();
if (ImGui::Button("X")) {
bgfx::destroy(it->handle);
@ -1607,8 +1602,8 @@ namespace K::UI {
if (ImGui::Button("Apply"))
for (auto& layer : s.layers)
for (auto& u : layer.track.uniforms)
for (auto& n : std::get<Plugboard::CompositionOut>(*u.connection.p).show_nodes[u.connection.index]) {
auto& [chain, _] = std::get<Plugboard::Chain>(*n.p).extra;
for (auto& n : std::get<Plugboard::CompositionOut*>(u.connection.p)->show_nodes[u.connection.index]) {
auto& [chain, _] = std::get<Plugboard::Chain*>(n.p)->extra;
for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) {
auto nit = std::next(it);
if (nit != chain.segments.end() && std::find(chain.selected.begin(), chain.selected.end(), std::distance(chain.segments.begin(), it)) != chain.selected.end() &&

View file

@ -48,7 +48,7 @@ namespace K {
for (auto& u : uniforms) {
f32 pack[4]{};
ShaderGraph::T_Map<ShaderGraph::T_Count>::type val_target;
if (u.connection.p == nullptr)
if (std::visit([](auto&& arg){ return arg == nullptr; }, u.connection.p))
val_target = u.val;
else {
std::visit([&u, &s, &val_target](auto&& arg) {
@ -61,7 +61,7 @@ namespace K {
}
else if constexpr (std::is_same_v<T, Plugboard::T_Map<Plugboard::T_Count>::type>)
val_target = PlugboardValToShader(arg);
}, std::visit([&u](auto&& arg) { return arg.in[u.connection.index].value; }, *u.connection.p));
}, std::visit([&u](auto&& arg) { return arg->in[u.connection.index].value; }, u.connection.p));
}
std::visit([&pack](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
@ -182,11 +182,10 @@ namespace K {
}
void VisualTrack::ExposeUniform(CompState& s, Uniform& uu) {
auto& n = std::get<Plugboard::CompositionOut>(s.plugboard.out);
uu.connection = {&s.plugboard.out, static_cast<u32>(n.in.size())};
uu.connection = {&s.plugboard.out, static_cast<u32>(s.plugboard.out.in.size())};
// this is shaky and bug prone -- relies on the shader types being in line with plugboard types
n.in.push_back(Plugboard::InSocket{ uu.name, Plugboard::Type(uu.val.index()), ShaderValToPlugboard(uu.val)});
n.show_nodes.push_back({});
s.plugboard.out.in.push_back(Plugboard::InSocket{ uu.name, Plugboard::Type(uu.val.index()), ShaderValToPlugboard(uu.val)});
s.plugboard.out.show_nodes.emplace_back();
}
void VisualTrack::ExposeUniform(CompState& s, u32 i) {
@ -212,7 +211,7 @@ namespace K {
for (auto& ss : samplers)
if (ss.name == s)
return;
uniforms.emplace_back(Uniform{s, bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val, {nullptr, 0}});
uniforms.emplace_back(Uniform{s, bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val, {{}, 0}});
}
void VisualTrack::AddSampler(const String& s) {

View file

@ -10,6 +10,15 @@
#include <variant>
namespace K::Plugboard {
template <typename T>
concept Node = requires(T node) {
{ T::name } -> std::same_as<const char* const&>;
{ node.in.size() } -> std::convertible_to<std::size_t>;
{ node.out.size() } -> std::convertible_to<std::size_t>;
{ node.fetch.size() } -> std::convertible_to<std::size_t>; // in the future enforce same_as<tuple_size<fetch>, tuple_size<out>> if both are static arrays
{ node.pos } -> std::convertible_to<ImVec2&>;
};
enum Type {
T_Float,
T_Int,
@ -82,32 +91,6 @@ namespace K::Plugboard {
struct CompositionIn;
struct CompositionOut;
using NodeInstance = std::variant<
Add,
Negate,
Subtract,
Multiply,
Divide,
Sign,
Sin,
Cos,
Tan,
Arcsin,
Arccos,
Arctan,
Atan2,
Minimum,
Maximum,
Power,
SquareRoot,
NaturalLogarithm,
AbsoluteValue,
Interpolation,
Chain,
CompositionIn,
CompositionOut
>;
enum K_P_Nodes {
K_P_Add = 0,
K_P_Negate,
@ -135,7 +118,38 @@ namespace K::Plugboard {
K_P_Count
};
NodeInstance MakeInstance(K_P_Nodes i);
template <typename... Ns>
struct NodeInstanceV {
using NodeInstanceP = std::variant<Ns*...>;
static_assert(std::variant_size_v<NodeInstanceP> == K_P_Count);
};
using NodeVar = NodeInstanceV<
Add,
Negate,
Subtract,
Multiply,
Divide,
Sign,
Sin,
Cos,
Tan,
Arcsin,
Arccos,
Arctan,
Atan2,
Minimum,
Maximum,
Power,
SquareRoot,
NaturalLogarithm,
AbsoluteValue,
Interpolation,
Chain,
CompositionIn,
CompositionOut>;
using NodeInstanceP = NodeVar::NodeInstanceP;
enum K_Interpolation {
K_I_CubicBezier,
@ -315,7 +329,7 @@ namespace K::Plugboard {
};
struct ConnectInfo {
NodeInstance *p; // NOTE: so NodeInstances must be stored in a list, otherwise vector realloc leads to UAF
NodeInstanceP p; // NOTE: so NodeInstances must be stored in a list, otherwise vector realloc leads to UAF
u32 index;
auto operator<=>(const ConnectInfo&) const = default; // too-modern c++ black magic ?
};
@ -332,42 +346,10 @@ namespace K::Plugboard {
Vector<ConnectInfo> outgoing;
};
template <typename F>
requires requires(F func, ConnectInfo p) {
{ func(p) } -> std::same_as<bool>; // abort condition -- continue if true
}
bool DFS(const NodeInstance& n, F&& f) { // returns false if aborted
return std::visit([&f](auto&& node) {
for (const auto& socket : node.in)
if (!std::visit([&f](auto&& info) {
if constexpr (std::is_same_v<std::decay_t<decltype(info)>, ConnectInfo>) {
if (f(info))
return DFS(*info.p, f);
else
return false;
}
else return true;
}, socket.value))
return false;
return true;
}, n);
}
void CollectChains(ConnectInfo& in, Vector<ConnectInfo>& chains);
template <typename T>
concept Node = requires(T node) {
{ T::name } -> std::same_as<const char* const&>;
{ node.in.size() } -> std::convertible_to<std::size_t>;
{ node.out.size() } -> std::convertible_to<std::size_t>;
{ node.fetch.size() } -> std::convertible_to<std::size_t>; // in the future enforce same_as<tuple_size<fetch>, tuple_size<out>> if both are static arrays
{ node.pos } -> std::convertible_to<ImVec2&>;
};
struct ConnectInfoHasher {
std::size_t operator()(const ConnectInfo& k) const
{
return std::hash<NodeInstance *>()(k.p) ^ std::hash<u32>()(k.index);
return std::hash<NodeInstanceP>()(k.p) ^ std::hash<u32>()(k.index);
}
};
@ -592,14 +574,113 @@ namespace K::Plugboard {
ImVec2 pos;
};
template <typename T, typename... Ts>
concept SameAsAny = (... or std::same_as<T, Ts>);
template <Node... Ns>
struct TNodeList {
static_assert(sizeof...(Ns) == K_P_Count);
template <SameAsAny<Ns...> N>
auto& Store(const N& v) {
return std::get<std::list<N>>(nodes).push_back(v);
}
template <SameAsAny<Ns...> N>
auto& Store(N&& v) {
return std::get<std::list<N>>(nodes).emplace_back(std::forward<N>(v));
}
auto& Store(const std::variant<Ns...>& v) {
return std::visit([this](auto&& arg) { return store(arg); }, v);
}
auto& Store(std::variant<Ns...>&& v) {
return std::visit([this](auto&& arg) { return store(std::forward<decltype(arg)>(arg)); }, std::move(v));
}
std::tuple<std::list<Ns>...> nodes;
template<int N, typename... Ts> using NthTypeOf =
typename std::tuple_element<N, std::tuple<Ts...>>::type;
template <std::size_t... Is>
void Store(K_P_Nodes i, std::index_sequence<Is...>) {
((Is == i ? Store(NthTypeOf<Is, Ns...>{}) : NthTypeOf<Is, Ns...>{}), ...);
}
void Store(K_P_Nodes i) {
Store(i, std::index_sequence_for<Ns...>{});
}
template <std::size_t... Is>
char const *GetName(K_P_Nodes i, std::index_sequence<Is...>) {
char const *p = nullptr;
((Is == i ? p = NthTypeOf<Is, Ns...>::name : nullptr), ...);
return p;
}
char const *GetName(K_P_Nodes i) {
return GetName(i, std::index_sequence_for<Ns...>{});
}
};
using NodeList = TNodeList<
Add,
Negate,
Subtract,
Multiply,
Divide,
Sign,
Sin,
Cos,
Tan,
Arcsin,
Arccos,
Arctan,
Atan2,
Minimum,
Maximum,
Power,
SquareRoot,
NaturalLogarithm,
AbsoluteValue,
Interpolation,
Chain,
CompositionIn,
CompositionOut
>;
struct Plugboard {
std::list<NodeInstance> nodes; // OK complexity wise since we would usually traverse the entire thing anyway, locality will likely be bad
NodeList nodes; // OK complexity wise since we would usually traverse the entire thing anyway, locality will likely be bad
Dict<ConnectInfo, LinksFromSource, ConnectInfoHasher> links_pos; // this is hilariously bad
NodeInstance in, out;
CompositionIn in;
CompositionOut out;
void RecollectChains();
};
void Connect(NodeInstance& from_instance, u8 input_index, NodeInstance& to_instance, u8 to_out_index);
void Connect(NodeInstanceP from_instance, u8 input_index, NodeInstanceP to_instance, u8 to_out_index);
void Disconnect(NodeInstance& from_instance, u8 input_index);
void Disconnect(NodeInstanceP from_instance, u8 input_index);
template <typename F>
requires requires(F func, ConnectInfo p) {
{ func(p) } -> std::same_as<bool>; // abort condition -- continue if true
}
bool DFS(const NodeInstanceP n, F&& f) { // returns false if aborted
return std::visit([&f](auto&& node) {
for (const auto& socket : node->in)
if (!std::visit([&f](auto&& info) {
if constexpr (std::is_same_v<std::decay_t<decltype(info)>, ConnectInfo>) {
if (f(info))
return DFS(info.p, f);
else
return false;
}
else return true;
}, socket.value))
return false;
return true;
}, n);
}
void CollectChains(ConnectInfo& in, Vector<ConnectInfo>& chains);
}

16
TODO.md
View file

@ -1,13 +1,4 @@
# NOW
## UI
- Keys
- deletion
- copy & paste
- Undo's
- Once this is done we can move the plugboard loop detection/chain finding out of the eval loop
- Node groups
- Simple export
## Compositor
- Video!
- Integrate with samplers
@ -17,12 +8,18 @@
- Data models
- Dump and read back state, (de)serialization!!!
- Pre-compose/Layer Groups (jokes -- can be completely UI side)
- Undo's
- Node groups
- Deleting layers/nodes/keys/etc upkeep
- Motion blur
- Text (idea: index-based evaluation in plugboard)
- Shapes (idea: embed glisp :mmtroll:, need to inquire -- still a lot of friction for simple shapes if we don't also get the glisp gizmos)
- External data driving (csv, json or something else?) -- use a node to select source
## UI
- Copy & Paste
- Simple export
# Later
## IO
- File dialogues pending SDL3
@ -47,3 +44,4 @@
- Wait for SDL3!
- SDL_mixer will be able to do all of wav ogg flac mp3 opus, we live in good times
- output needs to be handled differently (if we care at all -- likely not)
z