Skip to content

Commit

Permalink
Zephyr: Scene: optimize matrix transform updates
Browse files Browse the repository at this point in the history
  • Loading branch information
fleroviux committed May 27, 2024
1 parent 0b07862 commit 65d3085
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 25 deletions.
29 changes: 12 additions & 17 deletions app/next/src/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ namespace zephyr {
switch(key_event->keysym.sym) {
case SDLK_z: {
if(m_behemoth_scene) {
m_scene_root->Remove(m_behemoth_scene.get());
m_scene_graph->GetRoot()->Remove(m_behemoth_scene.get());
m_behemoth_scene.reset();
}
break;
Expand Down Expand Up @@ -95,13 +95,8 @@ namespace zephyr {
}

void MainWindow::RenderFrame() {
m_scene_root->Traverse([&](SceneNode* node) {
node->GetTransform().UpdateLocal();
node->GetTransform().UpdateWorld();
return true;
});

m_render_engine->RenderScene(m_scene_root.get());
m_scene_graph->UpdateTransforms();
m_render_engine->RenderScene(m_scene_graph->GetRoot());

m_frame++;

Expand Down Expand Up @@ -168,33 +163,33 @@ namespace zephyr {
}

void MainWindow::CreateScene() {
m_scene_root = SceneNode::New();
m_scene_graph = std::make_shared<SceneGraph>();

m_camera_node = m_scene_root->CreateChild("RenderCamera");
m_camera_node = m_scene_graph->GetRoot()->CreateChild("RenderCamera");
m_camera_node->CreateComponent<PerspectiveCameraComponent>(45.0f, 16.f / 9.f, 0.01f, 100.f);
m_camera_node->GetTransform().SetPosition({0.f, 0.f, 5.f});

GLTFLoader gltf_loader{};
std::shared_ptr<SceneNode> gltf_scene_1 = gltf_loader.Parse("models/DamagedHelmet/DamagedHelmet.gltf");
gltf_scene_1->GetTransform().SetPosition({1.0f, 0.0f, -5.0f});
gltf_scene_1->GetTransform().GetRotation().SetFromEuler(1.5f, 0.0f, 0.0f);
m_scene_root->Add(std::move(gltf_scene_1));
m_scene_graph->GetRoot()->Add(std::move(gltf_scene_1));

m_scene_root->Add(gltf_loader.Parse("models/triangleWithoutIndices/TriangleWithoutIndices.gltf"));
//m_scene_root->Add(gltf_loader.Parse("models/triangle/Triangle.gltf"));
m_scene_graph->GetRoot()->Add(gltf_loader.Parse("models/triangleWithoutIndices/TriangleWithoutIndices.gltf"));
//m_scene_graph->GetRoot()->Add(gltf_loader.Parse("models/triangle/Triangle.gltf"));

m_behemoth_scene = gltf_loader.Parse("models/Behemoth/scene.gltf");
m_behemoth_scene->GetTransform().SetPosition({-1.0f, 0.0f, -5.0f});
m_behemoth_scene->GetTransform().GetRotation().SetFromEuler(-M_PI * 0.5, M_PI, 0.0f);
m_behemoth_scene->GetTransform().SetScale({0.5f, 0.5f, 0.5f});
m_scene_root->Add(m_behemoth_scene);
m_scene_graph->GetRoot()->Add(m_behemoth_scene);
}

void MainWindow::CreateBenchmarkScene() {
m_scene_root = SceneNode::New();
m_scene_graph = std::make_shared<SceneGraph>();

// TODO(fleroviux): engine crashes when there is no camera in the scene! VERY BAD!!!
m_camera_node = m_scene_root->CreateChild("RenderCamera");
m_camera_node = m_scene_graph->GetRoot()->CreateChild("RenderCamera");
m_camera_node->CreateComponent<PerspectiveCameraComponent>(45.0f, 16.f / 9.f, 0.01f, 100.f);
m_camera_node->GetTransform().SetPosition({0.f, 0.f, 5.f});

Expand Down Expand Up @@ -269,7 +264,7 @@ namespace zephyr {
for(int x = -grid_size / 2; x < grid_size / 2; x++) {
for(int y = -grid_size / 2; y < grid_size / 2; y++) {
for(int z = -grid_size / 2; z < grid_size / 2; z++) {
std::shared_ptr<SceneNode> cube = m_scene_root->CreateChild("Cube");
std::shared_ptr<SceneNode> cube = m_scene_graph->GetRoot()->CreateChild("Cube");
cube->CreateComponent<MeshComponent>(cube_geometry, std::shared_ptr<Material>{});
cube->GetTransform().SetPosition({(f32)x, (f32)y, (f32)-z});
cube->GetTransform().SetScale({0.1, 0.1, 0.1});
Expand Down
3 changes: 2 additions & 1 deletion app/next/src/main_window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <zephyr/renderer/vulkan/vulkan_instance.hpp>
#include <zephyr/renderer/render_engine.hpp>
#include <zephyr/scene/node.hpp>
#include <zephyr/scene/scene_graph.hpp>
#include <zephyr/float.hpp>
#include <zephyr/integer.hpp>
#include <zephyr/panic.hpp>
Expand Down Expand Up @@ -41,7 +42,7 @@ namespace zephyr {
void CleanupOpenGL();

std::unique_ptr<RenderEngine> m_render_engine{};
std::shared_ptr<SceneNode> m_scene_root{};
std::shared_ptr<SceneGraph> m_scene_graph{};
std::shared_ptr<SceneNode> m_camera_node{};
std::shared_ptr<SceneNode> m_behemoth_scene{};

Expand Down
9 changes: 9 additions & 0 deletions zephyr/math/include/zephyr/math/rotation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <zephyr/math/matrix4.hpp>
#include <zephyr/math/quaternion.hpp>
#include <zephyr/math/vector.hpp>
#include <zephyr/event.hpp>
#include <zephyr/float.hpp>

namespace zephyr {
Expand Down Expand Up @@ -39,6 +40,11 @@ namespace zephyr {
SetFromQuaternion(quaternion);
}

/// @returns a reference to an event that is emitted whenever the rotation changes.
[[nodiscard]] VoidEvent& OnChange() const {
return m_event_on_change;
}

/// @returns the rotation in quaternion form
[[nodiscard]] const Quaternion& GetAsQuaternion() const {
return m_quaternion;
Expand Down Expand Up @@ -79,6 +85,7 @@ namespace zephyr {
// The 4x4 matrix is likely to be read and copying it now is faster than reconstructing it later.
m_matrix = matrix;
m_needs_euler_refresh = true;
m_event_on_change.Emit();
}

/**
Expand Down Expand Up @@ -138,6 +145,7 @@ namespace zephyr {
void MarkQuaternionAsChanged() {
m_needs_matrix_refresh = true;
m_needs_euler_refresh = true;
m_event_on_change.Emit();
}

/// Update the 4x4 matrix from the quaternion.
Expand Down Expand Up @@ -181,6 +189,7 @@ namespace zephyr {
mutable Vector3 m_euler{}; ///< a vector of euler angles which is updated from the quaternion on demand.
mutable bool m_needs_matrix_refresh{true}; ///< true when the 4x4 matrix (#{@link m_matrix}) is outdated and false otherwise.
mutable bool m_needs_euler_refresh{true}; ///< true whe euler angles (#{@link m_euler}) are outdated and false otherwise.
mutable VoidEvent m_event_on_change{}; ///< An event that is emitted when the rotation has changed.
};

} // namespace zephyr
3 changes: 3 additions & 0 deletions zephyr/scene/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@

set(SOURCES
src/scene_graph.cpp
src/transform.cpp
)

set(HEADERS
)

set(HEADERS_PUBLIC
include/zephyr/scene/component.hpp
include/zephyr/scene/scene_graph.hpp
include/zephyr/scene/node.hpp
include/zephyr/scene/transform.hpp
)
Expand Down
31 changes: 28 additions & 3 deletions zephyr/scene/include/zephyr/scene/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#pragma once

#include <zephyr/scene/component.hpp>
#include <zephyr/scene/scene_graph.hpp>
#include <zephyr/scene/transform.hpp>
#include <zephyr/non_copyable.hpp>
#include <zephyr/non_moveable.hpp>
Expand All @@ -21,9 +22,11 @@ namespace zephyr {

public:
explicit SceneNode(Private) {};
SceneNode(Private, std::string name) : m_name{std::move(name)} {}
SceneNode(Private, std::string name, SceneGraph* scene_graph = nullptr) : m_name{std::move(name)}, m_scene_graph{scene_graph} {}

~SceneNode() {
// TODO(fleroviux): debate if we should still signal to the scene graph that the children nodes have been unmounted.
// As far as I can tell at this point the node would only be bound to a scene graph if the entire scene graph is being destructed.
for(const auto& child : m_children) {
child->m_parent = nullptr;
}
Expand All @@ -47,16 +50,25 @@ namespace zephyr {
}

void Add(std::shared_ptr<SceneNode> node) {
SceneNode* node_raw = node.get();
node->RemoveFromParent();
node->m_parent = this;
m_children.push_back(std::move(node));

// If this node belongs to a scene graph, register the new child tree to that scene graph.
if(m_scene_graph) {
node_raw->Traverse([this](SceneNode* child_node) {
child_node->m_scene_graph = m_scene_graph;
return true;
});
m_scene_graph->SignalNodeMounted(node_raw);
}
}

template<typename... Args>
std::shared_ptr<SceneNode> CreateChild(Args&&... args) {
std::shared_ptr<SceneNode> node = New(std::forward<Args>(args)...);
node->m_parent = this;
m_children.push_back(node);
Add(node);
return std::move(node);
}

Expand All @@ -72,6 +84,16 @@ namespace zephyr {
std::shared_ptr<SceneNode> node_ptr = std::move(*it);
node_ptr->m_parent = nullptr;
m_children.erase(it);

// If this node belongs to a scene graph, unregister the removed child tree from that scene graph.
if(m_scene_graph) {
node_ptr->Traverse([this](SceneNode* child_node) {
child_node->m_scene_graph = nullptr;
return true;
});
m_scene_graph->SignalNodeRemoved(node_ptr.get());
}

return std::move(node_ptr);
}

Expand Down Expand Up @@ -156,6 +178,9 @@ namespace zephyr {
}

private:
friend Transform3D;

SceneGraph* m_scene_graph{};
SceneNode* m_parent{};
std::vector<std::shared_ptr<SceneNode>> m_children{};
std::string m_name{};
Expand Down
37 changes: 37 additions & 0 deletions zephyr/scene/include/zephyr/scene/scene_graph.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

#pragma once

#include <memory>
#include <vector>

namespace zephyr {

class SceneNode;

class SceneGraph {
public:
SceneGraph();

[[nodiscard]] const SceneNode* GetRoot() const {
return m_root_node.get();
}

[[nodiscard]] SceneNode* GetRoot() {
return m_root_node.get();
}

void UpdateTransforms();

private:
friend SceneNode;
friend class Transform3D;

void SignalNodeMounted(SceneNode* node);
void SignalNodeRemoved(SceneNode* node);
void SignalNodeTransformChanged(SceneNode* node);

std::shared_ptr<SceneNode> m_root_node{};
std::vector<SceneNode*> m_nodes_with_dirty_transform{};
};

} // namespace zephyr
13 changes: 9 additions & 4 deletions zephyr/scene/include/zephyr/scene/transform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace zephyr {
explicit Transform3D(SceneNode* node) : m_node{node} {
UpdateLocal();
UpdateWorld();
m_rotation.OnChange().Subscribe([this]() { SignalNodeTransformChanged(); });
}

[[nodiscard]] const Vector3& GetPosition() const {
Expand All @@ -22,6 +23,7 @@ namespace zephyr {

void SetPosition(const Vector3& position) {
m_position = position;
SignalNodeTransformChanged();
}

[[nodiscard]] const Vector3& GetScale() const {
Expand All @@ -30,6 +32,7 @@ namespace zephyr {

void SetScale(const Vector3& scale) {
m_scale = scale;
SignalNodeTransformChanged();
}

[[nodiscard]] const Rotation& GetRotation() const {
Expand All @@ -52,12 +55,14 @@ namespace zephyr {
void UpdateWorld();

private:
void SignalNodeTransformChanged();

SceneNode* m_node;
Vector3 m_position;
Vector3 m_position{};
Vector3 m_scale{1.0f, 1.0f, 1.0f};
Rotation m_rotation;
Matrix4 m_local_matrix;
Matrix4 m_world_matrix;
Rotation m_rotation{};
Matrix4 m_local_matrix{};
Matrix4 m_world_matrix{};
};

} // namespace zephyr
36 changes: 36 additions & 0 deletions zephyr/scene/src/scene_graph.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

#include <zephyr/scene/scene_graph.hpp>
#include <zephyr/scene/node.hpp>

namespace zephyr {

SceneGraph::SceneGraph() {
m_root_node = SceneNode::New("SceneRoot", this);
SignalNodeMounted(m_root_node.get());
}

void SceneGraph::UpdateTransforms() {
for(const auto node : m_nodes_with_dirty_transform) {
node->GetTransform().UpdateLocal();
node->GetTransform().UpdateWorld();
}
m_nodes_with_dirty_transform.clear();
}

void SceneGraph::SignalNodeMounted(SceneNode* node) {
SignalNodeTransformChanged(node);
}

void SceneGraph::SignalNodeRemoved(SceneNode* node) {
}

void SceneGraph::SignalNodeTransformChanged(SceneNode* node) {
// TODO(fleroviux): avoid updating nodes more than once.
// If a node is marked for update once and then later again, ideally skip the first update.
node->Traverse([this](SceneNode* child_node) {
m_nodes_with_dirty_transform.push_back(child_node);
return true;
});
}

} // namespace zephyr
7 changes: 7 additions & 0 deletions zephyr/scene/src/transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,11 @@ namespace zephyr {
}
}

void Transform3D::SignalNodeTransformChanged() {
SceneGraph* scene_graph = m_node->m_scene_graph;
if(scene_graph) {
scene_graph->SignalNodeTransformChanged(m_node);
}
}

} // namespace zephyr

0 comments on commit 65d3085

Please sign in to comment.