diff --git a/Keishiki/Keishiki.cpp b/Keishiki/Keishiki.cpp index 6e94d57..73df606 100644 --- a/Keishiki/Keishiki.cpp +++ b/Keishiki/Keishiki.cpp @@ -14,8 +14,8 @@ namespace { SDL_Window *window; - u16 window_width = 1920; - u16 window_height = 1080; + u16 window_width = 2200; + u16 window_height = 1200; u64 init_time, last_time, current_time, delta_t; u32 frame; diff --git a/Keishiki/PlugboardGraph.cpp b/Keishiki/PlugboardGraph.cpp new file mode 100644 index 0000000..f45cbad --- /dev/null +++ b/Keishiki/PlugboardGraph.cpp @@ -0,0 +1,21 @@ +#include + +namespace K::PlugboardGraph { + String VarToString(const T_Map::type& var) { + return std::visit([](auto&& arg) -> String { + using T = std::decay_t; + if constexpr (std::is_same_v::type>) + return std::to_string(arg) + "f"; + else if constexpr (std::is_same_v::type>) + return std::to_string(arg); + else if constexpr (std::is_same_v::type>) + return "vec4(" + std::to_string(arg.r) + "," + std::to_string(arg.g) + "," + std::to_string(arg.b) + "," + std::to_string(arg.a) + ")"; + else if constexpr (std::is_same_v::type>) + return "vec2(" + std::to_string(arg.x) + "," + std::to_string(arg.y) + ")"; + else if constexpr (std::is_same_v::type>) + return "vec3(" + std::to_string(arg.x) + "," + std::to_string(arg.y) + "," + std::to_string(arg.z) + ")"; + else if constexpr (std::is_same_v::type>) + return arg; + }, var); + } +} diff --git a/Keishiki/ShaderGraph.cpp b/Keishiki/ShaderGraph.cpp index 1fe7980..fde00e2 100644 --- a/Keishiki/ShaderGraph.cpp +++ b/Keishiki/ShaderGraph.cpp @@ -1,5 +1,4 @@ #include -#include #include "srell.hpp" namespace { diff --git a/Keishiki/UI.cpp b/Keishiki/UI.cpp index 48b55e6..27dd04e 100644 --- a/Keishiki/UI.cpp +++ b/Keishiki/UI.cpp @@ -1,6 +1,8 @@ #include "UI.h" +#include "Graphics.h" +#include "VisualTrack.h" +#include "PlugboardNodes.h" -#include "ext/imgui/misc/cpp/imgui_stdlib.h" #include #include #include @@ -85,7 +87,7 @@ namespace K::UI { bg.pg = K::Graphics::load_shader_program("Checkerboard"); bg.add_uniform("f_hw", ShaderGraph::Type::T_XYZ); - bg.uniforms.begin()->second.second = ShaderGraph::XYZ{ static_cast(s.width), static_cast(s.height), 0.0f }; + bg.uniforms.begin()->second.val = ShaderGraph::XYZ{ static_cast(s.width), static_cast(s.height), 0.0f }; bx::mtxOrtho(proj, 0.0f, static_cast(s.width), 0.0f, static_cast(s.height), 0.1f, 100.0f, 0.f, bgfx::getCaps()->homogeneousDepth); @@ -107,7 +109,7 @@ namespace K::UI { std::free(save_buffer); bgfx::destroy(bg.pg); - bgfx::destroy(bg.uniforms.begin()->second.first); + bgfx::destroy(bg.uniforms.begin()->second.handle); bg.uniforms.erase(bg.uniforms.begin()); for (auto& layer : s.layers) @@ -116,7 +118,7 @@ namespace K::UI { void Viewport(CompState& s) { if (ImGui::Begin("Viewport", &draw_viewport)) { - ImTextureID idx = nullptr; + ImTextureID idx; bg.get_frame(composite_fb, s.width, s.height, proj, view, transform); bgfx::blit(Graphics::K_VIEW_COMP_COMPOSITE, composite_blit, 0, 0, composite); @@ -134,14 +136,13 @@ namespace K::UI { ImGui::SameLine(); - auto cp = save_called; - if (cp) ImGui::BeginDisabled(); + ImGui::BeginDisabled(save_called); if (ImGui::Button("Save to frame.png")) { bgfx::blit(Graphics::K_VIEW_COMP_SAVE, save, 0, 0, composite); ready_frame = bgfx::readTexture(save, save_buffer); save_called = true; } - if (cp) ImGui::EndDisabled(); + ImGui::EndDisabled(); } ImGui::End(); @@ -164,21 +165,21 @@ namespace K::UI { }; l.track.add_uniform("aspect_ratio", ShaderGraph::expand_type(ShaderGraph::T_XY)); if (s.width > s.height) - l.track.uniforms["aspect_ratio"].second = ShaderGraph::T_Map::type(static_cast(s.width)/static_cast(s.height), 1.0f); + l.track.uniforms["aspect_ratio"].val = ShaderGraph::T_Map::type(static_cast(s.width)/static_cast(s.height), 1.0f); else - l.track.uniforms["aspect_ratio"].second = ShaderGraph::T_Map::type(1.0f, static_cast(s.height)/static_cast(s.width)); + l.track.uniforms["aspect_ratio"].val = ShaderGraph::T_Map::type(1.0f, static_cast(s.height)/static_cast(s.width)); l.track.add_uniform("opacity", ShaderGraph::expand_type(ShaderGraph::T_Float)); - l.track.uniforms["opacity"].second = ShaderGraph::T_Map::type(1.0f); + l.track.uniforms["opacity"].val = ShaderGraph::T_Map::type(1.0f); l.track.add_uniform("rot", ShaderGraph::expand_type(ShaderGraph::T_Float)); - l.track.uniforms["rot"].second = ShaderGraph::T_Map::type(.0f); + l.track.uniforms["rot"].val = ShaderGraph::T_Map::type(.0f); l.track.add_uniform("scale", ShaderGraph::expand_type(ShaderGraph::T_XY)); - l.track.uniforms["scale"].second = ShaderGraph::T_Map::type(1.0f, 1.0f); + l.track.uniforms["scale"].val = ShaderGraph::T_Map::type(1.0f, 1.0f); l.track.add_uniform("translate", ShaderGraph::expand_type(ShaderGraph::T_XY)); - l.track.uniforms["translate"].second = ShaderGraph::T_Map::type(.0f, .0f); + l.track.uniforms["translate"].val = ShaderGraph::T_Map::type(.0f, .0f); l.track.shader = "void main() {\n" "\tfloat angle = -rot * M_PI / 180.0f;\n" @@ -266,7 +267,7 @@ namespace K::UI { if (!no_selection) source_flags |= ImGuiDragDropFlags_SourceNoPreviewTooltip; if ((no_selection || s.selected.contains(i)) && ImGui::BeginDragDropSource(source_flags)) { if (s.selected.empty()) ImGui::Text("Swap with #%u %s", i, s.layers[i].name.c_str()); - ImGui::SetDragDropPayload("DND_DEMO_NAME", &i, sizeof(u32)); + ImGui::SetDragDropPayload("K_COMP_REORDER", &i, sizeof(u32)); ImGui::EndDragDropSource(); } if ((no_selection || !s.selected.contains(i)) && ImGui::BeginDragDropTarget()) { @@ -278,7 +279,7 @@ namespace K::UI { ImGui::SetTooltip("Before #%u %s", i, s.layers[i].name.c_str()); } - if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("DND_DEMO_NAME")) { + if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_COMP_REORDER")) { move_from = *(const int*)payload->Data; move_to = i; } @@ -338,160 +339,197 @@ namespace K::UI { ImGui::End(); } + void PlugboardNodeDrawSockets(PlugboardGraph::NodeInstance& n, ImGuiStyle& style, + bool& dragging_on_socket, ImVec2& source, std::unordered_map& link_pos, + bool dummy = true) { + i32 row = 1; + for (u32 out = 0; out < n.node->out_names.size(); out++, row++) { + ImGui::PushID(row); + ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + if (dummy) + ImGui::Dummy({100.0f - ImGui::CalcTextSize(n.node->out_names[out].c_str()).x - style.ItemSpacing.x, 0.0f}); + ImGui::SameLine(); + ImGui::Text("%s", n.node->out_names[out].c_str()); + ImGui::TableNextColumn(); + ImGui::RadioButton("##Out", false); + if (ImGui::IsItemActive()) { + dragging_on_socket = true; + source = ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y}; + } + + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + PlugboardGraph::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")) { + PlugboardGraph::ConnectInfo in = *(const PlugboardGraph::ConnectInfo*)payload->Data; + in.p->connect(in.index, &n, out); + } + ImGui::EndDragDropTarget(); + } + ImGui::PopID(); + + // Update link info if needed + if (n.outputs_going[out].p != nullptr) { + auto pos = ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y}; + auto& d = link_pos[std::get(n.outputs_going[out].p->inputs_fed[n.outputs_going[out].index])]; + d.w = pos.x; + d.z = pos.y; + } + } + for (u32 in = 0; in < n.inputs_fed.size(); in++, row++) { + ImGui::PushID(row); + ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); + ImGui::TableNextColumn(); + ImGui::RadioButton("##In", false); + if (ImGui::IsItemActive()) { + dragging_on_socket = true; + source = ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y}; + } + + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + PlugboardGraph::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")) { + PlugboardGraph::ConnectInfo out = *(const PlugboardGraph::ConnectInfo*)payload->Data; + n.connect(in, out.p, out.index); + } + ImGui::EndDragDropTarget(); + } + + // Update link info if needed + if (n.inputs_fed[in].index() == 0) { + auto pos = ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y}; + auto& d = link_pos[std::get(n.inputs_fed[in])]; + d.x = pos.x; + d.y = pos.y; + } + + ImGui::TableNextColumn(); + ImGui::Text("%s", n.node->in_names[in].c_str()); + ImGui::TableNextColumn(); + ImGui::PopID(); + + } + } + + void PlugboardDrawNode(PlugboardGraph::NodeInstance& n, ImVec2& view_pos, i32 id, ImGuiIO& io, ImGuiStyle& style, + bool& dragging_on_socket, ImVec2& source, std::unordered_map& link_pos) { + ImGui::SetCursorPos(view_pos + n.pos); + ImGui::PushID(id); + ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF080813); + ImGui::BeginChild(n.node->name.c_str(), {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); + ImGui::PopStyleColor(); + if (ImGui::BeginTable(n.node->name.c_str(), 3)) { + ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + ImGui::Button(n.node->name.c_str(), {100.0f , 0.0f}); + if (ImGui::IsItemClicked()) { + n.old_pos = n.pos; + } + if (ImGui::IsItemActive()) { + n.pos = n.old_pos + (io.MousePos - io.MouseClickedPos[0]); + } + ImGui::TableNextColumn(); + + PlugboardNodeDrawSockets(n, style, dragging_on_socket, source, link_pos); + + ImGui::EndTable(); + } + + ImGui::EndChild(); + ImGui::PopID(); + } + void Plugboard(CompState& s) { if (!ImGui::Begin("Plugboard", &draw_plugboard)) { ImGui::End(); return; } + if (ImGui::Button("Add Sine")) + s.plugboard.nodes.push_back(PlugboardGraph::MakeInstance(PlugboardNodes::Sine)); + ImGuiIO& io = ImGui::GetIO(); ImGuiStyle style = ImGui::GetStyle(); + static f32 view_scale = 1.0f; static ImVec2 view_pos{}, old_view_pos{}; + bool dragging_on_socket{}; + static ImVec2 drag_source{}; + ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF100500); - ImGui::BeginChild("view_port", ImGui::GetContentRegionAvail(), 0, ImGuiWindowFlags_NoMove); + ImGui::BeginChild("Nodes", ImGui::GetContentRegionAvail(), 0, ImGuiWindowFlags_NoMove); ImGui::PopStyleColor(); - if (ImGui::IsMouseClicked(0)) { + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("K_PLUG_IN_TO_OUT")) { + PlugboardGraph::ConnectInfo in = *(const PlugboardGraph::ConnectInfo*)payload->Data; + in.p->disconnect(in.index); + } + ImGui::EndDragDropTarget(); + } + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("K_PLUG_OUT_TO_IN")) { + PlugboardGraph::ConnectInfo out = *(const PlugboardGraph::ConnectInfo*)payload->Data; + auto& in = out.p->outputs_going[out.index]; + in.p->disconnect(in.index); + } + ImGui::EndDragDropTarget(); + } + + ImGuiWindow* window = ImGui::GetCurrentWindow(); + + s.plugboard.links_pos.clear(); + + if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { old_view_pos = view_pos; } if (ImGui::IsItemActive()) { view_pos = old_view_pos + (io.MousePos - io.MouseClickedPos[0]); } - static ImVec2 out{}, in{}, out2{}, in2{}; - - static ImVec2 pos{}, old_pos{}; - ImGui::SetCursorPos(view_pos + pos); - static bool active{}; - ImGui::PushID(1); - ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF080813); - ImGui::BeginChild("sad", {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); - ImGui::PopStyleColor(); - if (ImGui::BeginTable("s", 3)) { - ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); - ImGui::TableNextColumn(); - ImGui::TableNextColumn(); - ImGui::Button("Asdf", {100.0f , 0.0f}); - if (ImGui::IsItemClicked()) { - old_pos = pos; - } - if (ImGui::IsItemActive()) { - pos = old_pos + (io.MousePos - io.MouseClickedPos[0]); - } - ImGui::TableNextColumn(); - - ImGui::PushID(1); - ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); - ImGui::TableNextColumn(); - ImGui::TableNextColumn(); - ImGui::Dummy({100.0f - ImGui::CalcTextSize("asd").x - style.ItemSpacing.x, 0.0f}); - ImGui::SameLine(); - ImGui::Text("qq"); - ImGui::TableNextColumn(); - ImGui::RadioButton("##asd", active); - out = ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y }; - ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); - ImGui::TableNextColumn(); - ImGui::RadioButton("##asd", active); - in2 = ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y }; - ImGui::TableNextColumn(); - ImGui::Text("asd"); - ImGui::TableNextColumn(); - ImGui::PopID(); - ImGui::EndTable(); - } - - - -/* - if (ImGui::IsItemActive()) { - } - - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - OutToIn d{ &n, u }; - ImGui::SetDragDropPayload("K_SHADER_OUT_TO_IN", &d, sizeof(d)); - ImGui::Text("asdf"); - ImGui::EndDragDropSource(); - } - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("K_SHADER_IN_TO_OUT")) { - InSlot* in = *(InSlot**)payload->Data; - } - ImGui::EndDragDropTarget(); - } -*/ - -/* for (InSlot& s : n.inputs) { - ImGui::PushID(i); - - bool active = s.node != nullptr; - if (active) { - window->DrawList->AddLine(window->DC.CursorPos, io.MousePos, 0xFFAAAAAA, 2.0f); - } - ImGui::RadioButton((s.name).c_str(), active); - if (ImGui::IsItemActive()) - window->DrawList->AddLine(io.MouseClickedPos[0], io.MousePos, 0xFFAAAAAA, 2.0f); - - //if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - // const InSlot* ptr = &s; - // ImGui::SetDragDropPayload("K_SHADER_IN_TO_OUT", &ptr, sizeof(ptr)); - // ImGui::Text("%s", Type_To_Str[s.type]); - // ImGui::EndDragDropSource(); - //} - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("K_SHADER_OUT_TO_IN")) { - OutToIn out = *(const OutToIn*)payload->Data; - if (out.node->out[out.index].first == s.type) { - s.node = out.node; - s.out_index = out.index; - } - } - ImGui::EndDragDropTarget(); - } - - ImGui::PopID(); - i++; - }*/ - - ImGui::EndChild(); - ImGui::PopID(); + for (u32 i = 0; i < s.plugboard.nodes.size(); i++) + PlugboardDrawNode(s.plugboard.nodes[i], view_pos, i, io, style, dragging_on_socket, drag_source, s.plugboard.links_pos); ImGui::SetCursorPos({}); ImGui::PushStyleColor(ImGuiCol_ChildBg, 0x33FFFFFF); - ImGui::BeginChild("asdasd", {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY ); + ImGui::BeginChild(s.plugboard.comp_in.name.c_str(), {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY ); ImGui::PopStyleColor(); - if (ImGui::BeginTable("Source", 2)) { - ImGui::PushID(1); - ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); - ImGui::TableNextColumn(); - ImGui::Text("Time"); - ImGui::TableNextColumn(); - ImGui::RadioButton("##asd", active); - out2 = ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y }; - ImGui::PopID(); + if (ImGui::BeginTable(s.plugboard.comp_in.name.c_str(), 3)) { + PlugboardNodeDrawSockets(s.plugboard.in_instance, style, dragging_on_socket, drag_source, s.plugboard.links_pos, false); ImGui::EndTable(); } + ImGui::EndChild(); - ImGui::SetCursorPos({ImGui::GetContentRegionAvail().x - 100.0f, 0.0f}); + static f32 right_offset = 0.0f; // check after drawing this frame, so next frame will be correct + ImGui::SetCursorPos({ImGui::GetContentRegionAvail().x - right_offset, 0.0f}); ImGui::PushStyleColor(ImGuiCol_ChildBg, 0x33FFFFFF); - ImGui::BeginChild("ert", {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY); + ImGui::BeginChild(s.plugboard.comp_out.name.c_str(), {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY); ImGui::PopStyleColor(); - if (ImGui::BeginTable("Source", 1)) { - ImGui::PushID(1); - ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); - ImGui::TableNextColumn(); - ImGui::RadioButton("asfnjklanf", active); - in = ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y }; - ImGui::PopID(); + if (ImGui::BeginTable(s.plugboard.comp_out.name.c_str(), 3)) { + PlugboardNodeDrawSockets(s.plugboard.out_instance, style, dragging_on_socket, drag_source, s.plugboard.links_pos, false); ImGui::EndTable(); } ImGui::EndChild(); - ImGui::GetCurrentWindow()->DrawList->AddLine(in, out, 0xFFFFFFFF, 2.0f); - ImGui::GetCurrentWindow()->DrawList->AddLine(in2, out2, 0xFFAAAAAA, 2.0f); + right_offset += ImGui::GetItemRectMax().x - ImGui::GetWindowWidth(); + + for (auto& [_, link] : s.plugboard.links_pos) + window->DrawList->AddLine({link.x, link.y}, {link.w, link.z}, 0xFFFFFFFF, 2.0f); + + if (dragging_on_socket) + window->DrawList->AddLine(ImGui::GetMousePos(), drag_source, 0xFFFFFFFF, 2.0f); ImGui::EndChild(); @@ -537,13 +575,36 @@ namespace K::UI { ImGui::TableNextColumn(); ImGui::Text("%s", it->first.c_str()); ImGui::TableNextColumn(); - ImGui::Text("%s", ShaderGraph::Type_To_Str[it->second.second.index()]); + ImGui::Text("%s", ShaderGraph::Type_To_Str[it->second.val.index()]); ImGui::TableNextColumn(); - bool temp{}; - ImGui::Checkbox("##Expose", &temp); + bool d = it->second.exposure.p != nullptr, cp = d; + ImGui::Checkbox("##Expose", &d); + if (d != cp) { + if (d) { + String id = s.layers[s.active].name + "." + it->first; + it->second.exposure = {&s.plugboard.out_instance, static_cast(s.plugboard.out_instance.node->in_names.size())}; + s.plugboard.out_instance.node->in_names.push_back(id); + s.plugboard.out_instance.node->in_types.push_back(PlugboardGraph::Type(it->second.val.index())); // this is shaky and bug prone -- relies on the shader types being in line with plugboard types + s.plugboard.out_instance.inputs_fed.push_back(ShaderValToPlugboard(it->second.val)); + } + else { + it->second.exposure.p->disconnect(it->second.exposure.index); + + for (auto& l : s.layers) + for (auto& u : l.track.uniforms) + if (u.second.exposure.index > it->second.exposure.index) + u.second.exposure.index--; + s.plugboard.comp_out.in_names.erase(s.plugboard.out_instance.node->in_names.begin() + it->second.exposure.index); + s.plugboard.comp_out.in_types.erase(s.plugboard.out_instance.node->in_types.begin() + it->second.exposure.index); + s.plugboard.out_instance.inputs_fed.erase(s.plugboard.out_instance.inputs_fed.begin() + it->second.exposure.index); + + it->second.exposure = {nullptr, 0}; + } + } ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-FLT_MIN); - std::visit([it](auto&& arg) { + ImGui::BeginDisabled(it->second.exposure.p != nullptr); + std::visit([](auto&& arg) { using T = std::decay_t; if constexpr (std::is_same_v::type>) ImGui::DragFloat("##", &arg, 0.005f); @@ -555,10 +616,11 @@ namespace K::UI { ImGui::DragFloat2("##", &arg.x, 0.005f); else if constexpr (std::is_same_v::type>) ImGui::DragFloat3("##", &arg.x, 0.005f); - }, it->second.second); + }, it->second.val); + ImGui::EndDisabled(); ImGui::TableNextColumn(); if (ImGui::Button("X")) { - bgfx::destroy(it->second.first); + bgfx::destroy(it->second.handle); it = s.layers[s.active].track.uniforms.erase(it); } else it++; @@ -632,7 +694,7 @@ namespace K::UI { break; default: LogError("Unsupported Renderer"); - }; + } } void Shutdown(CompState& s) { diff --git a/Keishiki/include/Common.h b/Keishiki/include/Common.h index a63a0b6..3e373d4 100644 --- a/Keishiki/include/Common.h +++ b/Keishiki/include/Common.h @@ -23,7 +23,7 @@ namespace K { using Byte = u8; using Char = char; template using Vector = std::vector; - template using Dict = std::unordered_map; // to be replaced + template using Dict = std::unordered_map; // to be replaced inline void LogBase(const String& s, u8 level) { static const char *levels[] = { diff --git a/Keishiki/include/Keishiki.h b/Keishiki/include/Keishiki.h index 5a4077b..15c6e29 100644 --- a/Keishiki/include/Keishiki.h +++ b/Keishiki/include/Keishiki.h @@ -3,6 +3,7 @@ #include "VisualTrack.h" #include #include +#include "PlugboardGraph.h" namespace K { bool Init(); @@ -18,13 +19,19 @@ namespace K { }; struct CompState { + // Time u64 current_frame; u64 frame_max; - u32 fps; + u32 fps; + u32 width, height; + Vector layers; i32 active = -1; // index for layers std::set selected; // indices for layers std::unordered_set disabled; // indices for layers + + + PlugboardGraph::PlugboardGraph plugboard; }; } diff --git a/Keishiki/include/PlugboardGraph.h b/Keishiki/include/PlugboardGraph.h new file mode 100644 index 0000000..fe70258 --- /dev/null +++ b/Keishiki/include/PlugboardGraph.h @@ -0,0 +1,162 @@ +#pragma once +#include "Common.h" +#include "Graphics.h" +#include +#include +#include + +namespace K::PlugboardGraph { + enum Type { + T_Float, + T_Int, + T_RGBA, + T_XY, + T_XYZ, + T_String, + T_Count + }; + static const char *Type_To_Str[] = { + "Float", + "Int", + "RGBA", + "XY", + "XYZ", + "String", + "Error" + }; + + struct RGBA { f32 r, g, b, a; }; + struct XYZ { f32 x, y, z; }; + struct XY { f32 x, y; }; + template struct T_Map; + template<> struct T_Map { + using type = f32; + }; + template<> struct T_Map { + using type = i32; + }; + template<> struct T_Map { + using type = RGBA; + }; + template<> struct T_Map { + using type = XY; + }; + template<> struct T_Map { + using type = XYZ; + }; + template<> struct T_Map { + using type = String; + }; + template<> struct T_Map { + using type = std::variant; + }; + String VarToString(const T_Map::type& var); + + inline T_Map::type expand_type(Type i) { + static T_Map::type table[] = {f32{}, i32{}, PlugboardGraph::RGBA{}, PlugboardGraph::XY{}, PlugboardGraph::XYZ{}, String{}}; + return table[i]; + } + + struct Node { + String name; + + Vector in_names; + Vector in_types; + + Vector out_names; + Vector out_types; + Vector::type(Vector::type>)>> fetch; // maybe change to a function pointer later + // todo investigate how you might wanna jit this instead of evaluating a tree for each output at runtime + }; + + struct NodeInstance; + + struct ConnectInfo { + NodeInstance *p; u32 index; + auto operator<=>(const ConnectInfo&) const = default; + }; + + struct _ConnectInfoHasher + { + std::size_t operator()(const ConnectInfo& k) const + { + return std::hash()(k.p); + } + }; + + struct NodeInstance { + Node *node; + + Vector::type>> inputs_fed; + Vector outputs_going; + + ImVec2 pos, old_pos; + + void connect(u32 input_index, NodeInstance *to, u32 to_out_index) { + inputs_fed[input_index] = ConnectInfo{to, to_out_index}; + to->outputs_going[to_out_index] = {this, input_index}; + } + + void disconnect(u32 input_index) { + std::visit([](auto&& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) + arg.p->outputs_going[arg.index] = {nullptr, 0}; + else if constexpr (std::is_same_v::type>) + ; + }, inputs_fed[input_index]); + inputs_fed[input_index] = expand_type(node->in_types[input_index]); + } + }; + + + inline NodeInstance MakeInstance(Node& n) { + Vector::type>> inp; + Vector out; + for (auto t : n.in_types) + inp.push_back(expand_type(t)); + for (auto t : n.out_types) + out.push_back({}); + return { + .node = &n, + .inputs_fed = std::move(inp), + .outputs_going = std::move(out) + }; + } + + inline T_Map::type Eval(const ConnectInfo& info) { + Vector::type> pack; + for (auto& v : info.p->inputs_fed) + std::visit([&pack](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) + pack.push_back(Eval(arg)); + else if constexpr (std::is_same_v::type>) + pack.push_back(arg); + }, v); + return info.p->node->fetch[info.index](pack); + } + + struct PlugboardGraph { + Vector nodes; + std::unordered_map links_pos; // this is hilariously bad + Node comp_in = { + "Composition In", + {}, + {}, + {"Time"}, + {T_Float}, + {[](Vector::type> v) { return T_Map::type{ static_cast(SDL_GetTicks()) / 20.0f }; }} // kill me + }, + comp_out = { + "Composition Out", + {}, + {}, + {}, + {}, + {} + }; + NodeInstance in_instance = {&comp_in, {}, {{nullptr, 0}}}, out_instance = {&comp_out}; + }; + +} diff --git a/Keishiki/include/PlugboardNodes.h b/Keishiki/include/PlugboardNodes.h new file mode 100644 index 0000000..1fcd412 --- /dev/null +++ b/Keishiki/include/PlugboardNodes.h @@ -0,0 +1,17 @@ +#pragma once +#include "Common.h" +#include "PlugboardGraph.h" + +namespace K::PlugboardNodes { + PlugboardGraph::Node Sine = { + .name = "Sine", + .in_names = { "In" }, + .in_types = { PlugboardGraph::T_Float }, + + .out_names = { "Out" }, + .out_types = { PlugboardGraph::T_Float }, + .fetch = {[](const Vector::type>& arg) { + return std::sin(std::get(arg[0])); + }} + }; +} diff --git a/Keishiki/include/ShaderGraph.h b/Keishiki/include/ShaderGraph.h index b7910e5..3127f3a 100644 --- a/Keishiki/include/ShaderGraph.h +++ b/Keishiki/include/ShaderGraph.h @@ -2,9 +2,8 @@ #include "Common.h" #include "Graphics.h" #include -#include "imgui.h" -#include "Graphics.h" +// todo a lot of problems in this unit can lead to very bad performance namespace K::ShaderGraph { enum Type { T_Float, diff --git a/Keishiki/include/UI.h b/Keishiki/include/UI.h index 0d06d9e..dfa41f4 100644 --- a/Keishiki/include/UI.h +++ b/Keishiki/include/UI.h @@ -1,8 +1,6 @@ #pragma once #include "Common.h" #include "Keishiki.h" -#include "Graphics.h" -#include "VisualTrack.h" namespace K::UI { void Init(SDL_Window *window); diff --git a/Keishiki/include/VisualTrack.h b/Keishiki/include/VisualTrack.h index 6944482..755fa15 100644 --- a/Keishiki/include/VisualTrack.h +++ b/Keishiki/include/VisualTrack.h @@ -5,6 +5,7 @@ #include #include #include +#include "PlugboardGraph.h" namespace K { template @@ -18,11 +19,51 @@ namespace K { } }; + struct Uniform { + bgfx::UniformHandle handle; + ShaderGraph::T_Map::type val; + PlugboardGraph::ConnectInfo exposure; + }; + + inline ShaderGraph::T_Map::type PlugboardValToShader(const PlugboardGraph::T_Map::type& val) { + return std::visit([](auto&& arg) -> ShaderGraph::T_Map::type { + using T = std::decay_t; + if constexpr (std::is_same_v::type>) + return ShaderGraph::T_Map::type(arg); + else if constexpr (std::is_same_v::type>) + return ShaderGraph::T_Map::type(arg); + else if constexpr (std::is_same_v::type>) + return ShaderGraph::T_Map::type(arg.r, arg.g, arg.b, arg.a); + else if constexpr (std::is_same_v::type>) + return ShaderGraph::T_Map::type(arg.x, arg.y); + else if constexpr (std::is_same_v::type>) + return ShaderGraph::T_Map::type(arg.x, arg.y, arg.z); + else if constexpr (std::is_same_v::type>) + return ShaderGraph::T_Map::type{}; + },val); + } + + inline PlugboardGraph::T_Map::type ShaderValToPlugboard(const ShaderGraph::T_Map::type& val) { + return std::visit([](auto&& arg) -> PlugboardGraph::T_Map::type { + using T = std::decay_t; + if constexpr (std::is_same_v::type>) + return PlugboardGraph::T_Map::type(arg); + else if constexpr (std::is_same_v::type>) + return PlugboardGraph::T_Map::type(arg); + else if constexpr (std::is_same_v::type>) + return PlugboardGraph::T_Map::type(arg.r, arg.g, arg.b, arg.a); + else if constexpr (std::is_same_v::type>) + return PlugboardGraph::T_Map::type(arg.x, arg.y); + else if constexpr (std::is_same_v::type>) + return PlugboardGraph::T_Map::type(arg.x, arg.y, arg.z); + },val); + } + struct VisualTrack { ShaderGraph::ShaderGraph tree; bgfx::ProgramHandle pg = BGFX_INVALID_HANDLE; String shader; - Dict::type>> uniforms; + Dict uniforms; // Vector samplers; bgfx::TextureHandle get_frame(bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16], f32 view[16], f32 transform[16]) const { bgfx::touch(K::Graphics::K_VIEW_COMP_COMPOSITE); @@ -35,6 +76,18 @@ namespace K { for (auto& [_name, u] : uniforms) { f32 pack[4]{}; + ShaderGraph::T_Map::type val_target; + if (u.exposure.p == nullptr) + val_target = u.val; + else { + std::visit([&val_target](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) + val_target = PlugboardValToShader(PlugboardGraph::Eval(arg)); + else if constexpr (std::is_same_v::type>) + val_target = PlugboardValToShader(arg); + }, u.exposure.p->inputs_fed[u.exposure.index]); + } std::visit([&pack](auto&& arg) { using T = std::decay_t; if constexpr (std::is_same_v::type> || std::is_same_v::type>) @@ -54,8 +107,8 @@ namespace K { pack[1] = arg.y; pack[2] = arg.z; } - }, u.second); - bgfx::setUniform(u.first, pack); + }, val_target); + bgfx::setUniform(u.handle, pack); } Graphics::DrawTextureWithTransform(K::Graphics::K_VIEW_COMP_COMPOSITE, Graphics::mmaker->tx, transform, @@ -65,7 +118,7 @@ namespace K { } void add_uniform(const String& s, ShaderGraph::T_Map::type&& val) { if (!uniforms.contains(s)) - uniforms.emplace(s, std::make_pair(bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val)); + uniforms.emplace(s, Uniform{bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val, {nullptr, 0}}); } void compile() { std::ofstream f("temp.frag"); @@ -88,7 +141,7 @@ namespace K { f << ".xy"; else if constexpr (std::is_same_v::type>) f << ".xyz"; - }, u.second.second); + }, u.second.val); f << "\n"; } f << shader; @@ -144,7 +197,7 @@ namespace K { if (bgfx::isValid(pg)) bgfx::destroy(pg); for (auto& uniform : uniforms) { - bgfx::destroy(uniform.second.first); + bgfx::destroy(uniform.second.handle); } uniforms.clear(); } diff --git a/Keishiki/shaders/BinaryComposite/BinaryComposite.frag b/Keishiki/shaders/BinaryComposite/BinaryComposite.frag index 5b4ed26..c0b8ea2 100644 --- a/Keishiki/shaders/BinaryComposite/BinaryComposite.frag +++ b/Keishiki/shaders/BinaryComposite/BinaryComposite.frag @@ -143,9 +143,4 @@ void main() float alpha = _A.a + alpha_b; gl_FragColor = vec4((_A.rgb * _A.a + _B.rgb * alpha_b)/alpha, alpha); } - else if (f_mode.x == 26.0f) { - float alpha_b = _A.a * (1.0f - _B.a); - float alpha = _A.a + alpha_b; - gl_FragColor = vec4((_A.rgb * _A.a + _B.rgb * alpha_b)/alpha, alpha); - } } diff --git a/TODO.md b/TODO.md index bdddaee..7df8a40 100644 --- a/TODO.md +++ b/TODO.md @@ -7,7 +7,7 @@ - Non-negotiables: - 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 tools) - - External data driving (json?) + - External data driving (json or something else?) - Layer Groups (jokes -- can be completely UI side) ## UI @@ -20,6 +20,9 @@ - std::unordered_map -> std::flat_map pending compiler support - Simple 3D engine +## UI +- Adapt nodes for shader graph -- code editor will be fine for now + ## Audio - Wait for SDL3! - SDL_mixer will be able to do all of wav ogg flac mp3 opus, we live in good times