Skip to content

Commit

Permalink
Zephyr: Scene: optimize transform updates
Browse files Browse the repository at this point in the history
  • Loading branch information
fleroviux committed May 24, 2024
1 parent 58c5569 commit 35d8714
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 33 deletions.
31 changes: 12 additions & 19 deletions app/next/src/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include "main_window.hpp"

static const bool enable_validation_layers = true;
static const bool benchmark_scene_size = false;
static const bool benchmark_scene_size = true;

namespace zephyr {

Expand Down 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->GetRoot()->Remove(m_behemoth_scene.get());
m_behemoth_scene.reset();
}
break;
Expand Down Expand Up @@ -94,14 +94,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->UpdateTransforms();
m_render_engine->RenderScene(m_scene->GetRoot());
m_frame++;

UpdateFramesPerSecondCounter();
Expand Down Expand Up @@ -167,33 +161,32 @@ namespace zephyr {
}

void MainWindow::CreateScene() {
m_scene_root = SceneNode::New();
m_scene = std::make_unique<Scene>();

m_camera_node = m_scene_root->CreateChild("RenderCamera");
m_camera_node = m_scene->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->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->GetRoot()->Add(gltf_loader.Parse("models/triangleWithoutIndices/TriangleWithoutIndices.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->GetRoot()->Add(m_behemoth_scene);
}

void MainWindow::CreateBenchmarkScene() {
m_scene_root = SceneNode::New();
m_scene = std::make_unique<Scene>();

// 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->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 @@ -268,7 +261,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->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
4 changes: 2 additions & 2 deletions app/next/src/main_window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <zephyr/logger/logger.hpp>
#include <zephyr/renderer/vulkan/vulkan_instance.hpp>
#include <zephyr/renderer/render_engine.hpp>
#include <zephyr/scene/node.hpp>
#include <zephyr/scene/scene.hpp>
#include <zephyr/float.hpp>
#include <zephyr/integer.hpp>
#include <zephyr/panic.hpp>
Expand Down Expand Up @@ -41,7 +41,7 @@ namespace zephyr {
void CleanupOpenGL();

std::unique_ptr<RenderEngine> m_render_engine{};
std::shared_ptr<SceneNode> m_scene_root{};
std::shared_ptr<Scene> m_scene{};
std::shared_ptr<SceneNode> m_camera_node{};
std::shared_ptr<SceneNode> m_behemoth_scene{};

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

/**
* @todo: make this class thread-safe ideally, so that at the least calling Get*() operations
* from multiple threads is safe.
*/

namespace zephyr {

/**
Expand All @@ -36,6 +32,10 @@ namespace zephyr {
SetFromEuler(x, y, z);
}

[[nodiscard]] VoidEvent& OnChange() const {
return m_event_on_change;
}

/**
* Construct a Rotation from a quaternion.
* @param quaternion the Quaternion
Expand Down Expand Up @@ -84,6 +84,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 @@ -142,6 +143,8 @@ namespace zephyr {
/// Signal internally that the quaternion has changed
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 @@ -184,7 +187,9 @@ namespace zephyr {
mutable Matrix4 m_matrix{}; ///< a 4x4 rotation matrix which is updated from the quaternion on demand.
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 bool m_needs_euler_refresh{true}; ///< true when euler angles (#{@link m_euler}) are outdated and false otherwise.

mutable VoidEvent m_event_on_change{};
};

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

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

Expand All @@ -8,6 +9,7 @@ set(HEADERS

set(HEADERS_PUBLIC
include/zephyr/scene/node.hpp
include/zephyr/scene/scene.hpp
include/zephyr/scene/transform.hpp
)

Expand Down
34 changes: 29 additions & 5 deletions zephyr/scene/include/zephyr/scene/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,19 @@

namespace zephyr {

class Scene;

class SceneNode : public std::enable_shared_from_this<SceneNode>, NonCopyable, NonMoveable {
struct Private {};

public:
explicit SceneNode(Private) {};
SceneNode(Private, std::string name) : m_name{std::move(name)} {}
explicit SceneNode(Private) {
ListenForTransformChange();
}

SceneNode(Private, std::string name) : m_name{std::move(name)} {
ListenForTransformChange();
}

~SceneNode() {
for(const auto& child : m_children) {
Expand Down Expand Up @@ -50,15 +57,18 @@ namespace zephyr {
node->RemoveFromParent();
node->m_parent = this;
node->m_parent_weak = shared_from_this();
node->Traverse([&](SceneNode* child_node){
child_node->m_scene = m_scene;
child_node->MarkTransformAsDirty();
return true;
});
m_children.push_back(std::move(node));
}

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

Expand All @@ -74,7 +84,12 @@ namespace zephyr {
std::shared_ptr<SceneNode> node_ptr = std::move(*it);
node_ptr->m_parent = nullptr;
node_ptr->m_parent_weak.reset();
node->Traverse([](SceneNode* child_node){
child_node->m_scene = nullptr;
return true;
});
m_children.erase(it);

return std::move(node_ptr);
}

Expand Down Expand Up @@ -159,13 +174,22 @@ namespace zephyr {
}

private:
friend Scene;

void ListenForTransformChange() {
m_transform.OnChange().Subscribe([this] {MarkTransformAsDirty();});
}

void MarkTransformAsDirty();

SceneNode* m_parent{};
std::weak_ptr<SceneNode> m_parent_weak{};
std::vector<std::shared_ptr<SceneNode>> m_children{};
std::string m_name{};
bool m_is_visible{true};
Transform3D m_transform{this};
std::unordered_map<std::type_index, std::unique_ptr<Component>> m_components{};
Scene* m_scene{};
};

} // namespace zephyr
53 changes: 53 additions & 0 deletions zephyr/scene/include/zephyr/scene/scene.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

#pragma once

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

namespace zephyr {

class Scene {
public:
Scene() {
m_root_node = SceneNode::New("SceneRoot");

// TODO(fleroviux): make passing the scene into the node less dirty.
m_root_node->m_scene = this;

// TODO(fleroviux): kinda sucks that we have to do this.
m_root_node->GetTransform().UpdateLocal();
m_root_node->GetTransform().UpdateWorld();
}

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

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

void UpdateTransforms() {
for(auto node : m_nodes_with_dirty_transforms) {
node->Traverse([](SceneNode* child_node) {
child_node->GetTransform().UpdateLocal();
child_node->GetTransform().UpdateWorld();
return true;
});
}

m_nodes_with_dirty_transforms.clear();
}

private:
friend SceneNode;

void MarkSceneNodeTransformAsDirty(SceneNode* node) {
m_nodes_with_dirty_transforms.insert(node);
}

std::shared_ptr<SceneNode> m_root_node{};
std::unordered_set<SceneNode*> m_nodes_with_dirty_transforms{};
};

} // namespace zephyr
14 changes: 13 additions & 1 deletion zephyr/scene/include/zephyr/scene/transform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,31 @@
#include <zephyr/math/matrix4.hpp>
#include <zephyr/math/rotation.hpp>
#include <zephyr/math/vector.hpp>
#include <zephyr/event.hpp>

namespace zephyr {

class SceneNode;

class Transform3D {
public:
explicit Transform3D(SceneNode* node) : m_node{node} {}
explicit Transform3D(SceneNode* node) : m_node{node} {
m_rotation.OnChange().Subscribe([this] {
OnChange().Emit();
});
}

[[nodiscard]] VoidEvent& OnChange() const {
return m_event_on_change;
}

[[nodiscard]] const Vector3& GetPosition() const {
return m_position;
}

void SetPosition(const Vector3& position) {
m_position = position;
m_event_on_change.Emit();
}

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

void SetScale(const Vector3& scale) {
m_scale = scale;
m_event_on_change.Emit();
}

[[nodiscard]] const Rotation& GetRotation() const {
Expand Down Expand Up @@ -55,6 +66,7 @@ namespace zephyr {
Rotation m_rotation;
Matrix4 m_local_matrix;
Matrix4 m_world_matrix;
mutable VoidEvent m_event_on_change{};
};

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

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

namespace zephyr {

void SceneNode::MarkTransformAsDirty() {
if(m_scene) {
m_scene->MarkSceneNodeTransformAsDirty(this);
}
}

} // namespace zephyr

0 comments on commit 35d8714

Please sign in to comment.