finally fixed the timeline

This commit is contained in:
lachrymaLF 2024-06-28 19:34:07 -04:00
parent cacc2d48ca
commit 25468bad25
7 changed files with 205 additions and 146 deletions

View file

@ -1,4 +1,5 @@
#include "Graphics.h" #include "Graphics.h"
#include <Keishiki.h>
#include <Resource.h> #include <Resource.h>
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
@ -351,15 +352,15 @@ namespace K::Graphics {
return pos_x; return pos_x;
} }
void Composite(u32 view_id, bgfx::FrameBufferHandle fb, bgfx::TextureHandle composite, bgfx::TextureHandle from, Blending mode, u16 w, u16 h, f32 proj[16], f32 transform[16]) { void Composite(bgfx::FrameBufferHandle fb, bgfx::TextureHandle composite, bgfx::TextureHandle from, Blending mode, u16 w, u16 h, f32 proj[16], f32 transform[16]) {
static f32 pack[4]{}; static f32 pack[4]{};
pack[3] = pack[2] = pack[1] = pack[0] = static_cast<f32>(mode); pack[3] = pack[2] = pack[1] = pack[0] = static_cast<f32>(mode);
bgfx::setViewMode(view_id, bgfx::ViewMode::Sequential); bgfx::setViewMode(app_state.render_view, bgfx::ViewMode::Sequential);
bgfx::setViewClear(view_id, BGFX_CLEAR_COLOR); bgfx::setViewClear(app_state.render_view, BGFX_CLEAR_COLOR);
bgfx::setViewFrameBuffer(view_id, fb); bgfx::setViewFrameBuffer(app_state.render_view, fb);
bgfx::setViewTransform(view_id, composite_view, proj); bgfx::setViewTransform(app_state.render_view, composite_view, proj);
bgfx::setViewRect(view_id, 0, 0, w, h); bgfx::setViewRect(app_state.render_view, 0, 0, w, h);
bgfx::setTransform(transform); bgfx::setTransform(transform);
bgfx::setVertexBuffer(0, vbh); bgfx::setVertexBuffer(0, vbh);
@ -368,6 +369,6 @@ namespace K::Graphics {
bgfx::setTexture(1, composite_B, composite); // B bgfx::setTexture(1, composite_B, composite); // B
bgfx::setUniform(composite_mode, pack); // A over B bgfx::setUniform(composite_mode, pack); // A over B
bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A); bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
bgfx::submit(view_id, composite_pg); bgfx::submit(app_state.render_view++, composite_pg);
} }
} }

View file

@ -46,9 +46,9 @@ namespace K {
if (std::ranges::find(disabled, i) != disabled.end()) continue; if (std::ranges::find(disabled, i) != disabled.end()) continue;
if (current_frame > layers[i].out || current_frame < layers[i].in) continue; if (current_frame > layers[i].out || current_frame < layers[i].in) continue;
layers[i].track.GetFrame(*this, app_state.render_view++, render_fb, width, layers[i].track.GetFrame(*this, render_fb, width,
height, proj, view, transform); height, proj, view, transform);
Graphics::Composite(app_state.render_view++, composite_fb[(layers_done + 1) % 2], composite[layers_done % 2], render, layers[i].mode, width, height, proj, transform); Graphics::Composite(composite_fb[(layers_done + 1) % 2], composite[layers_done % 2], render, layers[i].mode, width, height, proj, transform);
layers_done++; layers_done++;
} }
@ -57,6 +57,10 @@ namespace K {
} }
void CompositionState::Destroy() { void CompositionState::Destroy() {
for (auto& layer : layers) {
layer.track.Clear();
}
bgfx::destroy(composite_fb[0]); bgfx::destroy(composite_fb[0]);
bgfx::destroy(composite_fb[1]); bgfx::destroy(composite_fb[1]);
bgfx::destroy(composite[0]); bgfx::destroy(composite[0]);

View file

@ -30,7 +30,7 @@ namespace {
draw_interpolation = true, draw_interpolation = true,
draw_assets = true; draw_assets = true;
const f32 row_height = 20.0f; constexpr f32 row_height = 26.0f;
// Viewport // Viewport
K::VisualTrack bg{}; K::VisualTrack bg{};
@ -41,6 +41,15 @@ namespace {
K::Byte *save_buffer; K::Byte *save_buffer;
K::CompositionState *comp_save_called = nullptr; K::CompositionState *comp_save_called = nullptr;
u32 ready_frame; u32 ready_frame;
enum DeleteRequest {
K_DR_Selected_Compositions,
K_DR_Selected_Layers,
K_DR_Selected_Keys,
K_DR_Selected_Nodes,
K_DR_Count
};
bool delete_requests[K_DR_Count]{};
} }
@ -252,7 +261,7 @@ namespace K::UI {
template <Plugboard::Node N> template <Plugboard::Node N>
void PlugboardDrawNode(CompositionState& s, N& n, ImVec2& view_pos, ImGuiIO& io, ImGuiStyle& style, void PlugboardDrawNode(CompositionState& s, N& n, ImVec2& view_pos, ImGuiIO& io, ImGuiStyle& style,
bool& dragging_on_socket, ImVec2& source, bool& delete_request, Dict<Plugboard::ConnectInfo, Plugboard::LinksFromSource, Plugboard::ConnectInfoHasher>& links_pos) { bool& dragging_on_socket, ImVec2& source, Dict<Plugboard::ConnectInfo, Plugboard::LinksFromSource, Plugboard::ConnectInfoHasher>& links_pos) {
Plugboard::NodeInstanceP ptr{&n}; Plugboard::NodeInstanceP ptr{&n};
bool selected = std::ranges::find(s.plugboard.selected_nodes, ptr) != s.plugboard.selected_nodes.end(); bool selected = std::ranges::find(s.plugboard.selected_nodes, ptr) != s.plugboard.selected_nodes.end();
const auto& pos = n.pos; const auto& pos = n.pos;
@ -270,7 +279,7 @@ namespace K::UI {
ImGui::Button(name, {100.0f , 0.0f}); ImGui::Button(name, {100.0f , 0.0f});
if (ImGui::BeginPopupContextItem()) { if (ImGui::BeginPopupContextItem()) {
if (!s.plugboard.selected_nodes.empty() && ImGui::Button("Delete")) { if (!s.plugboard.selected_nodes.empty() && ImGui::Button("Delete")) {
delete_request = true; delete_requests[K_DR_Selected_Nodes] = true;
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::EndPopup(); ImGui::EndPopup();
@ -337,9 +346,9 @@ namespace K::UI {
bgfx::TextureHandle present = BGFX_INVALID_HANDLE; bgfx::TextureHandle present = BGFX_INVALID_HANDLE;
if (do_bg) { if (do_bg) {
bg.GetFrame(s, app_state.render_view++, s.render_fb, s.width, s.height, s.proj, s.view, s.transform); bg.GetFrame(s, s.render_fb, s.width, s.height, s.proj, s.view, s.transform);
Graphics::Composite(app_state.render_view++, s.composite_fb[1 - result_index], Graphics::Composite(s.composite_fb[1 - result_index],
s.render, s.composite[result_index], Graphics::K_V_AlphaOver, s.width, s.height, s.render, s.composite[result_index], Graphics::K_V_AlphaOver, s.width, s.height,
s.proj, s.transform); s.proj, s.transform);
present = s.composite[1 - result_index]; present = s.composite[1 - result_index];
@ -392,51 +401,6 @@ namespace K::UI {
}; };
void Composition(CompositionState& s) { void Composition(CompositionState& s) {
static bool layer_delete_requested = false;
if (layer_delete_requested) {
if (std::ranges::find(s.selected, s.active) != s.selected.end()) // unset active if active is selected
s.active = -1;
u32 deleted = 0, before_active = 0; // stupid counting tricks
const u32 sz = s.layers.size();
for (u32 j = 0; j < sz; j++) { // im sure this isn't the best way to go about this
if (std::ranges::find(s.selected, j) != s.selected.end()) {
s.layers[j - deleted].track.Clear();
for (auto& u : s.layers[j - deleted].track.uniforms)
VisualTrack::HideUniform(s, u);
s.layers.erase(s.layers.begin() + j - deleted);
std::erase(s.disabled, j);
if (static_cast<i32>(j) < s.active)
before_active++;
deleted++;
}
else if (auto it = std::ranges::find(s.disabled, j); it != s.disabled.end())
*it -= deleted;
}
s.selected.clear();
if (s.active != -1)
s.active -= static_cast<i32>(before_active);
layer_delete_requested = false;
}
static bool node_delete_requested = false;
if (node_delete_requested) {
for (auto p : s.plugboard.selected_nodes) {
std::visit([&s](auto&& arg){
for (u32 i = 0; i < arg->in.size(); i++)
Plugboard::Disconnect(arg, i);
for (auto& socket : arg->out)
for (Plugboard::ConnectInfo& connection : socket.outgoing)
Plugboard::Disconnect(connection.p, connection.index);
s.plugboard.nodes.Remove(arg);
}, p);
s.plugboard.RecollectChains();
}
s.plugboard.selected_nodes.clear();
node_delete_requested = false;
}
if (!ImGui::Begin("Composition", nullptr, ImGuiWindowFlags_NoScrollbar)) { if (!ImGui::Begin("Composition", nullptr, ImGuiWindowFlags_NoScrollbar)) {
ImGui::End(); ImGui::End();
return; return;
@ -462,7 +426,6 @@ namespace K::UI {
if (ImGui::Selectable(s.plugboard.nodes.GetName(static_cast<Plugboard::K_P_Nodes>(n)), is_selected)) if (ImGui::Selectable(s.plugboard.nodes.GetName(static_cast<Plugboard::K_P_Nodes>(n)), is_selected))
node_current = n; node_current = n;
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if (is_selected) if (is_selected)
ImGui::SetItemDefaultFocus(); ImGui::SetItemDefaultFocus();
} }
@ -536,7 +499,7 @@ namespace K::UI {
(([&](auto&& arg){ (([&](auto&& arg){
for (auto& node : arg) { for (auto& node : arg) {
PlugboardDrawNode<std::decay_t<decltype(node)>>(s, node, nodes_view_pos, io, style, dragging_on_socket, PlugboardDrawNode<std::decay_t<decltype(node)>>(s, node, nodes_view_pos, io, style, dragging_on_socket,
drag_source, node_delete_requested, links_pos); drag_source, links_pos);
} }
}(args)), ...); }(args)), ...);
}, s.plugboard.nodes.nodes); }, s.plugboard.nodes.nodes);
@ -583,7 +546,7 @@ namespace K::UI {
static Vector<Plugboard::ChainSegment*> keys_selected_by_bg_drag{}; static Vector<Plugboard::ChainSegment*> 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) { 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 f32 pan_x, view_left_old, view_right_old;
ImGui::InvisibleButton("##TL_BG", ImVec2{ w == 0.0f ? view_width + style.CellPadding.x * 2 : w, h == 0.0f ? row_height + style.CellPadding.y : h}); ImGui::InvisibleButton("##TL_BG", ImVec2{ w == 0.0f ? view_width + style.CellPadding.x * 2 : w, h == 0.0f ? row_height : h});
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
tl_clear_selection_request = !(io.KeyCtrl || io.KeyShift); tl_clear_selection_request = !(io.KeyCtrl || io.KeyShift);
bg_drag_select_active = !io.KeyShift; bg_drag_select_active = !io.KeyShift;
@ -616,14 +579,7 @@ namespace K::UI {
} }
} }
if (ImGui::IsKeyPressed(ImGuiKey_Delete) && ImGui::IsItemHovered()) { if (ImGui::IsKeyPressed(ImGuiKey_Delete) && ImGui::IsItemHovered()) {
for (auto& chain : s.plugboard.selected_nodes) { delete_requests[K_DR_Selected_Keys] = true;
if (auto *c = std::get_if<Plugboard::Chain*>(&chain)) {
auto& [segments, selected] = (*c)->extra.chain;
for (u32 i : selected)
segments.erase(segments.begin() + i);
selected.clear();
}
}
} }
}; };
@ -681,6 +637,10 @@ namespace K::UI {
} }
}; };
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{});
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2{});
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2, 0});
if (ImGui::BeginTable("Layers", 5, ImGuiTableFlags_BordersInner | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { if (ImGui::BeginTable("Layers", 5, ImGuiTableFlags_BordersInner | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_IndentDisable); ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_IndentDisable);
@ -690,18 +650,13 @@ namespace K::UI {
ImGui::TableSetupColumn("##Timeline", ImGui::TableSetupColumn("##Timeline",
ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch); ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers, row_height); ImGui::TableNextRow(ImGuiTableRowFlags_Headers, view_height);
for (int column = 0; column < 4; column++) { for (int column = 0; column < 4; column++) {
ImGui::TableSetColumnIndex(column); ImGui::TableSetColumnIndex(column);
ImGui::TableHeader(ImGui::TableGetColumnName(column)); ImGui::TableHeader(ImGui::TableGetColumnName(column));
} }
// Timeline controls // Timeline controls
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{});
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2{});
ImGui::TableSetColumnIndex(4); ImGui::TableSetColumnIndex(4);
auto *window = ImGui::GetCurrentWindow(); auto *window = ImGui::GetCurrentWindow();
@ -765,9 +720,8 @@ 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_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); {tl_init_pos.x + view_width * view_right, tl_init_pos.y + view_height * .8f}, 0x33FFFFFF, 2.0f);
ImGui::PopStyleVar(3);
ImGui::SameLine(); ImGui::SameLine();
ImGui::TableHeader(""); ImGui::TableHeader(""); // TL header
// Main rows // Main rows
i32 move_from = -1, move_to = -1; i32 move_from = -1, move_to = -1;
@ -798,16 +752,15 @@ namespace K::UI {
auto flags = base_flags; auto flags = base_flags;
if (selected) if (selected)
flags |= ImGuiTreeNodeFlags_Selected; flags |= ImGuiTreeNodeFlags_Selected;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {0.0f, (row_height - ImGui::GetTextLineHeight()) / 2.0f});
{0.0f, (row_height - ImGui::GetTextLineHeight()) / 2.0f});
bool layer_open = ImGui::TreeNodeEx(current_layer.name.c_str(), flags); bool layer_open = ImGui::TreeNodeEx(current_layer.name.c_str(), flags);
if (ImGui::BeginPopupContextItem()) { if (ImGui::BeginPopupContextItem()) {
if (!s.selected.empty()) { if (!s.selected.empty()) {
if (ImGui::Button("Delete")) { if (ImGui::Button("Delete")) {
layer_delete_requested = true; delete_requests[K_DR_Selected_Layers] = true;
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
if (ImGui::Button("Make Sub-composition")) { if (ImGui::Button("Make Subcomposition")) {
CompositionState new_comp{.width = s.width, .height = s.height}; CompositionState new_comp{.width = s.width, .height = s.height};
u32 index_in_new_comp = 0; u32 index_in_new_comp = 0;
u64 frame_min = -1, frame_max = 0; u64 frame_min = -1, frame_max = 0;
@ -822,8 +775,8 @@ namespace K::UI {
new_comp.frame_max = frame_max - frame_min; new_comp.frame_max = frame_max - frame_min;
for (auto& layer : new_comp.layers) for (auto& layer : new_comp.layers)
layer.in -= frame_min; layer.in -= frame_min;
layer_delete_requested = true; delete_requests[K_DR_Selected_Layers] = true;
app_state.project.compositions.push_back(std::move(new_comp)); app_state.project.compositions.insert(std::move(new_comp));
/* todo make comp */ /* todo make comp */
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
@ -900,10 +853,6 @@ namespace K::UI {
f32 init_y = ImGui::GetCursorScreenPos().y; f32 init_y = ImGui::GetCursorScreenPos().y;
if (!show_curve_editor) { if (!show_curve_editor) {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{});
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2{});
const static f32 layer_bound_width = 5.0f; const static f32 layer_bound_width = 5.0f;
const bool l_le_right_extr = const bool l_le_right_extr =
static_cast<f32>(current_layer.in) / static_cast<f32>(s.frame_max + 1) <= view_right, static_cast<f32>(current_layer.in) / static_cast<f32>(s.frame_max + 1) <= view_right,
@ -923,7 +872,7 @@ namespace K::UI {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + in_pos); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + in_pos);
static f32 l_in_old; static f32 l_in_old;
ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666); ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666);
ImGui::Button("##Layer_L", {layer_bound_width, view_height}); ImGui::Button("##Layer_L", {layer_bound_width, row_height});
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
l_in_old = in_pos; l_in_old = in_pos;
} }
@ -943,7 +892,7 @@ namespace K::UI {
if (l_le_right_extr && l_ge_left_extr) { if (l_le_right_extr && l_ge_left_extr) {
ImGui::Button(current_layer.name.c_str(), ImGui::Button(current_layer.name.c_str(),
{std::min(out_pos + fr_step, view_width) - static_cast<f32>(l_in) * in_pos - {std::min(out_pos + fr_step, view_width) - static_cast<f32>(l_in) * in_pos -
layer_bound_width * static_cast<f32>(l_in + l_out), view_height}); layer_bound_width * static_cast<f32>(l_in + l_out), row_height});
static u32 in_old, out_old; static u32 in_old, out_old;
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
in_old = current_layer.in; in_old = current_layer.in;
@ -960,7 +909,7 @@ namespace K::UI {
static f32 r_out_old; static f32 r_out_old;
ImGui::SameLine(0.0f, 0.0f); ImGui::SameLine(0.0f, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666); ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666);
ImGui::Button("##Layer_R", {layer_bound_width, view_height}); ImGui::Button("##Layer_R", {layer_bound_width, row_height});
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
r_out_old = out_pos; r_out_old = out_pos;
} }
@ -976,18 +925,11 @@ namespace K::UI {
} }
ImGui::PopStyleColor(); ImGui::PopStyleColor();
} }
ImGui::SetCursorScreenPos(ImVec2{tl_init_pos.x, init_y});
tl_bg_handler();
ImGui::PopID(); ImGui::PopID();
ImGui::SetCursorScreenPos(ImVec2{tl_init_pos.x, init_y} - style.CellPadding);
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(mouse_tl_x, 0.0f, view_width),
s.frame_max);
}
ImGui::PopStyleVar(3);
} }
if (layer_open) { if (layer_open) {
@ -1309,6 +1251,7 @@ namespace K::UI {
ImGui::EndTable(); ImGui::EndTable();
} }
ImGui::PopStyleVar(4);
ImGui::SetCursorScreenPos(nodes_begin); ImGui::SetCursorScreenPos(nodes_begin);
if (ImGui::BeginChild("Node Connection Overlay", {table_left - ImGui::GetWindowPos().x, 0.0f}, false, ImGuiWindowFlags_NoInputs)) { if (ImGui::BeginChild("Node Connection Overlay", {table_left - ImGui::GetWindowPos().x, 0.0f}, false, ImGuiWindowFlags_NoInputs)) {
@ -1331,7 +1274,7 @@ namespace K::UI {
} }
ImGui::EndChild(); ImGui::EndChild();
ImVec2 tl_grid_cursor_pos = {tl_init_pos.x, tl_init_pos.y + row_height}; ImVec2 tl_grid_cursor_pos = {tl_init_pos.x, tl_init_pos.y + view_height};
if (show_curve_editor) { if (show_curve_editor) {
ImGui::SetCursorScreenPos(tl_grid_cursor_pos); ImGui::SetCursorScreenPos(tl_grid_cursor_pos);
ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF111111); ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF111111);
@ -1526,10 +1469,7 @@ namespace K::UI {
static_cast<f32>(s.frame_max + 1), tl_init_pos.y}, static_cast<f32>(s.frame_max + 1), tl_init_pos.y},
{tl_init_pos.x + view_width * static_cast<f32>(s.current_frame) / {tl_init_pos.x + view_width * static_cast<f32>(s.current_frame) /
static_cast<f32>(s.frame_max + 1), static_cast<f32>(s.frame_max + 1),
tl_init_pos.y + view_height}, 0xFF3333FF, 2.0f); tl_init_pos.y}, 0xFF3333FF, 2.0f);
ImGui::SetCursorScreenPos(tl_init_pos - style.CellPadding);
ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, row_height + style.CellPadding.y});
// Frame Grid // Frame Grid
static f32 frame_grid_min_width = 20.0f; static f32 frame_grid_min_width = 20.0f;
@ -1539,14 +1479,14 @@ namespace K::UI {
std::ceil(view_left * static_cast<f32>(s.frame_max + 1)), std::ceil(view_left * static_cast<f32>(s.frame_max + 1)),
s.frame_max) + tl_init_pos.x; s.frame_max) + tl_init_pos.x;
fr < tl_init_pos.x + view_width; fr += grid_step) fr < tl_init_pos.x + view_width; fr += grid_step)
window->DrawList->AddLine({fr, tl_init_pos.y + row_height}, window->DrawList->AddLine({fr, tl_init_pos.y},
{fr, ImGui::GetWindowSize().y + tl_init_pos.y}, 0x11FFFFFF, 1.0f); {fr, ImGui::GetWindowSize().y + tl_init_pos.y}, 0x11FFFFFF, 1.0f);
if (TimelineFrameInView(view_left, view_right, s.current_frame, s.frame_max)) { 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) + f32 fr = TimelineFrameToScreenView(view_left, view_amt, view_width, s.current_frame, s.frame_max) +
tl_init_pos.x; tl_init_pos.x;
window->DrawList->AddLine( window->DrawList->AddLine(
{fr, tl_init_pos.y + row_height}, {fr, tl_init_pos.y},
{fr, ImGui::GetWindowSize().y + tl_init_pos.y}, 0x773333FF, 1.0f); {fr, ImGui::GetWindowSize().y + tl_init_pos.y}, 0x773333FF, 1.0f);
} }
if (bg_drag_select_active) { if (bg_drag_select_active) {
@ -1683,15 +1623,37 @@ namespace K::UI {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s%s", it->name.c_str(), "_dims"); ImGui::Text("%s%s", it->name.c_str(), "_dims");
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::BeginCombo("##source", it->resource->filename.c_str())) { const char *r_name = std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, Resource::Resource<Resource::K_R_Still>*>) {
return arg->filename.c_str();
}
else if constexpr (std::is_same_v<T, CompositionState*>) {
return arg->name.c_str();
}
}, it->resource);
if (ImGui::BeginCombo("##source", r_name)) {
for (auto& [file, res_v] : Resource::resources) for (auto& [file, res_v] : Resource::resources)
if (auto *res = std::get_if<Resource::Resource<Resource::K_R_Still>>(&res_v)) { if (auto *res = std::get_if<Resource::Resource<Resource::K_R_Still>>(&res_v)) {
const bool is_selected = it->resource == res; const bool is_selected = r_name == res->filename.c_str(); // lol
if (ImGui::Selectable(file.c_str(), is_selected)) { if (ImGui::Selectable(file.c_str(), is_selected)) {
it->resource = res; it->resource = res;
it->dims->val = ShaderGraph::XY{static_cast<f32>(res->w), static_cast<f32>(res->h)}; it->dims->val = ShaderGraph::XY{static_cast<f32>(res->w), static_cast<f32>(res->h)};
} }
if (is_selected)
ImGui::SetItemDefaultFocus();
} }
for (auto& comp : app_state.project.compositions) {
const bool is_selected = r_name == comp.name.c_str(); // lol
if (ImGui::Selectable(comp.name.c_str(), is_selected)) {
it->resource = &comp;
it->dims->val = ShaderGraph::XY{static_cast<f32>(comp.width), static_cast<f32>(comp.height)};
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo(); ImGui::EndCombo();
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -1876,8 +1838,7 @@ namespace K::UI {
} }
if (ImGui::Button("OK", ImVec2(120, 0)) && !name.empty()) { if (ImGui::Button("OK", ImVec2(120, 0)) && !name.empty()) {
app_state.project.compositions.emplace_back(name, 0, frames, fps, dims[0], dims[1]); app_state.project.compositions.emplace(name, 0, frames, fps, dims[0], dims[1])->Setup();
app_state.project.compositions.back().Setup();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::SetItemDefaultFocus(); ImGui::SetItemDefaultFocus();
@ -1897,23 +1858,22 @@ namespace K::UI {
ImGui::TableSetupColumn("FPS", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("FPS", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
for (u32 j = 0; j < app_state.project.compositions.size(); j++) { for (auto& comp : app_state.project.compositions) {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::PushID(j); ImGui::PushID(&comp);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
auto& comp = app_state.project.compositions[j]; bool sel = app_state.project.inspecting_composition == &comp;
bool sel = app_state.project.inspecting_composition == j;
if (ImGui::Selectable(comp.name.c_str(), sel, ImGuiSelectableFlags_SpanAllColumns) && !sel) { if (ImGui::Selectable(comp.name.c_str(), sel, ImGuiSelectableFlags_SpanAllColumns) && !sel) {
app_state.project.inspecting_composition = static_cast<i32>(j); app_state.project.inspecting_composition = &comp;
bg.uniforms[0].val = ShaderGraph::XYZ{ bg.uniforms[0].val = ShaderGraph::XYZ{
static_cast<f32>(app_state.project.compositions[app_state.project.inspecting_composition].width), static_cast<f32>(comp.width),
static_cast<f32>(app_state.project.compositions[app_state.project.inspecting_composition].height), static_cast<f32>(comp.height),
0.0f }; 0.0f };
} }
if (sel && ImGui::BeginPopupContextItem()) { if (sel && ImGui::BeginPopupContextItem()) {
if (ImGui::Button("Delete")) { if (ImGui::Button("Delete")) {
/* todo this is gonna be rough... */ delete_requests[K_DR_Selected_Compositions] = true;
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::EndPopup(); ImGui::EndPopup();
@ -2013,6 +1973,88 @@ namespace K::UI {
ImGui::End(); ImGui::End();
} }
void ProcessDeleteRequests(CompositionState& s) {
if (delete_requests[K_DR_Selected_Compositions]) {
decltype(app_state.project.compositions)::iterator it = app_state.project.compositions.end();
// comp deletions should be rare, so this should be fine -- change to cache if this becomes a problem
for (auto itt = app_state.project.compositions.begin(); itt != app_state.project.compositions.end(); itt++) {
if (&(*itt) == &s) {
it = itt;
continue;
}
for (auto& layer : itt->layers)
for (auto& sampler : layer.track.samplers) {
sampler.resource = Resource::fallback_still;
sampler.dims->val = ShaderGraph::T_Map<ShaderGraph::T_XY>::type{
static_cast<f32>(Resource::fallback_still->w),
static_cast<f32>(Resource::fallback_still->h)
};
}
}
it->Destroy();
if (app_state.project.inspecting_composition == &(*it))
app_state.project.inspecting_composition = nullptr;
app_state.project.compositions.erase(it);
delete_requests[K_DR_Selected_Compositions] = false;
}
if (delete_requests[K_DR_Selected_Layers]) {
if (std::ranges::find(s.selected, s.active) != s.selected.end()) // unset active if active is selected
s.active = -1;
u32 deleted = 0, before_active = 0; // stupid counting tricks
const u32 sz = s.layers.size();
for (u32 j = 0; j < sz; j++) { // im sure this isn't the best way to go about this
if (std::ranges::find(s.selected, j) != s.selected.end()) {
s.layers[j - deleted].track.Clear();
for (auto &u: s.layers[j - deleted].track.uniforms)
VisualTrack::HideUniform(s, u);
s.layers.erase(s.layers.begin() + j - deleted);
std::erase(s.disabled, j);
if (static_cast<i32>(j) < s.active)
before_active++;
deleted++;
} else if (auto it = std::ranges::find(s.disabled, j); it != s.disabled.end())
*it -= deleted;
}
s.selected.clear();
if (s.active != -1)
s.active -= static_cast<i32>(before_active);
delete_requests[K_DR_Selected_Layers] = false;
}
if (delete_requests[K_DR_Selected_Nodes]) {
for (auto p: s.plugboard.selected_nodes) {
std::visit([&s](auto &&arg) {
for (u32 i = 0; i < arg->in.size(); i++)
Plugboard::Disconnect(arg, i);
for (auto &socket: arg->out)
for (Plugboard::ConnectInfo &connection: socket.outgoing)
Plugboard::Disconnect(connection.p, connection.index);
s.plugboard.nodes.Remove(arg);
}, p);
s.plugboard.RecollectChains();
}
s.plugboard.selected_nodes.clear();
delete_requests[K_DR_Selected_Nodes] = false;
}
if (delete_requests[K_DR_Selected_Keys]) {
for (auto& chain : s.plugboard.selected_nodes) {
if (auto *c = std::get_if<Plugboard::Chain*>(&chain)) {
auto& [segments, selected] = (*c)->extra.chain;
for (u32 i : selected)
segments.erase(segments.begin() + i);
selected.clear();
}
}
}
}
void Draw() { void Draw() {
ImGui_ImplSDL3_NewFrame(); ImGui_ImplSDL3_NewFrame();
ImGui_Implbgfx_NewFrame(); ImGui_Implbgfx_NewFrame();
@ -2027,8 +2069,8 @@ namespace K::UI {
if (draw_assets) Assets(); if (draw_assets) Assets();
if (app_state.project.inspecting_composition >= 0) { if (app_state.project.inspecting_composition != nullptr) {
auto &comp = app_state.project.compositions[app_state.project.inspecting_composition]; auto &comp = *app_state.project.inspecting_composition;
if (draw_viewport) Viewport(comp); if (draw_viewport) Viewport(comp);
if (draw_layer) Layer(comp); if (draw_layer) Layer(comp);
if (draw_comp) Composition(comp); if (draw_comp) Composition(comp);
@ -2040,6 +2082,8 @@ namespace K::UI {
static_cast<i32>(comp_save_called->width) * 4); static_cast<i32>(comp_save_called->width) * 4);
comp_save_called = nullptr; comp_save_called = nullptr;
} }
ProcessDeleteRequests(comp);
} }
ImGui::Render(); ImGui::Render();
@ -2125,11 +2169,11 @@ namespace K::UI {
bgfx::destroy(bg.uniforms[0].handle); bgfx::destroy(bg.uniforms[0].handle);
bg.uniforms.erase(bg.uniforms.begin()); bg.uniforms.erase(bg.uniforms.begin());
for (auto& comp : app_state.project.compositions) for (auto& comp : app_state.project.compositions) {
for (auto& layer : comp.layers) for (auto& layer : comp.layers)
layer.track.Clear(); layer.track.Clear();
comp.Destroy();
app_state.project.compositions[app_state.project.inspecting_composition].Destroy(); }
ImGui_Implbgfx_Shutdown(); ImGui_Implbgfx_Shutdown();
ImGui_ImplSDL3_Shutdown(); ImGui_ImplSDL3_Shutdown();

View file

@ -36,14 +36,29 @@ namespace K {
},val); },val);
} }
void VisualTrack::GetFrame(const CompositionState& s, u32 view_id, bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16], void VisualTrack::GetFrame(const CompositionState& s, bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16],
f32 view[16], f32 transform[16]) { f32 view[16], f32 transform[16]) {
bgfx::setViewMode(view_id, bgfx::ViewMode::Sequential); std::shared_lock lk{Resource::resource_lock};
bgfx::setViewClear(view_id, BGFX_CLEAR_COLOR); u32 sampler_stage = 0;
bgfx::setViewFrameBuffer(view_id, fb); for (auto& [_name, handle, res, _u] : samplers) {
bgfx::TextureHandle tx = std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, Resource::Resource<Resource::K_R_Still>*>)
return arg->tex;
else if constexpr (std::is_same_v<T, CompositionState*>) {
i32 slot = arg->GetFrame(0);
return arg->composite[slot];
}
}, res);
bgfx::setTexture(sampler_stage++, handle, tx, BGFX_SAMPLER_UVW_BORDER);
}
bgfx::setViewTransform(view_id, view, proj); bgfx::setViewMode(app_state.render_view, bgfx::ViewMode::Sequential);
bgfx::setViewRect(view_id, 0, 0, w, h); bgfx::setViewClear(app_state.render_view, BGFX_CLEAR_COLOR);
bgfx::setViewFrameBuffer(app_state.render_view, fb);
bgfx::setViewTransform(app_state.render_view, view, proj);
bgfx::setViewRect(app_state.render_view, 0, 0, w, h);
for (auto& u : uniforms) { for (auto& u : uniforms) {
f32 pack[4]{}; f32 pack[4]{};
@ -56,8 +71,8 @@ namespace K {
if constexpr (std::is_same_v<T, Plugboard::ConnectInfo>) { if constexpr (std::is_same_v<T, Plugboard::ConnectInfo>) {
bool good; bool good;
val_target = PlugboardValToShader(Plugboard::ConvertValue(Plugboard::Eval(s, arg), val_target = PlugboardValToShader(Plugboard::ConvertValue(Plugboard::Eval(s, arg),
static_cast<Plugboard::Type>(u.val.index()), // todo DANGEROUS !! static_cast<Plugboard::Type>(u.val.index()), // todo DANGEROUS !!
good)); good));
} }
else if constexpr (std::is_same_v<T, Plugboard::T_Map<Plugboard::T_Count>::type>) else if constexpr (std::is_same_v<T, Plugboard::T_Map<Plugboard::T_Count>::type>)
val_target = PlugboardValToShader(arg); val_target = PlugboardValToShader(arg);
@ -86,13 +101,7 @@ namespace K {
bgfx::setUniform(u.handle, pack); bgfx::setUniform(u.handle, pack);
} }
std::shared_lock lk{Resource::resource_lock}; Graphics::DrawQuad(app_state.render_view++, transform, BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A, pg);
u32 sampler_stage = 0;
for (auto& [_name, handle, res, _u] : samplers) {
bgfx::setTexture(sampler_stage++, handle, res->tex, BGFX_SAMPLER_UVW_BORDER);
}
Graphics::DrawQuad(view_id, transform, BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A, pg);
} }
void VisualTrack::Compile() { void VisualTrack::Compile() {

View file

@ -117,7 +117,7 @@ namespace K::Graphics {
"Invalid" "Invalid"
}; };
void Composite(u32 view_id, bgfx::FrameBufferHandle fb, bgfx::TextureHandle composite, bgfx::TextureHandle from, Blending mode, u16 w, u16 h, f32 proj[16], f32 transform[16]); void Composite(bgfx::FrameBufferHandle fb, bgfx::TextureHandle composite, bgfx::TextureHandle from, Blending mode, u16 w, u16 h, f32 proj[16], f32 transform[16]);
constexpr static bool CubicRealAccept(f64 t) { constexpr static bool CubicRealAccept(f64 t) {
return 0.0 <= t && t <= 1.0; return 0.0 <= t && t <= 1.0;

View file

@ -3,6 +3,7 @@
#include "Plugboard.h" #include "Plugboard.h"
#include "VisualTrack.h" #include "VisualTrack.h"
#include <atomic> #include <atomic>
#include <plf_colony.h>
namespace K { namespace K {
struct Layer { struct Layer {
@ -41,8 +42,8 @@ namespace K {
}; };
struct ProjectState { struct ProjectState {
i32 inspecting_composition = -1; CompositionState *inspecting_composition;
Vector<CompositionState> compositions{}; plf::colony<CompositionState> compositions{};
}; };
extern struct AppState { extern struct AppState {

View file

@ -26,7 +26,7 @@ namespace K {
struct Sampler { struct Sampler {
String name; String name;
bgfx::UniformHandle handle; bgfx::UniformHandle handle;
Resource::Resource<Resource::K_R_Still> *resource; std::variant<Resource::Resource<Resource::K_R_Still>*, CompositionState*> resource;
Uniform *dims; Uniform *dims;
}; };
@ -40,7 +40,7 @@ namespace K {
String shader; String shader;
Vector<Uniform> uniforms; Vector<Uniform> uniforms;
Vector<Sampler> samplers; Vector<Sampler> samplers;
void GetFrame(const CompositionState& s, u32 view_id, bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16], void GetFrame(const CompositionState& s, bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16],
f32 view[16], f32 transform[16]); f32 view[16], f32 transform[16]);
static void ExposeUniform(CompositionState& s, Uniform& uu); static void ExposeUniform(CompositionState& s, Uniform& uu);
void ExposeUniform(CompositionState& s, u32 i); void ExposeUniform(CompositionState& s, u32 i);