diff --git a/Keishiki/Keishiki.cpp b/Keishiki/Keishiki.cpp index 6751fc6..5717060 100644 --- a/Keishiki/Keishiki.cpp +++ b/Keishiki/Keishiki.cpp @@ -20,16 +20,6 @@ namespace { namespace K { K::AppState app_state; - PlugboardGraph::T_Map::type FetchCompFrame(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes = nullptr) { - return PlugboardGraph::T_Map::type{static_cast(s.current_frame)}; - } - PlugboardGraph::T_Map::type FetchCompTime(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes = nullptr) { - return PlugboardGraph::T_Map::type{static_cast(s.current_frame) / s.fps}; - } - PlugboardGraph::T_Map::type FetchAppTicks(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes = nullptr) { - return PlugboardGraph::T_Map::type{ static_cast(SDL_GetTicks64()) }; - } - bool Init() { if (SDL_Init(SDL_INIT_VIDEO)) { LogError(SDL_GetError()); @@ -101,22 +91,8 @@ namespace K { state.current_frame = 0; state.fps = 30.0f; state.frame_max = 200; - state.plugboard.comp_in = PlugboardGraph::Node{ - "Composition In", - {}, - {{"Frame", PlugboardGraph::T_Int}, {"Time (s)", PlugboardGraph::T_Float}, {"App Ticks", PlugboardGraph::T_Float}}, - { - FetchCompFrame, FetchCompTime, FetchAppTicks - } - }; - state.plugboard.comp_out = PlugboardGraph::Node{ - "Composition Out", - {}, - {}, - {} - }; - state.plugboard.in_instance = PlugboardGraph::MakeInstance(state.plugboard.comp_in); - state.plugboard.out_instance = PlugboardGraph::MakeInstance(state.plugboard.comp_in); + state.plugboard.in = Plugboard::MakeInstance(Plugboard::K_P_CompositionIn); + state.plugboard.out = Plugboard::MakeInstance(Plugboard::K_P_CompositionOut); K::UI::SetupViewport(state); return true; diff --git a/Keishiki/Plugboard.cpp b/Keishiki/Plugboard.cpp new file mode 100644 index 0000000..2fcbf33 --- /dev/null +++ b/Keishiki/Plugboard.cpp @@ -0,0 +1,414 @@ +#include +#include + +namespace K::Plugboard { + bool DetectCycle(const NodeInstance& n, Vector& nodes) { // true if cycle found + return !DFS(n, [&nodes](ConnectInfo info){ + if (std::find(nodes.begin(), nodes.end(), info.p) == nodes.end()) { + nodes.push_back(info.p); + return true; + } + else return false; + }); + } + + void CollectChains(ConnectInfo& in, Vector& chains) { + chains.clear(); + + if (std::holds_alternative(*in.p)) + chains.push_back(in); + + DFS(*in.p, [&chains](ConnectInfo info){ + if (std::holds_alternative(*info.p)) + chains.push_back(info); + return true; + }); + } + + void Connect(NodeInstance& from_instance, u8 input_index, NodeInstance& to_instance, u8 to_out_index) { + Vector nodes{&from_instance}; + if (!DetectCycle(to_instance, nodes)) // no cycle + std::visit([&](auto&& from, auto&& to) { + from.in[input_index].value = ConnectInfo{&to_instance, to_out_index}; + to.out[to_out_index].outgoing.push_back({&from_instance, input_index}); + }, from_instance, to_instance); + else + Log("Connect failed -- Cycle detected"); + } + + void Disconnect(NodeInstance& from_instance, u8 input_index) { + std::visit([&](auto&& from) { + std::visit([&](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + std::visit([&](auto&& a) { + auto& v = a.out[arg.index].outgoing; + for (auto it = v.begin(); it != v.end(); it++) { + if (it->p == &from_instance) { + v.erase(it); + break; + } + } + }, *arg.p); + from.in[input_index].value = ExpandVariant(from.in[input_index].type); + } + else if constexpr (std::is_same_v::type>) + ; // already disconnected + }, from.in[input_index].value); + }, from_instance); + } + + NodeInstance MakeInstance(K_P_Nodes i) { + static NodeInstance table[] = { + Add{}, + Negate{}, + Subtract{}, + Multiply{}, + Divide{}, + Sign{}, + Sin{}, + Cos{}, + Tan{}, + Arcsin{}, + Arccos{}, + Arctan{}, + Atan2{}, + Minimum{}, + Maximum{}, + Power{}, + SquareRoot{}, + NaturalLogarithm{}, + AbsoluteValue{}, + Interpolation{}, + Chain{}, + CompositionIn{}, + CompositionOut{} + }; + static_assert(sizeof(table)/sizeof(table[0]) == K_P_Count); + return table[i]; + } + + T_Map::type ExpandVariant(u32 i) { + static T_Map::type table[] = { f32{}, i32{}, RGBA{}, XY{}, XYZ{}, String{} }; + return table[i]; + } + + template + typename T_Map::type GetNodeInputArg(const CompState& s, const InSocket& socket) { + bool good; + typename T_Map::type v = std::get::type>(ConvertValue(std::visit([&s](auto&& arg) { + using U = std::decay_t; + if constexpr (std::is_same_v) + return Eval(s, arg); + else + return arg; + }, socket.value), type, good)); + if (!good) + K::LogError("Type mismatch on plugboard evaluation!"); + + return v; + } + + 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" + }; + + T_Map::type FetchAdd(const CompState& s, const Add& n) { + return GetNodeInputArg(s, n.in[0]) + GetNodeInputArg(s, n.in[1]); + } + + T_Map::type FetchNegate(const CompState& s, const Negate& n) { + return -GetNodeInputArg(s, n.in[0]); + } + + T_Map::type FetchSubtract(const CompState& s, const Subtract& n) { + return GetNodeInputArg(s, n.in[0]) - GetNodeInputArg(s, n.in[1]); + } + + T_Map::type FetchMultiply(const CompState& s, const Multiply& n) { + return GetNodeInputArg(s, n.in[0]) * GetNodeInputArg(s, n.in[1]); + } + + T_Map::type FetchDivide(const CompState& s, const Divide& n) { + return GetNodeInputArg(s, n.in[0]) / GetNodeInputArg(s, n.in[1]); + } + + T_Map::type FetchSign(const CompState& s, const Sign& n) { + return std::signbit(GetNodeInputArg(s, n.in[0])) ? -1.0f : 1.0f; + } + + T_Map::type FetchSin(const CompState& s, const Sin& n) { + return std::sin(GetNodeInputArg(s, n.in[0])); + } + + T_Map::type FetchCos(const CompState& s, const Cos& n) { + return std::cos(GetNodeInputArg(s, n.in[0])); + } + + T_Map::type FetchTan(const CompState& s, const Tan& n) { + return std::tan(GetNodeInputArg(s, n.in[0])); + } + + T_Map::type FetchArcsin(const CompState& s, const Arcsin& n) { + return std::asin(GetNodeInputArg(s, n.in[0])); + } + + T_Map::type FetchArccos(const CompState& s, const Arccos& n) { + return std::acos(GetNodeInputArg(s, n.in[0])); + } + + T_Map::type FetchArctan(const CompState& s, const Arctan& n) { + return std::atan(GetNodeInputArg(s, n.in[0])); + } + + T_Map::type FetchAtan2(const CompState& s, const Atan2& n) { + return std::atan2(GetNodeInputArg(s, n.in[0]), GetNodeInputArg(s, n.in[1])); + } + + T_Map::type FetchMinimum(const CompState& s, const Minimum& n) { + return std::min(GetNodeInputArg(s, n.in[0]), GetNodeInputArg(s, n.in[1])); + } + + T_Map::type FetchMaximum(const CompState& s, const Maximum& n) { + return std::max(GetNodeInputArg(s, n.in[0]), GetNodeInputArg(s, n.in[1])); + } + + T_Map::type FetchPower(const CompState& s, const Power& n) { + return std::pow(GetNodeInputArg(s, n.in[0]), GetNodeInputArg(s, n.in[1])); + } + + T_Map::type FetchSquareRoot(const CompState& s, const SquareRoot& n) { + return std::sqrt(GetNodeInputArg(s, n.in[0])); + } + + T_Map::type FetchNaturalLogarithm(const CompState& s, const NaturalLogarithm& n) { + return std::log(GetNodeInputArg(s, n.in[0])); + } + + T_Map::type FetchAbsoluteValue(const CompState& s, const AbsoluteValue& n) { + return std::abs(GetNodeInputArg(s, n.in[0])); + } + + T_Map::type FetchInterpolation(const CompState& s, const Interpolation& n) { + return EvalInterpolation(GetNodeInputArg(s, n.in[0]), n.extra); + } + + void Interpolation::Draw() { + ImGui::SetNextItemWidth(-FLT_MIN); + if (ImGui::BeginCombo("##Interp", K_Interpolation_Names[extra.interp], ImGuiComboFlags_None)) { + for (u32 i = 0; i < K_Interpolation::K_I_Count; i++) { + const bool is_selected = (extra.interp == i); + if (ImGui::Selectable(K_Interpolation_Names[i], is_selected)) + extra.interp = static_cast(i); + + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + } + + T_Map::type FetchChain(const CompState& s, const Chain& n) { + i32 frame = static_cast(GetNodeInputArg(s, n.in[0])); + + auto& v = n.extra.chain.segments; + + if (v.empty()) + return 0.0f; + if (v.back().frame <= frame) + return v.back().value; + if (v.front().frame >= frame) + return v.front().value; + + auto nit = std::upper_bound(v.cbegin(), v.cend(), frame, + [](i32 a, const ChainSegment& b) { + return a < b.frame; + }), it = std::prev(nit); + + i32 x1 = it->frame, x2 = nit->frame; + f32 t = static_cast(frame - x1) / static_cast(x2 - x1); + f32 normalized_val = EvalInterpolation(t, it->interp); + + return it->value + normalized_val * (nit->value - it->value); + } + + constexpr String VarToString(const T_Map::type& var) { + return std::visit([](auto&& arg) -> String { + using T = std::decay_t; + if constexpr (std::is_same_v::type>) + return std::to_string(arg) + "f"; + else if constexpr (std::is_same_v::type>) + return std::to_string(arg); + else if constexpr (std::is_same_v::type>) + return "vec4(" + std::to_string(arg.r) + "," + std::to_string(arg.g) + "," + std::to_string(arg.b) + "," + std::to_string(arg.a) + ")"; + else if constexpr (std::is_same_v::type>) + return "vec2(" + std::to_string(arg.x) + "," + std::to_string(arg.y) + ")"; + else if constexpr (std::is_same_v::type>) + return "vec3(" + std::to_string(arg.x) + "," + std::to_string(arg.y) + "," + std::to_string(arg.z) + ")"; + else if constexpr (std::is_same_v::type>) + return arg; + }, var); + } + + T_Map::type Eval(const CompState& s, const ConnectInfo& info) { + return std::visit([&info, &s](auto&& arg) { return arg.fetch[info.index](s, arg); }, *info.p); + } + + T_Map::type ConvertValue(const T_Map::type& v, Type target, bool& good) { + good = true; + return std::visit([&good, target](auto&& arg) -> T_Map::type { + using T = std::decay_t; + if constexpr (std::is_same_v::type>) { + switch (target) { + case T_Float: + return arg; + case T_Int: + return static_cast::type>(std::round(arg)); + case T_RGBA: + return RGBA{ arg, arg, arg, arg }; + case T_XY: + return XY{ arg, arg }; + case T_XYZ: + return XYZ{ arg, arg, arg }; + case T_String: + return VarToString(arg); + case T_Count: + good = false; + return {}; + } + } + else if constexpr (std::is_same_v::type>) { + auto f = static_cast::type>(arg); + switch (target) { + case T_Float: + return f; + case T_Int: + return arg; + case T_RGBA: + return RGBA{ f, f, f, f }; + case T_XY: + return XY{ f, f }; + case T_XYZ: + return XYZ{ f, f, f }; + case T_String: + return VarToString(arg); + case T_Count: + good = false; + return {}; + } + } + else if constexpr (std::is_same_v::type>) { + switch (target) { + case T_RGBA: + return arg; + case T_String: + return VarToString(arg); + default: + good = false; + return {}; + } + } + else if constexpr (std::is_same_v::type>) { + switch (target) { + case T_XY: + return arg; + case T_String: + return VarToString(arg); + default: + good = false; + return {}; + } + } + else if constexpr (std::is_same_v::type>) { + switch (target) { + case T_XYZ: + return arg; + case T_String: + return VarToString(arg); + default: + good = false; + return {}; + } + } + else if constexpr (std::is_same_v::type>) { + switch (target) { + case T_String: + return arg; + default: + good = false; + return {}; + } + } + return {}; + }, v); + } + + + T_Map::type FetchCompositionInFrame(const CompState& s, const CompositionIn& n) { + return T_Map::type{static_cast(s.current_frame)}; + } + + T_Map::type FetchCompositionInTime(const CompState& s, const CompositionIn& n) { + return T_Map::type{static_cast(s.current_frame) / s.fps}; + } + + T_Map::type FetchCompositionInAppTicks(const CompState& s, const CompositionIn& n) { + return T_Map::type{ static_cast(SDL_GetTicks64()) }; + } + + void Plugboard::RecollectChains() { + auto& node = std::get(out); + for (u32 i = 0; i < node.in.size(); i++) { + std::visit([i, &node](auto&& arg) { + if constexpr (std::is_same_v, ConnectInfo>) + CollectChains(arg, node.show_nodes[i]); + }, node.in[i].value); + } + } +} diff --git a/Keishiki/PlugboardGraph.cpp b/Keishiki/PlugboardGraph.cpp deleted file mode 100644 index bff2617..0000000 --- a/Keishiki/PlugboardGraph.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include - -namespace K::PlugboardGraph { - void NodeInstance::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}); - } - - void NodeInstance::disconnect(u32 input_index) { - std::visit([this](auto&& arg){ - using T = std::decay_t; - if constexpr (std::is_same_v) { - for (auto it = arg.p->outputs_going[arg.index].begin(); - it != arg.p->outputs_going[arg.index].end(); it++) { - if (it->p == this) { - arg.p->outputs_going[arg.index].erase(it); - break; - } - } - } - else if constexpr (std::is_same_v::type>) - ; - }, inputs_fed[input_index]); - inputs_fed[input_index] = expand_type(node->in[input_index].type); - } - - constexpr String VarToString(const T_Map::type& var) { - return std::visit([](auto&& arg) -> String { - using T = std::decay_t; - if constexpr (std::is_same_v::type>) - return std::to_string(arg) + "f"; - else if constexpr (std::is_same_v::type>) - return std::to_string(arg); - else if constexpr (std::is_same_v::type>) - return "vec4(" + std::to_string(arg.r) + "," + std::to_string(arg.g) + "," + std::to_string(arg.b) + "," + std::to_string(arg.a) + ")"; - else if constexpr (std::is_same_v::type>) - return "vec2(" + std::to_string(arg.x) + "," + std::to_string(arg.y) + ")"; - else if constexpr (std::is_same_v::type>) - return "vec3(" + std::to_string(arg.x) + "," + std::to_string(arg.y) + "," + std::to_string(arg.z) + ")"; - else if constexpr (std::is_same_v::type>) - return arg; - }, var); - } - - T_Map::type expand_type(Type i) { - static T_Map::type table[] = {f32{}, i32{}, RGBA{}, XY{}, XYZ{}, String{}}; - return table[i]; - } - - T_Map::type Eval(const CompState& s, const ConnectInfo& info, Vector *show_nodes) { - auto p = *info.p; - auto n = p.node; - if (show_nodes != nullptr) { - if (std::find(show_nodes->begin(), show_nodes->end(), info) != show_nodes->end()) { - Log("Failure to evaluate plugboard tree: loop detected!"); - return {}; // tree contains cycle -- abort - } - if (n == &PlugboardNodes::Chain) - show_nodes->push_back(info); - } - return n->fetch[info.index](s, p, show_nodes); - } - - NodeInstance MakeInstance(Node& n) { - Vector::type, ConnectInfo>> inp; - Vector> out; - for (const auto& t : n.in) - inp.emplace_back(expand_type(t.type)); - for (auto t : n.out) - out.emplace_back(); - 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::type ConvertValue(const T_Map::type& v, Type target, bool& good) { - good = true; - return std::visit([&good, target](auto&& arg) -> T_Map::type { - using T = std::decay_t; - if constexpr (std::is_same_v::type>) { - switch (target) { - case T_Float: - return arg; - case T_Int: - return static_cast::type>(std::round(arg)); - case T_RGBA: - return RGBA{ arg, arg, arg, arg }; - case T_XY: - return XY{ arg, arg }; - case T_XYZ: - return XYZ{ arg, arg, arg }; - case T_String: - return VarToString(arg); - case T_Count: - good = false; - return {}; - } - } - else if constexpr (std::is_same_v::type>) { - auto f = static_cast::type>(arg); - switch (target) { - case T_Float: - return f; - case T_Int: - return arg; - case T_RGBA: - return RGBA{ f, f, f, f }; - case T_XY: - return XY{ f, f }; - case T_XYZ: - return XYZ{ f, f, f }; - case T_String: - return VarToString(arg); - case T_Count: - good = false; - return {}; - } - } - else if constexpr (std::is_same_v::type>) { - switch (target) { - case T_RGBA: - return arg; - case T_String: - return VarToString(arg); - default: - good = false; - return {}; - } - } - else if constexpr (std::is_same_v::type>) { - switch (target) { - case T_XY: - return arg; - case T_String: - return VarToString(arg); - default: - good = false; - return {}; - } - } - else if constexpr (std::is_same_v::type>) { - switch (target) { - case T_XYZ: - return arg; - case T_String: - return VarToString(arg); - default: - good = false; - return {}; - } - } - else if constexpr (std::is_same_v::type>) { - switch (target) { - case T_String: - return arg; - default: - good = false; - return {}; - } - } - return {}; - }, v); - } -} diff --git a/Keishiki/PlugboardNodes.cpp b/Keishiki/PlugboardNodes.cpp deleted file mode 100644 index fdc54be..0000000 --- a/Keishiki/PlugboardNodes.cpp +++ /dev/null @@ -1,394 +0,0 @@ -#include -#include - -namespace K::PlugboardNodes { - std::array Nodes = std::to_array({ - &Add, - &Subtract, - &Multiply, - &Divide, - &Negate, - - &NaturalLogarithm, - &SquareRoot, - &Power, - - &AbsoluteValue, - &Sign, - &Minimum, - &Maximum, - - &Sin, - &Cos, - &Tan, - &Arcsin, - &Arccos, - &Arctan, - &Atan2, - - &Interpolation - }); - - 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" - }; - - PlugboardGraph::T_Map::type FetchAdd(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes), - y = GetNodeInputArg(s, n, 1, show_nodes); - return x + y; - } - - 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::type FetchNegate(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - return -x; - } - - PlugboardGraph::Node Negate = { - .name = "Negate", - .in = { {"a", PlugboardGraph::T_Float } }, - .out = { { "-a", PlugboardGraph::T_Float } }, - .fetch = { FetchNegate } - }; - - PlugboardGraph::T_Map::type FetchSubtract(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes), - y = GetNodeInputArg(s, n, 1, show_nodes); - 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::type FetchMultiply(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes), - y = GetNodeInputArg(s, n, 1, show_nodes); - 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::type FetchDivide(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes), - y = GetNodeInputArg(s, n, 1, show_nodes); - 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::type FetchSign(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - 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::type FetchSin(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - 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::type FetchCos(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - 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::type FetchTan(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - 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::type FetchArcsin(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - 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::type FetchArccos(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - 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::type FetchArctan(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - 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::type FetchAtan2(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 y = GetNodeInputArg(s, n, 0, show_nodes), - x = GetNodeInputArg(s, n, 1, show_nodes); - 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::type FetchMinimum(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 y = GetNodeInputArg(s, n, 0, show_nodes), - x = GetNodeInputArg(s, n, 1, show_nodes); - 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::type FetchMaximum(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 y = GetNodeInputArg(s, n, 0, show_nodes), - x = GetNodeInputArg(s, n, 1, show_nodes); - 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::type FetchPower(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes), - y = GetNodeInputArg(s, n, 1, show_nodes); - 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::type FetchSquareRoot(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - 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::type FetchNaturalLogarithm(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - 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::type FetchAbsoluteValue(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - return std::log(x); - } - - PlugboardGraph::Node AbsoluteValue = { - .name = "Absolute Value", - .in = { {"a", PlugboardGraph::T_Float } }, - .out = { { "|a|", PlugboardGraph::T_Float } }, - .fetch = { FetchAbsoluteValue } - }; - - PlugboardGraph::T_Map::type FetchInterpolation(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - auto *e = std::any_cast(&n.extra); - f32 x = GetNodeInputArg(s, n, 0, show_nodes); - return EvalInterpolation(x, *e); - } - - void DrawInterpolation(PlugboardGraph::NodeInstance& n) { - auto *v = std::any_cast(&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(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 - }; - - PlugboardGraph::T_Map::type FetchChain(const CompState& s, const PlugboardGraph::NodeInstance& n, Vector *show_nodes) { - i32 frame = static_cast(GetNodeInputArg(s, n, 0, show_nodes)); - - auto& [chain, _] = *std::any_cast(&n.extra); - auto& v = chain.segments; - - if (v.empty()) - return 0.0f; - if (v.back().frame <= frame) - return v.back().value; - if (v.front().frame >= frame) - return v.front().value; - - auto nit = std::upper_bound(v.cbegin(), v.cend(), frame, - [](i32 a, const PlugboardNodes::ChainSegment& b) { - return a < b.frame; - }), it = std::prev(nit); - - i32 x1 = it->frame, x2 = nit->frame; - f32 t = static_cast(frame - x1) / static_cast(x2 - x1); - f32 normalized_val = EvalInterpolation(t, it->interp); - - return it->value + normalized_val * (nit->value - it->value); - } - - void SetUpChain(PlugboardGraph::NodeInstance& n) { - n.extra = ChainExtra{}; - } - - PlugboardGraph::Node Chain = { - .name = "Chain", - .in = { {"In", PlugboardGraph::T_Int } }, - .out = { { "Out", PlugboardGraph::T_Float } }, - .fetch = { FetchChain }, - .special_draw = nullptr, - .extra_setup = SetUpChain - }; -} diff --git a/Keishiki/Resource.cpp b/Keishiki/Resource.cpp index 1ab92f0..f65dd29 100644 --- a/Keishiki/Resource.cpp +++ b/Keishiki/Resource.cpp @@ -49,7 +49,7 @@ namespace K::Resource { K::LogError("Image loading failed for " + p.string()); return nullptr; } - const bgfx::Memory *ref = bgfx::makeRef(res.buf, res.w * res.h * res.channels * sizeof(u8)); + const bgfx::Memory *ref = bgfx::makeRef(res.buf, res.w * res.h * res.channels * sizeof(Byte)); res.format = res.channels == 4 ? bgfx::TextureFormat::RGBA8 : bgfx::TextureFormat::RGB8; res.tex = bgfx::createTexture2D( res.w, @@ -105,12 +105,12 @@ namespace K::Resource { if constexpr (std::is_same_v>) { i32 w, h, channels; - u8 *buf = stbi_load(path.c_str(), &w, &h, &channels, 0); + Byte *buf = stbi_load(path.c_str(), &w, &h, &channels, 0); if (buf == nullptr) { K::LogError("Image loading failed for " + path.string()); return; } - const bgfx::Memory *ref = bgfx::makeRef(buf, w * h * channels * sizeof(u8)); + const bgfx::Memory *ref = bgfx::makeRef(buf, w * h * channels * sizeof(Byte)); auto format = channels == 4 ? bgfx::TextureFormat::RGBA8 : bgfx::TextureFormat::RGB8; auto tex = bgfx::createTexture2D( w, diff --git a/Keishiki/ShaderGraph.cpp b/Keishiki/ShaderGraph.cpp index 2124069..637e395 100644 --- a/Keishiki/ShaderGraph.cpp +++ b/Keishiki/ShaderGraph.cpp @@ -17,6 +17,10 @@ namespace { } namespace K::ShaderGraph { + T_Map::type ExpandVariant(u32 i) { + static T_Map::type table[] = { f32{}, i32{}, RGBA{}, XY{}, XYZ{} }; + return table[i]; + } String VarToString(const T_Map::type& var) { return std::visit([](auto&& arg) -> String { using T = std::decay_t; @@ -32,13 +36,8 @@ namespace K::ShaderGraph { return "vec3(" + std::to_string(arg.x) + "," + std::to_string(arg.y) + "," + std::to_string(arg.z) + ")"; }, var); } - String ShaderGraph::generate_shader() const { + String ShaderGraph::GenerateShader() const { if (RGBA_node->node->out_types[RGBA_node_out_index] != Type::T_RGBA) return {}; return BuildNode(*RGBA_node); } - - T_Map::type expand_type(Type i) { - static constexpr T_Map::type table[] = {f32{}, i32{}, RGBA{}, XY{}, XYZ{}}; - return table[i]; - } } diff --git a/Keishiki/UI.cpp b/Keishiki/UI.cpp index 7377674..9e3235d 100644 --- a/Keishiki/UI.cpp +++ b/Keishiki/UI.cpp @@ -1,7 +1,6 @@ #include "UI.h" #include "Graphics.h" #include "VisualTrack.h" -#include "PlugboardNodes.h" #include #include @@ -67,22 +66,22 @@ namespace K::UI { 0UL, s.frame_max }; - l.track.AddUniform("u_aspect_ratio", ShaderGraph::expand_type(ShaderGraph::T_XY)); + l.track.AddUniform("u_aspect_ratio", ShaderGraph::ExpandVariant(ShaderGraph::T_XY)); if (s.width > s.height) l.track.uniforms[0].val = ShaderGraph::T_Map::type{static_cast(s.width)/static_cast(s.height), 1.0f}; else l.track.uniforms[0].val = ShaderGraph::T_Map::type{1.0f, static_cast(s.height)/static_cast(s.width)}; - l.track.AddUniform("u_opacity", ShaderGraph::expand_type(ShaderGraph::T_Float)); + l.track.AddUniform("u_opacity", ShaderGraph::ExpandVariant(ShaderGraph::T_Float)); l.track.uniforms[1].val = ShaderGraph::T_Map::type(1.0f); - l.track.AddUniform("u_rot", ShaderGraph::expand_type(ShaderGraph::T_Float)); + l.track.AddUniform("u_rot", ShaderGraph::ExpandVariant(ShaderGraph::T_Float)); l.track.uniforms[2].val = ShaderGraph::T_Map::type(.0f); - l.track.AddUniform("u_scale", ShaderGraph::expand_type(ShaderGraph::T_XY)); + l.track.AddUniform("u_scale", ShaderGraph::ExpandVariant(ShaderGraph::T_XY)); l.track.uniforms[3].val = ShaderGraph::T_Map::type{1.0f, 1.0f}; - l.track.AddUniform("u_translate", ShaderGraph::expand_type(ShaderGraph::T_XY)); + l.track.AddUniform("u_translate", ShaderGraph::ExpandVariant(ShaderGraph::T_XY)); l.track.uniforms[4].val = ShaderGraph::T_Map::type{.0f, .0f}; l.track.shader = "void main() {\n" @@ -171,35 +170,36 @@ namespace K::UI { layer.track.Clear(); } - void DrawPlugboardVariableEditWidget(PlugboardGraph::T_Map::type& var) { + void DrawPlugboardVariableEditWidget(Plugboard::T_Map::type& var) { std::visit([](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_same_v::type>) + if constexpr (std::is_same_v::type>) ImGui::DragFloat("##", &arg, 0.005f); - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) ImGui::DragInt("##", &arg, 0.005f); - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) ImGui::DragFloat4("##", &arg.r, 0.005f); - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) ImGui::DragFloat2("##", &arg.x, 0.005f); - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) ImGui::DragFloat3("##", &arg.x, 0.005f); }, var); } - void PlugboardNodeDrawSockets(PlugboardGraph::NodeInstance& n, ImGuiStyle& style, - bool& dragging_on_socket, ImVec2& source, std::unordered_map& link_pos, + template + void PlugboardNodeDrawSockets(CompState& s, N& n, Plugboard::NodeInstance& instance, ImGuiStyle& style, + bool& dragging_on_socket, ImVec2& source, Dict& link_pos, bool dummy = true) { i32 row = 1; - for (u32 out = 0; out < n.node->out.size(); out++, row++) { + for (u32 out = 0; out < n.out.size(); out++, row++) { ImGui::PushID(row); ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); ImGui::TableNextColumn(); ImGui::TableNextColumn(); if (dummy) - ImGui::Dummy({100.0f - ImGui::CalcTextSize(n.node->out[out].name.c_str()).x - style.ItemSpacing.x, 0.0f}); + ImGui::Dummy({100.0f - ImGui::CalcTextSize(n.out[out].name.c_str()).x - style.ItemSpacing.x, 0.0f}); ImGui::SameLine(); - ImGui::TextUnformatted(n.node->out[out].name.c_str()); + ImGui::TextUnformatted(n.out[out].name.c_str()); ImGui::TableNextColumn(); ImGui::RadioButton("##Out", false); if (ImGui::IsItemActive()) { @@ -208,41 +208,43 @@ namespace K::UI { } if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - PlugboardGraph::ConnectInfo d{ &n, out }; + Plugboard::ConnectInfo d{ &instance, out }; ImGui::SetDragDropPayload("K_PLUG_OUT_TO_IN", &d, sizeof(d)); ImGui::EndDragDropSource(); } if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("K_PLUG_IN_TO_OUT")) { - PlugboardGraph::ConnectInfo in = *(const PlugboardGraph::ConnectInfo*)payload->Data; - in.p->connect(in.index, &n, out); + Plugboard::ConnectInfo in = *(const Plugboard::ConnectInfo*)payload->Data; + Plugboard::Connect(*in.p, in.index, instance, out); + s.plugboard.RecollectChains(); } ImGui::EndDragDropTarget(); } ImGui::PopID(); // Update link info if needed - if (!n.outputs_going[out].empty()) {\ - link_pos[{&n, out}].source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2, + if (!n.out[out].outgoing.empty()) {\ + link_pos[{&instance, out}].source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2, -ImGui::GetFrameHeight() / 2 - style.ItemInnerSpacing.y}; } } - for (u32 in = 0; in < n.inputs_fed.size(); in++, row++) { + for (u32 in = 0; in < n.in.size(); in++, row++) { ImGui::PushID(row); ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); ImGui::TableNextColumn(); ImGui::RadioButton("##In", false); if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - PlugboardGraph::ConnectInfo d{ &n, in }; + Plugboard::ConnectInfo d{ &instance, in }; ImGui::SetDragDropPayload("K_PLUG_IN_TO_OUT", &d, sizeof(d)); ImGui::EndDragDropSource(); } if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("K_PLUG_OUT_TO_IN")) { - PlugboardGraph::ConnectInfo out = *(const PlugboardGraph::ConnectInfo*)payload->Data; - n.connect(in, out.p, out.index); + Plugboard::ConnectInfo out = *(const Plugboard::ConnectInfo*)payload->Data; + Plugboard::Connect(instance, in, *out.p, out.index); + s.plugboard.RecollectChains(); } ImGui::EndDragDropTarget(); } @@ -250,12 +252,12 @@ namespace K::UI { 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()); + ImGui::TextUnformatted(n.in[in].name.c_str()); // Update link info std::visit([&socket_link_pos, &dragging_on_socket, &source, &link_pos](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { if (ImGui::IsItemActive()) link_pos[arg].sinks.push_back(ImGui::GetMousePos()); else @@ -265,33 +267,36 @@ namespace K::UI { dragging_on_socket = true; source = socket_link_pos; } - if constexpr (std::is_same_v::type>) { + if constexpr (std::is_same_v::type>) { ImGui::SameLine(); DrawPlugboardVariableEditWidget(arg); } - }, n.inputs_fed[in]); + }, n.in[in].value); ImGui::TableNextColumn(); ImGui::PopID(); - } } - bool PlugboardDrawNode(PlugboardGraph::NodeInstance& n, ImVec2& view_pos, i32 id, ImGuiIO& io, ImGuiStyle& style, - bool& dragging_on_socket, ImVec2& source, std::unordered_map& link_pos, bool selected) { + template + bool PlugboardDrawNode(CompState& s, N& n, Plugboard::NodeInstance& instance, ImVec2& view_pos, i32 id, ImGuiIO& io, ImGuiStyle& style, + bool& dragging_on_socket, ImVec2& source, Dict& link_pos, bool selected) { bool stat = false; - ImGui::SetCursorPos(view_pos + n.pos); + const auto& pos = n.pos; + const auto& name = n.name; + + ImGui::SetCursorPos(view_pos + pos); ImGui::PushID(id); ImGui::PushStyleColor(ImGuiCol_ChildBg, selected ? 0xAA202040 : 0xAA080813); static ImVec2 old_pos{}; - if (ImGui::BeginChild(n.node->name.c_str(), {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border)) { - if (ImGui::BeginTable(n.node->name.c_str(), 3)) { + if (ImGui::BeginChild(name, {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border)) { + if (ImGui::BeginTable(name, 3)) { ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); ImGui::TableNextColumn(); ImGui::TableNextColumn(); - ImGui::Button(n.node->name.c_str(), {100.0f , 0.0f}); + ImGui::Button(name, {100.0f , 0.0f}); if (ImGui::IsItemClicked()) { - old_pos = n.pos; + old_pos = pos; stat = true; } if (ImGui::IsItemActive()) { @@ -299,15 +304,15 @@ namespace K::UI { } ImGui::TableNextColumn(); - if (n.node->special_draw != nullptr) { + if constexpr (std::is_same_v) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::TableNextColumn(); - n.node->special_draw(n); + n.Draw(); ImGui::TableNextColumn(); } - PlugboardNodeDrawSockets(n, style, dragging_on_socket, source, link_pos); + PlugboardNodeDrawSockets(s, n, instance, style, dragging_on_socket, source, link_pos); ImGui::EndTable(); } @@ -408,7 +413,7 @@ namespace K::UI { f32 begin, end; i32 samples; const CompState &s; - PlugboardGraph::ConnectInfo &connected_v; + Plugboard::ConnectInfo &connected_v; }; void Composition(CompState& s) { @@ -423,23 +428,25 @@ namespace K::UI { static u32 node_current{}; if (ImGui::Button("Add")) { - auto n = PlugboardGraph::MakeInstance(*PlugboardNodes::Nodes[node_current]); + auto n = Plugboard::MakeInstance(Plugboard::K_P_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; + std::visit([](auto&& arg) { + if (ImGui::BeginCombo("##Node", arg.name, ImGuiComboFlags_None)) { + for (u32 n = 0; n < Plugboard::K_P_Count; n++) { + const bool is_selected = (node_current == n); + if (ImGui::Selectable(std::visit([](auto&& a) { return a.name; }, Plugboard::MakeInstance(Plugboard::K_P_Nodes(node_current))), is_selected)) + node_current = n; - // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) - if (is_selected) - ImGui::SetItemDefaultFocus(); + // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); } - ImGui::EndCombo(); - } + }, Plugboard::MakeInstance(Plugboard::K_P_Nodes(node_current))); // COPIES!!! ImGui::SameLine(); if (ImGui::Button(playing ? "Pause" : "Play")) @@ -510,8 +517,8 @@ namespace K::UI { if (ImGui::BeginDragDropTargetCustom(window->ContentRegionRect, window->ID)) { if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_PLUG_IN_TO_OUT")) { - PlugboardGraph::ConnectInfo in = *(const PlugboardGraph::ConnectInfo *) payload->Data; - in.p->disconnect(in.index); + Plugboard::ConnectInfo in = *(const Plugboard::ConnectInfo *) payload->Data; + Plugboard::Disconnect(*in.p, in.index); } ImGui::EndDragDropTarget(); } @@ -535,17 +542,20 @@ namespace K::UI { u32 i = 0; for (auto it = s.plugboard.nodes.begin(); it != s.plugboard.nodes.end(); it++, i++) - if (PlugboardDrawNode(*it, view_pos, static_cast(i), io, style, dragging_on_socket, drag_source, s.plugboard.links_pos, selected == i)) - selected = i; - + std::visit([&it, &i, &io, &style, &dragging_on_socket, &drag_source, &s](auto&& arg) { + using T = std::decay_t; + if (PlugboardDrawNode(s, arg, *it, view_pos, static_cast(i), io, style, dragging_on_socket, + drag_source, s.plugboard.links_pos, selected == i)) + selected = i; + }, *it); ImGui::SetCursorPos({}); ImGui::PushStyleColor(ImGuiCol_ChildBg, 0x33FFFFFF); - ImGui::BeginChild(s.plugboard.comp_in.name.c_str(), {}, + ImGui::BeginChild("Composition In", {}, ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY); ImGui::PopStyleColor(); - if (ImGui::BeginTable(s.plugboard.comp_in.name.c_str(), 3)) { - PlugboardNodeDrawSockets(s.plugboard.in_instance, style, dragging_on_socket, drag_source, + if (ImGui::BeginTable("Composition In", 3)) { + PlugboardNodeDrawSockets(s, std::get(s.plugboard.in), s.plugboard.in, style, dragging_on_socket, drag_source, s.plugboard.links_pos, false); ImGui::EndTable(); } @@ -572,14 +582,14 @@ namespace K::UI { f32 view_amt = view_right - view_left, mouse_tl_x; f32 fr_step; - static Vector selected_chains{}; + static Vector selected_chains{}; // Mouse Handlers // Drag playhead/pan static bool tl_clear_selection_request = false; bool tl_clear_selection_request_this_frame = tl_clear_selection_request; static bool bg_drag_select_active = false; - static Vector keys_selected_by_bg_drag{}; + static Vector keys_selected_by_bg_drag{}; auto draw_tl_bg_drag_area = [&view_width, &style, &view_amt, &s, &mouse_tl_x, &io](f32 h = 0.0f, f32 w = 0.0f) { static f32 pan_x, view_left_old, view_right_old; ImGui::InvisibleButton("##TL_BG", ImVec2{ w == 0.0f ? view_width + style.CellPadding.x * 2 : w, h == 0.0f ? row_height + style.CellPadding.y : h}); @@ -620,11 +630,11 @@ namespace K::UI { constexpr static f32 kf_tab_width = 6.0f; auto kf_drag_btn_handler = [&io, &drag_rect]( - PlugboardNodes::ChainSel *key_loop_target, bool dragging, PlugboardNodes::ChainSel& chain, i32 frame, bool is_sel, - Vector::iterator sel_it, const PlugboardNodes::InterpolationExtra& segment, f64 v, u32 ii, bool& started_dragging, const ImVec2& k_pos) { + Plugboard::ChainSel *key_loop_target, bool dragging, Plugboard::ChainSel& chain, i32 frame, bool is_sel, + Vector::iterator sel_it, const Plugboard::InterpolationExtra& segment, f64 v, u32 ii, bool& started_dragging, const ImVec2& k_pos) { if (dragging) { auto pos = std::lower_bound(chain.segments.begin(), chain.segments.end(), frame, - [](const PlugboardNodes::ChainSegment &a, i32 b) { + [](const Plugboard::ChainSegment &a, i32 b) { return a.frame < b; }); @@ -939,16 +949,18 @@ namespace K::UI { } if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("K_PLUG_OUT_TO_IN")) { - auto out = *(const PlugboardGraph::ConnectInfo *) payload->Data; - u.connection.p->connect(u.connection.index, out.p, out.index); + auto out = *(const Plugboard::ConnectInfo *) payload->Data; + Plugboard::Connect(*u.connection.p, u.connection.index, *out.p, out.index); + s.plugboard.RecollectChains(); } ImGui::EndDragDropTarget(); } - auto& connected_v = u.connection.p->inputs_fed[u.connection.index]; + + auto& connected_v = std::visit([&u](auto&& arg) -> auto& { return arg.in[u.connection.index].value; }, *u.connection.p); std::visit([&u, &dragging_on_socket, &drag_source, &s, &style, &io](auto &&arg) { using T = std::decay_t; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { if (ImGui::IsItemActive()) s.plugboard.links_pos[arg].sinks.push_back(io.MousePos); else @@ -959,10 +971,13 @@ namespace K::UI { } else if (ImGui::IsItemActive()) { if (ImGui::IsKeyPressed(ImGuiKey_LeftShift)) { - s.plugboard.nodes.push_back(PlugboardGraph::MakeInstance(PlugboardNodes::Chain)); + s.plugboard.nodes.push_back(Plugboard::MakeInstance(Plugboard::K_P_Chain)); auto it = s.plugboard.nodes.rbegin(); - u.connection.p->connect(u.connection.index, &*it, 0); - it->connect(0, &s.plugboard.in_instance, 0); + Plugboard::Connect(*u.connection.p, u.connection.index, *it, 0); + Plugboard::Connect(*it, 0, s.plugboard.in, 0); + auto& nodes = std::get(*u.connection.p).show_nodes[u.connection.index]; + nodes.clear(); + nodes.emplace_back(&*it, 0); } else { dragging_on_socket = true; @@ -986,15 +1001,15 @@ namespace K::UI { if (connected_v.index() == 0) std::visit([](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_same_v::type>) + if constexpr (std::is_same_v::type>) ImGui::DragFloat("##", &arg, 0.005f); - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) ImGui::DragInt("##", &arg, 0.005f); - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) ImGui::DragFloat4("##", &arg.r, 0.005f); - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) ImGui::DragFloat2("##", &arg.x, 0.005f); - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) ImGui::DragFloat3("##", &arg.x, 0.005f); }, std::get<0>(connected_v)); @@ -1026,8 +1041,8 @@ namespace K::UI { CompState ss = i.s; ss.current_frame = static_cast(std::round(x)); bool good; - Vector info; - return {x, std::get::type>(PlugboardGraph::ConvertValue(PlugboardGraph::Eval(ss, i.connected_v, &info), PlugboardGraph::T_Float, good))}; + Vector info; + return {x, std::get::type>(Plugboard::ConvertValue(Plugboard::Eval(ss, i.connected_v), Plugboard::T_Float, good))}; }, &info, info.samples); ImPlot::EndPlot(); } @@ -1042,7 +1057,7 @@ namespace K::UI { draw_tl_bg_drag_area(); if (uniform_open && connected_v.index() == 1) { - for (auto info: u.show_nodes) { + for (const auto& info: std::get(*u.connection.p).show_nodes[u.connection.index]) { ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height); ImGui::TableSetColumnIndex(0); ImGui::TableSetColumnIndex(1); @@ -1053,7 +1068,7 @@ namespace K::UI { auto chain_sel_it = std::find(selected_chains.begin(), selected_chains.end(), info.p); if (chain_sel_it != selected_chains.end()) n_flags |= ImGuiTreeNodeFlags_Selected; - ImGui::TreeNodeEx(info.p->node->name.c_str(), n_flags); + ImGui::TreeNodeEx(std::visit([](auto&& arg){ return arg.name; }, *info.p), n_flags); if (ImGui::IsItemClicked()) { if (chain_sel_it != selected_chains.end()) selected_chains.erase(chain_sel_it); @@ -1062,7 +1077,7 @@ namespace K::UI { } ImGui::TableSetColumnIndex(2); - auto& [chain, chain_copy] = *std::any_cast(&info.p->extra); + auto& [chain, chain_copy] = std::get(*info.p).extra; if (tl_clear_selection_request) chain.selected.clear(); @@ -1073,7 +1088,7 @@ namespace K::UI { if (ImGui::Button("<")) { s.current_frame = std::prev(std::lower_bound(chain.segments.begin(), chain.segments.end(), s.current_frame, - [](const PlugboardNodes::ChainSegment &a, i32 b) { + [](const Plugboard::ChainSegment &a, i32 b) { return a.frame < b; }))->frame; } @@ -1081,22 +1096,22 @@ namespace K::UI { ImGui::SameLine(); - f32 v = std::get::type>( - PlugboardGraph::Eval(s, info)); + f32 v = std::get::type>( + Plugboard::Eval(s, info)); if (ImGui::DragFloat("##value", &v, 0.005f)) { if (std::find(selected_chains.begin(), selected_chains.end(), info.p) == selected_chains.end()) selected_chains.push_back(info.p); auto l = std::lower_bound(chain.segments.begin(), chain.segments.end(), s.current_frame, - [](const PlugboardNodes::ChainSegment& a, i32 b) { return a.frame < b; }); + [](const Plugboard::ChainSegment& a, i32 b) { return a.frame < b; }); if (l != chain.segments.end() && l->frame == static_cast(s.current_frame)) l->value = v; else { for (auto& sel : chain.selected) if (sel >= std::distance(chain.segments.begin(), l)) sel++; - chain.segments.emplace(l, s.current_frame, v, PlugboardNodes::InterpolationExtra{ - m_has_before ? std::prev(l)->interp.interp : PlugboardNodes::K_I_Linear + chain.segments.emplace(l, s.current_frame, v, Plugboard::InterpolationExtra{ + m_has_before ? std::prev(l)->interp.interp : Plugboard::K_I_Linear }); } } @@ -1106,7 +1121,7 @@ namespace K::UI { ImGui::BeginDisabled(m_empty || chain.segments.back().frame <= s.current_frame); if (ImGui::Button(">")) s.current_frame = std::upper_bound(chain.segments.begin(), chain.segments.end(), s.current_frame, - [](i32 a, const PlugboardNodes::ChainSegment& b) { return a < b.frame; })->frame; + [](i32 a, const Plugboard::ChainSegment& b) { return a < b.frame; })->frame; ImGui::EndDisabled(); ImGui::TableSetColumnIndex(3); @@ -1173,10 +1188,10 @@ namespace K::UI { else for (auto &u: current_layer.track.uniforms) std::visit([table_left, &s](auto &&arg) { using T = std::decay_t; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) s.plugboard.links_pos[arg].sinks.push_back( {table_left, ImGui::GetCursorScreenPos().y - row_height / 2.0f}); - }, u.connection.p->inputs_fed[u.connection.index]); + }, std::visit([&u](auto&& arg) -> auto& { return arg.in[u.connection.index].value; }, *u.connection.p)); ImGui::PopStyleVar(); // FramePadding @@ -1266,8 +1281,8 @@ namespace K::UI { } u32 keys = 0; for (const auto& n : selected_chains) { - PlugboardGraph::ConnectInfo i {n, 0}; // it's a chain -- 0 is out - auto& [chain, chain_copy] = *std::any_cast(&n->extra); + Plugboard::ConnectInfo i {n, 0}; // it's a chain -- 0 is out + auto& [chain, chain_copy] = std::get(*n).extra; if (started_dragging) chain_copy = chain; @@ -1313,7 +1328,7 @@ namespace K::UI { kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel, sel_it, segment, v, ii, started_dragging, k_pos); - if (segment.interp == PlugboardNodes::K_I_CubicBezier && ii + 1 < key_loop_target->segments.size()) { + if (segment.interp == Plugboard::K_I_CubicBezier && ii + 1 < key_loop_target->segments.size()) { const auto& [k_nxt, val_nxt, _] = (key_loop_target->segments)[ii + 1]; i32 frame_nxt = k_nxt; f64 v_nxt = val_nxt; @@ -1377,10 +1392,10 @@ namespace K::UI { CompState ss = i.s; ss.current_frame = static_cast(x); bool good; - Vector info; - return {x, std::get::type>( - PlugboardGraph::ConvertValue(PlugboardGraph::Eval(ss, i.connected_v, &info), - PlugboardGraph::T_Float, good))}; + Vector info; + return {x, std::get::type>( + Plugboard::ConvertValue(Plugboard::Eval(ss, i.connected_v), + Plugboard::T_Float, good))}; }, &info, info.samples); } ImPlot::EndPlot(); @@ -1432,18 +1447,30 @@ namespace K::UI { window->DrawList->AddRectFilled(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, 0x55AA5555); window->DrawList->AddRect(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, 0xAAAA7777); } - if (window->Rect().Contains(io.MousePos) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && mouse_tl_x >= 0 && mouse_tl_x < view_width && io.MouseWheel != 0.0f) { - f32 center = mouse_tl_x / view_width * view_amt + view_left; - f32 new_view_amt = std::clamp(view_amt - 0.05f * io.MouseWheel, 0.05f, 1.0f); - view_left = center - new_view_amt / 2.0f - (mouse_tl_x / view_width - .5f) * new_view_amt; - view_right = center + new_view_amt / 2.0f - (mouse_tl_x / view_width - .5f) * new_view_amt; - if (view_left < 0.0f) { - view_right -= view_left; - view_left = 0.0f; - } - else if (view_right > 1.0f) { - view_left -= (view_right - 1.0f); - view_right = 1.0f; + if (window->Rect().Contains(io.MousePos) && mouse_tl_x >= 0 && mouse_tl_x < view_width) { + if (io.MouseWheel != 0.0f) { + if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) { + f32 center = mouse_tl_x / view_width * view_amt + view_left; + f32 new_view_amt = std::clamp(view_amt - 0.05f * io.MouseWheel, 0.05f, 1.0f); + view_left = center - new_view_amt / 2.0f - (mouse_tl_x / view_width - .5f) * new_view_amt; + view_right = center + new_view_amt / 2.0f - (mouse_tl_x / view_width - .5f) * new_view_amt; + if (view_left < 0.0f) { + view_right -= view_left; + view_left = 0.0f; + } + else if (view_right > 1.0f) { + view_left -= (view_right - 1.0f); + view_right = 1.0f; + } + } + if (show_curve_editor) { + if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) { + + } + else { + + } + } } } } @@ -1470,7 +1497,7 @@ namespace K::UI { static i32 type_current = 0; static String name{}; if (ImGui::Button("Add Uniform") && !name.empty() && !isdigit(name[0]) && !(name[0] == 'f' && name.length() == 1)) { - s.layers[s.active].track.AddUniform(name, ShaderGraph::expand_type( + s.layers[s.active].track.AddUniform(name, ExpandVariant( static_cast(type_current))); VisualTrack::ExposeUniform(s, s.layers[s.active].track.uniforms.back()); } @@ -1502,13 +1529,15 @@ namespace K::UI { ImGui::TextUnformatted(ShaderGraph::Type_To_Str[it->val.index()]); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-FLT_MIN); - std::visit([](auto&& arg) { - using T = std::decay_t; - if constexpr (std::is_same_v::type>) - DrawPlugboardVariableEditWidget(arg); - else - ImGui::Text("Plugged into %s!", arg.p->node->name.c_str()); - }, it->connection.p->inputs_fed[it->connection.index]); + std::visit([&it](auto&& n) { + std::visit([](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v::type>) + DrawPlugboardVariableEditWidget(arg); + else + ImGui::Text("Plugged into %s!", std::visit([](auto&& d) { return d.name; }, *arg.p)); + }, n.in[it->connection.index].value); + }, *it->connection.p); ImGui::TableNextColumn(); if (ImGui::Button("X")) { bgfx::destroy(it->handle); @@ -1570,7 +1599,7 @@ namespace K::UI { if (ImGui::Begin("Interpolation", &draw_interpolation, ImGuiWindowFlags_NoScrollbar)) { ImGuiIO& io = ImGui::GetIO(); - static PlugboardNodes::K_Interpolation type_current = PlugboardNodes::K_I_CubicBezier; + static Plugboard::K_Interpolation type_current = Plugboard::K_I_CubicBezier; static const ImVec2 p1 { 0.0f, 0.0f}, p4 {1.0f, 1.0f}; static ImVec2 p2{1.0f, 0.0f}, p3{0.0f, 1.0f}, p2_old = p2, p3_old = p3; @@ -1578,8 +1607,8 @@ namespace K::UI { if (ImGui::Button("Apply")) for (auto& layer : s.layers) for (auto& u : layer.track.uniforms) - for (auto& n : u.show_nodes) { - auto& [chain, _] = *std::any_cast(&n.p->extra); + for (auto& n : std::get(*u.connection.p).show_nodes[u.connection.index]) { + auto& [chain, _] = std::get(*n.p).extra; for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) { auto nit = std::next(it); if (nit != chain.segments.end() && std::find(chain.selected.begin(), chain.selected.end(), std::distance(chain.segments.begin(), it)) != chain.selected.end() && @@ -1591,10 +1620,10 @@ namespace K::UI { ImGui::SameLine(); - ImGui::Combo("##Type", (i32*)&type_current, PlugboardNodes::K_Interpolation_Names, PlugboardNodes::K_I_Count); + ImGui::Combo("##Type", (i32*)&type_current, Plugboard::K_Interpolation_Names, Plugboard::K_I_Count); f32 w_window = std::min(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y); - if (type_current == PlugboardNodes::K_I_CubicBezier) { + if (type_current == Plugboard::K_I_CubicBezier) { f32 y_max = std::max(std::max(p2.y, p3.y), p4.y), y_min = std::min(std::min(p1.y, p2.y), p3.y), y_range = y_max - y_min; @@ -1666,7 +1695,7 @@ namespace K::UI { ImPlot::SetupFinish(); constexpr static u32 samples = 100; - if (type_current == PlugboardNodes::K_I_CubicBezier) { + if (type_current == Plugboard::K_I_CubicBezier) { f64 p2x = p2.x, p2y = p2.y, p3x = p3.x, p3y= p3.y; if (ImPlot::DragPoint(0, &p2x, &p2y, {1.0f, 1.0f, 1.0f, 1.0f})) { p2.x = static_cast(std::clamp(p2x, 0.0, 1.0)); @@ -1680,7 +1709,7 @@ namespace K::UI { ImPlot::PlotLineG("v", [](i32 idx, void* user_data) { f32 t = static_cast(idx) / static_cast(samples); - return ImPlotPoint{t, PlugboardNodes::EvalInterpolation(t, {.interp = type_current, .v = {p2.x,p2.y, p3.x, p3.y}})}; + return ImPlotPoint{t, Plugboard::EvalInterpolation(t, {.interp = type_current, .v = {p2.x,p2.y, p3.x, p3.y}})}; }, nullptr, samples); ImPlot::EndPlot(); diff --git a/Keishiki/VisualTrack.cpp b/Keishiki/VisualTrack.cpp index cc0172f..2f275a7 100644 --- a/Keishiki/VisualTrack.cpp +++ b/Keishiki/VisualTrack.cpp @@ -2,36 +2,36 @@ #include "Keishiki.h" namespace K { - PlugboardGraph::T_Map::type ShaderValToPlugboard(const ShaderGraph::T_Map::type& val) { - return std::visit([](auto&& arg) -> PlugboardGraph::T_Map::type { + Plugboard::T_Map::type ShaderValToPlugboard(const ShaderGraph::T_Map::type& val) { + return std::visit([](auto&& arg) -> Plugboard::T_Map::type { using T = std::decay_t; if constexpr (std::is_same_v::type>) - return PlugboardGraph::T_Map::type(arg); + return Plugboard::T_Map::type(arg); else if constexpr (std::is_same_v::type>) - return PlugboardGraph::T_Map::type(arg); + return Plugboard::T_Map::type(arg); else if constexpr (std::is_same_v::type>) - return PlugboardGraph::T_Map::type{arg.r, arg.g, arg.b, arg.a}; + return Plugboard::T_Map::type{arg.r, arg.g, arg.b, arg.a}; else if constexpr (std::is_same_v::type>) - return PlugboardGraph::T_Map::type{arg.x, arg.y}; + return Plugboard::T_Map::type{arg.x, arg.y}; else if constexpr (std::is_same_v::type>) - return PlugboardGraph::T_Map::type{arg.x, arg.y, arg.z}; + return Plugboard::T_Map::type{arg.x, arg.y, arg.z}; },val); } - ShaderGraph::T_Map::type PlugboardValToShader(const PlugboardGraph::T_Map::type& val) { + ShaderGraph::T_Map::type PlugboardValToShader(const Plugboard::T_Map::type& val) { return std::visit([](auto&& arg) -> ShaderGraph::T_Map::type { using T = std::decay_t; - if constexpr (std::is_same_v::type>) + if constexpr (std::is_same_v::type>) return ShaderGraph::T_Map::type(arg); - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) return ShaderGraph::T_Map::type(arg); - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) return ShaderGraph::T_Map::type{arg.r, arg.g, arg.b, arg.a}; - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) return ShaderGraph::T_Map::type{arg.x, arg.y}; - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) return ShaderGraph::T_Map::type{arg.x, arg.y, arg.z}; - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) return ShaderGraph::T_Map::type{}; },val); } @@ -53,16 +53,15 @@ namespace K { else { std::visit([&u, &s, &val_target](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { bool good; - u.show_nodes.clear(); - val_target = PlugboardValToShader(PlugboardGraph::ConvertValue(PlugboardGraph::Eval(s, arg, &u.show_nodes), - static_cast(u.val.index()), // todo DANGEROUS !! + val_target = PlugboardValToShader(Plugboard::ConvertValue(Plugboard::Eval(s, arg), + static_cast(u.val.index()), // todo DANGEROUS !! good)); } - else if constexpr (std::is_same_v::type>) + else if constexpr (std::is_same_v::type>) val_target = PlugboardValToShader(arg); - }, u.connection.p->inputs_fed[u.connection.index]); + }, std::visit([&u](auto&& arg) { return arg.in[u.connection.index].value; }, *u.connection.p)); } std::visit([&pack](auto&& arg) { using T = std::decay_t; @@ -183,16 +182,18 @@ namespace K { } void VisualTrack::ExposeUniform(CompState& s, Uniform& uu) { - uu.connection = {&s.plugboard.out_instance, static_cast(s.plugboard.out_instance.node->in.size())}; - s.plugboard.out_instance.node->in.push_back({ uu.name, PlugboardGraph::Type(uu.val.index())}); // this is shaky and bug prone -- relies on the shader types being in line with plugboard types - s.plugboard.out_instance.inputs_fed.emplace_back(ShaderValToPlugboard(uu.val)); + auto& n = std::get(s.plugboard.out); + uu.connection = {&s.plugboard.out, static_cast(n.in.size())}; + // this is shaky and bug prone -- relies on the shader types being in line with plugboard types + n.in.push_back(Plugboard::InSocket{ uu.name, Plugboard::Type(uu.val.index()), ShaderValToPlugboard(uu.val)}); + n.show_nodes.push_back({}); } void VisualTrack::ExposeUniform(CompState& s, u32 i) { ExposeUniform(s, uniforms[i]); } - void VisualTrack::HideUniform(CompState& s, Uniform& uu) { +/* void VisualTrack::HideUniform(CompState& s, Uniform& uu) { uu.connection.p->disconnect(uu.connection.index); for (auto& l : s.layers) @@ -202,7 +203,7 @@ namespace K { s.plugboard.comp_out.in.erase(s.plugboard.out_instance.node->in.begin() + uu.connection.index); s.plugboard.out_instance.inputs_fed.erase(s.plugboard.out_instance.inputs_fed.begin() + uu.connection.index); uu.connection = {nullptr, 0}; - } + }*/ void VisualTrack::AddUniform(const String& s, ShaderGraph::T_Map::type&& val) { for (auto& u : uniforms) diff --git a/Keishiki/include/Common.h b/Keishiki/include/Common.h index a072f09..ba66713 100644 --- a/Keishiki/include/Common.h +++ b/Keishiki/include/Common.h @@ -23,7 +23,7 @@ namespace K { using Byte = u8; using Char = char; template using Vector = std::vector; - template using Dict = std::unordered_map; + template using Dict = std::unordered_map; inline void LogBase(const std::string_view& s, u8 level) { static const char *levels[] = { diff --git a/Keishiki/include/Keishiki.h b/Keishiki/include/Keishiki.h index 9c8cc94..61ae1b9 100644 --- a/Keishiki/include/Keishiki.h +++ b/Keishiki/include/Keishiki.h @@ -1,6 +1,6 @@ #pragma once #include "Common.h" -#include "PlugboardGraph.h" +#include "Plugboard.h" #include "VisualTrack.h" #include @@ -31,7 +31,7 @@ namespace K { Vector selected; // indices for layers Vector disabled; // indices for layers - PlugboardGraph::PlugboardGraph plugboard; + Plugboard::Plugboard plugboard; }; extern struct AppState { diff --git a/Keishiki/include/Plugboard.h b/Keishiki/include/Plugboard.h new file mode 100644 index 0000000..3707ba0 --- /dev/null +++ b/Keishiki/include/Plugboard.h @@ -0,0 +1,605 @@ +#pragma once +#include "Common.h" +#include "Graphics.h" +#include +#include +#include +#include +#include +#include +#include + +namespace K::Plugboard { + enum Type { + T_Float, + T_Int, + T_RGBA, + T_XY, + T_XYZ, + T_String, + T_Count + }; + static const char *Type_To_Str[] = { + "Float", + "Int", + "RGBA", + "XY", + "XYZ", + "String", + "Error" + }; + + struct RGBA { f32 r, g, b, a; }; + struct XYZ { f32 x, y, z; }; + struct XY { f32 x, y; }; + template struct T_Map; + template<> struct T_Map { + using type = f32; + }; + template<> struct T_Map { + using type = i32; + }; + template<> struct T_Map { + using type = RGBA; + }; + template<> struct T_Map { + using type = XY; + }; + template<> struct T_Map { + using type = XYZ; + }; + template<> struct T_Map { + using type = String; + }; + template<> struct T_Map { + using type = std::variant; + }; + constexpr String VarToString(const T_Map::type& var); + + T_Map::type ExpandVariant(u32 i); + + struct Add; + struct Negate; + struct Subtract; + struct Multiply; + struct Divide; + struct Sign; + struct Sin; + struct Cos; + struct Tan; + struct Arcsin; + struct Arccos; + struct Arctan; + struct Atan2; + struct Minimum; + struct Maximum; + struct Power; + struct SquareRoot; + struct NaturalLogarithm; + struct AbsoluteValue; + struct Interpolation; + struct Chain; + struct CompositionIn; + struct CompositionOut; + + using NodeInstance = std::variant< + Add, + Negate, + Subtract, + Multiply, + Divide, + Sign, + Sin, + Cos, + Tan, + Arcsin, + Arccos, + Arctan, + Atan2, + Minimum, + Maximum, + Power, + SquareRoot, + NaturalLogarithm, + AbsoluteValue, + Interpolation, + Chain, + CompositionIn, + CompositionOut + >; + + enum K_P_Nodes { + K_P_Add = 0, + K_P_Negate, + K_P_Subtract, + K_P_Multiply, + K_P_Divide, + K_P_Sign, + K_P_Sin, + K_P_Cos, + K_P_Tan, + K_P_Arcsin, + K_P_Arccos, + K_P_Arctan, + K_P_Atan2, + K_P_Minimum, + K_P_Maximum, + K_P_Power, + K_P_SquareRoot, + K_P_NaturalLogarithm, + K_P_AbsoluteValue, + K_P_Interpolation, + K_P_Chain, + K_P_CompositionIn, + K_P_CompositionOut, + K_P_Count + }; + + NodeInstance MakeInstance(K_P_Nodes i); + + 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 + }; + + extern const char *K_Interpolation_Names[]; + + struct InterpolationExtra { + K_Interpolation interp; + f32 v[4]; // currently used only for bezier tangent points -- between [0,1]^2 fitted to segment + }; + + constexpr 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 = Graphics::GetCubicUniqueReal(-a+3.0f*b-3.0f*c+d, 3.0f*a-6.0f*b+3.0f*c, -3.0f*a+3.0f*b,a-x); + return static_cast(Graphics::CubicBezier(0.0f, p2y, p3y, 1.0f, t)); + } + case K_I_Count: + std::unreachable(); + } + std::unreachable(); + } + + struct ChainSegment { + i32 frame; + f32 value; + InterpolationExtra interp; + }; + + struct ChainSel { + Vector segments; + Vector selected; // indices + }; + + struct ChainExtra { + ChainSel chain, + temp; // for UI -- does not represent any state + }; + + struct ConnectInfo { + NodeInstance *p; // NOTE: so NodeInstances must be stored in a list, otherwise vector realloc leads to UAF + u32 index; + auto operator<=>(const ConnectInfo&) const = default; // too-modern c++ black magic ? + }; + + struct InSocket { + String name; + Type type; + std::variant::type, ConnectInfo> value; + }; + + struct OutSocket { + String name; + Type type; + Vector outgoing; + }; + + template + requires requires(F func, ConnectInfo p) { + { func(p) } -> std::same_as; // abort condition -- continue if true + } + bool DFS(const NodeInstance& n, F&& f) { // returns false if aborted + return std::visit([&f](auto&& node) { + for (const auto& socket : node.in) + if (!std::visit([&f](auto&& info) { + if constexpr (std::is_same_v, ConnectInfo>) { + if (f(info)) + return DFS(*info.p, f); + else + return false; + } + else return true; + }, socket.value)) + return false; + return true; + }, n); + } + + void CollectChains(ConnectInfo& in, Vector& chains); + + template + concept Node = requires(T node) { + { T::name } -> std::same_as; + { node.in.size() } -> std::convertible_to; + { node.out.size() } -> std::convertible_to; + { node.fetch.size() } -> std::convertible_to; // in the future enforce same_as, tuple_size> if both are static arrays + { node.pos } -> std::convertible_to; + }; + + struct ConnectInfoHasher { + std::size_t operator()(const ConnectInfo& k) const + { + return std::hash()(k.p) ^ std::hash()(k.index); + } + }; + + T_Map::type ConvertValue(const T_Map::type& v, Type target, bool& good); + + T_Map::type Eval(const CompState& s, const ConnectInfo& info); + + struct LinksFromSource { + ImVec2 source; + Vector sinks; + }; + + T_Map::type FetchAdd(const CompState& s, const Add& n); + struct Add { + const static char constexpr *name = static_cast("Add"); + std::array in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } }; + std::array out = { OutSocket{ "a+b", T_Float } }; + std::array::type (*)(const CompState&, const Add&), 1> fetch = { FetchAdd }; + ImVec2 pos; + }; + + T_Map::type FetchNegate(const CompState& s, const Negate& n); + struct Negate { + const static char constexpr *name = static_cast("Add"); + std::array in = { InSocket{"a", T_Float } }; + std::array out = { OutSocket{ "-a", T_Float } }; + std::array::type (*)(const CompState&, const Negate&), 1> fetch = { FetchNegate }; + ImVec2 pos; + }; + + T_Map::type FetchSubtract(const CompState& s, const Subtract& n); + struct Subtract { + const static char constexpr *name = static_cast("Subtract"); + std::array in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } }; + std::array out = { OutSocket{ "a-b", T_Float } }; + std::array::type (*)(const CompState&, const Subtract&), 1>fetch = { FetchSubtract }; + ImVec2 pos; + }; + + T_Map::type FetchMultiply(const CompState& s, const Multiply& n); + struct Multiply { + const static char constexpr *name = static_cast("Multiply"); + std::array in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } }; + std::array out = { OutSocket{ "a*b", T_Float } }; + std::array::type (*)(const CompState&, const Multiply&), 1> fetch = { FetchMultiply }; + ImVec2 pos; + }; + + T_Map::type FetchDivide(const CompState& s, const Divide& n); + struct Divide { + const static char constexpr *name = static_cast("Divide"); + std::array in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } }; + std::array out = { OutSocket{ "a/b", T_Float } }; + std::array::type (*)(const CompState&, const Divide&), 1> fetch = { FetchDivide }; + ImVec2 pos; + }; + + T_Map::type FetchSign(const CompState& s, const Sign& n); + struct Sign { + const static char constexpr *name = static_cast("Sign"); + std::array in = { InSocket{"a", T_Float } }; + std::array out = { OutSocket{ "sgn(a)", T_Float } }; + std::array::type (*)(const CompState&, const Sign&), 1> fetch = { FetchSign }; + ImVec2 pos; + }; + + T_Map::type FetchSin(const CompState& s, const Sin& n); + struct Sin { + const static char constexpr *name = static_cast("Sin"); + std::array in = { InSocket{"a (rad)", T_Float } }; + std::array out = { OutSocket{ "sin(a)", T_Float } }; + std::array::type (*)(const CompState&, const Sin&), 1> fetch = { FetchSin }; + ImVec2 pos; + }; + + T_Map::type FetchCos(const CompState& s, const Cos& n); + struct Cos { + const static char constexpr *name = static_cast("Cos"); + std::array in = { InSocket{"a (rad)", T_Float } }; + std::array out = { OutSocket{ "cos(a)", T_Float } }; + std::array::type (*)(const CompState&, const Cos&), 1> fetch = { FetchCos }; + ImVec2 pos; + }; + + T_Map::type FetchTan(const CompState& s, const Tan& n); + struct Tan { + const static char constexpr *name = static_cast("Tan"); + std::array in = { InSocket{"a (rad)", T_Float } }; + std::array out = { OutSocket{ "tan(a)", T_Float } }; + std::array::type (*)(const CompState&, const Tan&), 1> fetch = { FetchTan }; + ImVec2 pos; + }; + + T_Map::type FetchArcsin(const CompState& s, const Arcsin& n); + struct Arcsin { + const static char constexpr *name = static_cast("Arcsin"); + std::array in = { InSocket{"a", T_Float } }; + std::array out = { OutSocket{ "arcsin(a) (rad)", T_Float } }; + std::array::type (*)(const CompState&, const Arcsin&), 1> fetch = { FetchArcsin }; + ImVec2 pos; + }; + + T_Map::type FetchArccos(const CompState& s, const Arccos& n); + struct Arccos { + const static char constexpr *name = static_cast("Arccos"); + std::array in = { InSocket{"a", T_Float } }; + std::array out = { OutSocket{ "arccos(a) (rad)", T_Float } }; + std::array::type (*)(const CompState&, const Arccos&), 1> fetch = { FetchArccos }; + ImVec2 pos; + }; + + T_Map::type FetchArctan(const CompState& s, const Arctan& n); + struct Arctan { + const static char constexpr *name = static_cast("Arctan"); + std::array in = { InSocket{"a", T_Float } }; + std::array out = { OutSocket{ "arctan(a) (rad)", T_Float } }; + std::array::type (*)(const CompState&, const Arctan&), 1> fetch = { FetchArctan }; + ImVec2 pos; + }; + + T_Map::type FetchAtan2(const CompState& s, const Atan2& n); + struct Atan2 { + const static char constexpr *name = static_cast("Atan2"); + std::array in = { InSocket{"y", T_Float }, InSocket{"x", T_Float } }; + std::array out = { OutSocket{ "atan2(y, x) (rad)", T_Float } }; + std::array::type (*)(const CompState&, const Atan2&), 1> fetch = { FetchAtan2 }; + ImVec2 pos; + }; + + T_Map::type FetchMinimum(const CompState& s, const Minimum& n); + struct Minimum { + const static char constexpr *name = static_cast("Minimum"); + std::array in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } }; + std::array out = { OutSocket{ "min(a, b)", T_Float } }; + std::array::type (*)(const CompState&, const Minimum&), 1> fetch = { FetchMinimum }; + ImVec2 pos; + }; + + T_Map::type FetchMaximum(const CompState& s, const Maximum& n); + struct Maximum { + const static char constexpr *name = static_cast("Maximum"); + std::array in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } }; + std::array out = { OutSocket{ "max(a, b)", T_Float } }; + std::array::type (*)(const CompState&, const Maximum&), 1> fetch = { FetchMaximum }; + ImVec2 pos; + }; + + T_Map::type FetchPower(const CompState& s, const Power& n); + struct Power { + const static char constexpr *name = static_cast("Power"); + std::array in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } }; + std::array out = { OutSocket{ "a^b", T_Float } }; + std::array::type (*)(const CompState&, const Power&), 1> fetch = { FetchPower }; + ImVec2 pos; + }; + + T_Map::type FetchSquareRoot(const CompState& s, const SquareRoot& n); + struct SquareRoot { + const static char constexpr *name = static_cast("Square Root"); + std::array in = { InSocket{"a", T_Float } }; + std::array out = { OutSocket{ "sqrt(a)", T_Float } }; + std::array::type (*)(const CompState&, const SquareRoot&), 1> fetch = { FetchSquareRoot }; + ImVec2 pos; + }; + + T_Map::type FetchNaturalLogarithm(const CompState& s, const NaturalLogarithm& n); + struct NaturalLogarithm { + const static char constexpr *name = static_cast("Natural Logarithm"); + std::array in = { InSocket{"a", T_Float } }; + std::array out = { OutSocket{ "log(a)", T_Float } }; + std::array::type (*)(const CompState&, const NaturalLogarithm&), 1> fetch = { FetchNaturalLogarithm }; + ImVec2 pos; + }; + + T_Map::type FetchAbsoluteValue(const CompState& s, const AbsoluteValue& n); + struct AbsoluteValue { + const static char constexpr *name = static_cast("Absolute Value"); + std::array in = { InSocket{"a", T_Float } }; + std::array out = { OutSocket{ "|a|", T_Float } }; + std::array::type (*)(const CompState&, const AbsoluteValue&), 1> fetch = { FetchAbsoluteValue }; + ImVec2 pos; + }; + + T_Map::type FetchInterpolation(const CompState& s, const Interpolation& n); + struct Interpolation { + const static char constexpr *name = static_cast("Interpolation"); + std::array in = { InSocket{"t", T_Float } }; + std::array out = { OutSocket{ "Value", T_Float } }; + std::array::type (*)(const CompState&, const Interpolation&), 1> fetch = { FetchInterpolation }; + ImVec2 pos; + InterpolationExtra extra{}; + void Draw(); + }; + + T_Map::type FetchChain(const CompState& s, const Chain& n); + struct Chain { + const static char constexpr *name = static_cast("Chain"); + std::array in = { InSocket{"In", T_Int } }; + std::array out = { OutSocket{ "Out", T_Float } }; + std::array::type (*)(const CompState&, const Chain&), 1> fetch = { FetchChain }; + ImVec2 pos; + ChainExtra extra{}; + }; + + T_Map::type FetchCompositionInFrame(const CompState& s, const CompositionIn& n); + T_Map::type FetchCompositionInTime(const CompState& s, const CompositionIn& n); + T_Map::type FetchCompositionInAppTicks(const CompState& s, const CompositionIn& n); + struct CompositionIn { + const static char constexpr *name = static_cast("Composition In"); + std::array in = {}; + std::array out = { OutSocket{"Frame", T_Int}, OutSocket{"Time (s)", T_Float}, OutSocket{"App Ticks", T_Float} }; + std::array::type (*)(const CompState&, const CompositionIn&), 3> fetch = { FetchCompositionInFrame, FetchCompositionInTime, FetchCompositionInAppTicks }; + ImVec2 pos; + }; + + struct CompositionOut { + const static char constexpr *name = static_cast("Composition Out"); + Vector in = {}; + Vector> show_nodes; + std::array out = {}; + std::array::type (*)(const CompState&, const CompositionOut&), 0> fetch = {}; + ImVec2 pos; + }; + + struct Plugboard { + std::list nodes; // OK complexity wise since we would usually traverse the entire thing anyway, locality will likely be bad + Dict links_pos; // this is hilariously bad + NodeInstance in, out; + void RecollectChains(); + }; + + void Connect(NodeInstance& from_instance, u8 input_index, NodeInstance& to_instance, u8 to_out_index); + + void Disconnect(NodeInstance& from_instance, u8 input_index); +} diff --git a/Keishiki/include/PlugboardGraph.h b/Keishiki/include/PlugboardGraph.h deleted file mode 100644 index b37b57e..0000000 --- a/Keishiki/include/PlugboardGraph.h +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once -#include "Common.h" -#include "Graphics.h" -#include -#include -#include -#include -#include - -namespace K::PlugboardGraph { - enum Type { - T_Float, - T_Int, - T_RGBA, - T_XY, - T_XYZ, - T_String, - T_Count - }; - static const char *Type_To_Str[] = { - "Float", - "Int", - "RGBA", - "XY", - "XYZ", - "String", - "Error" - }; - - struct RGBA { f32 r, g, b, a; }; - struct XYZ { f32 x, y, z; }; - struct XY { f32 x, y; }; - template struct T_Map; - template<> struct T_Map { - using type = f32; - }; - template<> struct T_Map { - using type = i32; - }; - template<> struct T_Map { - using type = RGBA; - }; - template<> struct T_Map { - using type = XY; - }; - template<> struct T_Map { - using type = XYZ; - }; - template<> struct T_Map { - using type = String; - }; - template<> struct T_Map { - using type = std::variant; - }; - constexpr String VarToString(const T_Map::type& var); - - T_Map::type expand_type(Type i); - - struct Socket { - String name; - Type type; - }; - - struct NodeInstance; - - struct ConnectInfo { - NodeInstance *p; // NOTE: so NodeInstances must be stored in a list, otherwise vector realloc leads to UAF - u32 index; - auto operator<=>(const ConnectInfo&) const = default; // too-modern c++ black magic ? - }; - - struct Node { - String name; // will likely get SSO for most stuff - - Vector in; - Vector out; - - Vector::type(*)(const CompState&, const NodeInstance&, Vector*)> 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 ConnectInfoHasher { - std::size_t operator()(const ConnectInfo& k) const - { - return std::hash()(k.p) ^ std::hash()(k.index); - } - }; - - struct NodeInstance { - Node *node; // should be safe if we require those to be static, UAF might pop up later - - Vector::type, ConnectInfo>> inputs_fed; - Vector> outputs_going; - - ImVec2 pos, old_pos; - - std::any extra; - - void connect(u32 input_index, NodeInstance *to, u32 to_out_index); - void disconnect(u32 input_index); - }; - - - NodeInstance MakeInstance(Node& n); - - T_Map::type ConvertValue(const T_Map::type& v, Type target, bool& good); - - T_Map::type Eval(const CompState& s, const ConnectInfo& info, Vector *show_nodes = nullptr); - - struct LinksFromSource { - ImVec2 source; - Vector sinks; - }; - - struct PlugboardGraph { - std::list nodes; // OK complexity wise since we would usually traverse the entire thing anyway, locality will likely be bad - std::unordered_map links_pos; // this is hilariously bad - Node comp_in, comp_out; - NodeInstance in_instance, out_instance; - }; - -} diff --git a/Keishiki/include/PlugboardNodes.h b/Keishiki/include/PlugboardNodes.h deleted file mode 100644 index b704095..0000000 --- a/Keishiki/include/PlugboardNodes.h +++ /dev/null @@ -1,229 +0,0 @@ -#pragma once -#include "Common.h" -#include "PlugboardGraph.h" -#include -#include -#include -#include - -namespace { - template - typename K::PlugboardGraph::T_Map::type GetNodeInputArg(const K::CompState& s, const K::PlugboardGraph::NodeInstance& n, u32 index, K::Vector *show_nodes) { - bool good; - typename K::PlugboardGraph::T_Map::type v = std::get::type>(K::PlugboardGraph::ConvertValue(std::visit([&show_nodes, &s](auto&& arg) { - using U = std::decay_t; - if constexpr (std::is_same_v) - return K::PlugboardGraph::Eval(s, arg, show_nodes); - else - return arg; - }, n.inputs_fed[index]), type, good)); - if (!good) - K::LogError("Type mismatch on plugboard evaluation!"); - - return v; - } -} - -namespace K::PlugboardNodes { - extern PlugboardGraph::Node Add; - extern PlugboardGraph::Node Negate; - extern PlugboardGraph::Node Subtract; - extern PlugboardGraph::Node Multiply; - extern PlugboardGraph::Node Divide; - extern PlugboardGraph::Node Sign; - extern PlugboardGraph::Node Sin; - extern PlugboardGraph::Node Cos; - extern PlugboardGraph::Node Tan; - extern PlugboardGraph::Node Arcsin; - extern PlugboardGraph::Node Arccos; - extern PlugboardGraph::Node Arctan; - extern PlugboardGraph::Node Atan2; - extern PlugboardGraph::Node Minimum; - extern PlugboardGraph::Node Maximum; - extern PlugboardGraph::Node Power; - extern PlugboardGraph::Node SquareRoot; - extern PlugboardGraph::Node NaturalLogarithm; - extern PlugboardGraph::Node AbsoluteValue; - - 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 - }; - - extern const char *K_Interpolation_Names[]; - - struct InterpolationExtra { - K_Interpolation interp; - f32 v[4]; // currently used only for bezier tangent points -- between [0,1]^2 fitted to segment - }; - - constexpr 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 = Graphics::GetCubicUniqueReal(-a+3.0f*b-3.0f*c+d, 3.0f*a-6.0f*b+3.0f*c, -3.0f*a+3.0f*b,a-x); - return static_cast(Graphics::CubicBezier(0.0f, p2y, p3y, 1.0f, t)); - } - default: - return {}; - } - } - - extern PlugboardGraph::Node Interpolation; - - struct ChainSegment { - i32 frame; - f32 value; - InterpolationExtra interp; - }; - - struct ChainSel { - Vector segments; - Vector selected; // indices - }; - - struct ChainExtra { - ChainSel chain, - temp; // for UI -- does not represent any state - }; - - extern PlugboardGraph::Node Chain; - - extern std::array Nodes; -} diff --git a/Keishiki/include/Resource.h b/Keishiki/include/Resource.h index ed5a8f9..92f099a 100644 --- a/Keishiki/include/Resource.h +++ b/Keishiki/include/Resource.h @@ -3,7 +3,6 @@ #include #include #include -#include namespace K::Resource { extern std::filesystem::path ffmpeg_path; @@ -33,7 +32,7 @@ namespace K::Resource { std::filesystem::path filename; std::filesystem::file_time_type last_updated; bgfx::TextureHandle tex; - u8 *buf; + Byte *buf; i32 h, w, channels; bgfx::TextureFormat::Enum format; }; diff --git a/Keishiki/include/ShaderGraph.h b/Keishiki/include/ShaderGraph.h index c13c309..fc89e67 100644 --- a/Keishiki/include/ShaderGraph.h +++ b/Keishiki/include/ShaderGraph.h @@ -1,7 +1,6 @@ #pragma once #include "Common.h" #include "Graphics.h" -#include // todo a lot of problems in this unit can lead to very bad performance namespace K::ShaderGraph { @@ -46,7 +45,7 @@ namespace K::ShaderGraph { }; String VarToString(const T_Map::type& var); - T_Map::type expand_type(Type i); + T_Map::type ExpandVariant(u32 i); struct Node; struct InSlot { @@ -73,6 +72,6 @@ namespace K::ShaderGraph { Vector nodes; NodeInstance *RGBA_node; u16 RGBA_node_out_index; - String generate_shader() const; + [[nodiscard]] String GenerateShader() const; }; } diff --git a/Keishiki/include/VisualTrack.h b/Keishiki/include/VisualTrack.h index 722dffc..a3ee6c8 100644 --- a/Keishiki/include/VisualTrack.h +++ b/Keishiki/include/VisualTrack.h @@ -5,15 +5,14 @@ #include #include #include -#include "PlugboardGraph.h" +#include "Plugboard.h" namespace K { struct Uniform { String name; bgfx::UniformHandle handle; ShaderGraph::T_Map::type val; - PlugboardGraph::ConnectInfo connection; - Vector show_nodes; + Plugboard::ConnectInfo connection; }; struct Sampler { @@ -22,9 +21,9 @@ namespace K { Resource::Resource *resource; }; - ShaderGraph::T_Map::type PlugboardValToShader(const PlugboardGraph::T_Map::type& val); + ShaderGraph::T_Map::type PlugboardValToShader(const Plugboard::T_Map::type& val); - PlugboardGraph::T_Map::type ShaderValToPlugboard(const ShaderGraph::T_Map::type& val); + Plugboard::T_Map::type ShaderValToPlugboard(const ShaderGraph::T_Map::type& val); struct VisualTrack { ShaderGraph::ShaderGraph tree; @@ -36,7 +35,7 @@ namespace K { f32 view[16], f32 transform[16]); static void ExposeUniform(CompState& s, Uniform& uu); void ExposeUniform(CompState& s, u32 i); - static void HideUniform(CompState& s, Uniform& uu); +// static void HideUniform(CompState& s, Uniform& uu); void AddUniform(const String& s, ShaderGraph::T_Map::type&& val); void AddSampler(const String& s);