finished refactoring dynamic node mess
This commit is contained in:
parent
dec9cb0497
commit
68d06aa660
6 changed files with 241 additions and 169 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
107
Keishiki/UI.cpp
107
Keishiki/UI.cpp
|
@ -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() &&
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
16
TODO.md
|
@ -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
|
Loading…
Add table
Reference in a new issue