ui! ui! ui! ui!

This commit is contained in:
lachrymaLF 2024-06-29 10:34:33 -04:00
parent 25468bad25
commit 4b7e5e3018
8 changed files with 232 additions and 93 deletions

View file

@ -26,8 +26,8 @@ namespace K {
i32 CompositionState::GetFrame(u32 frame) { i32 CompositionState::GetFrame(u32 frame) {
if (frame > frame_max) { if (frame > frame_max) {
Log(K_L_Error, "Comp {} frame out of range! Requesting {}, max {}", name, frame, frame_max); Log(K_L_Warning, "Comp {} frame out of range! Requesting {}, max {}", name, frame, frame_max);
return -1; frame = frame_max;
} }
u64 tmp = current_frame; u64 tmp = current_frame;

View file

@ -16,10 +16,40 @@ struct glz::meta<ImVec2> {
namespace K::Resource { namespace K::Resource {
void SaveCurrentProject() { void SaveCurrentProject() {
auto ec = glz::write_file_json("soon!", "./temp.json", String{}); 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) { if (ec.ec != glz::error_code::none) {
Log(K_L_Error, "{}", ec.includer_error); 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); 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<K_R_Still>(*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() { void HotReload() {
while (app_state.running.load()) { while (app_state.running.load()) {
for (auto& [path, resource] : resources) for (auto& [path, resource] : resources)

View file

@ -324,17 +324,11 @@ namespace K::UI {
} }
void Viewport(CompositionState& s) { void Viewport(CompositionState& s) {
if (playing) { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{});
if (static_cast<f32>(app_state.current_time - last_frame_tick) >= 1000.0f / s.fps) { if (ImGui::Begin("Viewport", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
f32 delta = static_cast<f32>(app_state.current_time - last_frame_tick); static bool do_bg = true, dragging = false;
f32 frames_passed = delta / (1000.0f / s.fps); static f32 size = 1.0f, angle = 0;
s.current_frame = (s.current_frame + static_cast<u64>(std::floor(frames_passed))) % (s.frame_max + 1); static ImVec2 pos{}, old = pos;
last_frame_tick = app_state.current_time - static_cast<u64>(std::fmod(delta, 1000.0f / s.fps));
}
}
if (ImGui::Begin("Viewport")) {
static bool do_bg = true;
if (ImGui::Shortcut(ImGuiKey_Space)) if (ImGui::Shortcut(ImGuiKey_Space))
TogglePlay(); TogglePlay();
@ -357,11 +351,31 @@ namespace K::UI {
present = s.composite[result_index]; present = s.composite[result_index];
ImTextureID idx = ImGui::toId(present, 1, 0); ImTextureID idx = ImGui::toId(present, 1, 0);
if (idx != nullptr) if (idx != nullptr) {
ImGui::Image(idx, ImVec2{ static_cast<f32>(s.width), static_cast<f32>(s.height) }); ImVec2 image_size = {static_cast<f32>(s.width) * size, static_cast<f32>(s.height) * size};
ImGui::Text("%ux%u", s.width, s.height); ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImGui::SameLine(); 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); /* ImGui::BeginDisabled(comp_save_called != nullptr);
if (ImGui::Button("Save to frame.png")) { if (ImGui::Button("Save to frame.png")) {
@ -371,13 +385,51 @@ namespace K::UI {
} }
ImGui::EndDisabled();*/ ImGui::EndDisabled();*/
ImGui::SetNextItemWidth(50.0f); ImGuiIO io = ImGui::GetIO();
ImGui::DragFloat("FPS", &s.fps, 1.0f, 1.0f, 120.0f); if (ImGui::IsWindowHovered()) {
ImGui::SameLine(); if (io.MouseWheel != 0.0f) {
ImGui::Checkbox("BG", &do_bg); 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::End();
ImGui::PopStyleVar();
} }
constexpr bool TimelineFrameInView(f32 view_left, f32 view_right, i64 frame, u64 frame_max) { constexpr bool TimelineFrameInView(f32 view_left, f32 view_right, i64 frame, u64 frame_max) {
@ -546,6 +598,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;
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}); 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);
@ -563,8 +616,9 @@ namespace K::UI {
pan_x = mouse_tl_x; pan_x = mouse_tl_x;
view_left_old = view_left; view_left_old = view_left;
view_right_old = view_right; 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); ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
f32 shift_amt = (mouse_tl_x - pan_x) / view_width * view_amt; f32 shift_amt = (mouse_tl_x - pan_x) / view_width * view_amt;
view_left = view_left_old - shift_amt; view_left = view_left_old - shift_amt;
@ -578,6 +632,8 @@ namespace K::UI {
view_right = 1.0f; view_right = 1.0f;
} }
} }
if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle))
middle_dragging = false;
if (ImGui::IsKeyPressed(ImGuiKey_Delete) && ImGui::IsItemHovered()) { if (ImGui::IsKeyPressed(ImGuiKey_Delete) && ImGui::IsItemHovered()) {
delete_requests[K_DR_Selected_Keys] = true; 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<f32>(s.frame_max), 1.0f); view_right = std::clamp(view_right_old + ImGui::GetMouseDragDelta().x / tl_width, view_left + 2.0f / static_cast<f32>(s.frame_max), 1.0f);
} }
window->DrawList->AddLine({tl_init_pos.x + static_cast<f32>(s.current_frame) / static_cast<f32>(s.frame_max + 1) * view_width, tl_init_pos.y},
{tl_init_pos.x + static_cast<f32>(s.current_frame) / static_cast<f32>(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}, 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); 0x22FFFFFF);
window->DrawList->AddRect({tl_init_pos.x + view_width - knob_width / 2.0f, tl_init_pos.y}, 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_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::SetCursorScreenPos(tl_init_pos);
ImGui::InvisibleButton("##TL_BG", {view_width, view_height}); // block bg interaction
ImGui::SameLine(); ImGui::SameLine();
ImGui::TableHeader(""); // TL header ImGui::TableHeader(""); // TL header
@ -935,7 +997,7 @@ namespace K::UI {
if (layer_open) { if (layer_open) {
i32 j = 0; i32 j = 0;
for (auto &u: current_layer.track.uniforms) { for (auto &u: current_layer.track.uniforms) {
if (u.status != K_U_Exposed) if (!(u.status & K_U_Exposed))
continue; continue;
ImGui::PushID(j); ImGui::PushID(j);
@ -1163,7 +1225,7 @@ namespace K::UI {
ImGui::SetCursorScreenPos(draw_pos); ImGui::SetCursorScreenPos(draw_pos);
ImGui::PushStyleColor(ImGuiCol_Button, 0x00000000); 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::Button((const char*)(is_sel ? u8"\u25c8" : u8"\u25c7"), {kf_tab_width, row_height * .8f});
ImGui::PopStyleVar(); ImGui::PopStyleVar();
ImGui::PopStyleColor(); ImGui::PopStyleColor();
@ -1206,7 +1268,7 @@ namespace K::UI {
ImGui::TreePop(); ImGui::TreePop();
} }
else for (auto& u: current_layer.track.uniforms) { else for (auto& u: current_layer.track.uniforms) {
if (u.status != K_U_Exposed) if (!(u.status & K_U_Exposed))
continue; continue;
std::visit([table_left, &init_y](auto &&arg) { std::visit([table_left, &init_y](auto &&arg) {
using T = std::decay_t<decltype(arg)>; using T = std::decay_t<decltype(arg)>;
@ -1463,13 +1525,7 @@ namespace K::UI {
ImGui::SetCursorScreenPos(tl_grid_cursor_pos); ImGui::SetCursorScreenPos(tl_grid_cursor_pos);
if (ImGui::BeginChild("TL Grid Overlay", ImGui::GetContentRegionAvail(), ImGuiChildFlags_None, ImGuiWindowFlags_NoInputs)) { if (ImGui::BeginChild("TL Grid Overlay", ImGui::GetContentRegionAvail(), ImGuiChildFlags_None, ImGuiWindowFlags_NoInputs)) {
// Playhead
auto window = ImGui::GetCurrentWindow(); auto window = ImGui::GetCurrentWindow();
window->DrawList->AddLine({tl_init_pos.x + view_width * static_cast<f32>(s.current_frame) /
static_cast<f32>(s.frame_max + 1), tl_init_pos.y},
{tl_init_pos.x + view_width * static_cast<f32>(s.current_frame) /
static_cast<f32>(s.frame_max + 1),
tl_init_pos.y}, 0xFF3333FF, 2.0f);
// Frame Grid // Frame Grid
static f32 frame_grid_min_width = 20.0f; static f32 frame_grid_min_width = 20.0f;
@ -1482,12 +1538,16 @@ namespace K::UI {
window->DrawList->AddLine({fr, tl_init_pos.y}, 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);
// Playhead
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}, {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);
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) { if (bg_drag_select_active) {
window->DrawList->AddRectFilled(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, 0x55AA5555); window->DrawList->AddRectFilled(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, 0x55AA5555);
@ -1565,7 +1625,7 @@ namespace K::UI {
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
i32 i = 0; i32 i = 0;
for (auto it = s.layers[s.active].track.uniforms.begin(); it != s.layers[s.active].track.uniforms.end();) { 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++; it++;
continue; continue;
} }
@ -1611,7 +1671,7 @@ namespace K::UI {
if (ImGui::BeginTable("Samplers", 4, ImGuiTableFlags_Borders)) { if (ImGui::BeginTable("Samplers", 4, ImGuiTableFlags_Borders)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Dims via", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed); 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::TableSetupColumn("##del", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed);
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
i32 i = 0; i32 i = 0;
@ -1632,27 +1692,41 @@ namespace K::UI {
return arg->name.c_str(); return arg->name.c_str();
} }
}, it->resource); }, it->resource);
u32 id = 0;
if (ImGui::BeginCombo("##source", r_name)) { if (ImGui::BeginCombo("##source", r_name)) {
ImGui::TextUnformatted("--Resources--");
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)) {
ImGui::PushID(id++);
const bool is_selected = r_name == res->filename.c_str(); // lol 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 (std::visit([](auto&& arg){ return arg != nullptr; }, it->frame->connection.p))
s.layers[s.active].track.HideUniform(s, *it->frame);
} }
if (is_selected) if (is_selected)
ImGui::SetItemDefaultFocus(); ImGui::SetItemDefaultFocus();
ImGui::PopID();
} }
ImGui::TextUnformatted("--Compositions--");
for (auto& comp : app_state.project.compositions) { for (auto& comp : app_state.project.compositions) {
ImGui::PushID(id++);
const bool is_selected = r_name == comp.name.c_str(); // lol const bool is_selected = r_name == comp.name.c_str(); // lol
if (ImGui::Selectable(comp.name.c_str(), is_selected)) { if (ImGui::Selectable(comp.name.c_str(), is_selected)) {
it->resource = &comp; it->resource = &comp;
it->dims->val = ShaderGraph::XY{static_cast<f32>(comp.width), static_cast<f32>(comp.height)}; it->dims->val = ShaderGraph::XY{static_cast<f32>(comp.width), static_cast<f32>(comp.height)};
it->frame->val = ShaderGraph::T_Map<ShaderGraph::T_Int>::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) if (is_selected)
ImGui::SetItemDefaultFocus(); ImGui::SetItemDefaultFocus();
ImGui::PopID();
} }
ImGui::EndCombo(); ImGui::EndCombo();
} }
@ -1691,7 +1765,7 @@ namespace K::UI {
if (ImGui::Button("Apply")) if (ImGui::Button("Apply"))
for (auto& layer : s.layers) for (auto& layer : s.layers)
for (auto& u : layer.track.uniforms) { for (auto& u : layer.track.uniforms) {
if (u.status != K_U_Exposed) if (!(u.status & K_U_Exposed))
continue; continue;
for (auto& n : s.plugboard.show_nodes[u.connection.index]) { for (auto& n : s.plugboard.show_nodes[u.connection.index]) {
auto& [chain, _] = std::get<Plugboard::Chain*>(n.p)->extra; auto& [chain, _] = std::get<Plugboard::Chain*>(n.p)->extra;
@ -1831,7 +1905,7 @@ namespace K::UI {
if (ImGui::InputFloat("FPS", &fps, 1.0f, 5.0f, "%.1f")) if (ImGui::InputFloat("FPS", &fps, 1.0f, 5.0f, "%.1f"))
fps = std::clamp(fps, 1.0f, 120.0f); 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)) { if (ImGui::InputInt2("Dimensions", dims)) {
dims[0] = std::max(1, dims[0]); dims[0] = std::max(1, dims[0]);
dims[1] = std::max(1, dims[1]); dims[1] = std::max(1, dims[1]);
@ -1891,40 +1965,7 @@ namespace K::UI {
} }
if (ImGui::Button("Load")) { if (ImGui::Button("Load")) {
static const SDL_DialogFileFilter import_filters[] = { Resource::LoadFile();
{ "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<Resource::K_R_Still>(*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);
} }
if (ImGui::BeginTable("Assets", 3, ImGuiTableFlags_Borders)) { if (ImGui::BeginTable("Assets", 3, ImGuiTableFlags_Borders)) {
@ -1989,6 +2030,7 @@ namespace K::UI {
static_cast<f32>(Resource::fallback_still->w), static_cast<f32>(Resource::fallback_still->w),
static_cast<f32>(Resource::fallback_still->h) static_cast<f32>(Resource::fallback_still->h)
}; };
layer.track.HideUniform(s, *sampler.frame);
} }
} }
it->Destroy(); it->Destroy();
@ -2047,11 +2089,13 @@ namespace K::UI {
for (auto& chain : s.plugboard.selected_nodes) { for (auto& chain : s.plugboard.selected_nodes) {
if (auto *c = std::get_if<Plugboard::Chain*>(&chain)) { if (auto *c = std::get_if<Plugboard::Chain*>(&chain)) {
auto& [segments, selected] = (*c)->extra.chain; auto& [segments, selected] = (*c)->extra.chain;
std::ranges::sort(selected, std::ranges::greater{});
for (u32 i : selected) for (u32 i : selected)
segments.erase(segments.begin() + i); segments.erase(segments.begin() + i);
selected.clear(); selected.clear();
} }
} }
delete_requests[K_DR_Selected_Keys] = false;
} }
} }
@ -2060,17 +2104,29 @@ namespace K::UI {
ImGui_Implbgfx_NewFrame(); ImGui_Implbgfx_NewFrame();
ImGui::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::DockSpaceOverViewport(0, ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
ImGui::ShowDemoWindow();
// ImGui::ShowDemoWindow();
MainMenuBar(); MainMenuBar();
if (draw_assets) Assets(); if (draw_assets) Assets();
if (app_state.project.inspecting_composition != nullptr) { if (app_state.project.inspecting_composition != nullptr) {
auto &comp = *app_state.project.inspecting_composition; auto &comp = *app_state.project.inspecting_composition;
if (playing) {
if (static_cast<f32>(app_state.current_time - last_frame_tick) >= 1000.0f / comp.fps) {
f32 delta = static_cast<f32>(app_state.current_time - last_frame_tick);
f32 frames_passed = delta / (1000.0f / comp.fps);
comp.current_frame = (comp.current_frame + static_cast<u64>(std::floor(frames_passed))) % (comp.frame_max + 1);
last_frame_tick = app_state.current_time - static_cast<u64>(std::fmod(delta, 1000.0f / comp.fps));
}
}
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);

View file

@ -40,13 +40,13 @@ namespace K {
f32 view[16], f32 transform[16]) { f32 view[16], f32 transform[16]) {
std::shared_lock lk{Resource::resource_lock}; std::shared_lock lk{Resource::resource_lock};
u32 sampler_stage = 0; u32 sampler_stage = 0;
for (auto& [_name, handle, res, _u] : samplers) { for (auto& [_name, handle, res, _u, u_frame] : samplers) {
bgfx::TextureHandle tx = std::visit([](auto&& arg) { bgfx::TextureHandle tx = std::visit([&s, &u_frame](auto&& arg) {
using T = std::decay_t<decltype(arg)>; using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, Resource::Resource<Resource::K_R_Still>*>) if constexpr (std::is_same_v<T, Resource::Resource<Resource::K_R_Still>*>)
return arg->tex; return arg->tex;
else if constexpr (std::is_same_v<T, CompositionState*>) { else if constexpr (std::is_same_v<T, CompositionState*>) {
i32 slot = arg->GetFrame(0); i32 slot = arg->GetFrame(std::get<Plugboard::T_Int>(std::get<Plugboard::T_Map<Plugboard::T_Count>::type>(s.plugboard.out_instance.in[u_frame->connection.index].value)));
return arg->composite[slot]; return arg->composite[slot];
} }
}, res); }, res);
@ -61,6 +61,9 @@ namespace K {
bgfx::setViewRect(app_state.render_view, 0, 0, w, h); bgfx::setViewRect(app_state.render_view, 0, 0, w, h);
for (auto& u : uniforms) { for (auto& u : uniforms) {
if (u.status & K_U_SamplerFrame)
continue;
f32 pack[4]{}; f32 pack[4]{};
ShaderGraph::T_Map<ShaderGraph::T_Count>::type val_target; ShaderGraph::T_Map<ShaderGraph::T_Count>::type val_target;
if (std::visit([](auto&& arg){ return arg == nullptr; }, u.connection.p)) if (std::visit([](auto&& arg){ return arg == nullptr; }, u.connection.p))
@ -110,7 +113,7 @@ namespace K {
"#include <bgfx_shader.sh>\n" "#include <bgfx_shader.sh>\n"
"#include <shaderlib.sh>\n"; "#include <shaderlib.sh>\n";
u32 sampler_loc = 0; 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"; f << "SAMPLER2D(" << name << ", " << sampler_loc++ << ");\n";
} }
for (auto& u : uniforms) { for (auto& u : uniforms) {
@ -150,6 +153,7 @@ namespace K {
if (bgfx::isValid(pg)) if (bgfx::isValid(pg))
bgfx::destroy(pg); bgfx::destroy(pg);
for (auto& uniform : uniforms) { for (auto& uniform : uniforms) {
if (!(uniform.status & K_U_SamplerFrame))
bgfx::destroy(uniform.handle); bgfx::destroy(uniform.handle);
} }
uniforms.clear(); uniforms.clear();
@ -164,6 +168,8 @@ namespace K {
// this is shaky and bug prone -- relies on the shader types being in line with plugboard types // 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.out_instance.in.push_back(Plugboard::InSocket{ uu.name, Plugboard::Type(uu.val.index()), ShaderValToPlugboard(uu.val)});
s.plugboard.show_nodes.emplace_back(); s.plugboard.show_nodes.emplace_back();
uu.status |= K_U_Exposed;
} }
void VisualTrack::ExposeUniform(CompositionState& s, u32 i) { void VisualTrack::ExposeUniform(CompositionState& s, u32 i) {
@ -171,7 +177,7 @@ namespace K {
} }
void VisualTrack::HideUniform(CompositionState& s, Uniform& uu) { void VisualTrack::HideUniform(CompositionState& s, Uniform& uu) {
if (uu.status != K_U_Exposed) if (!(uu.status & K_U_Exposed))
return; return;
Plugboard::Disconnect(uu.connection.p, uu.connection.index); 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); s.plugboard.out_instance.in.erase(s.plugboard.out_instance.in.begin() + uu.connection.index);
uu.connection = {(Plugboard::Add*)(nullptr), 0}; uu.connection = {(Plugboard::Add*)(nullptr), 0};
uu.status &= ~K_U_Exposed;
} }
void VisualTrack::AddUniform(const String& s, ShaderGraph::T_Map<ShaderGraph::T_Count>::type&& val, UniformStatus init) { void VisualTrack::AddUniform(const String& s, ShaderGraph::T_Map<ShaderGraph::T_Count>::type&& val, UniformStatus init) {
@ -197,7 +205,10 @@ namespace K {
for (auto& ss : samplers) for (auto& ss : samplers)
if (ss.name == s) if (ss.name == s)
return; return;
if (!(init & K_U_SamplerFrame))
uniforms.emplace_back(Uniform{s, bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val, {{}, 0}, init}); 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) { void VisualTrack::AddSampler(const String& s) {
@ -208,6 +219,7 @@ namespace K {
if (ss.name == s) if (ss.name == s)
return; return;
AddUniform(s + "_dims", ShaderGraph::XY{static_cast<f32>(Resource::fallback_still->w), static_cast<f32>(Resource::fallback_still->h)}, K_U_SamplerDims); AddUniform(s + "_dims", ShaderGraph::XY{static_cast<f32>(Resource::fallback_still->w), static_cast<f32>(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<ShaderGraph::T_Int>::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()});
} }
} }

View file

@ -24,8 +24,8 @@ namespace K {
Vector<Layer> layers; Vector<Layer> layers;
i32 active = -1; // index for layers i32 active = -1; // index for layers
Vector<u32> selected; // indices for layers Vector<u32> selected; // indices for layers todo use flat_set instead
Vector<u32> disabled; // indices for layers Vector<u32> disabled; // indices for layers todo use flat_set instead
Plugboard::Plugboard plugboard; Plugboard::Plugboard plugboard;
@ -36,13 +36,16 @@ namespace K {
render_fb = BGFX_INVALID_HANDLE; render_fb = BGFX_INVALID_HANDLE;
f32 proj[16], transform[16], view[16]; f32 proj[16], transform[16], view[16];
// Dependence
Vector<std::pair<CompositionState*, u32>> depends_on{}; // todo use flat_map instead
void Setup(); void Setup();
i32 GetFrame(u32 frame); i32 GetFrame(u32 frame);
void Destroy(); void Destroy();
}; };
struct ProjectState { struct ProjectState {
CompositionState *inspecting_composition; CompositionState *inspecting_composition = nullptr;
plf::colony<CompositionState> compositions{}; plf::colony<CompositionState> compositions{};
}; };

View file

@ -84,6 +84,8 @@ namespace K::Resource {
bgfx::ProgramHandle FetchUnmanagedShaderProgram(const String& shader_name); bgfx::ProgramHandle FetchUnmanagedShaderProgram(const String& shader_name);
void LoadFile();
void SaveCurrentProject(); void SaveCurrentProject();
} }

View file

@ -10,9 +10,9 @@
namespace K { namespace K {
enum UniformStatus { enum UniformStatus {
K_U_Inactive = 0, K_U_Inactive = 0,
K_U_Exposed, K_U_Exposed = 1 << 1,
K_U_SamplerDims, K_U_SamplerDims = 1 << 2,
K_U_Count K_U_SamplerFrame = 1 << 3 // "fake" frame -- since shader does not care about the frame of the image
}; };
struct Uniform { struct Uniform {
@ -20,14 +20,14 @@ namespace K {
bgfx::UniformHandle handle; bgfx::UniformHandle handle;
ShaderGraph::T_Map<ShaderGraph::T_Count>::type val; ShaderGraph::T_Map<ShaderGraph::T_Count>::type val;
Plugboard::ConnectInfo connection; Plugboard::ConnectInfo connection;
UniformStatus status = K_U_Inactive; i32 status = K_U_Inactive; // enum UniformStatus
}; };
struct Sampler { struct Sampler {
String name; String name;
bgfx::UniformHandle handle; bgfx::UniformHandle handle;
std::variant<Resource::Resource<Resource::K_R_Still>*, CompositionState*> resource; std::variant<Resource::Resource<Resource::K_R_Still>*, CompositionState*> resource;
Uniform *dims; Uniform *dims, *frame;
}; };
ShaderGraph::T_Map<ShaderGraph::T_Count>::type PlugboardValToShader(const Plugboard::T_Map<Plugboard::T_Count>::type& val); ShaderGraph::T_Map<ShaderGraph::T_Count>::type PlugboardValToShader(const Plugboard::T_Map<Plugboard::T_Count>::type& val);

View file

@ -7,7 +7,6 @@
- Blender previews resource - Blender previews resource
- Data models - Data models
- Dump and read back state, (de)serialization!!! - Dump and read back state, (de)serialization!!!
- Pre-compose/Layer Groups
- Undo's - Undo's
- Node groups - Node groups