Skip to content

Refactor Node hierarchy, implement animations #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: init
run: sudo apt install -yqq ninja-build xorg-dev g++-11
run: sudo apt update -yqq && sudo apt install -yqq ninja-build xorg-dev g++-11
- name: configure gcc
run: cmake -S . --preset=default -B build -DCMAKE_CXX_COMPILER=g++-11 -DFACADE_BUILD_SHADERS=OFF
- name: configure clang
Expand Down
6 changes: 3 additions & 3 deletions lib/engine/include/facade/engine/editor/drag_drop_id.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ void make_id_slot(Id<T>& out_id, char const* label, char const* payload_name) {
template <typename T>
void make_id_slot(Node& out_node, char const* label, char const* payload_name) {
auto oid = std::optional<Id<T>>{};
auto* id = out_node.find<Id<T>>();
auto id = out_node.find<T>();
if (id) { oid = *id; }
make_id_slot(oid, label, payload_name, {true});
if (id) {
if (!oid) {
out_node.detach<Id<T>>();
out_node.detach<T>();
} else {
*id = *oid;
out_node.attach<T>(*oid);
}
} else {
if (oid) { out_node.attach(*oid); }
Expand Down
7 changes: 4 additions & 3 deletions lib/engine/include/facade/engine/editor/scene_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@
#include <facade/engine/editor/common.hpp>
#include <facade/scene/id.hpp>
#include <facade/util/fixed_string.hpp>
#include <optional>

namespace facade {
class Scene;
class Node;
struct Node;

namespace editor {
///
/// \brief Target Node to inspect (driven by SceneTree)
///
struct InspectNode {
FixedString<128> name{"[Node]###Node"};
Id<Node> id{};
std::optional<Id<Node>> id{};

explicit operator bool() const { return id > Id<Node>{}; }
explicit operator bool() const { return id.has_value(); }
};

///
Expand Down
2 changes: 1 addition & 1 deletion lib/engine/include/facade/engine/scene_renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class SceneRenderer {
std::span<glm::mat4x4 const> make_instances(Node const& node, glm::mat4x4 const& parent) const;

void render(Renderer& renderer, vk::CommandBuffer cb, Skybox const& skybox) const;
void render(Renderer& renderer, vk::CommandBuffer cb, Node const& node, glm::mat4 const& parent = matrix_identity_v) const;
void render(Renderer& renderer, vk::CommandBuffer cb, Node const& node, glm::mat4 parent = matrix_identity_v) const;

Gfx m_gfx;
Sampler m_sampler;
Expand Down
12 changes: 6 additions & 6 deletions lib/engine/src/editor/inspector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,14 @@ void ResourceInspector::edit(Mesh& out_mesh, Id<Mesh> id) const {

void SceneInspector::camera() const {
auto& node = m_scene.camera();
auto* id = node.find<Id<Camera>>();
auto id = node.find<Camera>();
assert(id);

if (auto combo = Combo{"Select", FixedString{"{} ({})", node.name, *id}.c_str()}) {
for (auto const& cid : m_scene.cameras()) {
auto const* node = m_scene.find(cid);
for (auto cid : m_scene.cameras()) {
auto const* node = m_resources.nodes.find(cid);
assert(node);
auto const* cam = node->find<Id<Camera>>();
auto cam = node->find<Camera>();
assert(cam);
if (combo.item(FixedString{"{} ({})", node->name, *cam}.c_str(), {cid == *id})) {
if (!m_scene.select_camera(cid)) { logger::warn("[Inspector] Failed to select camera: [{}]", cid); }
Expand Down Expand Up @@ -189,7 +189,7 @@ void SceneInspector::instances(Node& out_node, Bool unified_scaling) const {
}

void SceneInspector::mesh(Node& out_node) const {
auto* mesh_id = out_node.find<Id<Mesh>>();
auto mesh_id = out_node.find<Mesh>();
if (auto tn = TreeNode{"Mesh", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed}) {
auto name = FixedString<128>{};
if (mesh_id) { name = FixedString<128>{"{} ({})", m_resources.meshes.find(*mesh_id)->name, *mesh_id}; }
Expand All @@ -198,7 +198,7 @@ void SceneInspector::mesh(Node& out_node) const {
}

void SceneInspector::node(Id<Node> node_id, Bool& out_unified_scaling) const {
auto* node = m_scene.find(node_id);
auto* node = m_resources.nodes.find(node_id);
if (!node) { return; }
transform(*node, out_unified_scaling);
instances(*node, out_unified_scaling);
Expand Down
20 changes: 11 additions & 9 deletions lib/engine/src/editor/scene_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,24 @@ namespace {
FixedString<128> node_name(Node const& node) {
auto ret = FixedString<128>{node.name};
if (ret.empty()) { ret = "(Unnamed)"; }
ret += FixedString{" ({})", node.id()};
ret += FixedString{" ({})", node.self};
return ret;
}

InspectNode inspect(Node const& node) {
auto ret = InspectNode{};
ret.id = node.id();
ret.id = node.self;
ret.name = node_name(node);
ret.name += FixedString{"###Node"};
return ret;
}

void walk(Node const& camera, Node& node, InspectNode& inout) {
void walk(Scene& out_scene, Node& node, InspectNode& inout) {
auto flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth;
if (node.id() == inout.id) { flags |= ImGuiTreeNodeFlags_Selected; }
if (node.children().empty()) {
if (node.self == inout.id) { flags |= ImGuiTreeNodeFlags_Selected; }
if (node.children.empty()) {
flags |= ImGuiTreeNodeFlags_Leaf;
if (&node == &camera && node.attachment_count() == 1) { return; }
if (&node == &out_scene.camera() && node.attachments.size() == 1) { return; }
}
auto tn = editor::TreeNode{node_name(node).c_str(), flags};
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
Expand All @@ -33,15 +33,17 @@ void walk(Node const& camera, Node& node, InspectNode& inout) {
inout = {};
}
if (tn) {
for (auto& child : node.children()) { walk(camera, child, inout); }
for (auto child : node.children) { walk(out_scene, *out_scene.resources().nodes.find(child), inout); }
}
}
} // namespace

bool SceneTree::render(NotClosed<Window>, InspectNode& inout) const {
auto const in = inout.id;
if (!m_scene.find(in)) { inout = {}; }
for (auto& root : m_scene.roots()) { walk(m_scene.camera(), root, inout); }
auto& nodes = m_scene.resources().nodes;
auto* node = in ? nodes.find(*inout.id) : nullptr;
if (!node) { inout = {}; }
for (auto id : m_scene.roots()) { walk(m_scene, *nodes.find(id), inout); }
return inout.id != in;
}
} // namespace facade::editor
4 changes: 3 additions & 1 deletion lib/engine/src/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,9 @@ auto Engine::poll() -> State const& {
// ImGui wants all widget calls within BeginFrame() / EndFrame(), so begin here
m_impl->window.gui->new_frame();
m_impl->window.window.get().glfw->poll_events();
return m_impl.get()->window.window.get().state();
auto const& ret = m_impl.get()->window.window.get().state();
m_impl->scene.tick(ret.dt);
return ret;
}

void Engine::render() {
Expand Down
11 changes: 6 additions & 5 deletions lib/engine/src/scene_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ void SceneRenderer::render(Scene const& scene, Ptr<Skybox const> skybox, Rendere
m_scene = &scene;
write_view(renderer.framebuffer_extent());
if (skybox) { render(renderer, cb, *skybox); }
for (auto const& node : m_scene->roots()) { render(renderer, cb, node); }
for (auto const& node : m_scene->roots()) { render(renderer, cb, m_scene->resources().nodes[node]); }
}

void SceneRenderer::write_view(glm::vec2 const extent) {
auto const& cam_node = m_scene->camera();
auto const* cam_id = cam_node.find<Id<Camera>>();
auto const cam_id = cam_node.find<Camera>();
assert(cam_id);
auto const& cam = m_scene->resources().cameras[*cam_id];
struct {
Expand Down Expand Up @@ -83,9 +83,9 @@ void SceneRenderer::render(Renderer& renderer, vk::CommandBuffer cb, Skybox cons
renderer.draw(pipeline, skybox.static_mesh(), {&instance, 1});
}

void SceneRenderer::render(Renderer& renderer, vk::CommandBuffer cb, Node const& node, glm::mat4 const& parent) const {
void SceneRenderer::render(Renderer& renderer, vk::CommandBuffer cb, Node const& node, glm::mat4 parent) const {
auto const& resources = m_scene->resources();
if (auto const* mesh_id = node.find<Id<Mesh>>()) {
if (auto mesh_id = node.find<Mesh>()) {
static auto const s_default_material = Material{std::make_unique<LitMaterial>()};
auto const& mesh = resources.meshes[*mesh_id];
for (auto const& primitive : mesh.primitives) {
Expand All @@ -103,6 +103,7 @@ void SceneRenderer::render(Renderer& renderer, vk::CommandBuffer cb, Node const&
}
}

for (auto const& child : node.m_children) { render(renderer, cb, child, parent * node.transform.matrix()); }
parent = parent * node.transform.matrix();
for (auto const& id : node.children) { render(renderer, cb, m_scene->resources().nodes[id], parent); }
}
} // namespace facade
Binary file modified lib/ext/src.zip
Binary file not shown.
4 changes: 4 additions & 0 deletions lib/scene/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ if(${${target_prefix_upper}_PCH})
endif()

target_sources(${PROJECT_NAME} PRIVATE
include/${target_prefix}/scene/animation.hpp
include/${target_prefix}/scene/animator.hpp
include/${target_prefix}/scene/camera.hpp
include/${target_prefix}/scene/fly_cam.hpp
include/${target_prefix}/scene/gltf_loader.hpp
include/${target_prefix}/scene/id.hpp
include/${target_prefix}/scene/interpolator.hpp
include/${target_prefix}/scene/lights.hpp
include/${target_prefix}/scene/load_status.hpp
include/${target_prefix}/scene/material.hpp
Expand All @@ -47,6 +50,7 @@ target_sources(${PROJECT_NAME} PRIVATE
include/${target_prefix}/scene/scene_resources.hpp
include/${target_prefix}/scene/scene.hpp

src/animator.cpp
src/camera.cpp
src/gltf_loader.cpp
src/material.cpp
Expand Down
10 changes: 10 additions & 0 deletions lib/scene/include/facade/scene/animation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once
#include <facade/scene/animator.hpp>
#include <facade/scene/id.hpp>

namespace facade {
struct Animation {
Animator animator{};
Id<Node> target{};
};
} // namespace facade
16 changes: 16 additions & 0 deletions lib/scene/include/facade/scene/animator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once
#include <facade/scene/interpolator.hpp>

namespace facade {
struct Node;

struct Animator {
Interpolator<glm::vec3> translation{};
Interpolator<glm::quat> rotation{};
Interpolator<glm::vec3> scale{};

float elapsed{};

void update(Node& out_node, float dt);
};
} // namespace facade
60 changes: 60 additions & 0 deletions lib/scene/include/facade/scene/interpolator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#pragma once
#include <facade/util/logger.hpp>
#include <facade/util/transform.hpp>
#include <optional>

namespace facade {
constexpr glm::vec3 lerp(glm::vec3 const& a, glm::vec3 const& b, float const t) {
return {std::lerp(a.x, b.x, t), std::lerp(a.y, b.y, t), std::lerp(a.z, b.z, t)};
}

inline glm::quat lerp(glm::quat const& a, glm::quat const& b, float const t) { return glm::slerp(a, b, t); }

enum class Interpolation {
eLinear,
eStep,
eCubic,
};

template <typename T>
struct Interpolator {
struct Keyframe {
T value{};
float timestamp{};
};

std::vector<Keyframe> keyframes{};
Interpolation interpolation{};

float duration() const { return keyframes.empty() ? 0.0f : keyframes.back().timestamp; }

std::optional<std::size_t> index_for(float time) const {
if (keyframes.empty()) { return {}; }

auto const it = std::lower_bound(keyframes.begin(), keyframes.end(), time, [](Keyframe const& k, float time) { return k.timestamp < time; });
if (it == keyframes.end()) { return {}; }
return static_cast<std::size_t>(it - keyframes.begin());
}

std::optional<T> operator()(float elapsed) const {
if (keyframes.empty()) { return {}; }

auto const i_next = index_for(elapsed);
if (!i_next) { return keyframes.back().value; }

assert(*i_next < keyframes.size());
auto const& next = keyframes[*i_next];
assert(elapsed <= next.timestamp);
if (*i_next == 0) { return next.value; }

auto const& prev = keyframes[*i_next - 1];
assert(prev.timestamp < elapsed);
if (interpolation == Interpolation::eStep) { return prev.value; }

auto const t = (elapsed - prev.timestamp) / (next.timestamp - prev.timestamp);
using facade::lerp;
using std::lerp;
return lerp(prev.value, next.value, t);
}
};
} // namespace facade
Loading