tl usability shortcuts
This commit is contained in:
parent
151d10671b
commit
2f958f7ede
6 changed files with 161 additions and 133 deletions
|
@ -446,4 +446,41 @@ namespace K::Graphics {
|
||||||
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(view_id, composite_pg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f64 GetCubicUniqueReal(f64 a, f64 b, f64 c, f64 d) {
|
||||||
|
auto accept = [](f64 t){ return 0.0 <= t && t <= 1.0; };
|
||||||
|
|
||||||
|
f64 a1 = b/a, a2 = c/a, a3 = d/a;
|
||||||
|
f64 Q = (a1 * a1 - 3.0 * a2) / 9.0,
|
||||||
|
R = (2.0 * a1 * a1 * a1 - 9.0 * a1 * a2 + 27.0 * a3) / 54.0,
|
||||||
|
Qcubed = Q * Q * Q,
|
||||||
|
D = Qcubed - R * R;
|
||||||
|
|
||||||
|
if (D >= 0) {
|
||||||
|
f64 theta = acos(R / sqrt(Qcubed));
|
||||||
|
f64 sqrtQ = sqrt(Q);
|
||||||
|
f64 r1 = -2.0 * sqrtQ * cos( theta / 3.0) - a1 / 3.0,
|
||||||
|
r2 = -2.0 * sqrtQ * cos((theta + 2.0 * std::numbers::pi) / 3.0) - a1 / 3.0,
|
||||||
|
r3 = -2.0 * sqrtQ * cos((theta + 4.0 * std::numbers::pi) / 3.0) - a1 / 3.0;
|
||||||
|
return accept(r1) ? r1 : (accept(r2) ? r2 : r3);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
f64 e = std::pow(std::sqrt(-D) + std::abs(R), 1.0 / 3.0);
|
||||||
|
if (R > 0.0)
|
||||||
|
e = -e;
|
||||||
|
return (e + Q / e) - a1 / 3.;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f64 CubicBezier(f64 a, f64 b, f64 c, f64 d, f64 t) {
|
||||||
|
f64 t2 = t * t, t3 = t2 * t, mt = 1.0-t, mt2 = mt * mt, mt3 = mt2 * mt;
|
||||||
|
return a*mt3 + b*3.0*mt2*t + c*3.0*mt*t2 + d*t3;
|
||||||
|
}
|
||||||
|
f64 InjectingCubicBezierFromX(f64 p2x, f64 p2y, f64 p3x, f64 p3y, f64 x) {
|
||||||
|
f64 a = 0.0f, b = p2x,
|
||||||
|
c = p3x, d = 1.0;
|
||||||
|
|
||||||
|
f64 t = GetCubicUniqueReal(-a+3.0*b-3.0*c+d, 3.0*a-6.0*b+3.0*c, -3.0*a+3.0*b,a-x);
|
||||||
|
return CubicBezier(0.0, p2y, p3y, 1.0, t);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
165
Keishiki/UI.cpp
165
Keishiki/UI.cpp
|
@ -318,6 +318,12 @@ namespace K::UI {
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TogglePlay() {
|
||||||
|
playing = !playing;
|
||||||
|
if (playing)
|
||||||
|
last_frame_tick = app_state.current_time;
|
||||||
|
}
|
||||||
|
|
||||||
void Viewport(CompState& s) {
|
void Viewport(CompState& s) {
|
||||||
if (playing) {
|
if (playing) {
|
||||||
if (static_cast<f32>(app_state.current_time - last_frame_tick) >= 1000.0f / s.fps) {
|
if (static_cast<f32>(app_state.current_time - last_frame_tick) >= 1000.0f / s.fps) {
|
||||||
|
@ -329,6 +335,10 @@ namespace K::UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::Begin("Viewport", &draw_viewport)) {
|
if (ImGui::Begin("Viewport", &draw_viewport)) {
|
||||||
|
if (ImGui::Shortcut(ImGuiKey_Space)) {
|
||||||
|
TogglePlay();
|
||||||
|
}
|
||||||
|
|
||||||
u32 view_count = 0, layers_done = 0;
|
u32 view_count = 0, layers_done = 0;
|
||||||
bgfx::setViewFrameBuffer(Graphics::K_VIEW_COMP_COMPOSITE + view_count, composite_fb[0]); // the other fb will be cleared in composite
|
bgfx::setViewFrameBuffer(Graphics::K_VIEW_COMP_COMPOSITE + view_count, composite_fb[0]); // the other fb will be cleared in composite
|
||||||
bgfx::setViewClear(Graphics::K_VIEW_COMP_COMPOSITE + view_count, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0x00000000);
|
bgfx::setViewClear(Graphics::K_VIEW_COMP_COMPOSITE + view_count, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0x00000000);
|
||||||
|
@ -357,31 +367,24 @@ namespace K::UI {
|
||||||
transform);
|
transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<bgfx::ViewId> views;
|
|
||||||
for (u32 i = 0; i < view_count; i++)
|
|
||||||
views.push_back(Graphics::K_VIEW_COMP_COMPOSITE + i);
|
|
||||||
views.push_back(Graphics::K_VIEW_COMP_SAVE);
|
|
||||||
views.push_back(Graphics::K_VIEW_UI);
|
|
||||||
views.push_back(Graphics::K_VIEW_LOGO);
|
|
||||||
bgfx::setViewOrder(0, views.size(), views.data());
|
|
||||||
|
|
||||||
ImTextureID idx = ImGui::toId(composite[layers_done % 2], 0, 0);
|
ImTextureID idx = ImGui::toId(composite[layers_done % 2], 0, 0);
|
||||||
if (idx != nullptr)
|
if (idx != nullptr)
|
||||||
ImGui::Image(idx, ImVec2{ static_cast<f32>(s.width), static_cast<f32>(s.height) });
|
ImGui::Image(idx, ImVec2{ static_cast<f32>(s.width), static_cast<f32>(s.height) });
|
||||||
|
|
||||||
ImGui::Text("Composition Size: %ux%u", s.width, s.height);
|
ImGui::Text("%ux%u", s.width, s.height);
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::DragFloat("FPS", &s.fps, 1.0f, 1.0f, 120.0f);
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(save_called);
|
ImGui::BeginDisabled(save_called);
|
||||||
if (ImGui::Button("Save to frame.png")) {
|
if (ImGui::Button("Save to frame.png")) {
|
||||||
bgfx::blit(Graphics::K_VIEW_COMP_SAVE, save, 0, 0, composite[layers_done % 2]);
|
bgfx::blit(Graphics::K_VIEW_COMP_COMPOSITE + view_count, save, 0, 0, composite[layers_done % 2]);
|
||||||
ready_frame = bgfx::readTexture(save, save_buffer);
|
ready_frame = bgfx::readTexture(save, save_buffer);
|
||||||
save_called = true;
|
save_called = true;
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::SetNextItemWidth(50.0f);
|
||||||
|
ImGui::DragFloat("FPS", &s.fps, 1.0f, 1.0f, 120.0f);
|
||||||
|
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
@ -400,10 +403,13 @@ namespace K::UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Composition(CompState& s) {
|
void Composition(CompState& s) {
|
||||||
if (!ImGui::Begin("Composition", &draw_comp)) {
|
if (!ImGui::Begin("Composition", &draw_comp, ImGuiWindowFlags_NoScrollbar)) {
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (ImGui::Shortcut(ImGuiKey_Space)) {
|
||||||
|
TogglePlay();
|
||||||
|
}
|
||||||
|
|
||||||
static u32 node_current{};
|
static u32 node_current{};
|
||||||
if (ImGui::Button("Add")) {
|
if (ImGui::Button("Add")) {
|
||||||
|
@ -426,14 +432,11 @@ namespace K::UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
bool playing_cp = playing;
|
if (ImGui::Button(playing ? "Pause" : "Play"))
|
||||||
ImGui::Checkbox("Playing", &playing);
|
TogglePlay();
|
||||||
if (!playing_cp && playing) {
|
|
||||||
last_frame_tick = app_state.current_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text("%lu / %lu Frames", s.current_frame, s.frame_max);
|
ImGui::Text("Frame %lu / %lu", s.current_frame, s.frame_max);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
const bool no_selection = s.selected.empty();
|
const bool no_selection = s.selected.empty();
|
||||||
|
@ -550,8 +553,57 @@ namespace K::UI {
|
||||||
view_left_old = view_left,
|
view_left_old = view_left,
|
||||||
view_right = 1.0f,
|
view_right = 1.0f,
|
||||||
view_right_old = view_right;
|
view_right_old = view_right;
|
||||||
f32 view_amt;
|
f32 view_amt, mouse_tl_x;
|
||||||
|
|
||||||
|
// Mouse Handlers
|
||||||
|
// Drag playhead/pan
|
||||||
|
auto draw_tl_bg_drag_area = [&view_width, &style, &view_amt, &s, &mouse_tl_x](f32 h = 0.0f) {
|
||||||
|
static f32 pan_x, view_left_old, view_right_old;
|
||||||
|
ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, h == 0.0f ? row_height + style.CellPadding.y : h});
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Middle)) {
|
||||||
|
pan_x = mouse_tl_x;
|
||||||
|
view_left_old = view_left;
|
||||||
|
view_right_old = view_right;
|
||||||
|
}
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_MouseMiddle) && ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||||
|
f32 shift_amt = (mouse_tl_x - pan_x) / view_width * view_amt;
|
||||||
|
view_left = view_left_old - shift_amt;
|
||||||
|
view_right = view_right_old - shift_amt;
|
||||||
|
if (view_left < 0.0f) {
|
||||||
|
view_right -= view_left;
|
||||||
|
view_left = 0.0f;
|
||||||
|
}
|
||||||
|
else if (view_right > 1.0f) {
|
||||||
|
view_left -= (view_right - 1.0f);
|
||||||
|
view_right = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Zoom
|
||||||
|
auto tl_scroll_zoom = [&mouse_tl_x, &view_width, &io, &view_amt]() {
|
||||||
|
if (ImGui::IsWindowHovered() && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && mouse_tl_x >= 0 && mouse_tl_x < view_width && io.MouseWheel != 0.0f) {
|
||||||
|
f32 center = mouse_tl_x / view_width * view_amt + view_left;
|
||||||
|
f32 new_view_amt = std::clamp(view_amt - 0.05f * io.MouseWheel, 0.05f, 1.0f);
|
||||||
|
view_left = center - new_view_amt / 2.0f - (mouse_tl_x / view_width - .5f) * new_view_amt;
|
||||||
|
view_right = center + new_view_amt / 2.0f - (mouse_tl_x / view_width - .5f) * new_view_amt;
|
||||||
|
if (view_left < 0.0f) {
|
||||||
|
view_right -= view_left;
|
||||||
|
view_left = 0.0f;
|
||||||
|
}
|
||||||
|
else if (view_right > 1.0f) {
|
||||||
|
view_left -= (view_right - 1.0f);
|
||||||
|
view_right = 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
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::TableSetupColumn("#", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_IndentDisable);
|
ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_IndentDisable);
|
||||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort);
|
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort);
|
||||||
ImGui::TableSetupColumn("Blending", ImGuiTableColumnFlags_NoSort, 100.0f);
|
ImGui::TableSetupColumn("Blending", ImGuiTableColumnFlags_NoSort, 100.0f);
|
||||||
|
@ -582,6 +634,7 @@ namespace K::UI {
|
||||||
control_left = tl_init_pos.x,
|
control_left = tl_init_pos.x,
|
||||||
control_right = tl_init_pos.x + view_width;
|
control_right = tl_init_pos.x + view_width;
|
||||||
view_amt = view_right - view_left;
|
view_amt = view_right - view_left;
|
||||||
|
mouse_tl_x = io.MousePos.x - tl_init_pos.x;
|
||||||
|
|
||||||
f32 delta_x =
|
f32 delta_x =
|
||||||
(std::clamp(io.MousePos.x, control_left, control_right) - io.MouseClickedPos[0].x) / view_width;
|
(std::clamp(io.MousePos.x, control_left, control_right) - io.MouseClickedPos[0].x) / view_width;
|
||||||
|
@ -672,6 +725,7 @@ namespace K::UI {
|
||||||
ImGui::TableHeader("");
|
ImGui::TableHeader("");
|
||||||
|
|
||||||
// Main rows
|
// Main rows
|
||||||
|
tl_scroll_zoom();
|
||||||
i32 move_from = -1, move_to = -1;
|
i32 move_from = -1, move_to = -1;
|
||||||
bool after{};
|
bool after{};
|
||||||
for (u32 i = 0; i < s.layers.size(); i++) {
|
for (u32 i = 0; i < s.layers.size(); i++) {
|
||||||
|
@ -930,10 +984,7 @@ namespace K::UI {
|
||||||
|
|
||||||
ImGui::SetCursorScreenPos(p);
|
ImGui::SetCursorScreenPos(p);
|
||||||
|
|
||||||
ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, row_height + style.CellPadding.y});
|
draw_tl_bg_drag_area();
|
||||||
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::TableSetColumnIndex(0);
|
ImGui::TableSetColumnIndex(0);
|
||||||
if (uniform_open && (connected_v.index() == 1) && std::get<1>(connected_v).p->node == &PlugboardNodes::Chain) {
|
if (uniform_open && (connected_v.index() == 1) && std::get<1>(connected_v).p->node == &PlugboardNodes::Chain) {
|
||||||
|
@ -978,11 +1029,13 @@ namespace K::UI {
|
||||||
|
|
||||||
static std::map<u32, PlugboardNodes::ChainSegment> m_copy{};
|
static std::map<u32, PlugboardNodes::ChainSegment> m_copy{};
|
||||||
static u32 dragging{};
|
static u32 dragging{};
|
||||||
|
static decltype(m) drag_m = nullptr;
|
||||||
static i64 drag_og = -1;
|
static i64 drag_og = -1;
|
||||||
if (drag_og == -1) { // premature optimization is the root of good performance !!
|
if (drag_m != m || drag_og == -1) { // premature optimization is the root of good performance !!
|
||||||
f32 *last_val = nullptr;
|
f32 *last_val = nullptr;
|
||||||
u32 last_x = 0;
|
u32 last_x = 0;
|
||||||
for (auto& [k, segment] : *m) {
|
for (auto& [k, segment] : *m) {
|
||||||
|
ImGui::PushID(k);
|
||||||
if (last_val != nullptr && *last_val != segment.value) {
|
if (last_val != nullptr && *last_val != segment.value) {
|
||||||
ImGui::SameLine(0.0f, 0.0f);
|
ImGui::SameLine(0.0f, 0.0f);
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + row_height / 2.5f);
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + row_height / 2.5f);
|
||||||
|
@ -996,16 +1049,20 @@ namespace K::UI {
|
||||||
ImGui::SetCursorScreenPos(
|
ImGui::SetCursorScreenPos(
|
||||||
{TimelineFrameToScreenView(view_left, view_amt, view_width, k,
|
{TimelineFrameToScreenView(view_left, view_amt, view_width, k,
|
||||||
s.frame_max) + begin_tl.x - 2.5f, begin_tl.y});
|
s.frame_max) + begin_tl.x - 2.5f, begin_tl.y});
|
||||||
ImGui::Button("k", {0.0f, row_height * .8f});
|
bool d;
|
||||||
|
ImGui::Selectable("k", &d, ImGuiSelectableFlags_None, {5.0f, row_height * .8f});
|
||||||
if (ImGui::IsItemClicked()) {
|
if (ImGui::IsItemClicked()) {
|
||||||
m_copy = *m;
|
m_copy = *m;
|
||||||
drag_og = k;
|
drag_og = k;
|
||||||
|
drag_m = m;
|
||||||
}
|
}
|
||||||
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m->clear();
|
m->clear();
|
||||||
for (auto& [k, segment] : m_copy) {
|
for (auto& [k, segment] : m_copy) {
|
||||||
|
ImGui::PushID(k);
|
||||||
if (drag_og != k) {
|
if (drag_og != k) {
|
||||||
if (TimelineFrameInView(view_left, view_right, k, s.frame_max)) {
|
if (TimelineFrameInView(view_left, view_right, k, s.frame_max)) {
|
||||||
ImGui::SetCursorScreenPos(
|
ImGui::SetCursorScreenPos(
|
||||||
|
@ -1030,16 +1087,15 @@ namespace K::UI {
|
||||||
if (drag_og == k && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
if (drag_og == k && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
||||||
m->emplace(dragging, segment);
|
m->emplace(dragging, segment);
|
||||||
drag_og = -1;
|
drag_og = -1;
|
||||||
|
drag_m = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SetCursorScreenPos(begin_tl);
|
ImGui::SetCursorScreenPos(begin_tl);
|
||||||
ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, row_height + style.CellPadding.y});
|
draw_tl_bg_drag_area();
|
||||||
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();
|
ImGui::PopID();
|
||||||
|
@ -1105,12 +1161,8 @@ namespace K::UI {
|
||||||
|
|
||||||
ImGui::SetCursorScreenPos(tl_end_begin);
|
ImGui::SetCursorScreenPos(tl_end_begin);
|
||||||
if (ImGui::BeginChild("TL Overlay")) {
|
if (ImGui::BeginChild("TL Overlay")) {
|
||||||
ImGui::InvisibleButton("##TL_BG", ImGui::GetContentRegionAvail());
|
draw_tl_bg_drag_area(ImGui::GetContentRegionAvail().y);
|
||||||
if (ImGui::IsItemActive()) {
|
tl_scroll_zoom();
|
||||||
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::EndChild();
|
||||||
|
|
||||||
|
@ -1184,42 +1236,6 @@ namespace K::UI {
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
f64 GetCubicUniqueReal(f64 a, f64 b, f64 c, f64 d) {
|
|
||||||
auto accept = [](f64 t){ return 0.0 <= t && t <= 1.0; };
|
|
||||||
|
|
||||||
f64 a1 = b/a, a2 = c/a, a3 = d/a;
|
|
||||||
f64 Q = (a1 * a1 - 3.0 * a2) / 9.0,
|
|
||||||
R = (2.0 * a1 * a1 * a1 - 9.0 * a1 * a2 + 27.0 * a3) / 54.0,
|
|
||||||
Qcubed = Q * Q * Q,
|
|
||||||
D = Qcubed - R * R;
|
|
||||||
|
|
||||||
if (D >= 0) {
|
|
||||||
f64 theta = acos(R / sqrt(Qcubed));
|
|
||||||
f64 sqrtQ = sqrt(Q);
|
|
||||||
f64 r1 = -2.0 * sqrtQ * cos( theta / 3.0) - a1 / 3.0,
|
|
||||||
r2 = -2.0 * sqrtQ * cos((theta + 2.0 * std::numbers::pi) / 3.0) - a1 / 3.0,
|
|
||||||
r3 = -2.0 * sqrtQ * cos((theta + 4.0 * std::numbers::pi) / 3.0) - a1 / 3.0;
|
|
||||||
return accept(r1) ? r1 : (accept(r2) ? r2 : r3);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
f64 e = std::pow(std::sqrt(-D) + std::abs(R), 1.0 / 3.0);
|
|
||||||
if (R > 0.0)
|
|
||||||
e = -e;
|
|
||||||
return (e + Q / e) - a1 / 3.;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f64 CubicBezier(f64 a, f64 b, f64 c, f64 d, f64 t) {
|
|
||||||
f64 t2 = t * t, t3 = t2 * t, mt = 1.0-t, mt2 = mt * mt, mt3 = mt2 * mt;
|
|
||||||
return a*mt3 + b*3.0*mt2*t + c*3.0*mt*t2 + d*t3;
|
|
||||||
}
|
|
||||||
f64 sb233asdf(f64 p2x, f64 p2y, f64 p3x, f64 p3y, f64 x) {
|
|
||||||
f64 a = 0.0f, b = p2x,
|
|
||||||
c = p3x, d = 1.0;
|
|
||||||
|
|
||||||
f64 t = GetCubicUniqueReal(-a+3.0*b-3.0*c+d, 3.0*a-6.0*b+3.0*c, -3.0*a+3.0*b,a-x);
|
|
||||||
return CubicBezier(0.0, p2y, p3y, 1.0, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interpolation(CompState& s) {
|
void Interpolation(CompState& s) {
|
||||||
if (ImGui::Begin("Interpolation", &draw_interpolation, ImGuiWindowFlags_NoScrollbar)) {
|
if (ImGui::Begin("Interpolation", &draw_interpolation, ImGuiWindowFlags_NoScrollbar)) {
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
@ -1250,7 +1266,7 @@ namespace K::UI {
|
||||||
dl->AddText(tp4 + ImVec2{-30.0f, 10.0f}, 0xFFFFFFFF, "1.0");
|
dl->AddText(tp4 + ImVec2{-30.0f, 10.0f}, 0xFFFFFFFF, "1.0");
|
||||||
|
|
||||||
f32 x = std::clamp((ImGui::GetMousePos().x - pos.x) / w, 0.0f, 1.0f);
|
f32 x = std::clamp((ImGui::GetMousePos().x - pos.x) / w, 0.0f, 1.0f);
|
||||||
f32 y = sb233asdf(p2.x, p2.y, p3.x, p3.y, x);
|
f32 y = Graphics::InjectingCubicBezierFromX(p2.x, p2.y, p3.x, p3.y, x);
|
||||||
dl->AddLine(pos + ImVec2{0, ((1.0f - y) - (1.0f - y_max)) * w / y_range}, pos + ImVec2{w, ((1.0f - y) - (1.0f - y_max)) * w / y_range}, 0xBB5555FF, 1.0f);
|
dl->AddLine(pos + ImVec2{0, ((1.0f - y) - (1.0f - y_max)) * w / y_range}, pos + ImVec2{w, ((1.0f - y) - (1.0f - y_max)) * w / y_range}, 0xBB5555FF, 1.0f);
|
||||||
dl->AddLine(pos + ImVec2{x * w, 0.0f}, pos + ImVec2{x * w, w}, 0x44FF5555, 1.0f);
|
dl->AddLine(pos + ImVec2{x * w, 0.0f}, pos + ImVec2{x * w, w}, 0x44FF5555, 1.0f);
|
||||||
|
|
||||||
|
@ -1317,7 +1333,7 @@ namespace K::UI {
|
||||||
if (draw_interpolation) Interpolation(s);
|
if (draw_interpolation) Interpolation(s);
|
||||||
if (draw_assets) Assets(s);
|
if (draw_assets) Assets(s);
|
||||||
|
|
||||||
if (save_called && ready_frame == frame) {
|
if (save_called && ready_frame <= frame) {
|
||||||
stbi_write_png("frame.png", static_cast<i32>(s.width), static_cast<i32>(s.height), 4, save_buffer, static_cast<i32>(s.width) * 4);
|
stbi_write_png("frame.png", static_cast<i32>(s.width), static_cast<i32>(s.height), 4, save_buffer, static_cast<i32>(s.width) * 4);
|
||||||
save_called = false;
|
save_called = false;
|
||||||
}
|
}
|
||||||
|
@ -1356,7 +1372,6 @@ namespace K::UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown(CompState& s) {
|
void Shutdown(CompState& s) {
|
||||||
|
|
||||||
DestroyViewport(s);
|
DestroyViewport(s);
|
||||||
ImGui_Implbgfx_Shutdown();
|
ImGui_Implbgfx_Shutdown();
|
||||||
ImGui_ImplSDL2_Shutdown();
|
ImGui_ImplSDL2_Shutdown();
|
||||||
|
|
|
@ -7,11 +7,12 @@
|
||||||
|
|
||||||
namespace K::Graphics {
|
namespace K::Graphics {
|
||||||
enum VIEW_ID {
|
enum VIEW_ID {
|
||||||
K_VIEW_COMP_SAVE,
|
|
||||||
K_VIEW_UI,
|
K_VIEW_UI,
|
||||||
K_VIEW_LOGO,
|
K_VIEW_LOGO,
|
||||||
|
|
||||||
K_VIEW_COMP_COMPOSITE, // Remember to call setViewOrder!
|
K_VIEW_COMP_SAVE, // bgfx::setViewOrder doesn't seem to work unfortunately !!
|
||||||
|
|
||||||
|
K_VIEW_COMP_COMPOSITE
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Init(u16 width, u16 height);
|
bool Init(u16 width, u16 height);
|
||||||
|
@ -123,4 +124,8 @@ namespace K::Graphics {
|
||||||
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(u32 view_id, bgfx::FrameBufferHandle fb, bgfx::TextureHandle composite, bgfx::TextureHandle from, Blending mode, u16 w, u16 h, f32 proj[16], f32 transform[16]);
|
||||||
|
|
||||||
extern Graphics::ImageTexture *mmaker;
|
extern Graphics::ImageTexture *mmaker;
|
||||||
|
|
||||||
|
f64 GetCubicUniqueReal(f64 a, f64 b, f64 c, f64 d);
|
||||||
|
f64 CubicBezier(f64 a, f64 b, f64 c, f64 d, f64 t);
|
||||||
|
f64 InjectingCubicBezierFromX(f64 p2x, f64 p2y, f64 p3x, f64 p3y, f64 x);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,37 +22,6 @@ namespace {
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume cubic injects over [0, 1]
|
|
||||||
f64 GetCubicUniqueReal(f64 a, f64 b, f64 c, f64 d) {
|
|
||||||
auto accept = [](f64 t){ return 0.0 <= t && t <= 1.0; };
|
|
||||||
|
|
||||||
f64 a1 = b/a, a2 = c/a, a3 = d/a;
|
|
||||||
f64 Q = (a1 * a1 - 3.0 * a2) / 9.0,
|
|
||||||
R = (2.0 * a1 * a1 * a1 - 9.0 * a1 * a2 + 27.0 * a3) / 54.0,
|
|
||||||
Qcubed = Q * Q * Q,
|
|
||||||
D = Qcubed - R * R;
|
|
||||||
|
|
||||||
if (D >= 0) {
|
|
||||||
f64 theta = acos(R / sqrt(Qcubed));
|
|
||||||
f64 sqrtQ = sqrt(Q);
|
|
||||||
f64 r1 = -2.0 * sqrtQ * cos(theta / 3.0) - a1 / 3.0,
|
|
||||||
r2 = -2.0 * sqrtQ * cos((theta + 2.0 * std::numbers::pi) / 3.0) - a1 / 3.0,
|
|
||||||
r3 = -2.0 * sqrtQ * cos((theta + 4.0 * std::numbers::pi) / 3.0) - a1 / 3.0;
|
|
||||||
return accept(r1) ? r1 : (accept(r2) ? r2 : r3);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
f64 e = std::pow(std::sqrt(-D) + std::abs(R), 1.0 / 3.0);
|
|
||||||
if (R > 0.0)
|
|
||||||
e = -e;
|
|
||||||
return (e + Q / e) - a1 / 3.;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f64 CubicBezier(f64 a, f64 b, f64 c, f64 d, f64 t) {
|
|
||||||
f64 t2 = t * t, t3 = t2 * t, mt = 1.0-t, mt2 = mt * mt, mt3 = mt2 * mt;
|
|
||||||
return a*mt3 + b*3.0*mt2*t + c*3.0*mt*t2 + d*t3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace K::PlugboardNodes {
|
namespace K::PlugboardNodes {
|
||||||
|
@ -497,8 +466,8 @@ namespace K::PlugboardNodes {
|
||||||
case K_I_CubicBezier: {
|
case K_I_CubicBezier: {
|
||||||
f32 p2x = extra.v[0], p2y = extra.v[1], p3x = extra.v[2], p3y = extra.v[3];
|
f32 p2x = extra.v[0], p2y = extra.v[1], p3x = extra.v[2], p3y = extra.v[3];
|
||||||
f32 a = 0.0f, b = p2x, c = p3x, d = 1.0f;
|
f32 a = 0.0f, b = p2x, c = p3x, d = 1.0f;
|
||||||
f64 t = GetCubicUniqueReal(-a+3.0f*b-3.0f*c+d, 3.0f*a-6.0f*b+3.0f*c, -3.0f*a+3.0f*b,a-x);
|
f64 t = Graphics::GetCubicUniqueReal(-a+3.0f*b-3.0f*c+d, 3.0f*a-6.0f*b+3.0f*c, -3.0f*a+3.0f*b,a-x);
|
||||||
return static_cast<f32>(CubicBezier(0.0f, p2y, p3y, 1.0f, t));
|
return static_cast<f32>(Graphics::CubicBezier(0.0f, p2y, p3y, 1.0f, t));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
|
@ -507,7 +476,6 @@ namespace K::PlugboardNodes {
|
||||||
|
|
||||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchInterpolation(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchInterpolation(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||||
auto *e = std::any_cast<InterpolationExtra>(&n.extra);
|
auto *e = std::any_cast<InterpolationExtra>(&n.extra);
|
||||||
auto type = e->interp;
|
|
||||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||||
return EvalInterpolation(x, *e);
|
return EvalInterpolation(x, *e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,11 +52,11 @@ void main() {
|
||||||
vec4 _A = texture2D(s_A, uv);
|
vec4 _A = texture2D(s_A, uv);
|
||||||
vec4 _B = texture2D(s_B, uv);
|
vec4 _B = texture2D(s_B, uv);
|
||||||
// Premult
|
// Premult
|
||||||
_A.xyz = _A.xyz * _A.w;
|
// _A.xyz = _A.xyz * _A.w;
|
||||||
_B.xyz = _B.xyz * _B.w;
|
// _B.xyz = _B.xyz * _B.w;
|
||||||
|
|
||||||
// Porter-Duff Source Over
|
// Porter-Duff Source Over
|
||||||
gl_FragColor.w = _A.w + _B.w * (1 - _A.w);
|
gl_FragColor.w = _A.w + _B.w * (1.0f - _A.w);
|
||||||
vec3 blend = vec3(0.0f, 0.0f, 0.0f);
|
vec3 blend = vec3(0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
int mode = round(u_mode.x);
|
int mode = round(u_mode.x);
|
||||||
|
@ -161,8 +161,9 @@ void main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Porter-Duff Source Over
|
// Porter-Duff Source Over
|
||||||
gl_FragColor.xyz = blend + _B.xyz * (1.0f - _A.w);
|
blend = (1 - _B.w) * _A.xyz + _B.w * blend;
|
||||||
|
gl_FragColor.xyz = blend * _A.w + _B.xyz * _B.w * (1.0f - _A.w);
|
||||||
|
|
||||||
// Unmult
|
// Unmult
|
||||||
gl_FragColor.xyz = gl_FragColor.xyz / gl_FragColor.w;
|
// gl_FragColor.xyz = gl_FragColor.xyz / gl_FragColor.w;
|
||||||
}
|
}
|
||||||
|
|
36
TODO.md
36
TODO.md
|
@ -3,42 +3,44 @@
|
||||||
- Node groups
|
- Node groups
|
||||||
|
|
||||||
## Compositor
|
## Compositor
|
||||||
- Manage Samplers -- (Resources in general)
|
- Manage Resources
|
||||||
|
- Samplers
|
||||||
- Blender previews (important !)
|
- Blender previews (important !)
|
||||||
- Dump and read back state, (de)serialization!!!
|
- Video import (research opencv libavcodec etc)
|
||||||
- Non-negotiables:
|
- maybe https://superuser.com/a/1397578 -- no portable way to get stdout, this is messed up
|
||||||
- Text (idea: index-based evaluation in plugboard)
|
- boost::process? idk, might as well just go through a file
|
||||||
- Shapes (idea: embed glisp :mmtroll:, need to inquire -- still a lot of friction for simple shapes if we don't also get the glisp gizmos)
|
- Use BG thread to check timestamps for hot reloading
|
||||||
- External data driving (csv, json or something else?) -- use a node to select source
|
- Data models
|
||||||
- Data model for comps
|
- Dump and read back state, (de)serialization!!!
|
||||||
- Pre-compose/Layer Groups (jokes -- can be completely UI side)
|
- Pre-compose/Layer Groups (jokes -- can be completely UI side)
|
||||||
- Motion blur
|
- Motion blur
|
||||||
|
- Non-negotiable - Text (idea: index-based evaluation in plugboard)
|
||||||
|
- Non-negotiable - Shapes (idea: embed glisp :mmtroll:, need to inquire -- still a lot of friction for simple shapes if we don't also get the glisp gizmos)
|
||||||
|
- Non-negotiable - External data driving (csv, json or something else?) -- use a node to select source
|
||||||
|
|
||||||
## UI
|
## UI
|
||||||
- Key selection in comp panel
|
- Key selection in comp panel
|
||||||
- Graph editor
|
- Graph editor
|
||||||
- Node loop detection (separate DFS (extra work) or be smart in recursion)
|
- Node loop detection (separate DFS (extra work) or be smart in recursion)
|
||||||
- detect time-sensitive nodes in tree and display on timeline
|
- detect time-sensitive nodes in tree and display on timeline
|
||||||
- Viewport gizmos (Layer-specific & nodes -- can separate into different tools?) -- Not sure how to go about this
|
|
||||||
|
|
||||||
# Later
|
# Later
|
||||||
|
## IO
|
||||||
|
- File dialogues pending SDL3
|
||||||
|
- https://wiki.libsdl.org/SDL3/CategoryDialog
|
||||||
|
- OIIO output for more than PNG's
|
||||||
|
- don't care about video export -- leave it for ffmpeg
|
||||||
|
|
||||||
## Compositor
|
## Compositor
|
||||||
- std::unordered_map -> std::flat_map pending compiler support
|
- std::unordered_map -> std::flat_map pending compiler support
|
||||||
- Simple 3D engine
|
- Simple 3D engine
|
||||||
|
|
||||||
## UI
|
## UI
|
||||||
- Adapt nodes for shader graph -- code editor will be fine for now
|
- Adapt nodes for shader graph -- code editor will be fine for now
|
||||||
|
- Viewport gizmos (Layer-specific & nodes -- can separate into different tools?) -- Not sure how to go about this
|
||||||
|
- Baku vec2 drag is good, color drag is not so necessary because imgui has a good picker
|
||||||
|
|
||||||
## Audio
|
## Audio
|
||||||
- Wait for SDL3!
|
- Wait for SDL3!
|
||||||
- SDL_mixer will be able to do all of wav ogg flac mp3 opus, we live in good times
|
- SDL_mixer will be able to do all of wav ogg flac mp3 opus, we live in good times
|
||||||
- output needs to be handled differently (if we care at all -- likely not)
|
- output needs to be handled differently (if we care at all -- likely not)
|
||||||
|
|
||||||
## IO
|
|
||||||
- Video import (research opencv libavcodec etc)
|
|
||||||
- maybe https://superuser.com/a/1397578 -- no portable way to get stdout, this is messed up
|
|
||||||
- boost::process? idk, might as well just go through a file
|
|
||||||
- File dialogues pending SDL3
|
|
||||||
- https://wiki.libsdl.org/SDL3/CategoryDialog
|
|
||||||
- Down the road: OIIO output for more than PNG's
|
|
||||||
- don't care about video export -- leave it for ffmpeg
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue