kfs
This commit is contained in:
parent
f5ae66e82d
commit
7b91f99ac4
10 changed files with 866 additions and 130 deletions
|
@ -22,6 +22,13 @@ add_subdirectory("ext/bgfx")
|
|||
|
||||
add_compile_definitions(IMGUI_DEFINE_MATH_OPERATORS)
|
||||
|
||||
# Disable RTTI
|
||||
if(MSVC)
|
||||
string(REGEX REPLACE "/GR" "/GR-" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
endif()
|
||||
|
||||
|
||||
if (WIN32)
|
||||
include_directories("include/windows")
|
||||
|
|
|
@ -26,6 +26,16 @@ namespace {
|
|||
}
|
||||
|
||||
namespace K {
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchCompFrame(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
return PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type{static_cast<i32>(s.current_frame)};
|
||||
}
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchCompTime(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
return PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type{static_cast<f32>(s.current_frame) / s.fps};
|
||||
}
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchAppTicks(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
return PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type{ static_cast<f32>(SDL_GetTicks64()) };
|
||||
}
|
||||
|
||||
bool Init() {
|
||||
if (SDL_Init(SDL_INIT_VIDEO)) {
|
||||
LogError(SDL_GetError());
|
||||
|
@ -101,9 +111,7 @@ namespace K {
|
|||
{},
|
||||
{{"Frame", PlugboardGraph::T_Int}, {"Time (s)", PlugboardGraph::T_Float}, {"App Ticks", PlugboardGraph::T_Float}},
|
||||
{
|
||||
[](const CompState& s, const Vector<PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type>& v) { return PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type{static_cast<i32>(s.current_frame)}; },
|
||||
[](const CompState& s, const Vector<PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type>& v) { return PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type{static_cast<f32>(s.current_frame) / s.fps}; },
|
||||
[](const CompState& s, const Vector<PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type>& v) { return PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type{ static_cast<f32>(SDL_GetTicks()) / 18.0f }; } // kill me
|
||||
FetchCompFrame, FetchCompTime, FetchAppTicks
|
||||
}
|
||||
};
|
||||
state.plugboard.comp_out = PlugboardGraph::Node{
|
||||
|
|
|
@ -25,22 +25,7 @@ namespace K::PlugboardGraph {
|
|||
}
|
||||
|
||||
T_Map<T_Count>::type Eval(const CompState& s, const ConnectInfo& info) {
|
||||
Vector<T_Map<T_Count>::type> pack;
|
||||
for (u32 i = 0; i < info.p->inputs_fed.size(); i++) {
|
||||
bool good;
|
||||
auto val = ConvertValue(std::visit([&s](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, ConnectInfo>)
|
||||
return Eval(s,arg);
|
||||
else if constexpr (std::is_same_v<T, T_Map<Type::T_Count>::type>)
|
||||
return arg;
|
||||
}, info.p->inputs_fed[i]), info.p->node->in[i].type, good);
|
||||
if (good) {
|
||||
pack.push_back(val);
|
||||
}
|
||||
else return {}; // todo panic!
|
||||
}
|
||||
return info.p->node->fetch[info.index](s, pack);
|
||||
return info.p->node->fetch[info.index](s, *info.p);
|
||||
}
|
||||
|
||||
NodeInstance MakeInstance(Node& n) {
|
||||
|
@ -50,11 +35,14 @@ namespace K::PlugboardGraph {
|
|||
inp.emplace_back(expand_type(t.type));
|
||||
for (auto t : n.out)
|
||||
out.emplace_back();
|
||||
return {
|
||||
auto instance = NodeInstance{
|
||||
.node = &n,
|
||||
.inputs_fed = std::move(inp),
|
||||
.outputs_going = std::move(out)
|
||||
};
|
||||
if (n.extra_setup != nullptr)
|
||||
n.extra_setup(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
T_Map<T_Count>::type ConvertValue(const T_Map<T_Count>::type& v, Type target, bool& good) {
|
||||
|
@ -66,7 +54,7 @@ namespace K::PlugboardGraph {
|
|||
case T_Float:
|
||||
return arg;
|
||||
case T_Int:
|
||||
return static_cast<T_Map<T_Float>::type>(arg);
|
||||
return static_cast<T_Map<T_Int>::type>(std::round(arg));
|
||||
case T_RGBA:
|
||||
return RGBA{ arg, arg, arg, arg };
|
||||
case T_XY:
|
||||
|
|
314
Keishiki/UI.cpp
314
Keishiki/UI.cpp
|
@ -19,7 +19,7 @@ namespace {
|
|||
draw_shader = true,
|
||||
draw_comp = true,
|
||||
draw_interpolation = true,
|
||||
draw_properties = true,
|
||||
// draw_properties = true,
|
||||
draw_assets = true;
|
||||
|
||||
const f32 row_height = 20.0f;
|
||||
|
@ -117,6 +117,22 @@ namespace K::UI {
|
|||
layer.track.clear();
|
||||
}
|
||||
|
||||
void DrawPlugboardVariableEditWidget(PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type& var) {
|
||||
std::visit([](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_Float>::type>)
|
||||
ImGui::DragFloat("##", &arg, 0.005f);
|
||||
else if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_Int>::type>)
|
||||
ImGui::DragInt("##", &arg, 0.005f);
|
||||
else if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_RGBA>::type>)
|
||||
ImGui::DragFloat4("##", &arg.r, 0.005f);
|
||||
else if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_XY>::type>)
|
||||
ImGui::DragFloat2("##", &arg.x, 0.005f);
|
||||
else if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_XYZ>::type>)
|
||||
ImGui::DragFloat3("##", &arg.x, 0.005f);
|
||||
}, var);
|
||||
}
|
||||
|
||||
void PlugboardNodeDrawSockets(PlugboardGraph::NodeInstance& n, ImGuiStyle& style,
|
||||
bool& dragging_on_socket, ImVec2& source, std::unordered_map<PlugboardGraph::ConnectInfo, PlugboardGraph::LinksFromSource, PlugboardGraph::ConnectInfoHasher>& link_pos,
|
||||
bool dummy = true) {
|
||||
|
@ -177,34 +193,42 @@ namespace K::UI {
|
|||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
auto socket_link_pos = ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y};
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(n.node->in[in].name.c_str());
|
||||
|
||||
// Update link info
|
||||
std::visit([&dragging_on_socket, &source, &link_pos, &style](auto&& arg) {
|
||||
std::visit([&socket_link_pos, &dragging_on_socket, &source, &link_pos, &style](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, PlugboardGraph::ConnectInfo>) {
|
||||
if (ImGui::IsItemActive())
|
||||
link_pos[arg].sinks.push_back(ImGui::GetMousePos());
|
||||
else
|
||||
link_pos[arg].sinks.push_back(ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y});
|
||||
link_pos[arg].sinks.push_back(socket_link_pos);
|
||||
}
|
||||
else if (ImGui::IsItemActive()) {
|
||||
dragging_on_socket = true;
|
||||
source = ImGui::GetCursorScreenPos() + ImVec2{ ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y};
|
||||
source = socket_link_pos;
|
||||
}
|
||||
if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::Type::T_Count>::type>) {
|
||||
ImGui::SameLine();
|
||||
DrawPlugboardVariableEditWidget(arg);
|
||||
}
|
||||
}, n.inputs_fed[in]);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(n.node->in[in].name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PopID();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void PlugboardDrawNode(PlugboardGraph::NodeInstance& n, ImVec2& view_pos, i32 id, ImGuiIO& io, ImGuiStyle& style,
|
||||
bool& dragging_on_socket, ImVec2& source, std::unordered_map<PlugboardGraph::ConnectInfo, PlugboardGraph::LinksFromSource, PlugboardGraph::ConnectInfoHasher>& link_pos) {
|
||||
bool PlugboardDrawNode(PlugboardGraph::NodeInstance& n, ImVec2& view_pos, i32 id, ImGuiIO& io, ImGuiStyle& style,
|
||||
bool& dragging_on_socket, ImVec2& source, std::unordered_map<PlugboardGraph::ConnectInfo, PlugboardGraph::LinksFromSource, PlugboardGraph::ConnectInfoHasher>& link_pos, bool selected) {
|
||||
bool stat = false;
|
||||
ImGui::SetCursorPos(view_pos + n.pos);
|
||||
ImGui::PushID(id);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xAA080813);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, selected ? 0xAA202040 : 0xAA080813);
|
||||
if (ImGui::BeginChild(n.node->name.c_str(), {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border)) {
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::BeginTable(n.node->name.c_str(), 3)) {
|
||||
|
@ -214,12 +238,21 @@ namespace K::UI {
|
|||
ImGui::Button(n.node->name.c_str(), {100.0f , 0.0f});
|
||||
if (ImGui::IsItemClicked()) {
|
||||
n.old_pos = n.pos;
|
||||
stat = true;
|
||||
}
|
||||
if (ImGui::IsItemActive()) {
|
||||
n.pos = n.old_pos + (io.MousePos - io.MouseClickedPos[0]);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (n.node->special_draw != nullptr) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
n.node->special_draw(n);
|
||||
ImGui::TableNextColumn();
|
||||
}
|
||||
|
||||
PlugboardNodeDrawSockets(n, style, dragging_on_socket, source, link_pos);
|
||||
|
||||
ImGui::EndTable();
|
||||
|
@ -227,6 +260,7 @@ namespace K::UI {
|
|||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::PopID();
|
||||
return stat;
|
||||
}
|
||||
|
||||
void Viewport(CompState& s) {
|
||||
|
@ -280,8 +314,25 @@ namespace K::UI {
|
|||
return;
|
||||
}
|
||||
|
||||
if (ImGui::Button("Add Sine"))
|
||||
s.plugboard.nodes.push_back(PlugboardGraph::MakeInstance(PlugboardNodes::Sine));
|
||||
static u32 node_current{};
|
||||
if (ImGui::Button("Add")) {
|
||||
auto n = PlugboardGraph::MakeInstance(*PlugboardNodes::Nodes[node_current]);
|
||||
s.plugboard.nodes.push_back(std::move(n));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(150.0f);
|
||||
if (ImGui::BeginCombo("##Node", PlugboardNodes::Nodes[node_current]->name.c_str(), ImGuiComboFlags_None)) {
|
||||
for (int n = 0; n < PlugboardNodes::Nodes.size(); n++) {
|
||||
const bool is_selected = (node_current == n);
|
||||
if (ImGui::Selectable(PlugboardNodes::Nodes[n]->name.c_str(), is_selected))
|
||||
node_current = n;
|
||||
|
||||
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%lu / %lu Frames", s.current_frame, s.frame_max);
|
||||
|
@ -294,7 +345,9 @@ namespace K::UI {
|
|||
auto l = Layer{
|
||||
VisualTrack{},
|
||||
name,
|
||||
Graphics::K_V_AlphaOver
|
||||
Graphics::K_V_AlphaOver,
|
||||
0UL,
|
||||
s.frame_max
|
||||
};
|
||||
l.track.add_uniform("aspect_ratio", ShaderGraph::expand_type(ShaderGraph::T_XY));
|
||||
if (s.width > s.height)
|
||||
|
@ -373,9 +426,12 @@ namespace K::UI {
|
|||
ImVec2 drag_source{};
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF100500);
|
||||
ImGui::SetNextWindowSizeConstraints({avail.x * .1f, -1}, { avail.x * .8f, -1 });
|
||||
if (ImGui::BeginChild("Nodes", {avail.x * .4f, avail.y}, ImGuiChildFlags_ResizeX, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar)) {
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
static u32 selected;
|
||||
|
||||
ImGuiWindow *window = ImGui::GetCurrentWindow();
|
||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||
nodes_begin = window_pos;
|
||||
|
@ -407,7 +463,8 @@ namespace K::UI {
|
|||
|
||||
u32 i = 0;
|
||||
for (auto it = s.plugboard.nodes.begin(); it != s.plugboard.nodes.end(); it++, i++)
|
||||
PlugboardDrawNode(*it, view_pos, static_cast<i32>(i), io, style, dragging_on_socket, drag_source, s.plugboard.links_pos);
|
||||
if (PlugboardDrawNode(*it, view_pos, static_cast<i32>(i), io, style, dragging_on_socket, drag_source, s.plugboard.links_pos, selected == i))
|
||||
selected = i;
|
||||
|
||||
ImGui::SetCursorPos({});
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, 0x33FFFFFF);
|
||||
|
@ -462,9 +519,9 @@ namespace K::UI {
|
|||
ImGui::TableSetColumnIndex(4);
|
||||
|
||||
auto *window = ImGui::GetCurrentWindow();
|
||||
const f32 knob_width = 8.0f;
|
||||
const static f32 knob_width = 8.0f;
|
||||
tl_init_pos = ImGui::GetCursorScreenPos();
|
||||
view_width = ImGui::GetColumnWidth(),
|
||||
view_width = ImGui::GetColumnWidth() - 10.0f,
|
||||
tl_width = view_width - knob_width,
|
||||
view_height = ImGui::TableGetHeaderRowHeight(),
|
||||
control_left = tl_init_pos.x,
|
||||
|
@ -511,15 +568,15 @@ namespace K::UI {
|
|||
view_right = std::clamp(view_right_old + delta_x, view_left, 1.0f);
|
||||
}
|
||||
|
||||
window->DrawList->AddLine({tl_init_pos.x, tl_init_pos.y}, {tl_init_pos.x, tl_init_pos.y + view_height},
|
||||
0x22FFFFFF, 2.0f);
|
||||
window->DrawList->AddLine({tl_init_pos.x + view_width, tl_init_pos.y},
|
||||
{tl_init_pos.x + view_width, tl_init_pos.y + view_height}, 0x22FFFFFF, 2.0f);
|
||||
window->DrawList->AddLine({tl_init_pos.x + view_width * view_left, tl_init_pos.y},
|
||||
{tl_init_pos.x + view_width * view_left, tl_init_pos.y + view_height},
|
||||
window->DrawList->AddRect({tl_init_pos.x - knob_width / 2.0f, tl_init_pos.y}, {tl_init_pos.x + knob_width / 2.0f, tl_init_pos.y + view_height},
|
||||
0x22FFFFFF);
|
||||
window->DrawList->AddRect({tl_init_pos.x + view_width - knob_width / 2.0f, tl_init_pos.y},
|
||||
{tl_init_pos.x + view_width + knob_width / 2.0f, tl_init_pos.y + view_height}, 0x22FFFFFF);
|
||||
window->DrawList->AddRectFilled({tl_init_pos.x + view_width * view_left - knob_width / 2.0f, tl_init_pos.y},
|
||||
{tl_init_pos.x + view_width * view_left + knob_width / 2.0f, tl_init_pos.y + view_height},
|
||||
0xAAFFFFFF, 2.0f);
|
||||
window->DrawList->AddLine({tl_init_pos.x + view_width * view_right, tl_init_pos.y},
|
||||
{tl_init_pos.x + view_width * view_right, tl_init_pos.y + view_height},
|
||||
window->DrawList->AddRectFilled({tl_init_pos.x + view_width * view_right - knob_width / 2.0f, tl_init_pos.y},
|
||||
{tl_init_pos.x + view_width * view_right + knob_width / 2.0f, tl_init_pos.y + view_height},
|
||||
0xAAFFFFFF, 2.0f);
|
||||
window->DrawList->AddRectFilled(
|
||||
{tl_init_pos.x + view_width * view_left, tl_init_pos.y + view_height * .2f},
|
||||
|
@ -560,10 +617,10 @@ namespace K::UI {
|
|||
ImGui::TableHeader("");
|
||||
|
||||
// Main rows
|
||||
// ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, {0.0f, 0.0f});
|
||||
i32 move_from = -1, move_to = -1;
|
||||
bool after{};
|
||||
for (u32 i = 0; i < s.layers.size(); i++) {
|
||||
auto& current_layer = s.layers[i];
|
||||
const bool selected = s.selected.contains(i), disabled = s.disabled.contains(i);
|
||||
ImGui::PushID(static_cast<i32>(i));
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height);
|
||||
|
@ -579,8 +636,7 @@ namespace K::UI {
|
|||
flags |= ImGuiTreeNodeFlags_Selected;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
|
||||
{0.0f, (row_height - ImGui::GetTextLineHeight()) / 2.0f});
|
||||
bool layer_open = ImGui::TreeNodeEx(s.layers[i].name.c_str(), flags);
|
||||
ImGui::PopStyleVar();
|
||||
bool layer_open = ImGui::TreeNodeEx(current_layer.name.c_str(), flags);
|
||||
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) {
|
||||
if (io.KeyCtrl) {
|
||||
if (selected)
|
||||
|
@ -601,7 +657,7 @@ namespace K::UI {
|
|||
ImGuiDragDropFlags_SourceNoDisableHover | ImGuiDragDropFlags_SourceNoHoldToOpenOthers;
|
||||
if (!no_selection) source_flags |= ImGuiDragDropFlags_SourceNoPreviewTooltip;
|
||||
if ((no_selection || s.selected.contains(i)) && ImGui::BeginDragDropSource(source_flags)) {
|
||||
if (s.selected.empty()) ImGui::Text("Swap with #%u %s", i, s.layers[i].name.c_str());
|
||||
if (s.selected.empty()) ImGui::Text("Swap with #%u %s", i, current_layer.name.c_str());
|
||||
ImGui::SetDragDropPayload("K_COMP_REORDER", &i, sizeof(u32));
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
@ -610,9 +666,9 @@ namespace K::UI {
|
|||
after = ImGui::GetMousePos().y - ImGui::GetWindowPos().y >
|
||||
ImGui::GetCursorPosY() - row_height / 2.0f;
|
||||
if (after)
|
||||
ImGui::SetTooltip("After #%u %s", i, s.layers[i].name.c_str());
|
||||
ImGui::SetTooltip("After #%u %s", i, current_layer.name.c_str());
|
||||
else
|
||||
ImGui::SetTooltip("Before #%u %s", i, s.layers[i].name.c_str());
|
||||
ImGui::SetTooltip("Before #%u %s", i, current_layer.name.c_str());
|
||||
}
|
||||
|
||||
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_COMP_REORDER")) {
|
||||
|
@ -624,11 +680,11 @@ namespace K::UI {
|
|||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
if (ImGui::BeginCombo("##Blending", Graphics::BlendingToString[s.layers[i].mode])) {
|
||||
if (ImGui::BeginCombo("##Blending", Graphics::BlendingToString[current_layer.mode])) {
|
||||
for (i32 b = Graphics::K_V_AlphaOver; b != Graphics::K_V_Count; b++) {
|
||||
const bool is_selected = (static_cast<Graphics::Blending>(b) == s.layers[i].mode);
|
||||
const bool is_selected = (static_cast<Graphics::Blending>(b) == current_layer.mode);
|
||||
if (ImGui::Selectable(Graphics::BlendingToString[b], is_selected))
|
||||
s.layers[i].mode = static_cast<Graphics::Blending>(b);
|
||||
current_layer.mode = static_cast<Graphics::Blending>(b);
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
|
@ -653,23 +709,8 @@ namespace K::UI {
|
|||
|
||||
f32 init_y = ImGui::GetCursorScreenPos().y;
|
||||
|
||||
// do stuff here
|
||||
static u32 pos = 20, pos_old = pos;
|
||||
|
||||
f32 pos_view = static_cast<f32>(pos) / static_cast<f32>(s.frame_max + 1);
|
||||
if (pos_view >= view_left && pos_view < view_right) {
|
||||
ImGui::Dummy({(pos_view - view_left) / view_amt * view_width - 5.0f, 0.0f});
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("kf", {0.0f, row_height * .8f});
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
pos_old = pos;
|
||||
}
|
||||
if (ImGui::IsItemActive()) {
|
||||
pos = std::clamp(i64(pos_old) +
|
||||
i64(std::round(delta_x * (view_amt * static_cast<f32>(s.frame_max + 1)))), 0L, i64(s.frame_max)
|
||||
);
|
||||
}
|
||||
}
|
||||
ImGui::Dummy({TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.in, s.frame_max) + tl_init_pos.x, 0.0f});
|
||||
ImGui::Button(current_layer.name.c_str(), {std::min(TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.out, s.frame_max) + fr_step, view_width), view_height});
|
||||
|
||||
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});
|
||||
|
@ -681,7 +722,7 @@ namespace K::UI {
|
|||
|
||||
if (layer_open) {
|
||||
i32 j = 0;
|
||||
for (auto &u: s.layers[i].track.uniforms) {
|
||||
for (auto &u: current_layer.track.uniforms) {
|
||||
ImGui::PushID(j);
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height);
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
|
@ -699,8 +740,9 @@ namespace K::UI {
|
|||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
auto& connected_v = u.second.connection.p->inputs_fed[u.second.connection.index];
|
||||
|
||||
std::visit([&dragging_on_socket, &drag_source, &s, &style](auto &&arg) {
|
||||
std::visit([&u, &dragging_on_socket, &drag_source, &s, &style](auto &&arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, PlugboardGraph::ConnectInfo>) {
|
||||
if (ImGui::IsItemActive())
|
||||
|
@ -710,25 +752,36 @@ namespace K::UI {
|
|||
ImVec2{ImGui::GetFrameHeight() / 2,
|
||||
-ImGui::GetFrameHeight() / 2 -
|
||||
style.ItemInnerSpacing.y});
|
||||
} else if (ImGui::IsItemActive()) {
|
||||
}
|
||||
else if (ImGui::IsItemActive()) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_LeftShift)) {
|
||||
s.plugboard.nodes.push_back(PlugboardGraph::MakeInstance(PlugboardNodes::Chain));
|
||||
auto it = s.plugboard.nodes.rbegin();
|
||||
u.second.connection.p->connect(u.second.connection.index, &*it, 0);
|
||||
it->connect(0, &s.plugboard.in_instance, 0);
|
||||
// what happens to atomicity? though taking address is safe since we use a list now
|
||||
}
|
||||
else {
|
||||
dragging_on_socket = true;
|
||||
drag_source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2,
|
||||
-ImGui::GetFrameHeight() / 2 -
|
||||
style.ItemInnerSpacing.y};
|
||||
}
|
||||
}, u.second.connection.p->inputs_fed[u.second.connection.index]);
|
||||
}
|
||||
}, connected_v);
|
||||
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::TreeNodeEx(u.first.c_str(),
|
||||
ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf |
|
||||
ImGuiTreeNodeFlags_NoTreePushOnOpen);
|
||||
auto u_flags = ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_FramePadding;
|
||||
if (connected_v.index() != 1)
|
||||
u_flags |= ImGuiTreeNodeFlags_Leaf;
|
||||
bool uniform_open = ImGui::TreeNodeEx(u.first.c_str(),u_flags);
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
|
||||
bool plugged = u.second.connection.p->inputs_fed[u.second.connection.index].index() == 1;
|
||||
if (!plugged)
|
||||
std::visit([](auto &&arg) {
|
||||
|
||||
if (connected_v.index() == 0) {
|
||||
std::visit([](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_Float>::type>)
|
||||
ImGui::DragFloat("##", &arg, 0.005f);
|
||||
|
@ -740,21 +793,73 @@ namespace K::UI {
|
|||
ImGui::DragFloat2("##", &arg.x, 0.005f);
|
||||
else if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_XYZ>::type>)
|
||||
ImGui::DragFloat3("##", &arg.x, 0.005f);
|
||||
}, std::get<0>(u.second.connection.p->inputs_fed[u.second.connection.index]));
|
||||
|
||||
}, std::get<0>(connected_v));
|
||||
}
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
ImGui::TableSetColumnIndex(4);
|
||||
auto p = ImGui::GetCursorScreenPos();
|
||||
|
||||
// draw on tl
|
||||
|
||||
ImGui::SetCursorScreenPos(p);
|
||||
|
||||
ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, row_height + style.CellPadding.y});
|
||||
if (ImGui::IsItemActive()) {
|
||||
s.current_frame = TimelineScreenViewToFrame(view_left, view_amt, view_width, std::clamp(ImGui::GetMousePos().x - tl_init_pos.x, 0.0f, view_width), s.frame_max);
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
if (uniform_open && (connected_v.index() == 1) && std::get<1>(connected_v).p->node == &PlugboardNodes::Chain) {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height);
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Indent();
|
||||
ImGui::TreeNodeEx(std::get<1>(connected_v).p->node->name.c_str(),
|
||||
ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_FramePadding);
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
auto *m = std::any_cast<std::map<u32, PlugboardNodes::ChainSegment>>(&std::get<1>(connected_v).p->extra);
|
||||
f32 v = std::get<PlugboardGraph::T_Map<PlugboardGraph::T_Float>::type>(PlugboardGraph::Eval(s, std::get<1>(connected_v))), copy = v;
|
||||
ImGui::DragFloat("##value", &v);
|
||||
if (v != copy) {
|
||||
auto l = m->lower_bound(s.current_frame);
|
||||
auto interp = l == m->end() ? PlugboardNodes::K_I_Linear : l->second.interp.interp;
|
||||
m->insert_or_assign(s.current_frame, PlugboardNodes::ChainSegment{
|
||||
.value = v,
|
||||
.interp = {
|
||||
.interp = interp
|
||||
}
|
||||
});
|
||||
}
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
ImGui::Text("%lu", m->size());
|
||||
ImGui::TableSetColumnIndex(4);
|
||||
ImVec2 begin_tl = ImGui::GetCursorScreenPos();
|
||||
static u32 drag_old{};
|
||||
for (auto& [k, segment] : *m)
|
||||
if (TimelineFrameInView(view_left, view_right, k, s.frame_max)) {
|
||||
ImGui::SetCursorScreenPos({TimelineFrameToScreenView(view_left, view_amt, view_width, k, s.frame_max) + begin_tl.x - 2.5f, begin_tl.y});
|
||||
ImGui::Button("k", {0.0f, row_height * .8f});
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
drag_old = k;
|
||||
}
|
||||
if (ImGui::IsItemActive()) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetCursorScreenPos(begin_tl);
|
||||
ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, row_height + style.CellPadding.y});
|
||||
if (ImGui::IsItemActive()) {
|
||||
s.current_frame = TimelineScreenViewToFrame(view_left, view_amt, view_width, std::clamp(ImGui::GetMousePos().x - tl_init_pos.x, 0.0f, view_width), s.frame_max);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
j++;
|
||||
}
|
||||
ImGui::TreePop();
|
||||
} else
|
||||
for (auto &u: s.layers[i].track.uniforms)
|
||||
for (auto &u: current_layer.track.uniforms)
|
||||
std::visit([table_left, &s](auto &&arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, PlugboardGraph::ConnectInfo>)
|
||||
|
@ -762,9 +867,10 @@ namespace K::UI {
|
|||
{table_left, ImGui::GetCursorScreenPos().y - row_height / 2.0f});
|
||||
}, u.second.connection.p->inputs_fed[u.second.connection.index]);
|
||||
|
||||
|
||||
ImGui::PopStyleVar(); // FramePadding
|
||||
ImGui::PopID();
|
||||
}
|
||||
// ImGui::PopStyleVar();
|
||||
|
||||
if (move_from != -1 && move_to != -1) {
|
||||
if (no_selection)
|
||||
|
@ -796,15 +902,16 @@ namespace K::UI {
|
|||
|
||||
ImGui::SetCursorScreenPos(nodes_begin);
|
||||
if (ImGui::BeginChild("Overlay", {table_left - ImGui::GetWindowPos().x, 0.0f}, false, ImGuiWindowFlags_NoInputs)) {
|
||||
auto *window = ImGui::GetCurrentWindow();
|
||||
for (auto &[_, link]: s.plugboard.links_pos)
|
||||
for (auto &sink: link.sinks)
|
||||
ImGui::GetCurrentWindow()->DrawList->AddBezierCubic(link.source,
|
||||
window->DrawList->AddBezierCubic(link.source,
|
||||
{link.source.x + 60.0f, link.source.y},
|
||||
{sink.x - 60.0f, sink.y}, sink, 0xFFFFFFFF,
|
||||
2.0f);
|
||||
|
||||
if (dragging_on_socket)
|
||||
ImGui::GetCurrentWindow()->DrawList->AddLine(ImGui::GetMousePos(), drag_source, 0xFFFFFFFF, 2.0f);
|
||||
window->DrawList->AddLine(ImGui::GetMousePos(), drag_source, 0xFFFFFFFF, 2.0f);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
|
@ -833,6 +940,7 @@ namespace K::UI {
|
|||
static String name{};
|
||||
if (ImGui::Button("Add Uniform") && !name.empty() && !isdigit(name[0]) && !(name[0] == 'f' && name.length() == 1)) {
|
||||
s.layers[s.active].track.add_uniform(name, ShaderGraph::expand_type(static_cast<ShaderGraph::Type>(type_current)));
|
||||
s.layers[s.active].track.expose_uniform(s, name);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
struct TextFilters { static int VariableName(ImGuiInputTextCallbackData *data) {
|
||||
|
@ -869,19 +977,7 @@ namespace K::UI {
|
|||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
if (it->second.connection.p->inputs_fed[it->second.connection.index].index() == 0)
|
||||
std::visit([](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_Float>::type>)
|
||||
ImGui::DragFloat("##", &arg, 0.005f);
|
||||
else if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_Int>::type>)
|
||||
ImGui::DragInt("##", &arg, 0.005f);
|
||||
else if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_RGBA>::type>)
|
||||
ImGui::DragFloat4("##", &arg.r, 0.005f);
|
||||
else if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_XY>::type>)
|
||||
ImGui::DragFloat2("##", &arg.x, 0.005f);
|
||||
else if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_XYZ>::type>)
|
||||
ImGui::DragFloat3("##", &arg.x, 0.005f);
|
||||
}, std::get<0>(it->second.connection.p->inputs_fed[it->second.connection.index]));
|
||||
DrawPlugboardVariableEditWidget(std::get<0>(it->second.connection.p->inputs_fed[it->second.connection.index]));
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button("X")) {
|
||||
bgfx::destroy(it->second.handle);
|
||||
|
@ -900,6 +996,42 @@ namespace K::UI {
|
|||
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) {
|
||||
if (ImGui::Begin("Interpolation", &draw_interpolation, ImGuiWindowFlags_NoScrollbar)) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
@ -927,7 +1059,21 @@ namespace K::UI {
|
|||
dl->AddLine(tp1, {tp1.x + w, tp1.y}, 0x44FFFFFF, 2.0f);
|
||||
dl->AddLine(tp4, {tp4.x - w, tp4.y}, 0x44FFFFFF, 2.0f);
|
||||
dl->AddText(tp1 + ImVec2{10.0f, -20.0f}, 0xFFFFFFFF, "0.0");
|
||||
dl->AddText(tp4 + ImVec2{-30.0f, 15.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 y = sb233asdf(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{x * w, 0.0f}, pos + ImVec2{x * w, w}, 0x44FF5555, 1.0f);
|
||||
|
||||
ImGui::SetCursorScreenPos({tp1.x, ((1.0f - y) - (1.0f - y_max)) * w / y_range + pos.y});
|
||||
ImGui::Text("%.2f, %.2f", x, y);
|
||||
|
||||
ImGui::SetCursorPos(ImVec2{0.0f, 20.0f});
|
||||
ImGui::Text("%.2f", y_max);
|
||||
|
||||
ImGui::SetCursorPos(ImVec2{w * .8f, w - 15.0f,});
|
||||
ImGui::Text("%.2f", y_min);
|
||||
|
||||
ImVec2 size = {15.0f, 15.0f};
|
||||
ImGui::SetCursorScreenPos(tp2 - size / 2.0f);
|
||||
|
@ -959,16 +1105,9 @@ namespace K::UI {
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
void Properties(CompState& s) {
|
||||
if (ImGui::Begin("Properties", &draw_properties)) {
|
||||
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void Assets(CompState& s) {
|
||||
if (ImGui::Begin("Assets", &draw_assets)) {
|
||||
|
||||
ImGui::Text("mmoker");
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
@ -988,7 +1127,6 @@ namespace K::UI {
|
|||
if (draw_shader) Shader(s);
|
||||
if (draw_comp) Composition(s);
|
||||
if (draw_interpolation) Interpolation(s);
|
||||
if (draw_properties) Properties(s);
|
||||
if (draw_assets) Assets(s);
|
||||
|
||||
if (save_called && ready_frame == frame) {
|
||||
|
|
|
@ -151,7 +151,7 @@ namespace K {
|
|||
case bgfx::RendererType::Vulkan: s = "./ext/bgfx/cmake/bgfx/shaderc "
|
||||
"-f temp.frag "
|
||||
"--type fragment "
|
||||
"--platform windows "
|
||||
"--platform linux "
|
||||
"--profile " "spirv" " ""--varyingdef temp.varying.def.sc "
|
||||
"-i ./ "
|
||||
"-o shaders/" "spirv" "/temp.frag.bin"; break;
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace K {
|
|||
template <typename T> using Vector = std::vector<T>;
|
||||
template <typename T, typename U> using Dict = std::unordered_map<T, U>; // to be replaced
|
||||
|
||||
inline void LogBase(const String& s, u8 level) {
|
||||
inline void LogBase(const std::string_view& s, u8 level) {
|
||||
static const char *levels[] = {
|
||||
"Info",
|
||||
"Warning",
|
||||
|
@ -33,10 +33,10 @@ namespace K {
|
|||
};
|
||||
level > 1 ? std::cerr : std::cout << "[" << levels[level] << "] Keishiki: " << s << std::endl;
|
||||
}
|
||||
inline void Log(const String& s) {
|
||||
inline void Log(const std::string_view& s) {
|
||||
LogBase(s, 0);
|
||||
}
|
||||
inline void LogError(const String& s) {
|
||||
inline void LogError(const std::string_view& s) {
|
||||
LogBase(s, 2);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace K {
|
|||
VisualTrack track;
|
||||
String name;
|
||||
Graphics::Blending mode;
|
||||
u64 in, out;
|
||||
};
|
||||
|
||||
struct CompState {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <functional>
|
||||
#include <imgui.h>
|
||||
#include <list>
|
||||
#include <any>
|
||||
|
||||
namespace K::PlugboardGraph {
|
||||
enum Type {
|
||||
|
@ -60,17 +61,18 @@ namespace K::PlugboardGraph {
|
|||
Type type;
|
||||
};
|
||||
|
||||
struct NodeInstance;
|
||||
struct Node {
|
||||
String name; // will likely get SSO for most stuff
|
||||
|
||||
Vector<Socket> in;
|
||||
Vector<Socket> out;
|
||||
|
||||
Vector<std::function<T_Map<T_Count>::type(const CompState&, const Vector<T_Map<T_Count>::type>&)>> fetch; // maybe change to a function pointer later
|
||||
// todo investigate how you might wanna jit this instead of evaluating a tree for each output at runtime esp. when a project is set for rendering
|
||||
Vector<T_Map<T_Count>::type(*)(const CompState&, const NodeInstance&)> fetch; // todo investigate how you might wanna jit this instead of evaluating a tree for each output at runtime esp. when a project is set for rendering
|
||||
void (*special_draw)(NodeInstance&);
|
||||
void (*extra_setup)(NodeInstance&);
|
||||
};
|
||||
|
||||
struct NodeInstance;
|
||||
|
||||
struct ConnectInfo {
|
||||
NodeInstance *p; // NOTE: so NodeInstances must be stored in a list, otherwise vector realloc leads to UAF
|
||||
|
@ -81,7 +83,7 @@ namespace K::PlugboardGraph {
|
|||
struct ConnectInfoHasher {
|
||||
std::size_t operator()(const ConnectInfo& k) const
|
||||
{
|
||||
return std::hash<NodeInstance *>()(k.p);
|
||||
return std::hash<NodeInstance *>()(k.p) ^ std::hash<u32>()(k.index);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -93,6 +95,8 @@ namespace K::PlugboardGraph {
|
|||
|
||||
ImVec2 pos, old_pos;
|
||||
|
||||
std::any extra;
|
||||
|
||||
void connect(u32 input_index, NodeInstance *to, u32 to_out_index) {
|
||||
inputs_fed[input_index] = ConnectInfo{to, to_out_index};
|
||||
to->outputs_going[to_out_index].push_back({this, input_index});
|
||||
|
|
|
@ -1,15 +1,607 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "PlugboardGraph.h"
|
||||
#include <map>
|
||||
#include <numbers>
|
||||
#include <array>
|
||||
#include <bx/easing.h>
|
||||
|
||||
namespace {
|
||||
template <K::PlugboardGraph::Type type>
|
||||
static K::PlugboardGraph::T_Map<type>::type GetNodeInputArg(const K::CompState& s, const K::PlugboardGraph::NodeInstance& n, u32 index) {
|
||||
bool good;
|
||||
typename K::PlugboardGraph::T_Map<type>::type v = std::get<typename K::PlugboardGraph::T_Map<type>::type>(K::PlugboardGraph::ConvertValue(std::visit([&s](auto&& arg) {
|
||||
using U = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<U, K::PlugboardGraph::ConnectInfo>)
|
||||
return K::PlugboardGraph::Eval(s, arg);
|
||||
else
|
||||
return arg;
|
||||
}, n.inputs_fed[index]), type, good));
|
||||
if (!good)
|
||||
K::LogError("Type mismatch on plugboard evaluation!");
|
||||
|
||||
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 {
|
||||
PlugboardGraph::Node Sine = {
|
||||
.name = "Sine",
|
||||
.in = { {"In", PlugboardGraph::T_Float } },
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchAdd(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0),
|
||||
y = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 1);
|
||||
return x + y;
|
||||
}
|
||||
|
||||
.out = { { "Out", PlugboardGraph::T_Float } },
|
||||
.fetch = {[](const CompState&, const Vector<PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type>& arg) {
|
||||
return std::sin(std::get<PlugboardGraph::T_Float>(arg[0]));
|
||||
}}
|
||||
PlugboardGraph::Node Add = {
|
||||
.name = "Add",
|
||||
.in = { {"a", PlugboardGraph::T_Float }, {"b", PlugboardGraph::T_Float } },
|
||||
.out = { { "a+b", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchAdd }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchNegate(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return -x;
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Negate = {
|
||||
.name = "Negate",
|
||||
.in = { {"a", PlugboardGraph::T_Float } },
|
||||
.out = { { "-a", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchNegate }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchSubtract(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0),
|
||||
y = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 1);
|
||||
return x-y;
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Subtract = {
|
||||
.name = "Subtract",
|
||||
.in = { {"a", PlugboardGraph::T_Float }, {"b", PlugboardGraph::T_Float } },
|
||||
.out = { { "a-b", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchSubtract }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchMultiply(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0),
|
||||
y = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 1);
|
||||
return x*y;
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Multiply = {
|
||||
.name = "Multiply",
|
||||
.in = { {"a", PlugboardGraph::T_Float }, {"b", PlugboardGraph::T_Float } },
|
||||
.out = { { "a*b", PlugboardGraph::T_Float } },
|
||||
.fetch = {FetchMultiply }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchDivide(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0),
|
||||
y = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 1);
|
||||
return x/y;
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Divide = {
|
||||
.name = "Divide",
|
||||
.in = { {"a", PlugboardGraph::T_Float }, {"b", PlugboardGraph::T_Float } },
|
||||
.out = { { "a/b", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchDivide }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchSign(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return std::signbit(x) ? -1.0f : 1.0f;
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Sign = {
|
||||
.name = "Sign",
|
||||
.in = { {"a", PlugboardGraph::T_Float } },
|
||||
.out = { { "sgn(a)", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchSign }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchSin(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return std::sin(x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Sin = {
|
||||
.name = "Sin",
|
||||
.in = { {"a (rad)", PlugboardGraph::T_Float } },
|
||||
.out = { { "sin(a)", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchSin }
|
||||
};
|
||||
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchCos(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return std::cos(x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Cos = {
|
||||
.name = "Cos",
|
||||
.in = { {"a (rad)", PlugboardGraph::T_Float } },
|
||||
.out = { { "cos(a)", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchCos }
|
||||
};
|
||||
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchTan(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return std::tan(x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Tan = {
|
||||
.name = "Tan",
|
||||
.in = { {"a (rad)", PlugboardGraph::T_Float } },
|
||||
.out = { { "tan(a)", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchTan }
|
||||
};
|
||||
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchArcsin(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return std::asin(x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Arcsin = {
|
||||
.name = "Arcsin",
|
||||
.in = { {"a", PlugboardGraph::T_Float } },
|
||||
.out = { { "arcsin(a) (rad)", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchArcsin }
|
||||
};
|
||||
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchArccos(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return std::acos(x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Arccos = {
|
||||
.name = "Arccos",
|
||||
.in = { {"a", PlugboardGraph::T_Float } },
|
||||
.out = { { "arccos(a) (rad)", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchArccos }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchArctan(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return std::atan(x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Arctan = {
|
||||
.name = "Arctan",
|
||||
.in = { {"a", PlugboardGraph::T_Float } },
|
||||
.out = { { "arctan(a) (rad)", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchArctan }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchAtan2(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 y = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0),
|
||||
x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 1);
|
||||
return std::atan2(y, x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Atan2 = {
|
||||
.name = "Atan2",
|
||||
.in = { {"y", PlugboardGraph::T_Float }, {"x", PlugboardGraph::T_Float } },
|
||||
.out = { { "atan2(y, x) (rad)", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchAtan2 }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchMinimum(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 y = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0),
|
||||
x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 1);
|
||||
return std::min(y, x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Minimum = {
|
||||
.name = "Minimum",
|
||||
.in = { {"a", PlugboardGraph::T_Float }, {"b", PlugboardGraph::T_Float } },
|
||||
.out = { { "min(a, b)", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchMinimum }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchMaximum(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 y = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0),
|
||||
x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 1);
|
||||
return std::max(y, x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Maximum = {
|
||||
.name = "Maximum",
|
||||
.in = { {"a", PlugboardGraph::T_Float }, {"b", PlugboardGraph::T_Float } },
|
||||
.out = { { "max(a, b)", PlugboardGraph::T_Float } },
|
||||
.fetch = {FetchMaximum }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchPower(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0),
|
||||
y = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 1);
|
||||
return std::pow(x, y);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Power = {
|
||||
.name = "Power",
|
||||
.in = { {"a", PlugboardGraph::T_Float }, {"b", PlugboardGraph::T_Float } },
|
||||
.out = { { "a^b", PlugboardGraph::T_Float } },
|
||||
.fetch = {FetchPower }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchSquareRoot(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return std::sqrt(x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node SquareRoot = {
|
||||
.name = "Square Root",
|
||||
.in = { {"a", PlugboardGraph::T_Float } },
|
||||
.out = { { "sqrt(a)", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchSquareRoot }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchNaturalLogarithm(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return std::log(x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node NaturalLogarithm = {
|
||||
.name = "Natural Logarithm",
|
||||
.in = { {"a", PlugboardGraph::T_Float } },
|
||||
.out = { { "log(a)", PlugboardGraph::T_Float } },
|
||||
.fetch = {FetchNaturalLogarithm }
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchAbsoluteValue(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return std::log(x);
|
||||
}
|
||||
|
||||
PlugboardGraph::Node AbsoluteValue = {
|
||||
.name = "Absolute Value",
|
||||
.in = { {"a", PlugboardGraph::T_Float } },
|
||||
.out = { { "|a|", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchAbsoluteValue }
|
||||
};
|
||||
|
||||
enum K_Interpolation {
|
||||
K_I_CubicBezier,
|
||||
K_I_InConstant,
|
||||
K_I_OutConstant,
|
||||
K_I_Linear,
|
||||
K_I_Step,
|
||||
K_I_SmoothStep,
|
||||
K_I_InQuadratic,
|
||||
K_I_OutQuadratic,
|
||||
K_I_InOutQuadratic,
|
||||
K_I_OutInQuadratic,
|
||||
K_I_InCubic,
|
||||
K_I_OutCubic,
|
||||
K_I_InOutCubic,
|
||||
K_I_OutInCubic,
|
||||
K_I_InQuartic,
|
||||
K_I_OutQuartic,
|
||||
K_I_InOutQuartic,
|
||||
K_I_OutInQuartic,
|
||||
K_I_InQuintic,
|
||||
K_I_OutQuintic,
|
||||
K_I_InOutQuintic,
|
||||
K_I_OutInQuintic,
|
||||
K_I_InSine,
|
||||
K_I_OutSine,
|
||||
K_I_InOutSine,
|
||||
K_I_OutInSine,
|
||||
K_I_InExponential,
|
||||
K_I_OutExponential,
|
||||
K_I_InOutExponential,
|
||||
K_I_OutInExponential,
|
||||
K_I_InCircular,
|
||||
K_I_OutCircular,
|
||||
K_I_InOutCircular,
|
||||
K_I_OutInCircular,
|
||||
K_I_InElastic,
|
||||
K_I_OutElastic,
|
||||
K_I_InOutElastic,
|
||||
K_I_OutInElastic,
|
||||
K_I_InBack,
|
||||
K_I_OutBack,
|
||||
K_I_InOutBack,
|
||||
K_I_OutInBack,
|
||||
K_I_InBounce,
|
||||
K_I_OutBounce,
|
||||
K_I_InOutBounce,
|
||||
K_I_OutInBounce,
|
||||
K_I_Count
|
||||
};
|
||||
|
||||
|
||||
const char *K_Interpolation_Names[] {
|
||||
"Cubic Bezier",
|
||||
"In Constant",
|
||||
"Out Constant",
|
||||
"Linear",
|
||||
"Step",
|
||||
"Smooth Step",
|
||||
"In Quadratic",
|
||||
"Out Quadratic",
|
||||
"In Out Quadratic",
|
||||
"Out In Quadratic",
|
||||
"In Cubic",
|
||||
"Out Cubic",
|
||||
"In Out Cubic",
|
||||
"Out In Cubic",
|
||||
"In Quartic",
|
||||
"Out Quartic",
|
||||
"In Out Quartic",
|
||||
"Out In Quartic",
|
||||
"In Quintic",
|
||||
"Out Quintic",
|
||||
"In Out Quintic",
|
||||
"Out In Quintic",
|
||||
"In Sine",
|
||||
"Out Sine",
|
||||
"In Out Sine",
|
||||
"Out In Sine",
|
||||
"In Exponential",
|
||||
"Out Exponential",
|
||||
"In Out Exponential",
|
||||
"Out In Exponential",
|
||||
"In Circular",
|
||||
"Out Circular",
|
||||
"In Out Circular",
|
||||
"Out In Circular",
|
||||
"In Elastic",
|
||||
"Out Elastic",
|
||||
"In Out Elastic",
|
||||
"Out In Elastic",
|
||||
"In Back",
|
||||
"Out Back",
|
||||
"In Out Back",
|
||||
"Out In Back",
|
||||
"In Bounce",
|
||||
"Out Bounce",
|
||||
"In Out Bounce",
|
||||
"Out In Bounce",
|
||||
"Invalid"
|
||||
};
|
||||
|
||||
struct InterpolationExtra {
|
||||
K_Interpolation interp;
|
||||
f32 v[4];
|
||||
};
|
||||
|
||||
f32 EvalInterpolation(f32 x, const InterpolationExtra& extra) {
|
||||
switch (extra.interp) {
|
||||
case K_I_InConstant:
|
||||
return 0.0f;
|
||||
case K_I_OutConstant:
|
||||
return 1.0f;
|
||||
case K_I_Linear:
|
||||
return x;
|
||||
case K_I_Step:
|
||||
return bx::getEaseFunc(bx::Easing::Step)(x);
|
||||
case K_I_SmoothStep:
|
||||
return bx::getEaseFunc(bx::Easing::SmoothStep)(x);
|
||||
case K_I_InQuadratic:
|
||||
return bx::getEaseFunc(bx::Easing::InQuad)(x);
|
||||
case K_I_OutQuadratic:
|
||||
return bx::getEaseFunc(bx::Easing::OutQuad)(x);
|
||||
case K_I_InOutQuadratic:
|
||||
return bx::getEaseFunc(bx::Easing::InOutQuad)(x);
|
||||
case K_I_OutInQuadratic:
|
||||
return bx::getEaseFunc(bx::Easing::OutInQuad)(x);
|
||||
case K_I_InCubic:
|
||||
return bx::getEaseFunc(bx::Easing::InCubic)(x);
|
||||
case K_I_OutCubic:
|
||||
return bx::getEaseFunc(bx::Easing::OutCubic)(x);
|
||||
case K_I_InOutCubic:
|
||||
return bx::getEaseFunc(bx::Easing::InOutCubic)(x);
|
||||
case K_I_OutInCubic:
|
||||
return bx::getEaseFunc(bx::Easing::OutInCubic)(x);
|
||||
case K_I_InQuartic:
|
||||
return bx::getEaseFunc(bx::Easing::InQuart)(x);
|
||||
case K_I_OutQuartic:
|
||||
return bx::getEaseFunc(bx::Easing::OutQuart)(x);
|
||||
case K_I_InOutQuartic:
|
||||
return bx::getEaseFunc(bx::Easing::InOutQuart)(x);
|
||||
case K_I_OutInQuartic:
|
||||
return bx::getEaseFunc(bx::Easing::OutInQuart)(x);
|
||||
case K_I_InQuintic:
|
||||
return bx::getEaseFunc(bx::Easing::InQuint)(x);
|
||||
case K_I_OutQuintic:
|
||||
return bx::getEaseFunc(bx::Easing::OutInQuint)(x);
|
||||
case K_I_InOutQuintic:
|
||||
return bx::getEaseFunc(bx::Easing::InOutQuint)(x);
|
||||
case K_I_OutInQuintic:
|
||||
return bx::getEaseFunc(bx::Easing::OutInQuint)(x);
|
||||
case K_I_InSine:
|
||||
return bx::getEaseFunc(bx::Easing::InSine)(x);
|
||||
case K_I_OutSine:
|
||||
return bx::getEaseFunc(bx::Easing::OutSine)(x);
|
||||
case K_I_InOutSine:
|
||||
return bx::getEaseFunc(bx::Easing::InOutSine)(x);
|
||||
case K_I_OutInSine:
|
||||
return bx::getEaseFunc(bx::Easing::OutInSine)(x);
|
||||
case K_I_InExponential:
|
||||
return bx::getEaseFunc(bx::Easing::InExpo)(x);
|
||||
case K_I_OutExponential:
|
||||
return bx::getEaseFunc(bx::Easing::OutExpo)(x);
|
||||
case K_I_InOutExponential:
|
||||
return bx::getEaseFunc(bx::Easing::InOutExpo)(x);
|
||||
case K_I_OutInExponential:
|
||||
return bx::getEaseFunc(bx::Easing::OutInExpo)(x);
|
||||
case K_I_InCircular:
|
||||
return bx::getEaseFunc(bx::Easing::InCirc)(x);
|
||||
case K_I_OutCircular:
|
||||
return bx::getEaseFunc(bx::Easing::OutCirc)(x);
|
||||
case K_I_InOutCircular:
|
||||
return bx::getEaseFunc(bx::Easing::InOutCirc)(x);
|
||||
case K_I_OutInCircular:
|
||||
return bx::getEaseFunc(bx::Easing::OutInCirc)(x);
|
||||
case K_I_InElastic:
|
||||
return bx::getEaseFunc(bx::Easing::InElastic)(x);
|
||||
case K_I_OutElastic:
|
||||
return bx::getEaseFunc(bx::Easing::OutElastic)(x);
|
||||
case K_I_InOutElastic:
|
||||
return bx::getEaseFunc(bx::Easing::InOutElastic)(x);
|
||||
case K_I_OutInElastic:
|
||||
return bx::getEaseFunc(bx::Easing::OutInElastic)(x);
|
||||
case K_I_InBack:
|
||||
return bx::getEaseFunc(bx::Easing::InBack)(x);
|
||||
case K_I_OutBack:
|
||||
return bx::getEaseFunc(bx::Easing::OutBack)(x);
|
||||
case K_I_InOutBack:
|
||||
return bx::getEaseFunc(bx::Easing::InOutBack)(x);
|
||||
case K_I_OutInBack:
|
||||
return bx::getEaseFunc(bx::Easing::OutInBack)(x);
|
||||
case K_I_InBounce:
|
||||
return bx::getEaseFunc(bx::Easing::InBounce)(x);
|
||||
case K_I_OutBounce:
|
||||
return bx::getEaseFunc(bx::Easing::OutBounce)(x);
|
||||
case K_I_InOutBounce:
|
||||
return bx::getEaseFunc(bx::Easing::InOutBounce)(x);
|
||||
case K_I_OutInBounce:
|
||||
return bx::getEaseFunc(bx::Easing::OutInBounce)(x);
|
||||
case K_I_CubicBezier: {
|
||||
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;
|
||||
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);
|
||||
return static_cast<f32>(CubicBezier(0.0f, p2y, p3y, 1.0f, t));
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchInterpolation(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
auto *e = std::any_cast<InterpolationExtra>(&n.extra);
|
||||
auto type = e->interp;
|
||||
f32 x = GetNodeInputArg<PlugboardGraph::T_Float>(s, n, 0);
|
||||
return EvalInterpolation(x, *e);
|
||||
}
|
||||
|
||||
void DrawInterpolation(PlugboardGraph::NodeInstance& n) {
|
||||
auto *v = std::any_cast<InterpolationExtra>(&n.extra);
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
if (ImGui::BeginCombo("##Interp", K_Interpolation_Names[v->interp], ImGuiComboFlags_None)) {
|
||||
for (u32 i = 0; i < K_Interpolation::K_I_Count; i++) {
|
||||
const bool is_selected = (v->interp == i);
|
||||
if (ImGui::Selectable(K_Interpolation_Names[i], is_selected))
|
||||
v->interp = static_cast<K_Interpolation>(i);
|
||||
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
void SetUpInterpolation(PlugboardGraph::NodeInstance& n) {
|
||||
n.extra = InterpolationExtra{};
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Interpolation = {
|
||||
.name = "Interpolation",
|
||||
.in = { {"t", PlugboardGraph::T_Float } },
|
||||
.out = { { "Value", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchInterpolation },
|
||||
.special_draw = DrawInterpolation,
|
||||
.extra_setup = SetUpInterpolation
|
||||
};
|
||||
|
||||
struct ChainSegment {
|
||||
f32 value;
|
||||
InterpolationExtra interp;
|
||||
};
|
||||
|
||||
PlugboardGraph::T_Map<PlugboardGraph::T_Count>::type FetchChain(const CompState& s, const PlugboardGraph::NodeInstance& n) {
|
||||
u32 frame = GetNodeInputArg<PlugboardGraph::T_Int>(s, n, 0);
|
||||
|
||||
auto *v = std::any_cast<std::map<u32, ChainSegment>>(&n.extra);
|
||||
if (v->empty())
|
||||
return 0.0f;
|
||||
if (frame < v->begin()->first)
|
||||
return v->begin()->second.value;
|
||||
if (frame > v->rbegin()->first)
|
||||
return v->rbegin()->second.value;
|
||||
|
||||
auto nit = v->upper_bound(frame), it = std::prev(nit);
|
||||
u32 x1 = it->first, x2 = nit->first;
|
||||
f32 t = static_cast<f32>(frame - x1) / static_cast<f32>(x2 - x1);
|
||||
f32 normalized_val = EvalInterpolation(t, it->second.interp);
|
||||
|
||||
return it->second.value + normalized_val * (nit->second.value - it->second.value);
|
||||
}
|
||||
|
||||
void SetUpChain(PlugboardGraph::NodeInstance& n) {
|
||||
n.extra = std::map<u32, ChainSegment>{};
|
||||
}
|
||||
|
||||
PlugboardGraph::Node Chain = {
|
||||
.name = "Chain",
|
||||
.in = { {"In", PlugboardGraph::T_Int } },
|
||||
.out = { { "Out", PlugboardGraph::T_Float } },
|
||||
.fetch = { FetchChain },
|
||||
.special_draw = nullptr,
|
||||
.extra_setup = SetUpChain
|
||||
};
|
||||
|
||||
auto Nodes = std::to_array<PlugboardGraph::Node*>({
|
||||
&Add,
|
||||
&Subtract,
|
||||
&Multiply,
|
||||
&Divide,
|
||||
&Negate,
|
||||
|
||||
&NaturalLogarithm,
|
||||
&SquareRoot,
|
||||
&Power,
|
||||
|
||||
&AbsoluteValue,
|
||||
&Sign,
|
||||
&Minimum,
|
||||
&Maximum,
|
||||
|
||||
&Sin,
|
||||
&Cos,
|
||||
&Tan,
|
||||
&Arcsin,
|
||||
&Arccos,
|
||||
&Arctan,
|
||||
&Atan2,
|
||||
|
||||
&Interpolation
|
||||
});
|
||||
}
|
||||
|
|
6
TODO.md
6
TODO.md
|
@ -1,7 +1,6 @@
|
|||
# NOW
|
||||
## Chores
|
||||
- Blending modes
|
||||
- Basic plugboard nodes
|
||||
- Node groups
|
||||
|
||||
## Compositor
|
||||
|
@ -13,15 +12,14 @@
|
|||
- Text (idea: index-based evaluation in plugboard)
|
||||
- Shapes (idea: embed glisp :mmtroll:, need to inquire -- still a lot of friction for simple shapes if we don't also get the glisp tools)
|
||||
- External data driving (json or something else?)
|
||||
- Pre compose
|
||||
- Pre-compose
|
||||
- Motion blur
|
||||
- Layer Groups (jokes -- can be completely UI side)
|
||||
|
||||
## UI
|
||||
- Interpolation Editor init
|
||||
- Timeline/Dope sheet init
|
||||
- Viewport gizmos (Layer-specific & nodes -- can separate into different tools?)
|
||||
- Node loop detection (separate DFS (extra work) or be smart in recursion)
|
||||
- detect time-sensitive nodes in tree and display on timeline
|
||||
|
||||
# Later
|
||||
## Compositor
|
||||
|
|
Loading…
Add table
Reference in a new issue