From 4b7e5e30188088a919242ce56afb90c0810d46f4 Mon Sep 17 00:00:00 2001 From: lachrymaLF Date: Sat, 29 Jun 2024 10:34:33 -0400 Subject: [PATCH] ui! ui! ui! ui! --- Keishiki/Keishiki.cpp | 4 +- Keishiki/Resource.cpp | 75 ++++++++++++- Keishiki/UI.cpp | 194 +++++++++++++++++++++------------ Keishiki/VisualTrack.cpp | 28 +++-- Keishiki/include/Keishiki.h | 9 +- Keishiki/include/Resource.h | 2 + Keishiki/include/VisualTrack.h | 12 +- TODO.md | 1 - 8 files changed, 232 insertions(+), 93 deletions(-) diff --git a/Keishiki/Keishiki.cpp b/Keishiki/Keishiki.cpp index 8250a1e..1d4ac2e 100644 --- a/Keishiki/Keishiki.cpp +++ b/Keishiki/Keishiki.cpp @@ -26,8 +26,8 @@ namespace K { i32 CompositionState::GetFrame(u32 frame) { if (frame > frame_max) { - Log(K_L_Error, "Comp {} frame out of range! Requesting {}, max {}", name, frame, frame_max); - return -1; + Log(K_L_Warning, "Comp {} frame out of range! Requesting {}, max {}", name, frame, frame_max); + frame = frame_max; } u64 tmp = current_frame; diff --git a/Keishiki/Resource.cpp b/Keishiki/Resource.cpp index ea91e5e..4d21535 100644 --- a/Keishiki/Resource.cpp +++ b/Keishiki/Resource.cpp @@ -16,10 +16,40 @@ struct glz::meta { namespace K::Resource { void SaveCurrentProject() { - auto ec = glz::write_file_json("soon!", "./temp.json", String{}); - if (ec.ec != glz::error_code::none) { - Log(K_L_Error, "{}", ec.includer_error); - } + static const SDL_DialogFileFilter save_filters[] = { + { "JSON", "json" }, + { NULL, NULL } + }; + struct SaveCallback { + static void SDLCALL f(void *userdata, const char* const* filelist, i32 filter) { + if (!filelist) { + Log(K_L_Error, "{}", SDL_GetError()); + return; + } + else if (!*filelist) // The user did not select any file. Most likely, the dialog was canceled. + return; + + while (*filelist) { + auto ec = glz::write_file_json("soon! :)", *filelist, String{}); + if (ec.ec != glz::error_code::none) { + Log(K_L_Error, "{}", ec.includer_error); + } + filelist++; + } + + if (filter == -1) { + Log(K_L_Info, "Platform does not support fetching file filters."); + } + else if (filter < SDL_arraysize(save_filters)) { + SDL_Log("The filter selected by the user is '%s' (%s).", + save_filters[filter].pattern, save_filters[filter].name); + } + else { + SDL_Log("The user did not select any filter."); + } + } + }; + SDL_ShowSaveFileDialog(SaveCallback::f, nullptr, app_state.window, save_filters, 1, nullptr); } } @@ -103,6 +133,43 @@ namespace K::Resource { return bgfx::createProgram(LoadShader(vert), LoadShader(frag), true); } + void LoadFile() { + static const SDL_DialogFileFilter import_filters[] = { + { "PNG images", "png" }, + { "JPEG images", "jpg;jpeg" }, + { "All images", "png;jpg;jpeg" }, + { NULL, NULL } + }; + struct ImportCallback { + static void SDLCALL f(void *userdata, const char* const* filelist, i32 filter) { + if (!filelist) { + Log(K_L_Error, "{}", SDL_GetError()); + return; + } + else if (!*filelist) // The user did not select any file. Most likely, the dialog was canceled. + return; + + while (*filelist) { + Load(*filelist); + filelist++; + } + + if (filter == -1) { + Log(K_L_Info, "Platform does not support fetching file filters."); + } + else if (filter < SDL_arraysize(import_filters)) { + SDL_Log("The filter selected by the user is '%s' (%s).", + import_filters[filter].pattern, import_filters[filter].name); + } + else { + SDL_Log("The user did not select any filter."); + } + } + }; + + SDL_ShowOpenFileDialog(ImportCallback::f, nullptr, app_state.window, import_filters, 3, nullptr, true); + } + void HotReload() { while (app_state.running.load()) { for (auto& [path, resource] : resources) diff --git a/Keishiki/UI.cpp b/Keishiki/UI.cpp index be67d37..a34d721 100644 --- a/Keishiki/UI.cpp +++ b/Keishiki/UI.cpp @@ -324,17 +324,11 @@ namespace K::UI { } void Viewport(CompositionState& s) { - if (playing) { - if (static_cast(app_state.current_time - last_frame_tick) >= 1000.0f / s.fps) { - f32 delta = static_cast(app_state.current_time - last_frame_tick); - f32 frames_passed = delta / (1000.0f / s.fps); - s.current_frame = (s.current_frame + static_cast(std::floor(frames_passed))) % (s.frame_max + 1); - last_frame_tick = app_state.current_time - static_cast(std::fmod(delta, 1000.0f / s.fps)); - } - } - - if (ImGui::Begin("Viewport")) { - static bool do_bg = true; + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{}); + if (ImGui::Begin("Viewport", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { + static bool do_bg = true, dragging = false; + static f32 size = 1.0f, angle = 0; + static ImVec2 pos{}, old = pos; if (ImGui::Shortcut(ImGuiKey_Space)) TogglePlay(); @@ -357,11 +351,31 @@ namespace K::UI { present = s.composite[result_index]; ImTextureID idx = ImGui::toId(present, 1, 0); - if (idx != nullptr) - ImGui::Image(idx, ImVec2{ static_cast(s.width), static_cast(s.height) }); + if (idx != nullptr) { + ImVec2 image_size = {static_cast(s.width) * size, static_cast(s.height) * size}; - ImGui::Text("%ux%u", s.width, s.height); - ImGui::SameLine(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + static auto rot = [](const ImVec2& v, float cos_a, float sin_a) { + return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); + }; + ImVec2 center = ImGui::GetCursorScreenPos() + ImGui::GetContentRegionAvail() * .5f + pos; + f32 cos_a = std::cosf(angle / 180.0f * std::numbers::pi); + f32 sin_a = std::sinf(angle / 180.0f * std::numbers::pi); + ImVec2 quad[4] = { + center + rot(ImVec2(-image_size.x * 0.5f, -image_size.y * 0.5f), cos_a, sin_a), + center + rot(ImVec2(+image_size.x * 0.5f, -image_size.y * 0.5f), cos_a, sin_a), + center + rot(ImVec2(+image_size.x * 0.5f, +image_size.y * 0.5f), cos_a, sin_a), + center + rot(ImVec2(-image_size.x * 0.5f, +image_size.y * 0.5f), cos_a, sin_a) + }; + ImVec2 uvs[4] = { + ImVec2(0.0f, 0.0f), + ImVec2(1.0f, 0.0f), + ImVec2(1.0f, 1.0f), + ImVec2(0.0f, 1.0f) + }; + + draw_list->AddImageQuad(idx, quad[0], quad[1], quad[2], quad[3], uvs[0], uvs[1], uvs[2], uvs[3], IM_COL32_WHITE); + } /* ImGui::BeginDisabled(comp_save_called != nullptr); if (ImGui::Button("Save to frame.png")) { @@ -371,13 +385,51 @@ namespace K::UI { } ImGui::EndDisabled();*/ - ImGui::SetNextItemWidth(50.0f); - ImGui::DragFloat("FPS", &s.fps, 1.0f, 1.0f, 120.0f); - ImGui::SameLine(); - ImGui::Checkbox("BG", &do_bg); + ImGuiIO io = ImGui::GetIO(); + if (ImGui::IsWindowHovered()) { + if (io.MouseWheel != 0.0f) { + size += io.MouseWheel * 0.25f; + size = std::max(size, 0.25f); + } + if (io.MouseClicked[ImGuiMouseButton_Middle]) { + dragging = true; + old = pos; + } + if (io.MouseDoubleClicked[ImGuiMouseButton_Middle]) { + size = 1.0f; + pos.x = 0.0f; + pos.y = 0.0f; + old = pos; + } + } + if (dragging) { + pos = old + ImGui::GetMouseDragDelta(ImGuiMouseButton_Middle); + } + if (io.MouseReleased[ImGuiMouseButton_Middle]) { + dragging = false; + } + ImGui::SetCursorPosY(ImGui::GetWindowHeight() - 25.0f); + ImGui::PushStyleColor(ImGuiCol_ChildBg, 0x99000000); + if (ImGui::BeginChild("Controls")) { + ImGui::Text("%ux%u@%.1f", s.width, s.height, s.fps); + ImGui::SameLine(); + ImGui::Checkbox("Transparency Grid", &do_bg); + ImGui::SameLine(); + f32 percentage = size * 100.0f; + ImGui::SetNextItemWidth(110.0f); + if (ImGui::InputFloat("View Scale", &percentage, 15.0f, 25.0f, "%.2f%%")) { + size = std::max(.25f, percentage / 100.0f); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(50.0f); + ImGui::DragFloat("Rotation", &angle); + } + ImGui::EndChild(); + ImGui::PopStyleColor(); } ImGui::End(); + ImGui::PopStyleVar(); } constexpr bool TimelineFrameInView(f32 view_left, f32 view_right, i64 frame, u64 frame_max) { @@ -546,6 +598,7 @@ namespace K::UI { static Vector keys_selected_by_bg_drag{}; auto tl_bg_handler = [&view_width, &style, &view_amt, &s, &mouse_tl_x, &io](f32 h = 0.0f, f32 w = 0.0f) { static f32 pan_x, view_left_old, view_right_old; + static bool middle_dragging = false; ImGui::InvisibleButton("##TL_BG", ImVec2{ w == 0.0f ? view_width + style.CellPadding.x * 2 : w, h == 0.0f ? row_height : h}); if (ImGui::IsItemClicked()) { tl_clear_selection_request = !(io.KeyCtrl || io.KeyShift); @@ -563,8 +616,9 @@ namespace K::UI { pan_x = mouse_tl_x; view_left_old = view_left; view_right_old = view_right; + middle_dragging = true; } - if (ImGui::IsKeyDown(ImGuiKey_MouseMiddle) && ImGui::IsItemHovered()) { + if (middle_dragging && ImGui::IsKeyDown(ImGuiKey_MouseMiddle)) { ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); f32 shift_amt = (mouse_tl_x - pan_x) / view_width * view_amt; view_left = view_left_old - shift_amt; @@ -578,6 +632,8 @@ namespace K::UI { view_right = 1.0f; } } + if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle)) + middle_dragging = false; if (ImGui::IsKeyPressed(ImGuiKey_Delete) && ImGui::IsItemHovered()) { delete_requests[K_DR_Selected_Keys] = true; } @@ -706,6 +762,9 @@ namespace K::UI { view_right = std::clamp(view_right_old + ImGui::GetMouseDragDelta().x / tl_width, view_left + 2.0f / static_cast(s.frame_max), 1.0f); } + window->DrawList->AddLine({tl_init_pos.x + static_cast(s.current_frame) / static_cast(s.frame_max + 1) * view_width, tl_init_pos.y}, + {tl_init_pos.x + static_cast(s.current_frame) / static_cast(s.frame_max + 1) * view_width, tl_init_pos.y + view_height}, + 0x773333FF); window->DrawList->AddRect({tl_init_pos.x - knob_width / 2.0f, tl_init_pos.y}, {tl_init_pos.x + knob_width / 2.0f, tl_init_pos.y + view_height}, 0x22FFFFFF); window->DrawList->AddRect({tl_init_pos.x + view_width - knob_width / 2.0f, tl_init_pos.y}, @@ -720,6 +779,9 @@ namespace K::UI { {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); + ImGui::SetCursorScreenPos(tl_init_pos); + ImGui::InvisibleButton("##TL_BG", {view_width, view_height}); // block bg interaction + ImGui::SameLine(); ImGui::TableHeader(""); // TL header @@ -935,7 +997,7 @@ namespace K::UI { if (layer_open) { i32 j = 0; for (auto &u: current_layer.track.uniforms) { - if (u.status != K_U_Exposed) + if (!(u.status & K_U_Exposed)) continue; ImGui::PushID(j); @@ -1163,7 +1225,7 @@ namespace K::UI { ImGui::SetCursorScreenPos(draw_pos); ImGui::PushStyleColor(ImGuiCol_Button, 0x00000000); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1.0f, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1.0f, (row_height - ImGui::GetTextLineHeight()) / 2.0f)); ImGui::Button((const char*)(is_sel ? u8"\u25c8" : u8"\u25c7"), {kf_tab_width, row_height * .8f}); ImGui::PopStyleVar(); ImGui::PopStyleColor(); @@ -1206,7 +1268,7 @@ namespace K::UI { ImGui::TreePop(); } else for (auto& u: current_layer.track.uniforms) { - if (u.status != K_U_Exposed) + if (!(u.status & K_U_Exposed)) continue; std::visit([table_left, &init_y](auto &&arg) { using T = std::decay_t; @@ -1463,13 +1525,7 @@ namespace K::UI { ImGui::SetCursorScreenPos(tl_grid_cursor_pos); if (ImGui::BeginChild("TL Grid Overlay", ImGui::GetContentRegionAvail(), ImGuiChildFlags_None, ImGuiWindowFlags_NoInputs)) { - // Playhead auto window = ImGui::GetCurrentWindow(); - 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}, 0xFF3333FF, 2.0f); // Frame Grid static f32 frame_grid_min_width = 20.0f; @@ -1482,12 +1538,16 @@ namespace K::UI { window->DrawList->AddLine({fr, tl_init_pos.y}, {fr, ImGui::GetWindowSize().y + tl_init_pos.y}, 0x11FFFFFF, 1.0f); + // Playhead 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; window->DrawList->AddLine( {fr, tl_init_pos.y}, {fr, ImGui::GetWindowSize().y + tl_init_pos.y}, 0x773333FF, 1.0f); + window->DrawList->AddRectFilled( + {fr, tl_init_pos.y}, + {fr + fr_step, ImGui::GetWindowSize().y + tl_init_pos.y}, 0x223333FF, 1.0f); } if (bg_drag_select_active) { window->DrawList->AddRectFilled(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, 0x55AA5555); @@ -1565,7 +1625,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->status != K_U_Exposed) { + if (!(it->status & K_U_Exposed)) { it++; continue; } @@ -1611,7 +1671,7 @@ namespace K::UI { if (ImGui::BeginTable("Samplers", 4, ImGuiTableFlags_Borders)) { ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("Dims via", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("Resource", ImGuiTableColumnFlags_NoSort); + ImGui::TableSetupColumn("Source", ImGuiTableColumnFlags_NoSort); ImGui::TableSetupColumn("##del", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed); ImGui::TableHeadersRow(); i32 i = 0; @@ -1632,27 +1692,41 @@ namespace K::UI { return arg->name.c_str(); } }, it->resource); + u32 id = 0; if (ImGui::BeginCombo("##source", r_name)) { + ImGui::TextUnformatted("--Resources--"); for (auto& [file, res_v] : Resource::resources) if (auto *res = std::get_if>(&res_v)) { + ImGui::PushID(id++); const bool is_selected = r_name == res->filename.c_str(); // lol if (ImGui::Selectable(file.c_str(), is_selected)) { it->resource = res; it->dims->val = ShaderGraph::XY{static_cast(res->w), static_cast(res->h)}; + if (std::visit([](auto&& arg){ return arg != nullptr; }, it->frame->connection.p)) + s.layers[s.active].track.HideUniform(s, *it->frame); } if (is_selected) ImGui::SetItemDefaultFocus(); + + ImGui::PopID(); } + ImGui::TextUnformatted("--Compositions--"); for (auto& comp : app_state.project.compositions) { + ImGui::PushID(id++); const bool is_selected = r_name == comp.name.c_str(); // lol if (ImGui::Selectable(comp.name.c_str(), is_selected)) { it->resource = ∁ it->dims->val = ShaderGraph::XY{static_cast(comp.width), static_cast(comp.height)}; + it->frame->val = ShaderGraph::T_Map::type(0); + if (std::visit([](auto&& arg){ return arg == nullptr; }, it->frame->connection.p)) + s.layers[s.active].track.ExposeUniform(s, *it->frame); } if (is_selected) ImGui::SetItemDefaultFocus(); + + ImGui::PopID(); } ImGui::EndCombo(); } @@ -1691,7 +1765,7 @@ namespace K::UI { if (ImGui::Button("Apply")) for (auto& layer : s.layers) for (auto& u : layer.track.uniforms) { - if (u.status != K_U_Exposed) + if (!(u.status & K_U_Exposed)) continue; for (auto& n : s.plugboard.show_nodes[u.connection.index]) { auto& [chain, _] = std::get(n.p)->extra; @@ -1831,7 +1905,7 @@ namespace K::UI { if (ImGui::InputFloat("FPS", &fps, 1.0f, 5.0f, "%.1f")) fps = std::clamp(fps, 1.0f, 120.0f); - static i32 dims[2] = { 1280, 720 }; + static i32 dims[2] = { 1280, 550 }; if (ImGui::InputInt2("Dimensions", dims)) { dims[0] = std::max(1, dims[0]); dims[1] = std::max(1, dims[1]); @@ -1891,40 +1965,7 @@ namespace K::UI { } if (ImGui::Button("Load")) { - static const SDL_DialogFileFilter import_filters[] = { - { "PNG images", "png" }, - { "JPEG images", "jpg;jpeg" }, - { "All images", "png;jpg;jpeg" }, - { NULL, NULL } - }; - struct ImportCallback { - static void SDLCALL f(void *userdata, const char* const* filelist, i32 filter) { - if (!filelist) { - Log(K_L_Error, "{}", SDL_GetError()); - return; - } - else if (!*filelist) // The user did not select any file. Most likely, the dialog was canceled. - return; - - while (*filelist) { - Resource::Load(*filelist); - filelist++; - } - - if (filter == -1) { - Log(K_L_Info, "Platform does not support fetching file filters."); - } - else if (filter < SDL_arraysize(import_filters)) { - SDL_Log("The filter selected by the user is '%s' (%s).", - import_filters[filter].pattern, import_filters[filter].name); - } - else { - SDL_Log("The user did not select any filter."); - } - } - }; - - SDL_ShowOpenFileDialog(ImportCallback::f, nullptr, app_state.window, import_filters, 3, nullptr, true); + Resource::LoadFile(); } if (ImGui::BeginTable("Assets", 3, ImGuiTableFlags_Borders)) { @@ -1989,6 +2030,7 @@ namespace K::UI { static_cast(Resource::fallback_still->w), static_cast(Resource::fallback_still->h) }; + layer.track.HideUniform(s, *sampler.frame); } } it->Destroy(); @@ -2047,11 +2089,13 @@ namespace K::UI { for (auto& chain : s.plugboard.selected_nodes) { if (auto *c = std::get_if(&chain)) { auto& [segments, selected] = (*c)->extra.chain; + std::ranges::sort(selected, std::ranges::greater{}); for (u32 i : selected) segments.erase(segments.begin() + i); selected.clear(); } } + delete_requests[K_DR_Selected_Keys] = false; } } @@ -2060,17 +2104,29 @@ namespace K::UI { ImGui_Implbgfx_NewFrame(); ImGui::NewFrame(); - Graphics::DrawTextureImageAlpha(K::Graphics::K_VIEW_LOGO, logo, app_state.window_width - logo->w + 10, 0, .4f); + Graphics::DrawTextureImageAlpha(K::Graphics::K_VIEW_LOGO, logo, app_state.window_width - logo->w + 10, 0, .7f); ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode); - ImGui::ShowDemoWindow(); + + +// ImGui::ShowDemoWindow(); MainMenuBar(); if (draw_assets) Assets(); if (app_state.project.inspecting_composition != nullptr) { auto &comp = *app_state.project.inspecting_composition; + + if (playing) { + if (static_cast(app_state.current_time - last_frame_tick) >= 1000.0f / comp.fps) { + f32 delta = static_cast(app_state.current_time - last_frame_tick); + f32 frames_passed = delta / (1000.0f / comp.fps); + comp.current_frame = (comp.current_frame + static_cast(std::floor(frames_passed))) % (comp.frame_max + 1); + last_frame_tick = app_state.current_time - static_cast(std::fmod(delta, 1000.0f / comp.fps)); + } + } + if (draw_viewport) Viewport(comp); if (draw_layer) Layer(comp); if (draw_comp) Composition(comp); diff --git a/Keishiki/VisualTrack.cpp b/Keishiki/VisualTrack.cpp index cb8077e..92d1e7b 100644 --- a/Keishiki/VisualTrack.cpp +++ b/Keishiki/VisualTrack.cpp @@ -40,13 +40,13 @@ namespace K { f32 view[16], f32 transform[16]) { std::shared_lock lk{Resource::resource_lock}; u32 sampler_stage = 0; - for (auto& [_name, handle, res, _u] : samplers) { - bgfx::TextureHandle tx = std::visit([](auto&& arg) { + for (auto& [_name, handle, res, _u, u_frame] : samplers) { + bgfx::TextureHandle tx = std::visit([&s, &u_frame](auto&& arg) { using T = std::decay_t; if constexpr (std::is_same_v*>) return arg->tex; else if constexpr (std::is_same_v) { - i32 slot = arg->GetFrame(0); + i32 slot = arg->GetFrame(std::get(std::get::type>(s.plugboard.out_instance.in[u_frame->connection.index].value))); return arg->composite[slot]; } }, res); @@ -61,6 +61,9 @@ namespace K { bgfx::setViewRect(app_state.render_view, 0, 0, w, h); for (auto& u : uniforms) { + if (u.status & K_U_SamplerFrame) + continue; + f32 pack[4]{}; ShaderGraph::T_Map::type val_target; if (std::visit([](auto&& arg){ return arg == nullptr; }, u.connection.p)) @@ -110,7 +113,7 @@ namespace K { "#include \n" "#include \n"; u32 sampler_loc = 0; - for (auto& [name, _tex, _res, _u] : samplers) { + for (auto& [name, _tex, _res, _u, _uu] : samplers) { f << "SAMPLER2D(" << name << ", " << sampler_loc++ << ");\n"; } for (auto& u : uniforms) { @@ -150,7 +153,8 @@ namespace K { if (bgfx::isValid(pg)) bgfx::destroy(pg); for (auto& uniform : uniforms) { - bgfx::destroy(uniform.handle); + if (!(uniform.status & K_U_SamplerFrame)) + bgfx::destroy(uniform.handle); } uniforms.clear(); for (auto& sampler : samplers) { @@ -164,6 +168,8 @@ namespace K { // this is shaky and bug prone -- relies on the shader types being in line with plugboard types s.plugboard.out_instance.in.push_back(Plugboard::InSocket{ uu.name, Plugboard::Type(uu.val.index()), ShaderValToPlugboard(uu.val)}); s.plugboard.show_nodes.emplace_back(); + + uu.status |= K_U_Exposed; } void VisualTrack::ExposeUniform(CompositionState& s, u32 i) { @@ -171,7 +177,7 @@ namespace K { } void VisualTrack::HideUniform(CompositionState& s, Uniform& uu) { - if (uu.status != K_U_Exposed) + if (!(uu.status & K_U_Exposed)) return; Plugboard::Disconnect(uu.connection.p, uu.connection.index); @@ -188,6 +194,8 @@ namespace K { s.plugboard.out_instance.in.erase(s.plugboard.out_instance.in.begin() + uu.connection.index); uu.connection = {(Plugboard::Add*)(nullptr), 0}; + + uu.status &= ~K_U_Exposed; } void VisualTrack::AddUniform(const String& s, ShaderGraph::T_Map::type&& val, UniformStatus init) { @@ -197,7 +205,10 @@ 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, {{}, 0}, init}); + if (!(init & K_U_SamplerFrame)) + uniforms.emplace_back(Uniform{s, bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val, {{}, 0}, init}); + else + uniforms.emplace_back(Uniform{s, BGFX_INVALID_HANDLE, val, {{}, 0}, init}); } void VisualTrack::AddSampler(const String& s) { @@ -208,6 +219,7 @@ namespace K { if (ss.name == s) return; AddUniform(s + "_dims", ShaderGraph::XY{static_cast(Resource::fallback_still->w), static_cast(Resource::fallback_still->h)}, K_U_SamplerDims); - samplers.emplace_back(Sampler{s, bgfx::createUniform(s.c_str(), bgfx::UniformType::Sampler), Resource::fallback_still, &uniforms.back()}); + AddUniform(s + "_frame", ShaderGraph::T_Map::type{0}, K_U_SamplerFrame); + samplers.emplace_back(Sampler{s, bgfx::createUniform(s.c_str(), bgfx::UniformType::Sampler), Resource::fallback_still, &*std::next(uniforms.rbegin()), &uniforms.back()}); } } diff --git a/Keishiki/include/Keishiki.h b/Keishiki/include/Keishiki.h index 2b4533d..e866884 100644 --- a/Keishiki/include/Keishiki.h +++ b/Keishiki/include/Keishiki.h @@ -24,8 +24,8 @@ namespace K { Vector layers; i32 active = -1; // index for layers - Vector selected; // indices for layers - Vector disabled; // indices for layers + Vector selected; // indices for layers todo use flat_set instead + Vector disabled; // indices for layers todo use flat_set instead Plugboard::Plugboard plugboard; @@ -36,13 +36,16 @@ namespace K { render_fb = BGFX_INVALID_HANDLE; f32 proj[16], transform[16], view[16]; + // Dependence + Vector> depends_on{}; // todo use flat_map instead + void Setup(); i32 GetFrame(u32 frame); void Destroy(); }; struct ProjectState { - CompositionState *inspecting_composition; + CompositionState *inspecting_composition = nullptr; plf::colony compositions{}; }; diff --git a/Keishiki/include/Resource.h b/Keishiki/include/Resource.h index 3f590d8..6258474 100644 --- a/Keishiki/include/Resource.h +++ b/Keishiki/include/Resource.h @@ -84,6 +84,8 @@ namespace K::Resource { bgfx::ProgramHandle FetchUnmanagedShaderProgram(const String& shader_name); + void LoadFile(); + void SaveCurrentProject(); } diff --git a/Keishiki/include/VisualTrack.h b/Keishiki/include/VisualTrack.h index f815a65..4db1542 100644 --- a/Keishiki/include/VisualTrack.h +++ b/Keishiki/include/VisualTrack.h @@ -9,10 +9,10 @@ namespace K { enum UniformStatus { - K_U_Inactive = 0, - K_U_Exposed, - K_U_SamplerDims, - K_U_Count + K_U_Inactive = 0, + K_U_Exposed = 1 << 1, + K_U_SamplerDims = 1 << 2, + K_U_SamplerFrame = 1 << 3 // "fake" frame -- since shader does not care about the frame of the image }; struct Uniform { @@ -20,14 +20,14 @@ namespace K { bgfx::UniformHandle handle; ShaderGraph::T_Map::type val; Plugboard::ConnectInfo connection; - UniformStatus status = K_U_Inactive; + i32 status = K_U_Inactive; // enum UniformStatus }; struct Sampler { String name; bgfx::UniformHandle handle; std::variant*, CompositionState*> resource; - Uniform *dims; + Uniform *dims, *frame; }; ShaderGraph::T_Map::type PlugboardValToShader(const Plugboard::T_Map::type& val); diff --git a/TODO.md b/TODO.md index 36f749c..68c20d7 100644 --- a/TODO.md +++ b/TODO.md @@ -7,7 +7,6 @@ - Blender previews resource - Data models - Dump and read back state, (de)serialization!!! - - Pre-compose/Layer Groups - Undo's - Node groups