This commit is contained in:
lachrymaLF 2024-06-26 13:11:59 -04:00
parent ccadf34f4c
commit cf0af93cac
14 changed files with 437 additions and 232 deletions

View file

@ -19,9 +19,9 @@ set_property(TARGET Keishiki PROPERTY CXX_STANDARD 23)
include_directories ("include" "include/ext" "ext/imgui" "ext/implot" "ext/plf_colony")
add_subdirectory("ext/fmt")
add_subdirectory("ext/glaze")
add_subdirectory("ext/freetype")
add_compile_definitions(WL_EGL_PLATFORM)
#add_compile_definitions(WL_EGL_PLATFORM)
add_subdirectory("ext/bgfx")
add_compile_definitions(IMGUI_DEFINE_MATH_OPERATORS)
@ -47,5 +47,5 @@ else ()
find_package(ECM REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_FIND_MODULE_DIR})
find_package(Wayland REQUIRED Egl)
target_link_libraries (Keishiki PRIVATE freetype bgfx bx SDL3::SDL3 ${Wayland_LIBRARIES} fmt)
target_link_libraries (Keishiki PRIVATE freetype bgfx bx SDL3::SDL3 ${Wayland_LIBRARIES} fmt glaze_glaze)
endif ()

View file

@ -19,12 +19,16 @@ namespace K {
K::AppState app_state;
bool Init() {
#if !WL_EGL_PLATFORM
SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "x11");
#endif
if (SDL_Init(SDL_INIT_VIDEO)) {
Log(K_L_Error, "{}", SDL_GetError());
return false;
}
app_state.window = SDL_CreateWindow("Keishiki", app_state.window_width, app_state.window_height, SDL_WINDOW_RESIZABLE);
app_state.window = SDL_CreateWindow("Keishiki", app_state.window_width, app_state.window_height, SDL_WINDOW_HIGH_PIXEL_DENSITY);
SDL_SetWindowResizable(app_state.window, true);
if (app_state.window == nullptr) {
Log(K_L_Error, "{}", SDL_GetError());
@ -69,16 +73,16 @@ namespace K {
if (!K::Resource::Init())
return false;
K::UI::Init(app_state.window);
UI::Init(app_state.window);
if (!K::Graphics::Init()) {
Log(K_L_Error, "Graphics init failed");
return false;
}
app_state.compositions.emplace_back("default project", 0, 100, 30.0f, 1280, 550);
app_state.inspecting_composition = 0;
K::UI::SetupViewport(app_state.compositions[0]);
app_state.project.compositions.emplace_back("default project", 0, 100, 30.0f, 1280, 550);
app_state.project.inspecting_composition = 0;
UI::SetupViewport(app_state.project.compositions[app_state.project.inspecting_composition]);
return true;
}
@ -98,8 +102,9 @@ namespace K {
break;
}
if (current_event.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
SDL_GetWindowSize(app_state.window, &app_state.window_width, &app_state.window_height);
bgfx::reset(app_state.window_width, app_state.window_height);
app_state.window_width = current_event.window.data1;
app_state.window_height = current_event.window.data2;
bgfx::reset(current_event.window.data1, current_event.window.data2, BGFX_RESET_VSYNC);
UI::SetLogoView();
}
}

View file

@ -48,7 +48,7 @@ namespace K::Plugboard {
}
template <Type type>
typename T_Map<type>::type GetNodeInputArg(const CompState& s, const InSocket& socket) {
typename T_Map<type>::type GetNodeInputArg(const CompositionState& s, const InSocket& socket) {
bool good;
typename T_Map<type>::type v = std::get<typename T_Map<type>::type>(ConvertValue(std::visit([&s](auto&& arg) {
using U = std::decay_t<decltype(arg)>;
@ -113,83 +113,83 @@ namespace K::Plugboard {
"Invalid"
};
T_Map<T_Count>::type FetchAdd(const CompState& s, const Add& n) {
T_Map<T_Count>::type FetchAdd(const CompositionState& s, const Add& n) {
return GetNodeInputArg<T_Float>(s, n.in[0]) + GetNodeInputArg<T_Float>(s, n.in[1]);
}
T_Map<T_Count>::type FetchNegate(const CompState& s, const Negate& n) {
T_Map<T_Count>::type FetchNegate(const CompositionState& s, const Negate& n) {
return -GetNodeInputArg<T_Float>(s, n.in[0]);
}
T_Map<T_Count>::type FetchSubtract(const CompState& s, const Subtract& n) {
T_Map<T_Count>::type FetchSubtract(const CompositionState& s, const Subtract& n) {
return GetNodeInputArg<T_Float>(s, n.in[0]) - GetNodeInputArg<T_Float>(s, n.in[1]);
}
T_Map<T_Count>::type FetchMultiply(const CompState& s, const Multiply& n) {
T_Map<T_Count>::type FetchMultiply(const CompositionState& s, const Multiply& n) {
return GetNodeInputArg<T_Float>(s, n.in[0]) * GetNodeInputArg<T_Float>(s, n.in[1]);
}
T_Map<T_Count>::type FetchDivide(const CompState& s, const Divide& n) {
T_Map<T_Count>::type FetchDivide(const CompositionState& s, const Divide& n) {
return GetNodeInputArg<T_Float>(s, n.in[0]) / GetNodeInputArg<T_Float>(s, n.in[1]);
}
T_Map<T_Count>::type FetchSign(const CompState& s, const Sign& n) {
T_Map<T_Count>::type FetchSign(const CompositionState& s, const Sign& n) {
return std::signbit(GetNodeInputArg<T_Float>(s, n.in[0])) ? -1.0f : 1.0f;
}
T_Map<T_Count>::type FetchSin(const CompState& s, const Sin& n) {
T_Map<T_Count>::type FetchSin(const CompositionState& s, const Sin& n) {
return std::sin(GetNodeInputArg<T_Float>(s, n.in[0]));
}
T_Map<T_Count>::type FetchCos(const CompState& s, const Cos& n) {
T_Map<T_Count>::type FetchCos(const CompositionState& s, const Cos& n) {
return std::cos(GetNodeInputArg<T_Float>(s, n.in[0]));
}
T_Map<T_Count>::type FetchTan(const CompState& s, const Tan& n) {
T_Map<T_Count>::type FetchTan(const CompositionState& s, const Tan& n) {
return std::tan(GetNodeInputArg<T_Float>(s, n.in[0]));
}
T_Map<T_Count>::type FetchArcsin(const CompState& s, const Arcsin& n) {
T_Map<T_Count>::type FetchArcsin(const CompositionState& s, const Arcsin& n) {
return std::asin(GetNodeInputArg<T_Float>(s, n.in[0]));
}
T_Map<T_Count>::type FetchArccos(const CompState& s, const Arccos& n) {
T_Map<T_Count>::type FetchArccos(const CompositionState& s, const Arccos& n) {
return std::acos(GetNodeInputArg<T_Float>(s, n.in[0]));
}
T_Map<T_Count>::type FetchArctan(const CompState& s, const Arctan& n) {
T_Map<T_Count>::type FetchArctan(const CompositionState& s, const Arctan& n) {
return std::atan(GetNodeInputArg<T_Float>(s, n.in[0]));
}
T_Map<T_Count>::type FetchAtan2(const CompState& s, const Atan2& n) {
T_Map<T_Count>::type FetchAtan2(const CompositionState& s, const Atan2& n) {
return std::atan2(GetNodeInputArg<T_Float>(s, n.in[0]), GetNodeInputArg<T_Float>(s, n.in[1]));
}
T_Map<T_Count>::type FetchMinimum(const CompState& s, const Minimum& n) {
T_Map<T_Count>::type FetchMinimum(const CompositionState& s, const Minimum& n) {
return std::min(GetNodeInputArg<T_Float>(s, n.in[0]), GetNodeInputArg<T_Float>(s, n.in[1]));
}
T_Map<T_Count>::type FetchMaximum(const CompState& s, const Maximum& n) {
T_Map<T_Count>::type FetchMaximum(const CompositionState& s, const Maximum& n) {
return std::max(GetNodeInputArg<T_Float>(s, n.in[0]), GetNodeInputArg<T_Float>(s, n.in[1]));
}
T_Map<T_Count>::type FetchPower(const CompState& s, const Power& n) {
T_Map<T_Count>::type FetchPower(const CompositionState& s, const Power& n) {
return std::pow(GetNodeInputArg<T_Float>(s, n.in[0]), GetNodeInputArg<T_Float>(s, n.in[1]));
}
T_Map<T_Count>::type FetchSquareRoot(const CompState& s, const SquareRoot& n) {
T_Map<T_Count>::type FetchSquareRoot(const CompositionState& s, const SquareRoot& n) {
return std::sqrt(GetNodeInputArg<T_Float>(s, n.in[0]));
}
T_Map<T_Count>::type FetchNaturalLogarithm(const CompState& s, const NaturalLogarithm& n) {
T_Map<T_Count>::type FetchNaturalLogarithm(const CompositionState& s, const NaturalLogarithm& n) {
return std::log(GetNodeInputArg<T_Float>(s, n.in[0]));
}
T_Map<T_Count>::type FetchAbsoluteValue(const CompState& s, const AbsoluteValue& n) {
T_Map<T_Count>::type FetchAbsoluteValue(const CompositionState& s, const AbsoluteValue& n) {
return std::abs(GetNodeInputArg<T_Float>(s, n.in[0]));
}
T_Map<T_Count>::type FetchInterpolation(const CompState& s, const Interpolation& n) {
T_Map<T_Count>::type FetchInterpolation(const CompositionState& s, const Interpolation& n) {
return EvalInterpolation(GetNodeInputArg<T_Float>(s, n.in[0]), n.extra);
}
@ -208,7 +208,7 @@ namespace K::Plugboard {
}
}
T_Map<T_Count>::type FetchChain(const CompState& s, const Chain& n) {
T_Map<T_Count>::type FetchChain(const CompositionState& s, const Chain& n) {
i32 frame = static_cast<i32>(GetNodeInputArg<T_Int>(s, n.in[0]));
auto& v = n.extra.chain.segments;
@ -250,7 +250,7 @@ namespace K::Plugboard {
}, var);
}
T_Map<T_Count>::type Eval(const CompState& s, const ConnectInfo& info) {
T_Map<T_Count>::type Eval(const CompositionState& s, const ConnectInfo& info) {
return std::visit([&info, &s](auto&& arg) { return arg->fetch[info.index](s, *arg); }, info.p);
}
@ -343,25 +343,32 @@ namespace K::Plugboard {
}, v);
}
T_Map<T_Count>::type FetchCompositionInFrame(const CompState& s, const CompositionIn& n) {
T_Map<T_Count>::type FetchCompositionInFrame(const CompositionState& s, const GroupIn& n) {
return T_Map<T_Count>::type{static_cast<i32>(s.current_frame)};
}
T_Map<T_Count>::type FetchCompositionInTime(const CompState& s, const CompositionIn& n) {
return T_Map<T_Count>::type{static_cast<f32>(s.current_frame) / s.fps};
}
T_Map<T_Count>::type FetchCompositionInAppTicks(const CompState& s, const CompositionIn& n) {
T_Map<T_Count>::type FetchCompositionInAppTicks(const CompositionState& s, const GroupIn& n) {
return T_Map<T_Count>::type{ static_cast<f32>(SDL_GetTicks()) };
}
Plugboard::Plugboard() {
AddOutSocket("Frame", T_Int, FetchCompositionInFrame);
AddOutSocket("App Ticks", T_Float, FetchCompositionInAppTicks);
AddInSocket("Frame", T_Int);
AddInSocket("Ticks", T_Int);
}
void Plugboard::AddInSocket(const String& s, Type t) {
in.emplace_back(s, t);
}
void Plugboard::AddOutSocket(const String& s, Type t, T_Map<T_Count>::type(*fetch)(const CompositionState&, const GroupIn&)) {
in_instance.out.emplace_back(s, t);
in_instance.fetch.push_back(fetch);
}
void Plugboard::RecollectChains() {
for (u32 i = 0; i < out.in.size(); i++) {
for (u32 i = 0; i < out_instance.in.size(); i++) {
std::visit([i, this](auto&& arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, ConnectInfo>)
CollectChains(arg, out.show_nodes[i]);
}, out.in[i].value);
CollectChains(arg, show_nodes[i]);
}, out_instance.in[i].value);
}
}
}

View file

@ -1,12 +1,28 @@
#include <Resource.h>
#include <Keishiki.h>
#include <chrono>
#include <mutex>
#include <thread>
#include <Keishiki.h>
#include <glaze/glaze.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
template <>
struct glz::meta<ImVec2> {
using T = ImVec2;
static constexpr auto value = array(&T::x, &T::y);
};
namespace K::Resource {
void SaveCurrentProject() {
auto ec = glz::write_file_json("soon!", "./temp.json", String{});
if (ec.ec != glz::error_code::none) {
Log(K_L_Error, "{}", ec.includer_error);
}
}
}
namespace {
bgfx::ShaderHandle LoadShader(const std::filesystem::path& file_path) {
FILE *file = fopen(file_path.c_str(), "rb");
@ -227,4 +243,77 @@ namespace K::Resource {
}
}, r.second);
}
static int DecodeURI(char *buf, int len) {
/* Decodes URI escape sequences in string buf of len bytes
(excluding the terminating NULL byte) in-place. Since
URI-encoded characters take three times the space of
normal characters, this should not be an issue.
Returns the number of decoded bytes that wound up in
the buffer, excluding the terminating NULL byte.
The buffer is guaranteed to be NULL-terminated but
may contain embedded NULL bytes.
On error, -1 is returned.
*/
int ri, wi, di;
char decode = '\0';
if (!buf || len < 0) {
errno = EINVAL;
return -1;
}
if (len == 0) {
len = SDL_strlen(buf);
}
for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) {
if (di == 0) {
/* start decoding */
if (buf[ri] == '%') {
decode = '\0';
di += 1;
continue;
}
/* normal write */
buf[wi] = buf[ri];
wi += 1;
continue;
} else if (di == 1 || di == 2) {
char off = '\0';
char isa = buf[ri] >= 'a' && buf[ri] <= 'f';
char isA = buf[ri] >= 'A' && buf[ri] <= 'F';
char isn = buf[ri] >= '0' && buf[ri] <= '9';
if (!(isa || isA || isn)) {
/* not a hexadecimal */
int sri;
for (sri = ri - di; sri <= ri; sri += 1) {
buf[wi] = buf[sri];
wi += 1;
}
di = 0;
continue;
}
/* itsy bitsy magicsy */
if (isn) {
off = 0 - '0';
} else if (isa) {
off = 10 - 'a';
} else if (isA) {
off = 10 - 'A';
}
decode |= (buf[ri] + off) << (2 - di) * 4;
if (di == 2) {
buf[wi] = decode;
wi += 1;
di = 0;
} else {
di += 1;
}
continue;
}
}
buf[wi] = '\0';
return wi;
}
}

View file

@ -18,6 +18,8 @@
#include <chrono>
#include <fmt/chrono.h>
#include <SDL3/SDL_dialog.h>
namespace {
const K::Resource::Resource<K::Resource::Type::K_R_Still> *logo;
@ -42,7 +44,7 @@ namespace {
K::VisualTrack bg{};
K::Byte *save_buffer;
K::CompState *comp_save_called = nullptr;
K::CompositionState *comp_save_called = nullptr;
u32 ready_frame;
}
@ -68,7 +70,7 @@ namespace K::UI {
bgfx::setViewTransform(K::Graphics::K_VIEW_LOGO, view, proj);
}
void AddTransformLayer(CompState& s, const String& name) {
void AddTransformLayer(CompositionState& s, const String& name) {
auto l = Layer{
VisualTrack{},
name,
@ -76,40 +78,38 @@ namespace K::UI {
0UL,
s.frame_max
};
l.track.AddUniform("u_aspect_ratio", ShaderGraph::ExpandVariant(ShaderGraph::T_XY));
if (s.width > s.height)
l.track.uniforms[0].val = ShaderGraph::T_Map<ShaderGraph::T_XY>::type{static_cast<f32>(s.width)/static_cast<f32>(s.height), 1.0f};
else
l.track.uniforms[0].val = ShaderGraph::T_Map<ShaderGraph::T_XY>::type{1.0f, static_cast<f32>(s.height)/static_cast<f32>(s.width)};
l.track.AddUniform("u_resolution", ShaderGraph::ExpandVariant(ShaderGraph::T_XY), K_U_Exposed);
l.track.uniforms[0].val = ShaderGraph::T_Map<ShaderGraph::T_XY>::type{static_cast<f32>(s.width), static_cast<f32>(s.height)};
K::VisualTrack::ExposeUniform(s, l.track.uniforms.back());
l.track.AddUniform("u_opacity", ShaderGraph::ExpandVariant(ShaderGraph::T_Float));
l.track.AddUniform("u_opacity", ShaderGraph::ExpandVariant(ShaderGraph::T_Float), K_U_Exposed);
l.track.uniforms[1].val = ShaderGraph::T_Map<ShaderGraph::T_Float>::type(1.0f);
K::VisualTrack::ExposeUniform(s, l.track.uniforms.back());
l.track.AddUniform("u_rot", ShaderGraph::ExpandVariant(ShaderGraph::T_Float));
l.track.AddUniform("u_rot", ShaderGraph::ExpandVariant(ShaderGraph::T_Float), K_U_Exposed);
l.track.uniforms[2].val = ShaderGraph::T_Map<ShaderGraph::T_Float>::type(.0f);
K::VisualTrack::ExposeUniform(s, l.track.uniforms.back());
l.track.AddUniform("u_scale", ShaderGraph::ExpandVariant(ShaderGraph::T_XY));
l.track.AddUniform("u_scale", ShaderGraph::ExpandVariant(ShaderGraph::T_XY), K_U_Exposed);
l.track.uniforms[3].val = ShaderGraph::T_Map<ShaderGraph::T_XY>::type{1.0f, 1.0f};
K::VisualTrack::ExposeUniform(s, l.track.uniforms.back());
l.track.AddUniform("u_translate", ShaderGraph::ExpandVariant(ShaderGraph::T_XY));
l.track.AddUniform("u_translate", ShaderGraph::ExpandVariant(ShaderGraph::T_XY), K_U_Exposed);
l.track.uniforms[4].val = ShaderGraph::T_Map<ShaderGraph::T_XY>::type{.0f, .0f};
K::VisualTrack::ExposeUniform(s, l.track.uniforms.back());
l.track.shader = "void main() {\n"
"\tfloat angle = -u_rot * M_PI / 180.0f;\n"
"\tvec2 uv = vec2(v_texcoord0.x, 1.0f - v_texcoord0.y) - .5f;\n"
"\tuv = uv * u_aspect_ratio;\n"
"\tuv = uv * u_resolution;\n"
"\tuv = uv - u_translate;\n"
"\tuv = vec2(cos(angle)*uv.x - sin(angle)*uv.y, sin(angle)*uv.x + cos(angle)*uv.y);\n"
"\tuv = uv / s_texColor_dims;\n"
"\tuv = uv / u_scale;\n"
"\tuv = uv + .5f;\n\n"
"\tvec4 tx = texture2D(s_texColor, uv);\n"
"\tgl_FragColor = vec4(tx.rgb, tx.a * u_opacity);\n"
"}\n";
l.track.ExposeUniform(s, 0);
l.track.ExposeUniform(s, 1);
l.track.ExposeUniform(s, 2);
l.track.ExposeUniform(s, 3);
l.track.ExposeUniform(s, 4);
l.track.AddSampler("s_texColor");
@ -119,6 +119,13 @@ namespace K::UI {
void MainMenuBar() {
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Save Project", "CTRL+S")) {
Resource::SaveCurrentProject();
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Edit")) {
// if (ImGui::MenuItem("Undo", "CTRL+Z")) {}
// if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item
@ -130,16 +137,18 @@ namespace K::UI {
}
if (ImGui::BeginMenu("View")) {
if (ImGui::MenuItem("Assets", "", &draw_assets)) {}
if (ImGui::MenuItem("Viewport", "", &draw_viewport)) {}
if (ImGui::MenuItem("Composition", "", &draw_comp)) {}
if (ImGui::MenuItem("Shader", "", &draw_layer)) {}
if (ImGui::MenuItem("Layer", "", &draw_layer)) {}
if (ImGui::MenuItem("Interpolation", "", &draw_interpolation)) {}
ImGui::EndMenu();
}
}
ImGui::EndMainMenuBar();
}
void SetupViewport(CompState& s) {
void SetupViewport(CompositionState& s) {
composite[0] = bgfx::createTexture2D(s.width, s.height, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT);
composite[1] = bgfx::createTexture2D(s.width, s.height, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT);
render = bgfx::createTexture2D(s.width, s.height, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT);
@ -165,7 +174,7 @@ namespace K::UI {
bx::mtxTranslate(view, 0.f, 0.f, 1.0f);
}
void DestroyViewport(CompState& s) {
void DestroyViewport(CompositionState& s) {
bgfx::destroy(composite_fb[0]);
bgfx::destroy(composite_fb[1]);
bgfx::destroy(composite[0]);
@ -197,8 +206,8 @@ namespace K::UI {
}
template <Plugboard::Node N>
void PlugboardNodeDrawSockets(CompState& s, N& n, ImGuiStyle& style,
bool& dragging_on_socket, ImVec2& source, bool dummy = true) {
void PlugboardNodeDrawSockets(CompositionState& s, N& n, ImGuiStyle& style,
bool& dragging_on_socket, ImVec2& source, Dict<Plugboard::ConnectInfo, Plugboard::LinksFromSource, Plugboard::ConnectInfoHasher>& links_pos, bool dummy = true) {
i32 row = 1;
for (u32 out = 0; out < n.out.size(); out++, row++) {
ImGui::PushID(row);
@ -233,7 +242,7 @@ namespace K::UI {
// Update link info if needed
if (!n.out[out].outgoing.empty()) {\
s.plugboard.links_pos[{&n, out}].source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2,
links_pos[{&n, out}].source = ImGui::GetCursorScreenPos() + ImVec2{ImGui::GetFrameHeight() / 2,
-ImGui::GetFrameHeight() / 2 -
style.ItemInnerSpacing.y};
}
@ -264,13 +273,13 @@ namespace K::UI {
ImGui::TextUnformatted(n.in[in].name.c_str());
// Update link info
std::visit([&socket_link_pos, &dragging_on_socket, &source, &s](auto&& arg) {
std::visit([&links_pos, &socket_link_pos, &dragging_on_socket, &source, &s](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, Plugboard::ConnectInfo>) {
if (ImGui::IsItemActive())
s.plugboard.links_pos[arg].sinks.push_back(ImGui::GetMousePos());
links_pos[arg].sinks.push_back(ImGui::GetMousePos());
else
s.plugboard.links_pos[arg].sinks.push_back(socket_link_pos);
links_pos[arg].sinks.push_back(socket_link_pos);
}
else if (ImGui::IsItemActive()) {
dragging_on_socket = true;
@ -288,8 +297,8 @@ namespace K::UI {
}
template <Plugboard::Node N>
void PlugboardDrawNode(CompState& s, N& n, ImVec2& view_pos, ImGuiIO& io, ImGuiStyle& style,
bool& dragging_on_socket, ImVec2& source, bool& delete_request) {
void PlugboardDrawNode(CompositionState& s, N& n, ImVec2& view_pos, ImGuiIO& io, ImGuiStyle& style,
bool& dragging_on_socket, ImVec2& source, bool& delete_request, Dict<Plugboard::ConnectInfo, Plugboard::LinksFromSource, Plugboard::ConnectInfoHasher>& links_pos) {
Plugboard::NodeInstanceP ptr{&n};
bool selected = std::ranges::find(s.plugboard.selected_nodes, ptr) != s.plugboard.selected_nodes.end();
const auto& pos = n.pos;
@ -335,7 +344,7 @@ namespace K::UI {
ImGui::TableNextColumn();
}
PlugboardNodeDrawSockets<N>(s, n, style, dragging_on_socket, source);
PlugboardNodeDrawSockets<N>(s, n, style, dragging_on_socket, source, links_pos);
ImGui::EndTable();
}
@ -351,7 +360,7 @@ namespace K::UI {
last_frame_tick = app_state.current_time;
}
void Viewport(CompState& s) {
void Viewport(CompositionState& s) {
if (playing) {
if (static_cast<f32>(app_state.current_time - last_frame_tick) >= 1000.0f / s.fps) {
f32 delta = static_cast<f32>(app_state.current_time - last_frame_tick);
@ -361,7 +370,7 @@ namespace K::UI {
}
}
if (ImGui::Begin("Viewport", &draw_viewport)) {
if (ImGui::Begin("Viewport")) {
if (ImGui::Shortcut(ImGuiKey_Space))
TogglePlay();
@ -436,11 +445,11 @@ namespace K::UI {
struct PlotInfo {
f32 begin, end;
i32 samples;
const CompState &s;
const CompositionState &s;
Plugboard::ConnectInfo &connected_v;
};
void Composition(CompState& s) {
void Composition(CompositionState& s) {
static bool layer_delete_requested = false;
if (layer_delete_requested) {
if (std::ranges::find(s.selected, s.active) != s.selected.end()) // unset active if active is selected
@ -486,7 +495,7 @@ namespace K::UI {
node_delete_requested = false;
}
if (!ImGui::Begin(("Composition: " + s.name).c_str(), &draw_comp, ImGuiWindowFlags_NoScrollbar)) {
if (!ImGui::Begin(("Composition: " + s.name).c_str(), nullptr, ImGuiWindowFlags_NoScrollbar)) {
ImGui::End();
return;
}
@ -501,6 +510,7 @@ namespace K::UI {
}
ImGui::SameLine();
ImGui::SetNextItemWidth(150.0f);
static Dict<Plugboard::ConnectInfo, Plugboard::LinksFromSource, Plugboard::ConnectInfoHasher> links_pos;
if (ImGui::BeginCombo("##Node", s.plugboard.nodes.GetName(static_cast<Plugboard::K_P_Nodes>(node_current)), ImGuiComboFlags_None)) {
for (u32 n = 0; n < Plugboard::K_P_Count; n++) {
const bool is_selected = (node_current == n);
@ -571,7 +581,7 @@ namespace K::UI {
// why are we re-getting positions on each frame?
// this is just convenient because sometimes we would be dragging on a socket!
s.plugboard.links_pos.clear();
links_pos.clear();
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
old_view_pos = view_pos;
@ -584,7 +594,7 @@ namespace K::UI {
(([&](auto&& arg){
for (auto& node : arg) {
PlugboardDrawNode<std::decay_t<decltype(node)>>(s, node, view_pos, io, style, dragging_on_socket,
drag_source, node_delete_requested);
drag_source, node_delete_requested, links_pos);
}
}(args)), ...);
}, s.plugboard.nodes.nodes);
@ -596,7 +606,7 @@ namespace K::UI {
ImGuiChildFlags_AutoResizeY);
ImGui::PopStyleColor();
if (ImGui::BeginTable("Composition In", 3)) {
PlugboardNodeDrawSockets(s, s.plugboard.in, style, dragging_on_socket, drag_source, false);
PlugboardNodeDrawSockets(s, s.plugboard.in_instance, style, dragging_on_socket, drag_source, links_pos, false);
ImGui::EndTable();
}
@ -928,9 +938,13 @@ namespace K::UI {
f32 init_y = ImGui::GetCursorScreenPos().y;
const static f32 layer_bound_width = 5.0f;
i32 l_in = TimelineFrameInView(view_left, view_right, current_layer.in, s.frame_max), l_out = TimelineFrameInView(view_left, view_right, current_layer.out, s.frame_max);
const bool l_le_right_extr = static_cast<f32>(current_layer.in) / static_cast<f32>(s.frame_max + 1) <= view_right,
l_in = l_le_right_extr && static_cast<f32>(current_layer.in) / static_cast<f32>(s.frame_max + 1) >= view_left,
l_ge_left_extr = static_cast<f32>(current_layer.out) / static_cast<f32>(s.frame_max + 1) >= view_left,
l_out = l_ge_left_extr && static_cast<f32>(current_layer.out) / static_cast<f32>(s.frame_max + 1) <= view_right;
f32 in_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.in, s.frame_max),
out_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.out, s.frame_max);
ImGui::PushID(0);
if (l_in) {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + in_pos);
static f32 l_in_old;
@ -949,9 +963,10 @@ namespace K::UI {
ImGui::PopStyleColor();
ImGui::SameLine(0.0f, 0.0f);
}
ImGui::PushID(0);
if (l_le_right_extr && l_ge_left_extr) {
ImGui::Button(current_layer.name.c_str(),
{std::min(out_pos + fr_step, view_width) - static_cast<f32>(l_in) * in_pos - layer_bound_width * static_cast<f32>(l_in + l_out), view_height});
{std::min(out_pos + fr_step, view_width) - static_cast<f32>(l_in) * in_pos -
layer_bound_width * static_cast<f32>(l_in + l_out), view_height});
static u32 in_old, out_old;
if (ImGui::IsItemClicked()) {
in_old = current_layer.in;
@ -963,7 +978,7 @@ namespace K::UI {
current_layer.in = in_old + off;
current_layer.out = out_old + off;
}
ImGui::PopID();
}
if (l_out) {
static f32 r_out_old;
ImGui::SameLine(0.0f, 0.0f);
@ -980,6 +995,7 @@ namespace K::UI {
}
ImGui::PopStyleColor();
}
ImGui::PopID();
ImGui::SetCursorScreenPos(ImVec2{tl_init_pos.x, init_y} - style.CellPadding);
ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, row_height + style.CellPadding.y});
@ -992,6 +1008,9 @@ namespace K::UI {
if (layer_open) {
i32 j = 0;
for (auto &u: current_layer.track.uniforms) {
if (u.status != K_U_Exposed)
continue;
ImGui::PushID(j);
ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height);
ImGui::TableSetColumnIndex(0);
@ -1017,9 +1036,9 @@ namespace K::UI {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, Plugboard::ConnectInfo>) {
if (ImGui::IsItemActive())
s.plugboard.links_pos[arg].sinks.push_back(io.MousePos);
links_pos[arg].sinks.push_back(io.MousePos);
else
s.plugboard.links_pos[arg].sinks.push_back(ImGui::GetCursorScreenPos() +
links_pos[arg].sinks.push_back(ImGui::GetCursorScreenPos() +
ImVec2{ImGui::GetFrameHeight() / 2,
-ImGui::GetFrameHeight() / 2 -
style.ItemInnerSpacing.y});
@ -1027,9 +1046,11 @@ namespace K::UI {
else if (ImGui::IsItemActive()) {
if (ImGui::IsKeyPressed(ImGuiKey_LeftShift)) {
auto& c = s.plugboard.nodes.Store(Plugboard::Chain{});
c.extra.chain.segments.emplace_back(s.current_frame, std::get<Plugboard::T_Map<Plugboard::T_Float>::type>(arg));
c.extra.chain.selected.push_back(0);
Plugboard::Connect(u.connection.p, u.connection.index, &c, 0);
Plugboard::Connect(&c, 0, &s.plugboard.in, 0);
auto& nodes = s.plugboard.out.show_nodes[u.connection.index];
Plugboard::Connect(&c, 0, &s.plugboard.in_instance, 0);
auto& nodes = s.plugboard.show_nodes[u.connection.index];
nodes.clear();
nodes.emplace_back(&c, 0);
}
@ -1044,10 +1065,10 @@ namespace K::UI {
ImGui::TableSetColumnIndex(1);
auto u_flags = ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_FramePadding;
auto u_flags = ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_FramePadding | ImGuiTreeNodeFlags_DefaultOpen;
if (connected_v.index() != 1)
u_flags |= ImGuiTreeNodeFlags_Leaf;
bool uniform_open = ImGui::TreeNodeEx(u.name.c_str(),u_flags);
bool uniform_open = ImGui::TreeNodeEx(u.name.c_str(), u_flags);
ImGui::TableSetColumnIndex(2);
ImGui::SetNextItemWidth(-FLT_MIN);
@ -1080,7 +1101,7 @@ namespace K::UI {
ImPlot::PlotLineG("v", [](int idx, void* user_data) -> ImPlotPoint {
PlotInfo i = *(PlotInfo*)user_data;
f32 x = i.begin + (i.end - i.begin) * static_cast<f32>(idx) / static_cast<f32>(i.samples);
CompState ss = i.s;
CompositionState ss = i.s;
ss.current_frame = static_cast<u64>(std::round(x));
bool good;
Vector<Plugboard::ConnectInfo> info;
@ -1099,7 +1120,7 @@ namespace K::UI {
tl_bg_handler();
if (uniform_open && connected_v.index() == 1) {
for (const auto& info: s.plugboard.out.show_nodes[u.connection.index]) {
for (const auto& info: s.plugboard.show_nodes[u.connection.index]) {
ImGui::TableNextRow(ImGuiTableRowFlags_None, row_height);
ImGui::TableSetColumnIndex(0);
ImGui::TableSetColumnIndex(1);
@ -1227,13 +1248,16 @@ namespace K::UI {
}
ImGui::TreePop();
}
else for (auto &u: current_layer.track.uniforms)
std::visit([table_left, &s](auto &&arg) {
else for (auto& u: current_layer.track.uniforms) {
if (u.status != K_U_Exposed)
continue;
std::visit([table_left](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, Plugboard::ConnectInfo>)
s.plugboard.links_pos[arg].sinks.push_back( // make nodes connect to collapsed layer row
links_pos[arg].sinks.push_back( // make nodes connect to collapsed layer row
{table_left, ImGui::GetCursorScreenPos().y - row_height / 2.0f});
}, std::visit([&u](auto&& arg) -> auto& { return arg->in[u.connection.index].value; }, u.connection.p));
}
ImGui::PopStyleVar(); // FramePadding
@ -1276,7 +1300,7 @@ namespace K::UI {
ImGui::SetCursorScreenPos(nodes_begin);
if (ImGui::BeginChild("Node Connection Overlay", {table_left - ImGui::GetWindowPos().x, 0.0f}, false, ImGuiWindowFlags_NoInputs)) {
auto *window = ImGui::GetCurrentWindow();
for (auto &[_, link]: s.plugboard.links_pos)
for (auto &[_, link]: links_pos)
for (auto &sink: link.sinks)
window->DrawList->AddBezierCubic(link.source,
{link.source.x + 60.0f, link.source.y},
@ -1446,7 +1470,7 @@ namespace K::UI {
ImPlot::PlotLineG("v", [](int idx, void *user_data) -> ImPlotPoint {
PlotInfo i = *(PlotInfo *) user_data;
f32 x = i.begin + (i.end - i.begin) * static_cast<f32>(idx) / static_cast<f32>(i.samples);
CompState ss = i.s;
CompositionState ss = i.s;
ss.current_frame = static_cast<u64>(x);
bool good;
Vector<Plugboard::ConnectInfo> info;
@ -1536,7 +1560,7 @@ namespace K::UI {
ImGui::End();
}
void Layer(CompState& s) {
void Layer(CompositionState& s) {
struct TextFilters { static int VariableName(ImGuiInputTextCallbackData *data) {
return !(data->EventChar < 256 &&
((data->EventChar >= 'a' && data->EventChar <= 'z') ||
@ -1544,7 +1568,7 @@ namespace K::UI {
(data->EventChar >= '0' && data->EventChar <= '9') ||
data->EventChar == '_'));
} };
if (ImGui::Begin("Layer", &draw_layer)) {
if (ImGui::Begin("Layer")) {
if (s.active == -1)
ImGui::TextUnformatted("No active layer.");
else {
@ -1554,8 +1578,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, ExpandVariant(
static_cast<ShaderGraph::Type>(type_current)));
s.layers[s.active].track.AddUniform("u_" + name, ExpandVariant(static_cast<ShaderGraph::Type>(type_current)), K_U_Exposed);
VisualTrack::ExposeUniform(s, s.layers[s.active].track.uniforms.back());
}
ImGui::SameLine();
@ -1575,8 +1598,10 @@ namespace K::UI {
ImGui::TableHeadersRow();
i32 i = 0;
for (auto it = s.layers[s.active].track.uniforms.begin(); it != s.layers[s.active].track.uniforms.end();) {
if (std::visit([](auto&& arg){ return arg == nullptr; }, it->connection.p))
if (it->status != K_U_Exposed) {
it++;
continue;
}
ImGui::TableNextRow();
ImGui::PushID(i++);
@ -1611,14 +1636,15 @@ namespace K::UI {
if (ImGui::BeginTabItem("Samplers")) {
static String name{};
if (ImGui::Button("Add Sampler") && !name.empty() && !isdigit(name[0])) {
s.layers[s.active].track.AddSampler(name);
s.layers[s.active].track.AddSampler("s_" + name);
}
ImGui::SameLine();
ImGui::InputText("##SamplerName", &name, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::VariableName);
if (ImGui::BeginTable("Samplers", 3, ImGuiTableFlags_Borders)) {
if (ImGui::BeginTable("Samplers", 4, ImGuiTableFlags_Borders)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Resource", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Dims via", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Resource", ImGuiTableColumnFlags_NoSort);
ImGui::TableSetupColumn("##del", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed);
ImGui::TableHeadersRow();
i32 i = 0;
@ -1628,7 +1654,19 @@ namespace K::UI {
ImGui::TableNextColumn();
ImGui::TextUnformatted(it->name.c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", it->resource->filename.c_str());
ImGui::Text("%s%s", it->name.c_str(), "_dims");
ImGui::TableNextColumn();
if (ImGui::BeginCombo("##source", it->resource->filename.c_str())) {
for (auto& [file, res_v] : Resource::resources)
if (auto *res = std::get_if<Resource::Resource<Resource::K_R_Still>>(&res_v)) {
const bool is_selected = it->resource == res;
if (ImGui::Selectable(file.c_str(), is_selected)) {
it->resource = res;
it->dims->val = ShaderGraph::XY{static_cast<f32>(res->w), static_cast<f32>(res->h)};
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
if (ImGui::Button("X")) {
bgfx::destroy(it->handle);
@ -1652,8 +1690,8 @@ namespace K::UI {
ImGui::End();
}
void Interpolation(CompState& s) {
if (ImGui::Begin("Interpolation", &draw_interpolation, ImGuiWindowFlags_NoScrollbar)) {
void Interpolation(CompositionState& s) {
if (ImGui::Begin("Interpolation", nullptr, ImGuiWindowFlags_NoScrollbar)) {
ImGuiIO& io = ImGui::GetIO();
static Plugboard::K_Interpolation type_current = Plugboard::K_I_CubicBezier;
@ -1663,8 +1701,10 @@ namespace K::UI {
if (ImGui::Button("Apply"))
for (auto& layer : s.layers)
for (auto& u : layer.track.uniforms)
for (auto& n : s.plugboard.out.show_nodes[u.connection.index]) {
for (auto& u : layer.track.uniforms) {
if (u.status != K_U_Exposed)
continue;
for (auto& n : s.plugboard.show_nodes[u.connection.index]) {
auto& [chain, _] = std::get<Plugboard::Chain*>(n.p)->extra;
for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) {
auto nit = std::next(it);
@ -1674,6 +1714,7 @@ namespace K::UI {
}
}
}
}
ImGui::SameLine();
@ -1784,6 +1825,42 @@ namespace K::UI {
void Assets() {
if (ImGui::Begin("Assets", &draw_assets)) {
if (ImGui::Button("Load")) {
static const SDL_DialogFileFilter import_filters[] = {
{ "PNG images", "png" },
{ "JPEG images", "jpg;jpeg" },
{ "All images", "png;jpg;jpeg" },
{ NULL, NULL }
};
struct ImportCallback {
static void SDLCALL f(void *userdata, const char* const* filelist, i32 filter) {
if (!filelist) {
Log(K_L_Error, "{}", SDL_GetError());
return;
}
else if (!*filelist) // The user did not select any file. Most likely, the dialog was canceled.
return;
while (*filelist) {
Resource::Load<Resource::K_R_Still>(*filelist);
filelist++;
}
if (filter == -1) {
Log(K_L_Info, "Platform does not support fetching file filters.");
}
else if (filter < SDL_arraysize(import_filters)) {
SDL_Log("The filter selected by the user is '%s' (%s).",
import_filters[filter].pattern, import_filters[filter].name);
}
else {
SDL_Log("The user did not select any filter.");
}
}
};
SDL_ShowOpenFileDialog(ImportCallback::f, nullptr, app_state.window, import_filters, 3, nullptr, true);
}
if (ImGui::BeginTable("Assets", 3, ImGuiTableFlags_Borders)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
@ -1791,7 +1868,7 @@ namespace K::UI {
ImGui::TableHeadersRow();
i32 i = 0;
for (auto& comp : app_state.compositions) {
for (auto& comp : app_state.project.compositions) {
ImGui::TableNextRow();
ImGui::PushID(i++);
ImGui::TableNextColumn();
@ -1826,10 +1903,10 @@ namespace K::UI {
}
ImGui::TableNextColumn();
if constexpr (std::is_same_v<T, Resource::Resource<Resource::K_R_ShaderProgram>>) {
ImGui::Text("%s", fmt::format("{:%Y-%m-%d %X}", std::chrono::file_clock::to_sys(arg.frag_last_updated)).c_str());
ImGui::Text("%s", fmt::format("{:%Y-%m-%d %H:%M:%S}", std::chrono::floor<std::chrono::seconds>(std::chrono::file_clock::to_sys(arg.frag_last_updated))).c_str());
}
else {
ImGui::Text("%s", fmt::format("{:%Y-%m-%d %X}", std::chrono::file_clock::to_sys(arg.last_updated)).c_str());
ImGui::Text("%s", fmt::format("{:%Y-%m-%d %H:%M:%S}", std::chrono::floor<std::chrono::seconds>(std::chrono::file_clock::to_sys(arg.last_updated))).c_str());
}
}, res);
ImGui::PopID();
@ -1854,7 +1931,7 @@ namespace K::UI {
if (draw_assets) Assets();
auto& comp = app_state.compositions[app_state.inspecting_composition];
auto& comp = app_state.project.compositions[app_state.project.inspecting_composition];
if (draw_viewport) Viewport(comp);
if (draw_layer) Layer(comp);
if (draw_comp) Composition(comp);
@ -1918,7 +1995,7 @@ namespace K::UI {
}
bg.pg = Resource::Load<Resource::K_R_ShaderProgram>("Checkerboard")->pg;
bg.AddUniform("f_hw", ShaderGraph::Type::T_XYZ);
bg.AddUniform("f_hw", ShaderGraph::Type::T_XYZ, K_U_Inactive);
logo = Resource::Load<Resource::K_R_Still>("Keishiki.png");
}
@ -1927,7 +2004,7 @@ namespace K::UI {
bgfx::destroy(bg.uniforms[0].handle);
bg.uniforms.erase(bg.uniforms.begin());
for (auto& comp : app_state.compositions)
for (auto& comp : app_state.project.compositions)
DestroyViewport(comp);
ImGui_Implbgfx_Shutdown();

View file

@ -36,7 +36,7 @@ namespace K {
},val);
}
void VisualTrack::GetFrame(const CompState& s, u32 view_id, bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16],
void VisualTrack::GetFrame(const CompositionState& s, u32 view_id, bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16],
f32 view[16], f32 transform[16]) {
bgfx::setViewMode(view_id, bgfx::ViewMode::Sequential);
bgfx::setViewClear(view_id, BGFX_CLEAR_COLOR);
@ -88,8 +88,8 @@ namespace K {
std::shared_lock lk{Resource::resource_lock};
u32 sampler_stage = 0;
for (auto& [_name, handle, res] : samplers) {
bgfx::setTexture(sampler_stage++, handle, res->tex);
for (auto& [_name, handle, res, _u] : samplers) {
bgfx::setTexture(sampler_stage++, handle, res->tex, BGFX_SAMPLER_UVW_BORDER);
}
Graphics::DrawQuad(view_id, transform, BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A, pg);
@ -101,7 +101,7 @@ namespace K {
"#include <bgfx_shader.sh>\n"
"#include <shaderlib.sh>\n";
u32 sampler_loc = 0;
for (auto& [name, _tex, _res] : samplers) {
for (auto& [name, _tex, _res, _u] : samplers) {
f << "SAMPLER2D(" << name << ", " << sampler_loc++ << ");\n";
}
for (auto& u : uniforms) {
@ -150,18 +150,21 @@ namespace K {
samplers.clear();
}
void VisualTrack::ExposeUniform(CompState& s, Uniform& uu) {
uu.connection = {&s.plugboard.out, static_cast<u32>(s.plugboard.out.in.size())};
void VisualTrack::ExposeUniform(CompositionState& s, Uniform& uu) {
uu.connection = {&s.plugboard.out_instance, static_cast<u32>(s.plugboard.out_instance.in.size())};
// this is shaky and bug prone -- relies on the shader types being in line with plugboard types
s.plugboard.out.in.push_back(Plugboard::InSocket{ uu.name, Plugboard::Type(uu.val.index()), ShaderValToPlugboard(uu.val)});
s.plugboard.out.show_nodes.emplace_back();
s.plugboard.out_instance.in.push_back(Plugboard::InSocket{ uu.name, Plugboard::Type(uu.val.index()), ShaderValToPlugboard(uu.val)});
s.plugboard.show_nodes.emplace_back();
}
void VisualTrack::ExposeUniform(CompState& s, u32 i) {
void VisualTrack::ExposeUniform(CompositionState& s, u32 i) {
ExposeUniform(s, uniforms[i]);
}
void VisualTrack::HideUniform(CompState& s, Uniform& uu) {
void VisualTrack::HideUniform(CompositionState& s, Uniform& uu) {
if (uu.status != K_U_Exposed)
return;
Plugboard::Disconnect(uu.connection.p, uu.connection.index);
// decrement indices of uniforms referencing CompositionOut whose index is greater
@ -170,22 +173,22 @@ namespace K {
if (u_other.connection.index > uu.connection.index)
u_other.connection.index--;
if (std::holds_alternative<Plugboard::ConnectInfo>(s.plugboard.out.in[uu.connection.index].value)) {
Plugboard::Disconnect(s.plugboard.out, uu.connection.index);
if (std::holds_alternative<Plugboard::ConnectInfo>(s.plugboard.out_instance.in[uu.connection.index].value)) {
Plugboard::Disconnect(s.plugboard.out_instance, uu.connection.index);
}
s.plugboard.out.in.erase(s.plugboard.out.in.begin() + uu.connection.index);
s.plugboard.out_instance.in.erase(s.plugboard.out_instance.in.begin() + uu.connection.index);
uu.connection = {(Plugboard::Add*)(nullptr), 0};
}
void VisualTrack::AddUniform(const String& s, ShaderGraph::T_Map<ShaderGraph::T_Count>::type&& val) {
void VisualTrack::AddUniform(const String& s, ShaderGraph::T_Map<ShaderGraph::T_Count>::type&& val, UniformStatus init) {
for (auto& u : uniforms)
if (u.name == s)
return;
for (auto& ss : samplers)
if (ss.name == s)
return;
uniforms.emplace_back(Uniform{s, bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val, {{}, 0}});
uniforms.emplace_back(Uniform{s, bgfx::createUniform(("__" + s).c_str(), bgfx::UniformType::Vec4), val, {{}, 0}, init});
}
void VisualTrack::AddSampler(const String& s) {
@ -195,6 +198,7 @@ namespace K {
for (auto& ss : samplers)
if (ss.name == s)
return;
samplers.emplace_back(Sampler{s, bgfx::createUniform(s.c_str(), bgfx::UniformType::Sampler), Resource::fallback_still});
AddUniform(s + "_dims", ShaderGraph::XY{static_cast<f32>(Resource::fallback_still->w), static_cast<f32>(Resource::fallback_still->h)}, K_U_SamplerDims);
samplers.emplace_back(Sampler{s, bgfx::createUniform(s.c_str(), bgfx::UniformType::Sampler), Resource::fallback_still, &uniforms.back()});
}
}

View file

@ -39,10 +39,11 @@ namespace K {
template <typename... Ts>
inline void Log(LogLevel level, fmt::format_string<Ts...> fmt, Ts&&... args) {
fmt::print(level > K_L_Info ? stderr : stdout, "[{}] Keishiki: ", LogLevelNames[level]);
fmt::print(fmt, std::forward<Ts>(args)...);
fmt::print("\n");
auto& t = level > K_L_Info ? stderr : stdout;
fmt::print(t, "[{}] Keishiki: ", LogLevelNames[level]);
fmt::print(t, fmt, std::forward<Ts>(args)...);
fmt::print(t, "\n");
}
struct CompState; // fwd declared -- Keishiki.h <-> VisualTrack.h has dependency cycle
struct CompositionState; // fwd declared -- Keishiki.h <-> VisualTrack.h has dependency cycle
}

View file

@ -16,7 +16,7 @@ namespace K {
u64 in, out;
};
struct CompState {
struct CompositionState {
String name;
// Time
u64 current_frame;
@ -33,10 +33,15 @@ namespace K {
Plugboard::Plugboard plugboard;
};
struct ProjectState {
i32 inspecting_composition = -1;
Vector<CompositionState> compositions{};
};
extern struct AppState {
SDL_Window *window = nullptr;
i32 window_width = 2300,
window_height = 1300;
i32 window_width = 2200,
window_height = 1200;
std::atomic_bool running {true};
@ -44,7 +49,6 @@ namespace K {
u32 bgfx_frame{};
u32 n_views{};
i32 inspecting_composition = -1;
Vector<CompState> compositions{};
ProjectState project{};
} app_state; // global !
}

View file

@ -86,8 +86,8 @@ namespace K::Plugboard {
struct AbsoluteValue;
struct Interpolation;
struct Chain;
struct CompositionIn;
struct CompositionOut;
struct GroupIn;
struct GroupOut;
enum K_P_Nodes {
K_P_Add = 0,
@ -144,8 +144,8 @@ namespace K::Plugboard {
AbsoluteValue,
Interpolation,
Chain,
CompositionIn,
CompositionOut>;
GroupIn,
GroupOut>;
using NodeInstanceP = NodeVar::NodeInstanceP;
@ -353,222 +353,220 @@ namespace K::Plugboard {
T_Map<T_Count>::type ConvertValue(const T_Map<T_Count>::type& v, Type target, bool& good);
T_Map<T_Count>::type Eval(const CompState& s, const ConnectInfo& info);
T_Map<T_Count>::type Eval(const CompositionState& s, const ConnectInfo& info);
struct LinksFromSource {
ImVec2 source;
Vector<ImVec2> sinks;
};
T_Map<T_Count>::type FetchAdd(const CompState& s, const Add& n);
T_Map<T_Count>::type FetchAdd(const CompositionState& s, const Add& n);
struct Add {
const static char constexpr *name = static_cast<const char*>("Add");
std::array<InSocket, 2> in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "a+b", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Add&), 1> fetch = { FetchAdd };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Add&), 1> fetch = {FetchAdd };
ImVec2 pos;
};
T_Map<T_Count>::type FetchNegate(const CompState& s, const Negate& n);
T_Map<T_Count>::type FetchNegate(const CompositionState& s, const Negate& n);
struct Negate {
const static char constexpr *name = static_cast<const char*>("Negate");
std::array<InSocket, 2> in = { InSocket{"a", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "-a", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Negate&), 1> fetch = { FetchNegate };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Negate&), 1> fetch = {FetchNegate };
ImVec2 pos;
};
T_Map<T_Count>::type FetchSubtract(const CompState& s, const Subtract& n);
T_Map<T_Count>::type FetchSubtract(const CompositionState& s, const Subtract& n);
struct Subtract {
const static char constexpr *name = static_cast<const char*>("Subtract");
std::array<InSocket, 2> in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "a-b", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Subtract&), 1>fetch = { FetchSubtract };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Subtract&), 1>fetch = {FetchSubtract };
ImVec2 pos;
};
T_Map<T_Count>::type FetchMultiply(const CompState& s, const Multiply& n);
T_Map<T_Count>::type FetchMultiply(const CompositionState& s, const Multiply& n);
struct Multiply {
const static char constexpr *name = static_cast<const char*>("Multiply");
std::array<InSocket, 2> in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "a*b", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Multiply&), 1> fetch = { FetchMultiply };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Multiply&), 1> fetch = {FetchMultiply };
ImVec2 pos;
};
T_Map<T_Count>::type FetchDivide(const CompState& s, const Divide& n);
T_Map<T_Count>::type FetchDivide(const CompositionState& s, const Divide& n);
struct Divide {
const static char constexpr *name = static_cast<const char*>("Divide");
std::array<InSocket, 2> in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "a/b", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Divide&), 1> fetch = { FetchDivide };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Divide&), 1> fetch = {FetchDivide };
ImVec2 pos;
};
T_Map<T_Count>::type FetchSign(const CompState& s, const Sign& n);
T_Map<T_Count>::type FetchSign(const CompositionState& s, const Sign& n);
struct Sign {
const static char constexpr *name = static_cast<const char*>("Sign");
std::array<InSocket, 1> in = { InSocket{"a", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "sgn(a)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Sign&), 1> fetch = { FetchSign };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Sign&), 1> fetch = {FetchSign };
ImVec2 pos;
};
T_Map<T_Count>::type FetchSin(const CompState& s, const Sin& n);
T_Map<T_Count>::type FetchSin(const CompositionState& s, const Sin& n);
struct Sin {
const static char constexpr *name = static_cast<const char*>("Sin");
std::array<InSocket, 1> in = { InSocket{"a (rad)", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "sin(a)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Sin&), 1> fetch = { FetchSin };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Sin&), 1> fetch = {FetchSin };
ImVec2 pos;
};
T_Map<T_Count>::type FetchCos(const CompState& s, const Cos& n);
T_Map<T_Count>::type FetchCos(const CompositionState& s, const Cos& n);
struct Cos {
const static char constexpr *name = static_cast<const char*>("Cos");
std::array<InSocket, 1> in = { InSocket{"a (rad)", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "cos(a)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Cos&), 1> fetch = { FetchCos };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Cos&), 1> fetch = {FetchCos };
ImVec2 pos;
};
T_Map<T_Count>::type FetchTan(const CompState& s, const Tan& n);
T_Map<T_Count>::type FetchTan(const CompositionState& s, const Tan& n);
struct Tan {
const static char constexpr *name = static_cast<const char*>("Tan");
std::array<InSocket, 1> in = { InSocket{"a (rad)", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "tan(a)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Tan&), 1> fetch = { FetchTan };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Tan&), 1> fetch = {FetchTan };
ImVec2 pos;
};
T_Map<T_Count>::type FetchArcsin(const CompState& s, const Arcsin& n);
T_Map<T_Count>::type FetchArcsin(const CompositionState& s, const Arcsin& n);
struct Arcsin {
const static char constexpr *name = static_cast<const char*>("Arcsin");
std::array<InSocket, 1> in = { InSocket{"a", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "arcsin(a) (rad)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Arcsin&), 1> fetch = { FetchArcsin };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Arcsin&), 1> fetch = {FetchArcsin };
ImVec2 pos;
};
T_Map<T_Count>::type FetchArccos(const CompState& s, const Arccos& n);
T_Map<T_Count>::type FetchArccos(const CompositionState& s, const Arccos& n);
struct Arccos {
const static char constexpr *name = static_cast<const char*>("Arccos");
std::array<InSocket, 1> in = { InSocket{"a", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "arccos(a) (rad)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Arccos&), 1> fetch = { FetchArccos };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Arccos&), 1> fetch = {FetchArccos };
ImVec2 pos;
};
T_Map<T_Count>::type FetchArctan(const CompState& s, const Arctan& n);
T_Map<T_Count>::type FetchArctan(const CompositionState& s, const Arctan& n);
struct Arctan {
const static char constexpr *name = static_cast<const char*>("Arctan");
std::array<InSocket, 1> in = { InSocket{"a", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "arctan(a) (rad)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Arctan&), 1> fetch = { FetchArctan };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Arctan&), 1> fetch = {FetchArctan };
ImVec2 pos;
};
T_Map<T_Count>::type FetchAtan2(const CompState& s, const Atan2& n);
T_Map<T_Count>::type FetchAtan2(const CompositionState& s, const Atan2& n);
struct Atan2 {
const static char constexpr *name = static_cast<const char*>("Atan2");
std::array<InSocket, 2> in = { InSocket{"y", T_Float }, InSocket{"x", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "atan2(y, x) (rad)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Atan2&), 1> fetch = { FetchAtan2 };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Atan2&), 1> fetch = {FetchAtan2 };
ImVec2 pos;
};
T_Map<T_Count>::type FetchMinimum(const CompState& s, const Minimum& n);
T_Map<T_Count>::type FetchMinimum(const CompositionState& s, const Minimum& n);
struct Minimum {
const static char constexpr *name = static_cast<const char*>("Minimum");
std::array<InSocket, 2> in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "min(a, b)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Minimum&), 1> fetch = { FetchMinimum };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Minimum&), 1> fetch = {FetchMinimum };
ImVec2 pos;
};
T_Map<T_Count>::type FetchMaximum(const CompState& s, const Maximum& n);
T_Map<T_Count>::type FetchMaximum(const CompositionState& s, const Maximum& n);
struct Maximum {
const static char constexpr *name = static_cast<const char*>("Maximum");
std::array<InSocket, 2> in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "max(a, b)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Maximum&), 1> fetch = { FetchMaximum };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Maximum&), 1> fetch = {FetchMaximum };
ImVec2 pos;
};
T_Map<T_Count>::type FetchPower(const CompState& s, const Power& n);
T_Map<T_Count>::type FetchPower(const CompositionState& s, const Power& n);
struct Power {
const static char constexpr *name = static_cast<const char*>("Power");
std::array<InSocket, 2> in = { InSocket{"a", T_Float }, InSocket{"b", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "a^b", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Power&), 1> fetch = { FetchPower };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Power&), 1> fetch = {FetchPower };
ImVec2 pos;
};
T_Map<T_Count>::type FetchSquareRoot(const CompState& s, const SquareRoot& n);
T_Map<T_Count>::type FetchSquareRoot(const CompositionState& s, const SquareRoot& n);
struct SquareRoot {
const static char constexpr *name = static_cast<const char*>("Square Root");
std::array<InSocket, 1> in = { InSocket{"a", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "sqrt(a)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const SquareRoot&), 1> fetch = { FetchSquareRoot };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const SquareRoot&), 1> fetch = {FetchSquareRoot };
ImVec2 pos;
};
T_Map<T_Count>::type FetchNaturalLogarithm(const CompState& s, const NaturalLogarithm& n);
T_Map<T_Count>::type FetchNaturalLogarithm(const CompositionState& s, const NaturalLogarithm& n);
struct NaturalLogarithm {
const static char constexpr *name = static_cast<const char*>("Natural Logarithm");
std::array<InSocket, 1> in = { InSocket{"a", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "log(a)", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const NaturalLogarithm&), 1> fetch = { FetchNaturalLogarithm };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const NaturalLogarithm&), 1> fetch = {FetchNaturalLogarithm };
ImVec2 pos;
};
T_Map<T_Count>::type FetchAbsoluteValue(const CompState& s, const AbsoluteValue& n);
T_Map<T_Count>::type FetchAbsoluteValue(const CompositionState& s, const AbsoluteValue& n);
struct AbsoluteValue {
const static char constexpr *name = static_cast<const char*>("Absolute Value");
std::array<InSocket, 1> in = { InSocket{"a", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "|a|", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const AbsoluteValue&), 1> fetch = { FetchAbsoluteValue };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const AbsoluteValue&), 1> fetch = {FetchAbsoluteValue };
ImVec2 pos;
};
T_Map<T_Count>::type FetchInterpolation(const CompState& s, const Interpolation& n);
T_Map<T_Count>::type FetchInterpolation(const CompositionState& s, const Interpolation& n);
struct Interpolation {
const static char constexpr *name = static_cast<const char*>("Interpolation");
std::array<InSocket, 1> in = { InSocket{"t", T_Float } };
std::array<OutSocket, 1> out = { OutSocket{ "Value", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Interpolation&), 1> fetch = { FetchInterpolation };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Interpolation&), 1> fetch = {FetchInterpolation };
ImVec2 pos;
InterpolationExtra extra{};
void Draw();
};
T_Map<T_Count>::type FetchChain(const CompState& s, const Chain& n);
T_Map<T_Count>::type FetchChain(const CompositionState& s, const Chain& n);
struct Chain {
const static char constexpr *name = static_cast<const char*>("Chain");
std::array<InSocket, 1> in = { InSocket{"In", T_Int } };
std::array<OutSocket, 1> out = { OutSocket{ "Out", T_Float } };
std::array<T_Map<T_Count>::type (*)(const CompState&, const Chain&), 1> fetch = { FetchChain };
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const Chain&), 1> fetch = {FetchChain };
ImVec2 pos;
ChainExtra extra{};
};
T_Map<T_Count>::type FetchCompositionInFrame(const CompState& s, const CompositionIn& n);
T_Map<T_Count>::type FetchCompositionInTime(const CompState& s, const CompositionIn& n);
T_Map<T_Count>::type FetchCompositionInAppTicks(const CompState& s, const CompositionIn& n);
struct CompositionIn {
const static char constexpr *name = static_cast<const char*>("Composition In");
std::array<InSocket, 0> in = {};
std::array<OutSocket, 3> out = { OutSocket{"Frame", T_Int}, OutSocket{"Time (s)", T_Float}, OutSocket{"App Ticks", T_Float} };
std::array<T_Map<T_Count>::type (*)(const CompState&, const CompositionIn&), 3> fetch = { FetchCompositionInFrame, FetchCompositionInTime, FetchCompositionInAppTicks };
T_Map<T_Count>::type FetchCompositionInFrame(const CompositionState& s, const GroupIn& n);
T_Map<T_Count>::type FetchCompositionInAppTicks(const CompositionState& s, const GroupIn& n);
struct GroupIn {
const static char constexpr *name = static_cast<const char*>("Group In");
std::array<InSocket, 0> in{};
Vector<OutSocket> out{};
Vector<T_Map<T_Count>::type (*)(const CompositionState&, const GroupIn&)> fetch;
ImVec2 pos;
};
struct CompositionOut {
const static char constexpr *name = static_cast<const char*>("Composition Out");
Vector<InSocket> in = {};
Vector<Vector<ConnectInfo>> show_nodes;
std::array<OutSocket, 0> out = {};
std::array<T_Map<T_Count>::type (*)(const CompState&, const CompositionOut&), 0> fetch = {};
struct GroupOut {
const static char constexpr *name = static_cast<const char*>("Group Out");
Vector<InSocket> in{};
std::array<OutSocket, 0> out{};
std::array<T_Map<T_Count>::type (*)(const CompositionState&, const GroupOut&), 0> fetch = {};
ImVec2 pos;
};
@ -648,18 +646,29 @@ namespace K::Plugboard {
AbsoluteValue,
Interpolation,
Chain,
CompositionIn,
CompositionOut
GroupIn,
GroupOut
>;
struct Plugboard {
struct Group {
const static char constexpr *name = static_cast<const char*>("Group");
Vector<InSocket> in;
Vector<OutSocket> out;
Vector<T_Map<T_Count>::type (*)(const CompositionState&, const GroupIn&)> fetch;
ImVec2 pos;
GroupIn in_instance;
GroupOut out_instance;
NodeList nodes; // OK complexity wise since we would usually traverse the entire thing anyway, locality will likely be bad
Dict<ConnectInfo, LinksFromSource, ConnectInfoHasher> links_pos; // this is hilariously bad
CompositionIn in;
CompositionOut out;
Vector<NodeInstanceP> selected_nodes{};
};
struct Plugboard : Group {
Vector<Vector<ConnectInfo>> show_nodes;
Plugboard();
void AddInSocket(const String& s, Type t);
void AddOutSocket(const String& s, Type t, T_Map<T_Count>::type(*fetch)(const CompositionState&, const GroupIn&));
void RecollectChains();
};

View file

@ -83,5 +83,7 @@ namespace K::Resource {
Resource<T> *Load(const std::filesystem::path& p);
bgfx::ProgramHandle FetchUnmanagedShaderProgram(const String& shader_name);
void SaveCurrentProject();
}

View file

@ -6,7 +6,7 @@ namespace K::UI {
void Init(SDL_Window *window);
void Draw();
void Shutdown();
void SetupViewport(CompState& s);
void DestroyViewport(CompState& s);
void SetupViewport(CompositionState& s);
void DestroyViewport(CompositionState& s);
void SetLogoView();
}

View file

@ -8,17 +8,26 @@
#include "Plugboard.h"
namespace K {
enum UniformStatus {
K_U_Inactive = 0,
K_U_Exposed,
K_U_SamplerDims,
K_U_Count
};
struct Uniform {
String name;
bgfx::UniformHandle handle;
ShaderGraph::T_Map<ShaderGraph::T_Count>::type val;
Plugboard::ConnectInfo connection;
UniformStatus status = K_U_Inactive;
};
struct Sampler {
String name;
bgfx::UniformHandle handle;
Resource::Resource<Resource::K_R_Still> *resource;
Uniform *dims;
};
ShaderGraph::T_Map<ShaderGraph::T_Count>::type PlugboardValToShader(const Plugboard::T_Map<Plugboard::T_Count>::type& val);
@ -31,13 +40,13 @@ namespace K {
String shader;
Vector<Uniform> uniforms;
Vector<Sampler> samplers;
void GetFrame(const CompState& s, u32 view_id, bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16],
void GetFrame(const CompositionState& s, u32 view_id, bgfx::FrameBufferHandle fb, u32 w, u32 h, f32 proj[16],
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 ExposeUniform(CompositionState& s, Uniform& uu);
void ExposeUniform(CompositionState& s, u32 i);
static void HideUniform(CompositionState& s, Uniform& uu);
void AddUniform(const String& s, ShaderGraph::T_Map<ShaderGraph::T_Count>::type&& val);
void AddUniform(const String& s, ShaderGraph::T_Map<ShaderGraph::T_Count>::type&& val, UniformStatus init);
void AddSampler(const String& s);
void Compile();

View file

@ -7,6 +7,7 @@
- [Dear ImGui](https://github.com/ocornut/imgui)
- [fmt](https://github.com/fmtlib/fmt)
- [FreeType](https://freetype.org/)
- [Glaze](https://github.com/stephenberry/glaze)
- [imgui_impl_bgfx](https://gist.github.com/pr0g/aff79b71bf9804ddb03f39ca7c0c3bbb)
- [ImPlot](https://github.com/epezent/implot)
- [SDL2](https://www.libsdl.org/)

View file

@ -23,8 +23,6 @@
# Later
## IO
- File dialogues pending SDL3
- https://wiki.libsdl.org/SDL3/CategoryDialog
- Clipboard pending SDL3
- OIIO output for more than PNG's
- don't care about video export -- leave it for ffmpeg
@ -45,4 +43,3 @@
- Wait for SDL3!
- SDL_mixer will be able to do all of wav ogg flac mp3 opus, we live in good times
- output needs to be handled differently (if we care at all -- likely not)
z