This commit is contained in:
lachrymaLF 2024-06-03 16:22:21 -04:00
parent f5ae66e82d
commit 7b91f99ac4
10 changed files with 866 additions and 130 deletions

View file

@ -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")

View file

@ -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{

View file

@ -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:

View file

@ -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) {

View file

@ -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;

View file

@ -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);
}

View file

@ -16,6 +16,7 @@ namespace K {
VisualTrack track;
String name;
Graphics::Blending mode;
u64 in, out;
};
struct CompState {

View file

@ -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});

View file

@ -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
});
}

View file

@ -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