From 8232b4d3f3d917ba228cac12cdbf1919ff420b84 Mon Sep 17 00:00:00 2001 From: lachrymaLF Date: Wed, 29 May 2024 12:32:37 -0400 Subject: [PATCH] success!! --- Keishiki/Keishiki.cpp | 27 +- Keishiki/UI.cpp | 418 +++++++++++++++++++----------- Keishiki/VisualTrack.cpp | 35 ++- Keishiki/include/Keishiki.h | 1 - Keishiki/include/PlugboardGraph.h | 34 +-- Keishiki/include/PlugboardNodes.h | 6 +- Keishiki/include/VisualTrack.h | 5 +- 7 files changed, 331 insertions(+), 195 deletions(-) diff --git a/Keishiki/Keishiki.cpp b/Keishiki/Keishiki.cpp index 88c118e..50036e7 100644 --- a/Keishiki/Keishiki.cpp +++ b/Keishiki/Keishiki.cpp @@ -99,24 +99,21 @@ namespace K { state.plugboard.comp_in = PlugboardGraph::Node{ "Composition In", {}, - {}, - {"Frame", "App Ticks"}, - {PlugboardGraph::T_Int, PlugboardGraph::T_Float}, + {{"Frame", PlugboardGraph::T_Int}, {"Time (s)", PlugboardGraph::T_Float}, {"App Ticks", PlugboardGraph::T_Float}}, { - [](const CompState& s, const Vector::type>& v) { return PlugboardGraph::T_Map::type{static_cast(s.current_frame)}; }, - [](const CompState& s, const Vector::type>& v) { return PlugboardGraph::T_Map::type{ static_cast(SDL_GetTicks()) / 18.0f }; } // kill me + [](const CompState& s, const Vector::type>& v) { return PlugboardGraph::T_Map::type{static_cast(s.current_frame)}; }, + [](const CompState& s, const Vector::type>& v) { return PlugboardGraph::T_Map::type{static_cast(s.current_frame) / s.fps}; }, + [](const CompState& s, const Vector::type>& v) { return PlugboardGraph::T_Map::type{ static_cast(SDL_GetTicks()) / 18.0f }; } // kill me } }; - state.plugboard.comp_out = { - "Composition Out", - {}, - {}, - {}, - {}, - {} + state.plugboard.comp_out = PlugboardGraph::Node{ + "Composition Out", + {}, + {}, + {} }; state.plugboard.in_instance = PlugboardGraph::MakeInstance(state.plugboard.comp_in); - state.plugboard.out_instance = PlugboardGraph::MakeInstance(state.plugboard.comp_out); + state.plugboard.out_instance = PlugboardGraph::MakeInstance(state.plugboard.comp_in); K::UI::SetupViewport(state); return true; @@ -130,13 +127,13 @@ namespace K { const bgfx::Stats *stat = bgfx::getStats(); const bgfx::Caps *caps = bgfx::getCaps(); bgfx::dbgTextClear(); - bgfx::dbgTextPrintf(0, window_height/16 - 2, 0xf8, + bgfx::dbgTextPrintf(0, window_height/16 - 4, 0xf8, " lachrymal.net :: %s Renderer :: Max Views %u :: Max FBs %u :: Max Texture Samplers %u :: Blitting %s :: %u FPS", caps->supported & BGFX_CAPS_RENDERER_MULTITHREADED ? "Multithreaded" : "Singlethreaded", caps->limits.maxViews, caps->limits.maxFrameBuffers, caps->limits.maxTextureSamplers, caps->supported & BGFX_CAPS_TEXTURE_BLIT ? "OK" : "NO", stat->cpuTimerFreq / stat->cpuTimeFrame); - bgfx::dbgTextPrintf(0, window_height/16 - 1, 0xf8, + bgfx::dbgTextPrintf(0, window_height/16 - 3, 0xf8, " Project Keishiki :: %s :: Build %s %s :: SDL %i.%i.%i :: bgfx 1.%i :: Dear ImGui %s :: %s", BX_COMPILER_NAME, __DATE__, __TIME__, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL, BGFX_API_VERSION, ImGui::GetVersion(), bgfx::getRendererName(bgfx::getRendererType())); diff --git a/Keishiki/UI.cpp b/Keishiki/UI.cpp index e36a77b..c1c4670 100644 --- a/Keishiki/UI.cpp +++ b/Keishiki/UI.cpp @@ -19,7 +19,9 @@ namespace { draw_shader = true, draw_comp = true, draw_interpolation = true, - draw_properties = true; + draw_properties = true, + draw_assets = true, + draw_color = true; const f32 row_height = 20.0f; @@ -117,18 +119,18 @@ namespace K::UI { } void PlugboardNodeDrawSockets(PlugboardGraph::NodeInstance& n, ImGuiStyle& style, - bool& dragging_on_socket, ImVec2& source, std::unordered_map& link_pos, + 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++) { + for (u32 out = 0; out < n.node->out.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::Dummy({100.0f - ImGui::CalcTextSize(n.node->out[out].name.c_str()).x - style.ItemSpacing.x, 0.0f}); ImGui::SameLine(); - ImGui::Text("%s", n.node->out_names[out].c_str()); + ImGui::TextUnformatted(n.node->out[out].name.c_str()); ImGui::TableNextColumn(); ImGui::RadioButton("##Out", false); if (ImGui::IsItemActive()) { @@ -192,7 +194,7 @@ namespace K::UI { }, n.inputs_fed[in]); ImGui::TableNextColumn(); - ImGui::Text("%s", n.node->in_names[in].c_str()); + ImGui::TextUnformatted(n.node->in[in].name.c_str()); ImGui::TableNextColumn(); ImGui::PopID(); @@ -200,7 +202,7 @@ namespace K::UI { } 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) { + bool& dragging_on_socket, ImVec2& source, std::unordered_map& link_pos) { ImGui::SetCursorPos(view_pos + n.pos); ImGui::PushID(id); ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xAA080813); @@ -267,7 +269,7 @@ namespace K::UI { inline f32 TimelineFrameToScreenView(f32 view_left, f32 view_amt, f32 view_width, u64 frame, u64 frame_max) { return (static_cast(frame) / static_cast(frame_max + 1) - view_left) / view_amt * view_width; - }; + } inline u64 TimelineScreenViewToFrame(f32 view_left, f32 view_amt, f32 view_width, f32 view, u64 frame_max) { return std::clamp(static_cast(std::round((view / view_width * view_amt + view_left) * static_cast(frame_max + 1))), 0UL, frame_max); @@ -283,7 +285,7 @@ namespace K::UI { s.plugboard.nodes.push_back(PlugboardGraph::MakeInstance(PlugboardNodes::Sine)); ImGui::SameLine(); - ImGui::Text("%u / %u Frames", s.current_frame, s.frame_max); + ImGui::Text("%lu / %lu Frames", s.current_frame, s.frame_max); ImGui::SameLine(); const bool no_selection = s.selected.empty(); @@ -325,6 +327,11 @@ namespace K::UI { "\tgl_FragColor = vec4(tx.rgb, tx.a * opacity);\n" "}\n"; l.track.compile(); + l.track.expose_uniform(s, "aspect_ratio"); + l.track.expose_uniform(s, "opacity"); + l.track.expose_uniform(s, "rot"); + l.track.expose_uniform(s, "scale"); + l.track.expose_uniform(s, "translate"); s.layers.push_back(std::move(l)); } ImGui::SameLine(); @@ -361,17 +368,18 @@ namespace K::UI { ImVec2 avail = ImGui::GetContentRegionAvail(); static f32 view_scale = 1.0f; - static ImVec2 view_pos{}, old_view_pos{}; + static ImVec2 view_pos{}, old_view_pos{}, nodes_begin{}; bool dragging_on_socket{}; - static ImVec2 drag_source{}; + ImVec2 drag_source{}; ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF100500); - if (ImGui::BeginChild("Nodes", {avail.x * .4f, avail.y}, 0, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar)) { + if (ImGui::BeginChild("Nodes", {avail.x * .4f, avail.y}, ImGuiChildFlags_ResizeX, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar)) { ImGui::PopStyleColor(); ImGuiWindow *window = ImGui::GetCurrentWindow(); ImVec2 window_pos = ImGui::GetWindowPos(); + nodes_begin = window_pos; if (ImGui::BeginDragDropTargetCustom(window->ContentRegionRect, window->ID)) { if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_PLUG_IN_TO_OUT")) { @@ -400,7 +408,7 @@ namespace K::UI { u32 i = 0; for (auto it = s.plugboard.nodes.begin(); it != s.plugboard.nodes.end(); it++, i++) - PlugboardDrawNode(*it, view_pos, i, io, style, dragging_on_socket, drag_source, s.plugboard.links_pos); + PlugboardDrawNode(*it, view_pos, static_cast(i), io, style, dragging_on_socket, drag_source, s.plugboard.links_pos); ImGui::SetCursorPos({}); ImGui::PushStyleColor(ImGuiCol_ChildBg, 0x33FFFFFF); @@ -415,48 +423,38 @@ namespace K::UI { } ImGui::EndChild(); - - 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(s.plugboard.comp_out.name.c_str(), {}, - ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | - ImGuiChildFlags_AutoResizeY); - ImGui::PopStyleColor(); - 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(); - - right_offset += ImGui::GetItemRectMax().x - ImGui::GetWindowWidth(); - - for (auto &[_, link]: s.plugboard.links_pos) - for (auto &sink: link.sinks) - window->DrawList->AddBezierCubic(link.source, {link.source.x + 60.0f, link.source.y}, - {sink.x - 60.0f, sink.y}, sink, 0xFFFFFFFF, 2.0f); - - if (dragging_on_socket) - window->DrawList->AddLine(ImGui::GetMousePos(), drag_source, 0xFFFFFFFF, 2.0f); } ImGui::EndChild(); - ImGui::SameLine(0.0f, 0.0f); + ImGui::SameLine(0.0f, 0.5f); - if (ImGui::BeginTable("Layers", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) { - ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_NoSort); + f32 table_left = ImGui::GetCursorScreenPos().x; + ImVec2 tl_init_pos, tl_end_begin; + f32 view_width, + tl_width, + view_height, + control_left, + control_right; + static f32 view_left = 0.0f, + view_left_old = view_left, + view_right = 1.0f, + view_right_old = view_right; + f32 view_amt; + if (ImGui::BeginTable("Layers", 5, ImGuiTableFlags_BordersInner | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { + ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_IndentDisable); ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort); ImGui::TableSetupColumn("Blending", ImGuiTableColumnFlags_NoSort, 100.0f); ImGui::TableSetupColumn("##Enable", ImGuiTableColumnFlags_NoSort); - ImGui::TableSetupColumn("##Timeline", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##Timeline", + ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch); - ImGui::TableNextRow(ImGuiTableRowFlags_Headers, ImGui::TableGetHeaderRowHeight()); + ImGui::TableNextRow(ImGuiTableRowFlags_Headers, row_height); for (int column = 0; column < 4; column++) { ImGui::TableSetColumnIndex(column); ImGui::TableHeader(ImGui::TableGetColumnName(column)); } + // Timeline controls ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{}); @@ -464,26 +462,21 @@ namespace K::UI { ImGui::TableSetColumnIndex(4); - const auto& io = ImGui::GetIO(); - const auto& style = ImGui::GetStyle(); auto *window = ImGui::GetCurrentWindow(); const f32 knob_width = 8.0f; - ImVec2 tl_init_pos = ImGui::GetCursorScreenPos(); - f32 view_width = ImGui::GetColumnWidth(), - tl_width = view_width - knob_width, - view_height = ImGui::TableGetHeaderRowHeight(), - control_left = tl_init_pos.x, - control_right = tl_init_pos.x + view_width; - static f32 view_left = 0.0f, - view_left_old = view_left, - view_right = 1.0f, - view_right_old = view_right; - f32 view_amt = view_right - view_left; + tl_init_pos = ImGui::GetCursorScreenPos(); + view_width = ImGui::GetColumnWidth(), + tl_width = view_width - knob_width, + view_height = ImGui::TableGetHeaderRowHeight(), + control_left = tl_init_pos.x, + control_right = tl_init_pos.x + view_width; + view_amt = view_right - view_left; - f32 delta_x = (std::clamp(io.MousePos.x, control_left, control_right) - io.MouseClickedPos[0].x) / view_width; + f32 delta_x = + (std::clamp(io.MousePos.x, control_left, control_right) - io.MouseClickedPos[0].x) / view_width; ImGui::SetCursorScreenPos({tl_init_pos.x - knob_width / 2.0f + view_width * view_left, tl_init_pos.y}); - ImGui::InvisibleButton("##TL_L", ImVec2{ knob_width, view_height }); + ImGui::InvisibleButton("##TL_L", ImVec2{knob_width, view_height}); if (ImGui::IsItemHovered()) { ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); } @@ -495,7 +488,7 @@ namespace K::UI { } ImGui::SameLine(); - ImGui::InvisibleButton("##TL_INTERVAL", ImVec2{ std::max(2.0f, tl_width * view_amt), view_height }); + ImGui::InvisibleButton("##TL_INTERVAL", ImVec2{std::max(2.0f, tl_width * view_amt), view_height}); if (ImGui::IsItemHovered()) { ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); } @@ -508,7 +501,7 @@ namespace K::UI { view_right = view_left + view_amt; } ImGui::SameLine(); - ImGui::InvisibleButton("##TL_R", ImVec2{ knob_width, view_height }); + ImGui::InvisibleButton("##TL_R", ImVec2{knob_width, view_height}); if (ImGui::IsItemHovered()) { ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); } @@ -519,54 +512,58 @@ namespace K::UI { view_right = std::clamp(view_right_old + delta_x, view_left, 1.0f); } - window->DrawList->AddLine({tl_init_pos.x, tl_init_pos.y }, {tl_init_pos.x, tl_init_pos.y + view_height }, 0x22FFFFFF, 2.0f); - window->DrawList->AddLine({tl_init_pos.x + view_width, tl_init_pos.y }, {tl_init_pos.x + view_width, tl_init_pos.y + view_height }, 0x22FFFFFF, 2.0f); - window->DrawList->AddLine({tl_init_pos.x + view_width * view_left, tl_init_pos.y }, {tl_init_pos.x + view_width * view_left, tl_init_pos.y + view_height }, 0xAAFFFFFF, 2.0f); - window->DrawList->AddLine({tl_init_pos.x + view_width * view_right, tl_init_pos.y }, {tl_init_pos.x + view_width * view_right, tl_init_pos.y + view_height }, 0xAAFFFFFF, 2.0f); - window->DrawList->AddRectFilled({tl_init_pos.x + view_width * view_left, tl_init_pos.y + view_height * .2f }, {tl_init_pos.x + view_width * view_right, tl_init_pos.y + view_height * .8f }, 0x33FFFFFF, 2.0f); + window->DrawList->AddLine({tl_init_pos.x, tl_init_pos.y}, {tl_init_pos.x, tl_init_pos.y + view_height}, + 0x22FFFFFF, 2.0f); + window->DrawList->AddLine({tl_init_pos.x + view_width, tl_init_pos.y}, + {tl_init_pos.x + view_width, tl_init_pos.y + view_height}, 0x22FFFFFF, 2.0f); + window->DrawList->AddLine({tl_init_pos.x + view_width * view_left, tl_init_pos.y}, + {tl_init_pos.x + view_width * view_left, tl_init_pos.y + view_height}, + 0xAAFFFFFF, 2.0f); + window->DrawList->AddLine({tl_init_pos.x + view_width * view_right, tl_init_pos.y}, + {tl_init_pos.x + view_width * view_right, tl_init_pos.y + view_height}, + 0xAAFFFFFF, 2.0f); + window->DrawList->AddRectFilled( + {tl_init_pos.x + view_width * view_left, tl_init_pos.y + view_height * .2f}, + {tl_init_pos.x + view_width * view_right, tl_init_pos.y + view_height * .8f}, 0x33FFFFFF, 2.0f); // Playhead - window->DrawList->AddLine({tl_init_pos.x + view_width * static_cast(s.current_frame) / static_cast(s.frame_max + 1), tl_init_pos.y }, {tl_init_pos.x + view_width * static_cast(s.current_frame) / static_cast(s.frame_max + 1), tl_init_pos.y + view_height }, 0xFF3333FF, 2.0f); + window->DrawList->AddLine({tl_init_pos.x + view_width * static_cast(s.current_frame) / + static_cast(s.frame_max + 1), tl_init_pos.y}, + {tl_init_pos.x + view_width * static_cast(s.current_frame) / + static_cast(s.frame_max + 1), + tl_init_pos.y + view_height}, 0xFF3333FF, 2.0f); - // Block out selectable row -- jank ImGui::SetCursorScreenPos(tl_init_pos - style.CellPadding); - ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, view_height + style.CellPadding.y * 2}); + ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, row_height + style.CellPadding.y}); // Frame Grid static f32 frame_grid_min_width = 20.0f; f32 fr_step = 1.0f / static_cast(s.frame_max + 1) * view_width / view_amt; - fr_step = fr_step >= frame_grid_min_width ? fr_step : std::ceil(frame_grid_min_width / fr_step) * fr_step; - for (f32 fr = TimelineFrameToScreenView(view_left, view_amt, view_width, std::ceil(view_left * static_cast(s.frame_max + 1)), s.frame_max) + tl_init_pos.x; - fr < tl_init_pos.x + view_width; fr += fr_step) - window->DrawList->AddLine({ fr, tl_init_pos.y + row_height }, { fr, ImGui::GetContentRegionAvail().y + tl_init_pos.y }, 0x11FFFFFF, 1.0f); + fr_step = + fr_step >= frame_grid_min_width ? fr_step : std::ceil(frame_grid_min_width / fr_step) * fr_step; + for (f32 fr = TimelineFrameToScreenView(view_left, view_amt, view_width, + std::ceil(view_left * static_cast(s.frame_max + 1)), + s.frame_max) + tl_init_pos.x; + fr < tl_init_pos.x + view_width; fr += fr_step) + window->DrawList->AddLine({fr, tl_init_pos.y + row_height}, + {fr, ImGui::GetWindowSize().y + tl_init_pos.y}, 0x11FFFFFF, 1.0f); if (TimelineFrameInView(view_left, view_right, s.current_frame, s.frame_max)) { - f32 fr = TimelineFrameToScreenView(view_left, view_amt, view_width, s.current_frame, s.frame_max) + tl_init_pos.x; + f32 fr = TimelineFrameToScreenView(view_left, view_amt, view_width, s.current_frame, s.frame_max) + + tl_init_pos.x; window->DrawList->AddLine( {fr, tl_init_pos.y + row_height}, - {fr, ImGui::GetContentRegionAvail().y + tl_init_pos.y}, 0x773333FF, 1.0f); + {fr, ImGui::GetWindowSize().y + tl_init_pos.y}, 0x773333FF, 1.0f); } ImGui::PopStyleVar(3); ImGui::SameLine(); ImGui::TableHeader(""); -// f32 pos_view = static_cast(pos) / static_cast(s.frame_max + 1); -// if (pos_view >= view_left && pos_view < view_right) { -// ImGui::Dummy({(pos_view - view_left) / view_amt * view_width, 0.0f}); -// ImGui::SameLine(); -// ImGui::Button("kf"); -// if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { -// pos_old = pos; -// } -// if (ImGui::IsItemActive()) { -// pos = std::clamp(i64(pos_old) + -// i64(std::round(delta_x * (view_amt * static_cast(s.frame_max + 1)))), 0L, i64(s.frame_max) -// ); -// } -// } - - i32 move_from = -1, move_to = -1; bool after{}; + // Main rows +// ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, {0.0f, 0.0f}); + i32 move_from = -1, move_to = -1; + bool after{}; for (u32 i = 0; i < s.layers.size(); i++) { const bool selected = s.selected.contains(i), disabled = s.disabled.contains(i); ImGui::PushID(static_cast(i)); @@ -576,19 +573,24 @@ namespace K::UI { ImGui::Text("%u", i); ImGui::TableSetColumnIndex(1); - if (ImGui::Selectable(s.layers[i].name.c_str(), selected, - ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap, - ImVec2(0, row_height))) { + static auto base_flags = ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_OpenOnArrow | + ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_FramePadding; + auto flags = base_flags; + if (s.selected.contains(i)) + flags |= ImGuiTreeNodeFlags_Selected; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, + {0.0f, (row_height - ImGui::GetTextLineHeight()) / 2.0f}); + bool layer_open = ImGui::TreeNodeEx(s.layers[i].name.c_str(), flags); + ImGui::PopStyleVar(); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { if (io.KeyCtrl) { if (selected) s.selected.erase(i); else s.selected.insert(i); - } - else if (io.KeyShift) { + } else if (io.KeyShift) { // TODO - } - else { + } else { s.selected.clear(); if (!selected) s.selected.insert(i); @@ -596,7 +598,8 @@ namespace K::UI { s.active = selected ? -1 : static_cast(i); } - auto source_flags = ImGuiDragDropFlags_SourceNoDisableHover | ImGuiDragDropFlags_SourceNoHoldToOpenOthers; + auto source_flags = + ImGuiDragDropFlags_SourceNoDisableHover | ImGuiDragDropFlags_SourceNoHoldToOpenOthers; 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()); @@ -605,7 +608,8 @@ namespace K::UI { } if ((no_selection || !s.selected.contains(i)) && ImGui::BeginDragDropTarget()) { if (!no_selection) { - after = ImGui::GetMousePos().y - ImGui::GetWindowPos().y > ImGui::GetCursorPosY() - row_height / 2.0f; + after = ImGui::GetMousePos().y - ImGui::GetWindowPos().y > + ImGui::GetCursorPosY() - row_height / 2.0f; if (after) ImGui::SetTooltip("After #%u %s", i, s.layers[i].name.c_str()); else @@ -613,8 +617,8 @@ namespace K::UI { } if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_COMP_REORDER")) { - move_from = *(const int*)payload->Data; - move_to = i; + move_from = *(const int *) payload->Data; + move_to = static_cast(i); } ImGui::EndDragDropTarget(); } @@ -641,10 +645,13 @@ namespace K::UI { s.disabled.insert(i); } + // Timeline ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{}); ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2{}); - ImGui::TableSetColumnIndex(4); + + ImGui::TableNextColumn(); + f32 init_y = ImGui::GetCursorScreenPos().y; // do stuff here @@ -654,14 +661,14 @@ namespace K::UI { if (pos_view >= view_left && pos_view < view_right) { ImGui::Dummy({(pos_view - view_left) / view_amt * view_width - 5.0f, 0.0f}); ImGui::SameLine(); - ImGui::Button("kf"); + ImGui::Button("kf", {0.0f, row_height * .8f}); if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { pos_old = pos; } if (ImGui::IsItemActive()) { pos = std::clamp(i64(pos_old) + - i64(std::round(delta_x * (view_amt * static_cast(s.frame_max + 1)))), 0L, i64(s.frame_max) - ); + i64(std::round(delta_x * (view_amt * static_cast(s.frame_max + 1)))), 0L, i64(s.frame_max) + ); } } @@ -673,8 +680,92 @@ namespace K::UI { ImGui::PopStyleVar(3); + if (layer_open) { + i32 j = 0; + for (auto &u: s.layers[i].track.uniforms) { + ImGui::PushID(j); + ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); + ImGui::TableSetColumnIndex(0); + ImGui::RadioButton("##Socket", false); + + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + ImGui::SetDragDropPayload("K_PLUG_IN_TO_OUT", &u.second.connection, + sizeof(u.second.connection)); + ImGui::EndDragDropSource(); + } + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_PLUG_OUT_TO_IN")) { + PlugboardGraph::ConnectInfo out = *(const PlugboardGraph::ConnectInfo *) payload->Data; + u.second.connection.p->connect(u.second.connection.index, out.p, out.index); + } + ImGui::EndDragDropTarget(); + } + + std::visit([&dragging_on_socket, &drag_source, &s, &style](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + if (ImGui::IsItemActive()) + s.plugboard.links_pos[arg].sinks.push_back(ImGui::GetMousePos()); + else + s.plugboard.links_pos[arg].sinks.push_back(ImGui::GetCursorScreenPos() + + ImVec2{ImGui::GetFrameHeight() / 2, + -ImGui::GetFrameHeight() / 2 - + style.ItemInnerSpacing.y}); + } else if (ImGui::IsItemActive()) { + dragging_on_socket = true; + drag_source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2, + -ImGui::GetFrameHeight() / 2 - + style.ItemInnerSpacing.y}; + } + }, u.second.connection.p->inputs_fed[u.second.connection.index]); + + + ImGui::TableSetColumnIndex(1); + ImGui::TreeNodeEx(u.first.c_str(), + ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf | + ImGuiTreeNodeFlags_NoTreePushOnOpen); + ImGui::TableSetColumnIndex(2); + ImGui::SetNextItemWidth(-FLT_MIN); + + bool plugged = u.second.connection.p->inputs_fed[u.second.connection.index].index() == 1; + if (!plugged) + std::visit([](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v::type>) + ImGui::DragFloat("##", &arg, 0.005f); + else if constexpr (std::is_same_v::type>) + ImGui::DragInt("##", &arg, 0.005f); + else if constexpr (std::is_same_v::type>) + ImGui::DragFloat4("##", &arg.r, 0.005f); + else if constexpr (std::is_same_v::type>) + ImGui::DragFloat2("##", &arg.x, 0.005f); + else if constexpr (std::is_same_v::type>) + ImGui::DragFloat3("##", &arg.x, 0.005f); + }, std::get<0>(u.second.connection.p->inputs_fed[u.second.connection.index])); + + ImGui::TableSetColumnIndex(3); + ImGui::TableSetColumnIndex(4); + ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, row_height + style.CellPadding.y}); + if (ImGui::IsItemActive()) { + s.current_frame = TimelineScreenViewToFrame(view_left, view_amt, view_width, std::clamp(ImGui::GetMousePos().x - tl_init_pos.x, 0.0f, view_width), s.frame_max); + } + + ImGui::PopID(); + j++; + } + ImGui::TreePop(); + } else + for (auto &u: s.layers[i].track.uniforms) + std::visit([table_left, &s](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) + s.plugboard.links_pos[arg].sinks.push_back( + {table_left, ImGui::GetCursorScreenPos().y - row_height / 2.0f}); + }, u.second.connection.p->inputs_fed[u.second.connection.index]); + ImGui::PopID(); } +// ImGui::PopStyleVar(); if (move_from != -1 && move_to != -1) { if (no_selection) @@ -686,7 +777,8 @@ namespace K::UI { u32 target = move_to + after, popped_before_target = 0; for (auto it = s.selected.rbegin(); it != s.selected.rend(); it++, inserted++) { u32 sel = *it; - if (sel > target) sel += inserted; // note that we iterate backwards, so cur index is always right if < target + if (sel > target) + sel += inserted; // note that we iterate backwards, so cur index is always right if < target Layer l = s.layers[sel]; s.layers.erase(s.layers.begin() + sel); if (sel < move_to) popped_before_target++; @@ -699,8 +791,36 @@ namespace K::UI { } } + tl_end_begin = ImGui::GetCursorScreenPos(); ImGui::EndTable(); } + + ImGui::SetCursorScreenPos(nodes_begin); + if (ImGui::BeginChild("Overlay", {}, false, ImGuiWindowFlags_NoInputs)) { + for (auto &[_, link]: s.plugboard.links_pos) + for (auto &sink: link.sinks) + ImGui::GetCurrentWindow()->DrawList->AddBezierCubic(link.source, + {link.source.x + 60.0f, link.source.y}, + {sink.x - 60.0f, sink.y}, sink, 0xFFFFFFFF, + 2.0f); + + if (dragging_on_socket) + ImGui::GetCurrentWindow()->DrawList->AddLine(ImGui::GetMousePos(), drag_source, 0xFFFFFFFF, 2.0f); + } + ImGui::EndChild(); + + ImGui::SetCursorScreenPos(tl_end_begin); + if (ImGui::BeginChild("TL Overlay")) { + ImGui::InvisibleButton("##TL_BG", ImGui::GetContentRegionAvail()); + if (ImGui::IsItemActive()) { + s.current_frame = TimelineScreenViewToFrame(view_left, view_amt, view_width, + std::clamp(ImGui::GetMousePos().x - tl_init_pos.x, 0.0f, + view_width), s.frame_max); + } + } + ImGui::EndChild(); + + ImGui::End(); } @@ -729,63 +849,39 @@ namespace K::UI { ImGui::SetNextItemWidth(-FLT_MIN); ImGui::Combo("##Type", &type_current, ShaderGraph::Type_To_Str, ShaderGraph::T_Count); - if (ImGui::BeginTable("Uniforms", 5, ImGuiTableFlags_Borders)) { + if (ImGui::BeginTable("Uniforms", 4, ImGuiTableFlags_Borders)) { ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("Expose", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("##del", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed); 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->second.connection.p == nullptr) + continue; + ImGui::TableNextRow(); ImGui::PushID(i++); ImGui::TableNextColumn(); - ImGui::Text("%s", it->first.c_str()); + ImGui::TextUnformatted(it->first.c_str()); ImGui::TableNextColumn(); - ImGui::Text("%s", ShaderGraph::Type_To_Str[it->second.val.index()]); - ImGui::TableNextColumn(); - 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.emplace_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::TextUnformatted(ShaderGraph::Type_To_Str[it->second.val.index()]); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-FLT_MIN); - 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); - else if constexpr (std::is_same_v::type>) - ImGui::DragInt("##", &arg, 0.005f); - else if constexpr (std::is_same_v::type>) - ImGui::DragFloat4("##", &arg.r, 0.005f); - else if constexpr (std::is_same_v::type>) - ImGui::DragFloat2("##", &arg.x, 0.005f); - else if constexpr (std::is_same_v::type>) - ImGui::DragFloat3("##", &arg.x, 0.005f); - }, it->second.val); - ImGui::EndDisabled(); + if (it->second.connection.p->inputs_fed[it->second.connection.index].index() == 0) + std::visit([](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v::type>) + ImGui::DragFloat("##", &arg, 0.005f); + else if constexpr (std::is_same_v::type>) + ImGui::DragInt("##", &arg, 0.005f); + else if constexpr (std::is_same_v::type>) + ImGui::DragFloat4("##", &arg.r, 0.005f); + else if constexpr (std::is_same_v::type>) + ImGui::DragFloat2("##", &arg.x, 0.005f); + else if constexpr (std::is_same_v::type>) + ImGui::DragFloat3("##", &arg.x, 0.005f); + }, std::get<0>(it->second.connection.p->inputs_fed[it->second.connection.index])); ImGui::TableNextColumn(); if (ImGui::Button("X")) { bgfx::destroy(it->second.handle); @@ -819,6 +915,20 @@ namespace K::UI { ImGui::End(); } + void Assets(CompState& s) { + if (ImGui::Begin("Assets", &draw_assets)) { + + } + ImGui::End(); + } + + void Color(CompState& s) { + if (ImGui::Begin("Color", &draw_color)) { + + } + ImGui::End(); + } + void Draw(u32 frame, CompState& s) { ImGui_ImplSDL2_NewFrame(); ImGui_Implbgfx_NewFrame(); @@ -826,18 +936,20 @@ namespace K::UI { ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode); -// ImGui::ShowDemoWindow(); - static ImGuiStyle& style = ImGui::GetStyle(); - style.GrabRounding = style.FrameRounding = 5.0f; + ImGui::ShowDemoWindow(); +// static ImGuiStyle& style = ImGui::GetStyle(); +// style.GrabRounding = style.FrameRounding = 5.0f; MainMenuBar(s); if (draw_viewport) Viewport(s); if (draw_shader) Shader(s); if (draw_comp) Composition(s); if (draw_interpolation) Interpolation(s); if (draw_properties) Properties(s); + if (draw_assets) Assets(s); + if (draw_color) Color(s); if (save_called && ready_frame == frame) { - stbi_write_png("frame.png", s.width, s.height, 4, save_buffer, s.width * 4); + stbi_write_png("frame.png", static_cast(s.width), static_cast(s.height), 4, save_buffer, static_cast(s.width) * 4); save_called = false; } diff --git a/Keishiki/VisualTrack.cpp b/Keishiki/VisualTrack.cpp index 90dfc4e..5ed8b6b 100644 --- a/Keishiki/VisualTrack.cpp +++ b/Keishiki/VisualTrack.cpp @@ -1,4 +1,5 @@ #include "VisualTrack.h" +#include "Keishiki.h" namespace K { PlugboardGraph::T_Map::type ShaderValToPlugboard(const ShaderGraph::T_Map::type& val) { @@ -48,16 +49,20 @@ namespace K { for (auto& [_name, u] : uniforms) { f32 pack[4]{}; ShaderGraph::T_Map::type val_target; - if (u.exposure.p == nullptr) + if (u.connection.p == nullptr) val_target = u.val; else { - std::visit([&s, &val_target](auto&& arg) { + std::visit([&u, &s, &val_target](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_same_v) - val_target = PlugboardValToShader(PlugboardGraph::Eval(s, arg)); + if constexpr (std::is_same_v) { + bool good; + val_target = PlugboardValToShader(PlugboardGraph::ConvertValue(PlugboardGraph::Eval(s, arg), + static_cast(u.val.index()), // todo DANGEROUS !! + good)); + } else if constexpr (std::is_same_v::type>) val_target = PlugboardValToShader(arg); - }, u.exposure.p->inputs_fed[u.exposure.index]); + }, u.connection.p->inputs_fed[u.connection.index]); } std::visit([&pack](auto&& arg) { using T = std::decay_t; @@ -170,6 +175,26 @@ namespace K { uniforms.clear(); } + void VisualTrack::expose_uniform(CompState& s, const String& u) { + auto& uu = uniforms[u]; + uu.connection = {&s.plugboard.out_instance, static_cast(s.plugboard.out_instance.node->in.size())}; + s.plugboard.out_instance.node->in.push_back({ u, PlugboardGraph::Type(uu.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.emplace_back(ShaderValToPlugboard(uu.val)); + } + + void VisualTrack::hide_uniform(CompState& s, const String& u) { + auto& uu = uniforms[u]; + uu.connection.p->disconnect(uu.connection.index); + + for (auto& l : s.layers) + for (auto& u_other : l.track.uniforms) + if (u_other.second.connection.index > uu.connection.index) + u_other.second.connection.index--; + s.plugboard.comp_out.in.erase(s.plugboard.out_instance.node->in.begin() + uu.connection.index); + s.plugboard.out_instance.inputs_fed.erase(s.plugboard.out_instance.inputs_fed.begin() + uu.connection.index); + uu.connection = {nullptr, 0}; + } + void VisualTrack::add_uniform(const String& s, ShaderGraph::T_Map::type&& val) { if (!uniforms.contains(s)) uniforms.emplace(s, Uniform{bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val, {nullptr, 0}}); diff --git a/Keishiki/include/Keishiki.h b/Keishiki/include/Keishiki.h index f61052e..fc00138 100644 --- a/Keishiki/include/Keishiki.h +++ b/Keishiki/include/Keishiki.h @@ -31,7 +31,6 @@ namespace K { 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 index b0f786a..7ac05ff 100644 --- a/Keishiki/include/PlugboardGraph.h +++ b/Keishiki/include/PlugboardGraph.h @@ -55,16 +55,19 @@ namespace K::PlugboardGraph { T_Map::type expand_type(Type i); + struct Socket { + String name; + Type type; + }; + struct Node { String name; // will likely get SSO for most stuff - Vector in_names; - Vector in_types; + Vector in; + Vector out; - Vector out_names; - Vector out_types; Vector::type(const CompState&, const 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 + // todo investigate how you might wanna jit this instead of evaluating a tree for each output at runtime esp. when a project is set for rendering }; struct NodeInstance; @@ -72,11 +75,10 @@ namespace K::PlugboardGraph { struct ConnectInfo { NodeInstance *p; // NOTE: so NodeInstances must be stored in a list, otherwise vector realloc leads to UAF u32 index; - auto operator<=>(const ConnectInfo&) const = default; + auto operator<=>(const ConnectInfo&) const = default; // too-modern c++ black magic ? }; - struct _ConnectInfoHasher - { + struct ConnectInfoHasher { std::size_t operator()(const ConnectInfo& k) const { return std::hash()(k.p); @@ -111,7 +113,7 @@ namespace K::PlugboardGraph { else if constexpr (std::is_same_v::type>) ; }, inputs_fed[input_index]); - inputs_fed[input_index] = expand_type(node->in_types[input_index]); + inputs_fed[input_index] = expand_type(node->in[input_index].type); } }; @@ -119,10 +121,10 @@ namespace K::PlugboardGraph { inline NodeInstance MakeInstance(Node& n) { Vector::type, ConnectInfo>> inp; Vector> out; - for (auto t : n.in_types) - inp.emplace_back(expand_type(t)); - for (auto t : n.out_types) - out.push_back({}); + for (const auto& t : n.in) + inp.emplace_back(expand_type(t.type)); + for (auto t : n.out) + out.emplace_back(); return { .node = &n, .inputs_fed = std::move(inp), @@ -229,13 +231,13 @@ namespace K::PlugboardGraph { return Eval(s,arg); else if constexpr (std::is_same_v::type>) return arg; - }, info.p->inputs_fed[i]), info.p->node->in_types[i], good); + }, info.p->inputs_fed[i]), info.p->node->in[i].type, good); if (good) { pack.push_back(val); } else return {}; // todo panic! } - return info.p->node->fetch[info.index](s, pack); + return info.p->node->fetch[info.index](s, pack); } struct LinksFromSource { @@ -245,7 +247,7 @@ namespace K::PlugboardGraph { struct PlugboardGraph { std::list nodes; // OK complexity wise since we would usually traverse the entire thing anyway, locality will likely be bad - std::unordered_map links_pos; // this is hilariously bad + std::unordered_map links_pos; // this is hilariously bad Node comp_in, comp_out; NodeInstance in_instance, out_instance; }; diff --git a/Keishiki/include/PlugboardNodes.h b/Keishiki/include/PlugboardNodes.h index bca8913..74431b5 100644 --- a/Keishiki/include/PlugboardNodes.h +++ b/Keishiki/include/PlugboardNodes.h @@ -5,11 +5,9 @@ namespace K::PlugboardNodes { PlugboardGraph::Node Sine = { .name = "Sine", - .in_names = { "In" }, - .in_types = { PlugboardGraph::T_Float }, + .in = { {"In", PlugboardGraph::T_Float } }, - .out_names = { "Out" }, - .out_types = { PlugboardGraph::T_Float }, + .out = { { "Out", PlugboardGraph::T_Float } }, .fetch = {[](const CompState&, const Vector::type>& arg) { return std::sin(std::get(arg[0])); }} diff --git a/Keishiki/include/VisualTrack.h b/Keishiki/include/VisualTrack.h index 9b44182..354ce72 100644 --- a/Keishiki/include/VisualTrack.h +++ b/Keishiki/include/VisualTrack.h @@ -24,7 +24,7 @@ namespace K { struct Uniform { bgfx::UniformHandle handle; ShaderGraph::T_Map::type val; - PlugboardGraph::ConnectInfo exposure; + PlugboardGraph::ConnectInfo connection; }; ShaderGraph::T_Map::type PlugboardValToShader(const PlugboardGraph::T_Map::type& val); @@ -38,6 +38,9 @@ namespace K { Dict uniforms; // Vector samplers; bgfx::TextureHandle get_frame(const CompState& s, bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16], f32 view[16], f32 transform[16]) const; + void expose_uniform(CompState& s, const String& u); + void hide_uniform(CompState& s, const String& u); + void add_uniform(const String& s, ShaderGraph::T_Map::type&& val); void compile();