From c9b2647ff5e4e82003ca5299520a0b313ecd60a6 Mon Sep 17 00:00:00 2001 From: oreyg Date: Fri, 26 Dec 2025 03:29:48 +0100 Subject: [PATCH 1/7] Scene node handles draft 1 --- CMakeLists.txt | 2 + src/commands/commandManager.cpp | 6 +- src/commands/nodeCommand.cpp | 73 +++++++------- src/commands/nodeCommand.hpp | 62 ++++++------ src/commands/nodeSnapshot.cpp | 139 +++++++++++++------------ src/commands/nodeSnapshot.hpp | 77 +++++++------- src/commands/snapshotBase.hpp | 32 +++--- src/passes/DebugBVHPass.cpp | 2 +- src/passes/GBuffer.cpp | 7 +- src/passes/ZPrePass.cpp | 3 +- src/passes/deferedPass.cpp | 12 ++- src/scene.cpp | 173 ++++++++++++++++++++------------ src/scene.hpp | 36 +++++-- src/sceneNode.cpp | 3 +- src/sceneNode.hpp | 8 +- src/sceneNodeHandle.hpp | 35 +++++++ 16 files changed, 392 insertions(+), 278 deletions(-) create mode 100644 src/sceneNodeHandle.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 42cfa1a..35ede75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.10.0) project(TimeToDX VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) add_definitions(-DNOMINMAX) diff --git a/src/commands/commandManager.cpp b/src/commands/commandManager.cpp index b2dfc93..6272c7c 100644 --- a/src/commands/commandManager.cpp +++ b/src/commands/commandManager.cpp @@ -8,7 +8,7 @@ bool CommandManager::commitSnapshot(std::unique_ptr&& snapshotBase { if (snapshotBase->containsChanges()) { - // If last entry in an undo buffer is a snapshot - attempt to merge + // If the last entry in an undo buffer is a snapshot - attempt to merge if (SnapshotBase* dataSnapshot = getLastUndoAsSnapshot()) { if (snapshotBase->merge(dataSnapshot)) @@ -96,12 +96,12 @@ void CommandManager::clearRedoBuffer() std::unique_ptr CommandManager::commitInternal(CommandBase* command) { - std::unique_ptr redoCommand = command->exec(); + std::unique_ptr commitCommand = command->exec(); if (auto snapshot = dynamic_cast(command)) { snapshot->onCommitTransaction(); } - return redoCommand; + return commitCommand; } SnapshotBase* CommandManager::getLastUndoAsSnapshot() diff --git a/src/commands/nodeCommand.cpp b/src/commands/nodeCommand.cpp index 2f4cf7c..e28e855 100644 --- a/src/commands/nodeCommand.cpp +++ b/src/commands/nodeCommand.cpp @@ -7,41 +7,42 @@ namespace Command { - DuplicateSceneNode::DuplicateSceneNode(Scene* inScene, SceneNode* inSceneNode, bool inValidateName) - : m_scene(inScene) - , m_sceneNode(inSceneNode) - , m_validateName(inValidateName) - { - assert(inSceneNode); - m_breakHistory = true; - m_sceneNodeClone = m_sceneNode->clone(); - } - - std::unique_ptr DuplicateSceneNode::exec() - { - if (m_validateName) - { - m_scene->validateName(m_sceneNodeClone.get()); - } - SceneNode* clonedNode = m_scene->adoptClonedNode(std::move(m_sceneNodeClone)); - return std::make_unique(m_scene, clonedNode); - } - - RemoveSceneNode::RemoveSceneNode(Scene* inScene, SceneNode* inSceneNode) - : m_scene(inScene) - , m_sceneNode(inSceneNode) - { - assert(inSceneNode); - - // Nodes in the registry are not memory-stable after recreation - m_breakHistory = true; - } - - std::unique_ptr RemoveSceneNode::exec() - { - auto createCommand = std::make_unique(m_scene, m_sceneNode, false); - m_scene->deleteNode(m_sceneNode); - return createCommand; - } +DuplicateSceneNode::DuplicateSceneNode( + Scene* inScene, + SceneNode* inSceneNode, + bool reuseNodeHandle) + : m_scene(inScene) + , m_nodeHandle(reuseNodeHandle ? inScene->getHandleOfNode(inSceneNode) : SceneNodeHandle::invalidHandle()) + , m_validateName(!reuseNodeHandle) +{ + assert(inSceneNode); + m_sceneNodeClone = inSceneNode->clone(); +} + +std::unique_ptr DuplicateSceneNode::exec() +{ + if (m_validateName) + { + m_scene->validateName(m_sceneNodeClone.get()); + } + SceneNode* clonedNode = m_scene->adoptClonedNode(std::move(m_sceneNodeClone), m_nodeHandle); + return std::make_unique(m_scene, clonedNode); +} + +RemoveSceneNode::RemoveSceneNode(Scene* inScene, SceneNode* inSceneNode) + : m_scene(inScene) + , m_nodeHandle(inScene->getHandleOfNode(inSceneNode)) +{ + assert(inSceneNode); +} + +std::unique_ptr RemoveSceneNode::exec() +{ + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto createCommand = std::make_unique(m_scene, sceneNode, true); + m_scene->deleteNode(sceneNode); + return createCommand; +} } \ No newline at end of file diff --git a/src/commands/nodeCommand.hpp b/src/commands/nodeCommand.hpp index 53e1dce..d016297 100644 --- a/src/commands/nodeCommand.hpp +++ b/src/commands/nodeCommand.hpp @@ -1,5 +1,6 @@ #pragma once +#include "sceneNodeHandle.hpp" #include "commandBase.hpp" class SceneNode; @@ -7,36 +8,35 @@ class Scene; namespace Command { - class DuplicateSceneNode final : public CommandBase - { - public: - DuplicateSceneNode( - Scene* inScene, - SceneNode* inSceneNode, - bool inValidateName = true); - - protected: - virtual std::unique_ptr exec() override; - - Scene* m_scene = nullptr; - SceneNode* m_sceneNode = nullptr; - std::unique_ptr m_sceneNodeClone = nullptr; - bool m_validateName = true; - }; - - class RemoveSceneNode final : public CommandBase - { - public: - RemoveSceneNode( - Scene* inScene, - SceneNode* inSceneNode); - - protected: - virtual std::unique_ptr exec() override; - - // TO-DO: switch from pointers to handles - Scene* m_scene = nullptr; - SceneNode* m_sceneNode = nullptr; - }; +class DuplicateSceneNode final : public CommandBase +{ +public: + DuplicateSceneNode( + Scene* inScene, + SceneNode* inSceneNode, + bool reuseNodeHandle = false); + +protected: + virtual std::unique_ptr exec() override; + + Scene* m_scene = nullptr; + SceneNodeHandle m_nodeHandle; + std::unique_ptr m_sceneNodeClone = nullptr; + bool m_validateName = true; +}; + +class RemoveSceneNode final : public CommandBase +{ +public: + RemoveSceneNode( + Scene* inScene, + SceneNode* inSceneNode); + +protected: + virtual std::unique_ptr exec() override; + + Scene* m_scene = nullptr; + SceneNodeHandle m_nodeHandle; +}; }; diff --git a/src/commands/nodeSnapshot.cpp b/src/commands/nodeSnapshot.cpp index a6de985..bce0df9 100644 --- a/src/commands/nodeSnapshot.cpp +++ b/src/commands/nodeSnapshot.cpp @@ -1,94 +1,107 @@ #include "nodeSnapshot.hpp" +#include "scene.hpp" #include "sceneNode.hpp" namespace Snapshot { - SceneNodeCopy::SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode) - : m_scene(inScene) - , m_sceneNode(inSceneNode) - { - assert(inSceneNode); - m_sceneNodeClone = m_sceneNode->clone(); - } +SceneNodeCopy::SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode) + : m_scene(inScene) + , m_nodeHandle(inScene->getHandleOfNode(inSceneNode)) +{ + assert(inSceneNode); + m_sceneNodeClone = inSceneNode->clone(); +} - std::unique_ptr SceneNodeCopy::exec() - { - auto redoTransaction = std::make_unique(m_scene, m_sceneNode); - m_sceneNode->copyFrom(m_sceneNodeClone.get()); - return redoTransaction; - } +std::unique_ptr SceneNodeCopy::exec() +{ + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto redoTransaction = std::make_unique(m_scene, sceneNode); + sceneNode->copyFrom(m_sceneNodeClone.get()); + return redoTransaction; +} - bool SceneNodeCopy::merge(SnapshotBase* command) +bool SceneNodeCopy::merge(SnapshotBase* command) +{ + if (getAllowMerging() && command->getAllowMerging()) { - if (getAllowMerging() && command->getAllowMerging()) + if (auto* castCommand = dynamic_cast(command)) { - if (auto* castCommand = dynamic_cast(command)) + if (castCommand->m_nodeHandle == m_nodeHandle) { - if (castCommand->m_sceneNode == m_sceneNode) - { - m_sceneNodeClone = std::move(castCommand->m_sceneNodeClone); - castCommand->m_sceneNodeClone = nullptr; - return true; - } + m_sceneNodeClone = std::move(castCommand->m_sceneNodeClone); + castCommand->m_sceneNodeClone = nullptr; + return true; } } - return false; } + return false; +} - bool SceneNodeCopy::containsChanges() const - { - return m_sceneNodeClone->differsFrom(m_sceneNode); - } +bool SceneNodeCopy::containsChanges() const +{ + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + return m_sceneNodeClone->differsFrom(sceneNode); +} - void SceneNodeCopy::onCommitTransaction() - { - m_sceneNode->onCommitTransaction(m_scene); - } +void SceneNodeCopy::onCommitTransaction() +{ + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + sceneNode->onCommitTransaction(m_scene); +} - SceneNodeTransform::SceneNodeTransform(Scene* inScene, SceneNode* inSceneNode) - : m_scene(inScene) - , m_sceneNode(inSceneNode) - { - m_savedTransform = m_sceneNode->transform; - } +SceneNodeTransform::SceneNodeTransform(Scene* inScene, SceneNode* inSceneNode) + : m_scene(inScene) + , m_nodeHandle(inScene->getHandleOfNode(inSceneNode)) +{ + m_savedTransform = inSceneNode->transform; +} - std::unique_ptr SceneNodeTransform::exec() - { - auto redoTransaction = std::make_unique(m_scene, m_sceneNode); - m_sceneNode->transform = m_savedTransform; - return redoTransaction; - } +std::unique_ptr SceneNodeTransform::exec() +{ + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto redoTransaction = std::make_unique(m_scene, sceneNode); + sceneNode->transform = m_savedTransform; + return redoTransaction; +} - bool SceneNodeTransform::merge(SnapshotBase* command) +bool SceneNodeTransform::merge(SnapshotBase* command) +{ + if (getAllowMerging() && command->getAllowMerging()) { - if (getAllowMerging() && command->getAllowMerging()) + if (const auto* castCommand = dynamic_cast(command)) { - if (const auto* castCommand = dynamic_cast(command)) + if (castCommand->m_nodeHandle == m_nodeHandle) { - if (castCommand->m_sceneNode == m_sceneNode) - { - m_savedTransform = castCommand->m_savedTransform; - return true; - } + m_savedTransform = castCommand->m_savedTransform; + return true; } } - return false; } + return false; +} - bool SceneNodeTransform::containsChanges() const - { - return - m_sceneNode->transform.position != m_savedTransform.position || - m_sceneNode->transform.rotation != m_savedTransform.rotation || - m_sceneNode->transform.scale != m_savedTransform.scale; - } +bool SceneNodeTransform::containsChanges() const +{ + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + return + sceneNode->transform.position != m_savedTransform.position || + sceneNode->transform.rotation != m_savedTransform.rotation || + sceneNode->transform.scale != m_savedTransform.scale; +} - void SceneNodeTransform::onCommitTransaction() - { - m_sceneNode->onCommitTransaction(m_scene); - } +void SceneNodeTransform::onCommitTransaction() +{ + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + sceneNode->onCommitTransaction(m_scene); +} } diff --git a/src/commands/nodeSnapshot.hpp b/src/commands/nodeSnapshot.hpp index 5091de3..e8f1591 100644 --- a/src/commands/nodeSnapshot.hpp +++ b/src/commands/nodeSnapshot.hpp @@ -1,7 +1,8 @@ #pragma once -#include "snapshotBase.hpp" #include "transform.hpp" +#include "sceneNodeHandle.hpp" +#include "snapshotBase.hpp" class SceneNode; class Scene; @@ -9,44 +10,40 @@ class Scene; namespace Snapshot { - // This command would save a snapshot of the scene node. - // This allows you to encompass arbitrary property changes. - // Be careful, as it might be excessively expensive to do this for some node types. - class SceneNodeCopy final : public SnapshotBase - { - public: - SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode); - - protected: - virtual std::unique_ptr exec() override; - virtual bool merge(SnapshotBase* command) override; - virtual bool containsChanges() const override; - virtual void onCommitTransaction() override; - - // TO-DO: switch from pointers to handles - - // this would prevent them from being invalidated, - // which would allow you to keep - Scene* m_scene = nullptr; - SceneNode* m_sceneNode = nullptr; - std::unique_ptr m_sceneNodeClone = nullptr; - }; - - // This command would only save a snapshot of the scene node transform. - class SceneNodeTransform final : public SnapshotBase - { - public: - SceneNodeTransform(Scene* inScene, SceneNode* inSceneNode); - - protected: - virtual std::unique_ptr exec() override; - virtual bool merge(SnapshotBase* command) override; - virtual bool containsChanges() const override; - virtual void onCommitTransaction() override; - - // TO-DO: switch from pointers to handles - Scene* m_scene = nullptr; - SceneNode* m_sceneNode = nullptr; - Transform m_savedTransform; - }; +// This command would save a snapshot of the scene node. +// This allows you to encompass arbitrary property changes. +// Be careful, as it might be excessively expensive to do this for some node types. +class SceneNodeCopy final : public SnapshotBase +{ +public: + SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode); + +protected: + virtual std::unique_ptr exec() override; + virtual bool merge(SnapshotBase* command) override; + virtual bool containsChanges() const override; + virtual void onCommitTransaction() override; + + Scene* m_scene = nullptr; + SceneNodeHandle m_nodeHandle; + std::unique_ptr m_sceneNodeClone = nullptr; +}; + +// This command would only save a snapshot of the scene node transform. +class SceneNodeTransform final : public SnapshotBase +{ +public: + SceneNodeTransform(Scene* inScene, SceneNode* inSceneNode); + +protected: + virtual std::unique_ptr exec() override; + virtual bool merge(SnapshotBase* command) override; + virtual bool containsChanges() const override; + virtual void onCommitTransaction() override; + + Scene* m_scene = nullptr; + SceneNodeHandle m_nodeHandle; + Transform m_savedTransform; +}; }; diff --git a/src/commands/snapshotBase.hpp b/src/commands/snapshotBase.hpp index a934df8..0e3c2c0 100644 --- a/src/commands/snapshotBase.hpp +++ b/src/commands/snapshotBase.hpp @@ -6,28 +6,28 @@ class SnapshotBase : public CommandBase { public: - void setAllowMerging(bool value) { m_allowMerging = value; } + void setAllowMerging(bool value) { m_allowMerging = value; } - [[nodiscard]] - bool getAllowMerging() const { return m_allowMerging; } + [[nodiscard]] + bool getAllowMerging() const { return m_allowMerging; } protected: - friend class CommandManager; + friend class CommandManager; - // Try to merge 2 snapshots together, this would effectively invalidate the passed-in snapshot. - // Would return true if snapshots are eligible for merge and were successfully merged together. - virtual bool merge(SnapshotBase* command) = 0; + // Try to merge 2 snapshots, this would effectively invalidate the passed-in snapshot. + // Would return true if snapshots are eligible for merge and were successfully merged. + virtual bool merge(SnapshotBase* command) = 0; - // Whether this snapshot contains relevant changes - If a new state of data is not identical to the old. - [[nodiscard]] - virtual bool containsChanges() const = 0; + // Whether this snapshot contains relevant changes - If a new state of data is not identical to the old. + [[nodiscard]] + virtual bool containsChanges() const = 0; - // Notify the underlying node that transaction has been completed - this represents a logical 'cutoff point', - // after which it would make sense to actualize the state of the nodes or perform heavy operations. - virtual void onCommitTransaction() = 0; + // Notify the underlying node that the transaction has been completed - this represents a logical 'cutoff point', + // after which it would make sense to actualize the state of the nodes or perform heavy operations. + virtual void onCommitTransaction() = 0; - // Whether this command could be merged with identical or similar snapshots - - // the exact specifics are dictated by 'merge' and 'containsChanges'. - bool m_allowMerging : 1 = true; + // Whether this command could be merged with identical or similar snapshots - + // the exact specifics are dictated by 'merge' and 'containsChanges'. + bool m_allowMerging : 1 = true; }; diff --git a/src/passes/DebugBVHPass.cpp b/src/passes/DebugBVHPass.cpp index 557c6eb..f043a61 100644 --- a/src/passes/DebugBVHPass.cpp +++ b/src/passes/DebugBVHPass.cpp @@ -246,7 +246,7 @@ void DebugBVHPass::updateVertexBuffer(Scene* scene, int maxDepth) // Also draw per-primitive BVHs if enabled if (m_showPrimitiveBVH) { - for (Primitive* prim : scene->getPrimitives()) + for (auto& [handle, prim] : scene->getPrimitives()) { addPrimitiveBVHBoxes(prim, maxDepth); if (m_vertices.size() > m_maxVertices - 24) break; diff --git a/src/passes/GBuffer.cpp b/src/passes/GBuffer.cpp index d16bf0c..2e135d4 100644 --- a/src/passes/GBuffer.cpp +++ b/src/passes/GBuffer.cpp @@ -135,13 +135,12 @@ void GBuffer::draw(const glm::mat4& view, static const UINT stride = sizeof(InterleavedData); static const UINT offset = 0; - for (int i = 0; i < scene->getPrimitiveCount(); i++) + for (auto& [handle, prim] : scene->getPrimitives()) { - auto objectID = i; - Primitive* prim = scene->getPrimitives()[i]; + auto objectID = static_cast(handle); if (!prim->material) { - std::cerr << "Primitive " << i << " has no material!" << std::endl; + std::cerr << "Primitive " << objectID << " has no material!" << std::endl; continue; } update(view, projection, cameraPosition, scene, objectID, prim); diff --git a/src/passes/ZPrePass.cpp b/src/passes/ZPrePass.cpp index b033a72..b02d43d 100644 --- a/src/passes/ZPrePass.cpp +++ b/src/passes/ZPrePass.cpp @@ -114,9 +114,8 @@ void ZPrePass::draw(const glm::mat4& view, const glm::mat4& projection, Scene* s static const UINT stride = sizeof(InterleavedData); static const UINT offset = 0; - for (int i = 0; i < scene->getPrimitiveCount(); i++) + for (auto& [handle, prim] : scene->getPrimitives()) { - Primitive* prim = scene->getPrimitives()[i]; update(view, projection, prim); // Bind albedo texture for alpha testing diff --git a/src/passes/deferedPass.cpp b/src/passes/deferedPass.cpp index 7354d9f..d818c3a 100644 --- a/src/passes/deferedPass.cpp +++ b/src/passes/deferedPass.cpp @@ -234,12 +234,14 @@ void DeferredPass::updateLights(Scene* scene) HRESULT hr = m_context->Map(m_lightsBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if (SUCCEEDED(hr)) { - LightData* lightData = static_cast(mappedResource.pData); + auto* lightData = static_cast(mappedResource.pData); const auto& lights = scene->getLights(); - for (size_t i = 0; i < lights.size(); ++i) - { - lightData[i] = lights[i]->getLightData(); - } + int32_t i = 0; + for (auto& [handle, light] : lights) + { + lightData[i] = light->getLightData(); + ++i; + } m_context->Unmap(m_lightsBuffer.Get(), 0); } } diff --git a/src/scene.cpp b/src/scene.cpp index 920d9a1..05d8a57 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -11,47 +11,101 @@ Scene::Scene(std::string name) { - this->name = name; -} - -SceneNode* Scene::getRootNode() + this->name = name; +} + +SceneNodeHandle Scene::getHandleOfNode(SceneNode *node) +{ + if (auto prim = dynamic_cast(node)) + { + const auto it = std::ranges::find_if( + m_primitives, [prim](const std::pair &x) { return x.second == prim; }); + if (it != m_primitives.end()) + { + return it->first; + } + } + if (auto light = dynamic_cast(node)) + { + const auto it = std::ranges::find_if( + m_lights, [light](const std::pair &x) { return x.second == light; }); + if (it != m_lights.end()) + { + return it->first; + } + } + if (auto camera = dynamic_cast(node)) + { + const auto it = std::ranges::find_if( + m_cameras, [camera](const std::pair &x) { return x.second == camera; }); + if (it != m_cameras.end()) + { + return it->first; + } + } + return SceneNodeHandle::invalidHandle(); +} + +SceneNode *Scene::getNodeByHandle(SceneNodeHandle handle) +{ + if (auto it = m_primitives.find(handle); it != m_primitives.end()) + { + return it->second; + } + if (auto it = m_lights.find(handle); it != m_lights.end()) + { + return it->second; + } + if (auto it = m_cameras.find(handle); it != m_cameras.end()) + { + return it->second; + } + return nullptr; +} + +SceneNode * Scene::getRootNode() { return children.front().get(); } bool Scene::isNameUsed(std::string name) { - return m_nodeNames.find(name) != m_nodeNames.end(); + return m_nodeNames.contains(name); } uint32_t& Scene::getNameCounter(std::string name) { - return m_nodeNames[name]; + return m_nodeNames[name]; } void Scene::addPrimitive(Primitive* primitive) { validateName(primitive); - m_primitives.push_back(primitive); + m_primitives.emplace(SceneNodeHandle::generateHandle(), primitive); } void Scene::addLight(Light* light) { validateName(light); - m_lights.push_back(light); + m_lights.emplace(SceneNodeHandle::generateHandle(), light); } -std::vector& Scene::getPrimitives() +std::unordered_map& Scene::getPrimitives() { return m_primitives; } Primitive* Scene::getPrimitiveByID(size_t id) { - return m_primitives[id]; + auto it = m_primitives.find(SceneNodeHandle{ static_cast(id) }); + if (it != m_primitives.end()) + { + return it->second; + } + return nullptr; } -std::vector& Scene::getLights() +std::unordered_map&Scene::getLights() { return m_lights; } @@ -103,15 +157,15 @@ SceneNode* Scene::getActiveNode() int32_t Scene::getActivePrimitiveID() { - if (auto prim = dynamic_cast(m_activeNode)) + if (auto activePrim = dynamic_cast(m_activeNode)) { - for (size_t i = 0; i < m_primitives.size(); i++) - { - if (m_primitives[i] == prim) - { - return static_cast(i); - } - } + for (auto& [handle, prim] : m_primitives) + { + if (activePrim == prim) + { + return static_cast(handle); + } + } } return -1; } @@ -144,12 +198,12 @@ void Scene::clearSelectedNodes() bool Scene::isNodeSelected(SceneNode* node) { - return m_selectedNodes.find(node) != m_selectedNodes.end(); + return m_selectedNodes.contains(node); } void Scene::addMaterial(std::shared_ptr&& material) { - m_materials[material->name] = std::move(material); + m_materials.emplace(material->name, material); } bool Scene::areLightsDirty() @@ -169,7 +223,7 @@ void Scene::setActiveCamera(Camera* camera) void Scene::deleteNode(SceneNode* node) { - std::unique_ptr ptr; + std::unique_ptr ptr; if (node->parent) { ptr = node->parent->removeChild(node); @@ -179,35 +233,24 @@ void Scene::deleteNode(SceneNode* node) m_activeNode = nullptr; } m_selectedNodes.erase(node); - if (auto prim = dynamic_cast(node)) + SceneNodeHandle nodeHandle = getHandleOfNode(node); + if (dynamic_cast(node)) { - auto it = std::find(m_primitives.begin(), m_primitives.end(), prim); - if (it != m_primitives.end()) - { - m_primitives.erase(it); - markSceneBVHDirty(); - } + m_primitives.erase(nodeHandle); + markSceneBVHDirty(); } - if (auto light = dynamic_cast(node)) + if (dynamic_cast(node)) { - auto it = std::find(m_lights.begin(), m_lights.end(), light); - if (it != m_lights.end()) - { - m_lights.erase(it); - setLightsDirty(); - } + m_lights.erase(nodeHandle); + setLightsDirty(); } if (auto camera = dynamic_cast(node)) { if (m_cameras.size() < 1) { - return; - } - auto it = std::find(m_cameras.begin(), m_cameras.end(), camera); - if (it != m_cameras.end()) - { - m_cameras.erase(it); + return; } + m_cameras.erase(nodeHandle); if (m_activeCamera == camera) { m_activeCamera = nullptr; @@ -221,53 +264,58 @@ SceneNode* Scene::duplicateNode(SceneNode* node) if (node->parent) { std::unique_ptr newNode = node->clone(); - validateName(newNode.get()); + // The pointer above would be moved, so let's copy it + nodeDuplicate = newNode.get(); + validateName(nodeDuplicate); node->parent->addChild(std::move(newNode)); - if (auto prim = dynamic_cast(node)) + SceneNodeHandle preferredHandle = SceneNodeHandle::generateHandle(); + if (auto newPrim = dynamic_cast(nodeDuplicate)) { - Primitive* newPrim = dynamic_cast(node->parent->children.back().get()); - m_primitives.push_back(newPrim); + m_primitives.emplace(preferredHandle, newPrim); markSceneBVHDirty(); - nodeDuplicate = newPrim; } - if (auto light = dynamic_cast(node)) + if (auto newLight = dynamic_cast(nodeDuplicate)) { - Light* newLight = dynamic_cast(node->parent->children.back().get()); - m_lights.push_back(newLight); + m_lights.emplace(preferredHandle, newLight); setLightsDirty(); - nodeDuplicate = newLight; } - if (auto camera = dynamic_cast(node)) + if (auto newCamera = dynamic_cast(nodeDuplicate)) { - Camera* newCamera = dynamic_cast(node->parent->children.back().get()); - m_cameras.push_back(newCamera); - nodeDuplicate = newCamera; + m_cameras.emplace(preferredHandle, newCamera); } } return nodeDuplicate; } -SceneNode* Scene::adoptClonedNode(std::unique_ptr&& clonedNode) +SceneNode* Scene::adoptClonedNode( + std::unique_ptr&& clonedNode, + SceneNodeHandle preferredHandle + ) { - // parent of a cloned node must be nullptr + if (!preferredHandle.isValid()) + { + preferredHandle = SceneNodeHandle::generateHandle(); + } + + // the parent of a cloned node must be nullptr assert(clonedNode->parent == nullptr); addChild(std::move(clonedNode)); SceneNode* nodeClone = nullptr; if (auto prim = dynamic_cast(children.back().get())) { - m_primitives.push_back(prim); + m_primitives.emplace(preferredHandle, prim); markSceneBVHDirty(); nodeClone = prim; } if (auto light = dynamic_cast(children.back().get())) { - m_lights.push_back(light); + m_lights.emplace(preferredHandle, light); setLightsDirty(); nodeClone = light; } if (auto camera = dynamic_cast(children.back().get())) { - m_cameras.push_back(camera); + m_cameras.emplace(preferredHandle, camera); nodeClone = camera; } setActiveNode(nodeClone); @@ -293,12 +341,13 @@ void Scene::buildSceneBVH() m_primBboxes.resize(primCount); m_primCenters.resize(primCount); - for (size_t i = 0; i < primCount; i++) + int32_t i = 0; + for (auto& [handle, prim] : m_primitives) { - Primitive* prim = m_primitives[i]; glm::mat4 worldMatrix = prim->getWorldMatrix(); m_primBboxes[i] = prim->getWorldBBox(worldMatrix); m_primCenters[i] = m_primBboxes[i].get_center(); + ++i; } m_sceneBVH = std::make_unique(bvh::v2::DefaultBuilder::build(*m_threadPool.get(), m_primBboxes, m_primCenters)); diff --git a/src/scene.hpp b/src/scene.hpp index c0ea789..51dc019 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -1,9 +1,13 @@ #pragma once +#include "sceneNode.hpp" +#include "sceneNodeHandle.hpp" #include #include #include -#include "sceneNode.hpp" + +#include +#include #include class Primitive; @@ -18,23 +22,31 @@ using BBox = bvh::v2::BBox; using Node = bvh::v2::Node; using Bvh = bvh::v2::Bvh; +template +using SceneUnorderedMap = std::unordered_map; + +template +using StringUnorderedMap = std::unordered_map; + class Scene : public SceneNode { public: Scene(std::string name = "Default Scene"); ~Scene() = default; + SceneNodeHandle getHandleOfNode(SceneNode* node); + SceneNode* getNodeByHandle(SceneNodeHandle handle); SceneNode* getRootNode(); bool isNameUsed(std::string name); uint32_t& getNameCounter(std::string name); void addPrimitive(Primitive* primitive); - std::vector& getPrimitives(); + SceneUnorderedMap& getPrimitives(); Primitive* getPrimitiveByID(size_t id); size_t getPrimitiveCount(); void addLight(Light* light); - std::vector& getLights(); + SceneUnorderedMap& getLights(); std::shared_ptr getTexture(std::string name); void addTexture(std::shared_ptr&& texture); @@ -57,7 +69,9 @@ class Scene : public SceneNode void deleteNode(SceneNode* node); SceneNode* duplicateNode(SceneNode* node); - SceneNode* adoptClonedNode(std::unique_ptr&& clonedNode); + SceneNode* adoptClonedNode( + std::unique_ptr&& clonedNode, + SceneNodeHandle preferredHandle = SceneNodeHandle::invalidHandle()); Camera* getActiveCamera(); void buildSceneBVH(); @@ -68,20 +82,20 @@ class Scene : public SceneNode const Bvh* getSceneBVH() const; private: SceneNode m_rootNode; - std::vector m_primitives; - std::unordered_map> m_textures; // path + actual texture - std::unordered_map> m_materials; // path + actual material - std::vector m_lights; - std::vector m_cameras; + SceneUnorderedMap m_primitives; + SceneUnorderedMap m_lights; + SceneUnorderedMap m_cameras; + StringUnorderedMap> m_textures; // path + actual texture + StringUnorderedMap> m_materials; // path + actual material Camera* m_activeCamera = nullptr; SceneNode* m_activeNode = nullptr; std::unordered_map m_selectedNodes; - bool m_lightsAreDirty; + bool m_lightsAreDirty = false; - std::unordered_map m_nodeNames; + StringUnorderedMap m_nodeNames; std::unique_ptr m_sceneBVH; std::vector m_primBboxes; // World-space bboxes for each primitive diff --git a/src/sceneNode.cpp b/src/sceneNode.cpp index 99634a1..94f8ab8 100644 --- a/src/sceneNode.cpp +++ b/src/sceneNode.cpp @@ -4,6 +4,7 @@ #include #include "scene.hpp" +std::atomic_int32_t SceneNodeHandle::s_handleGenerator = 0; SceneNode::SceneNode(SceneNode&& other) noexcept : transform(other.transform), children(std::move(other.children)), visible(other.visible), dirty(other.dirty), @@ -71,7 +72,7 @@ void SceneNode::addChild(std::unique_ptr&& child) } child->parent = this; - SceneNode* childPtr = child.get(); // Get raw pointer before moving + SceneNode* childPtr = child.get(); // Get a raw pointer before moving children.push_back(std::move(child)); if (dynamic_cast(this)) diff --git a/src/sceneNode.hpp b/src/sceneNode.hpp index bc050c5..2912212 100644 --- a/src/sceneNode.hpp +++ b/src/sceneNode.hpp @@ -1,11 +1,13 @@ #pragma once -#include "transform.hpp" -#include + #include #include -#include #include +#include + +#include "transform.hpp" + class Scene; class SceneNode diff --git a/src/sceneNodeHandle.hpp b/src/sceneNodeHandle.hpp new file mode 100644 index 0000000..aa7a593 --- /dev/null +++ b/src/sceneNodeHandle.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +class SceneNodeHandle +{ +private: + static std::atomic_int32_t s_handleGenerator; + int32_t handle = c_valueInvalid; + +public: + static constexpr int32_t c_valueInvalid = 0; + + SceneNodeHandle() = default; + explicit SceneNodeHandle(int32_t inHandle) : handle(inHandle) {} + explicit operator int32_t() const { return handle; } + + bool operator==(const SceneNodeHandle& other) const { return handle == other.handle; } + bool operator!=(const SceneNodeHandle& other) const { return !(*this == other); } + + static SceneNodeHandle generateHandle() { return SceneNodeHandle{ ++s_handleGenerator }; } + static SceneNodeHandle invalidHandle() { return SceneNodeHandle{ c_valueInvalid }; } + + bool isValid() const { return handle != c_valueInvalid; } +}; + +template<> +struct std::hash +{ + std::size_t operator()(const SceneNodeHandle& s) const noexcept + { + return static_cast(s); + } +}; From 2ab982419147dd77eec2acc73d9d45d69fa688c2 Mon Sep 17 00:00:00 2001 From: oreyg Date: Fri, 26 Dec 2025 04:10:45 +0100 Subject: [PATCH 2/7] Scene node handles draft 2 (adjust formatting) --- src/commands/commandBase.hpp | 1 - src/commands/commandManager.cpp | 6 +- src/commands/nodeCommand.cpp | 46 +++--- src/commands/nodeCommand.hpp | 33 ++--- src/commands/nodeSnapshot.cpp | 36 ++--- src/commands/nodeSnapshot.hpp | 2 +- src/commands/scopedTransaction.hpp | 6 +- src/commands/snapshotBase.hpp | 34 +++-- src/scene.cpp | 224 +++++++++++++---------------- src/scene.hpp | 25 ++-- src/sceneNode.cpp | 35 ++--- src/sceneNode.hpp | 2 +- src/sceneNodeHandle.hpp | 7 +- 13 files changed, 219 insertions(+), 238 deletions(-) diff --git a/src/commands/commandBase.hpp b/src/commands/commandBase.hpp index 1dc4815..959bdcc 100644 --- a/src/commands/commandBase.hpp +++ b/src/commands/commandBase.hpp @@ -19,5 +19,4 @@ class CommandBase // Whether this command would erase all previous history when committed bool m_breakHistory : 1 = false; - }; diff --git a/src/commands/commandManager.cpp b/src/commands/commandManager.cpp index 6272c7c..451a481 100644 --- a/src/commands/commandManager.cpp +++ b/src/commands/commandManager.cpp @@ -8,7 +8,7 @@ bool CommandManager::commitSnapshot(std::unique_ptr&& snapshotBase { if (snapshotBase->containsChanges()) { - // If the last entry in an undo buffer is a snapshot - attempt to merge + // If the last entry in an undo buffer is a snapshot - attempt to merge if (SnapshotBase* dataSnapshot = getLastUndoAsSnapshot()) { if (snapshotBase->merge(dataSnapshot)) @@ -96,12 +96,12 @@ void CommandManager::clearRedoBuffer() std::unique_ptr CommandManager::commitInternal(CommandBase* command) { - std::unique_ptr commitCommand = command->exec(); + std::unique_ptr commitCommand = command->exec(); if (auto snapshot = dynamic_cast(command)) { snapshot->onCommitTransaction(); } - return commitCommand; + return commitCommand; } SnapshotBase* CommandManager::getLastUndoAsSnapshot() diff --git a/src/commands/nodeCommand.cpp b/src/commands/nodeCommand.cpp index e28e855..c17360f 100644 --- a/src/commands/nodeCommand.cpp +++ b/src/commands/nodeCommand.cpp @@ -8,41 +8,41 @@ namespace Command { DuplicateSceneNode::DuplicateSceneNode( - Scene* inScene, - SceneNode* inSceneNode, - bool reuseNodeHandle) - : m_scene(inScene) - , m_nodeHandle(reuseNodeHandle ? inScene->getHandleOfNode(inSceneNode) : SceneNodeHandle::invalidHandle()) - , m_validateName(!reuseNodeHandle) + Scene* inScene, + SceneNode* inSceneNode, + bool reuseNodeHandle) + : m_scene(inScene) + , m_nodeHandle(reuseNodeHandle ? inScene->getHandleOfNode(inSceneNode) : SceneNodeHandle::invalidHandle()) + , m_validateName(!reuseNodeHandle) { - assert(inSceneNode); - m_sceneNodeClone = inSceneNode->clone(); + assert(inSceneNode); + m_sceneNodeClone = inSceneNode->clone(); } std::unique_ptr DuplicateSceneNode::exec() { - if (m_validateName) - { - m_scene->validateName(m_sceneNodeClone.get()); - } - SceneNode* clonedNode = m_scene->adoptClonedNode(std::move(m_sceneNodeClone), m_nodeHandle); - return std::make_unique(m_scene, clonedNode); + if (m_validateName) + { + m_scene->validateName(m_sceneNodeClone.get()); + } + SceneNode* clonedNode = m_scene->adoptClonedNode(std::move(m_sceneNodeClone), m_nodeHandle); + return std::make_unique(m_scene, clonedNode); } RemoveSceneNode::RemoveSceneNode(Scene* inScene, SceneNode* inSceneNode) - : m_scene(inScene) - , m_nodeHandle(inScene->getHandleOfNode(inSceneNode)) + : m_scene(inScene) + , m_nodeHandle(inScene->getHandleOfNode(inSceneNode)) { - assert(inSceneNode); + assert(inSceneNode); } std::unique_ptr RemoveSceneNode::exec() { - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); - auto createCommand = std::make_unique(m_scene, sceneNode, true); - m_scene->deleteNode(sceneNode); - return createCommand; + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto createCommand = std::make_unique(m_scene, sceneNode, true); + m_scene->deleteNode(sceneNode); + return createCommand; } -} \ No newline at end of file +} diff --git a/src/commands/nodeCommand.hpp b/src/commands/nodeCommand.hpp index d016297..8ed021d 100644 --- a/src/commands/nodeCommand.hpp +++ b/src/commands/nodeCommand.hpp @@ -6,37 +6,38 @@ class SceneNode; class Scene; -namespace Command { +namespace Command +{ class DuplicateSceneNode final : public CommandBase { public: - DuplicateSceneNode( - Scene* inScene, - SceneNode* inSceneNode, - bool reuseNodeHandle = false); + DuplicateSceneNode( + Scene* inScene, + SceneNode* inSceneNode, + bool reuseNodeHandle = false); protected: - virtual std::unique_ptr exec() override; + virtual std::unique_ptr exec() override; - Scene* m_scene = nullptr; - SceneNodeHandle m_nodeHandle; - std::unique_ptr m_sceneNodeClone = nullptr; - bool m_validateName = true; + Scene* m_scene = nullptr; + SceneNodeHandle m_nodeHandle; + std::unique_ptr m_sceneNodeClone = nullptr; + bool m_validateName = true; }; class RemoveSceneNode final : public CommandBase { public: - RemoveSceneNode( - Scene* inScene, - SceneNode* inSceneNode); + RemoveSceneNode( + Scene* inScene, + SceneNode* inSceneNode); protected: - virtual std::unique_ptr exec() override; + virtual std::unique_ptr exec() override; - Scene* m_scene = nullptr; - SceneNodeHandle m_nodeHandle; + Scene* m_scene = nullptr; + SceneNodeHandle m_nodeHandle; }; }; diff --git a/src/commands/nodeSnapshot.cpp b/src/commands/nodeSnapshot.cpp index bce0df9..ffd489d 100644 --- a/src/commands/nodeSnapshot.cpp +++ b/src/commands/nodeSnapshot.cpp @@ -17,11 +17,11 @@ SceneNodeCopy::SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode) std::unique_ptr SceneNodeCopy::exec() { - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); - auto redoTransaction = std::make_unique(m_scene, sceneNode); - sceneNode->copyFrom(m_sceneNodeClone.get()); - return redoTransaction; + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto redoTransaction = std::make_unique(m_scene, sceneNode); + sceneNode->copyFrom(m_sceneNodeClone.get()); + return redoTransaction; } bool SceneNodeCopy::merge(SnapshotBase* command) @@ -43,15 +43,15 @@ bool SceneNodeCopy::merge(SnapshotBase* command) bool SceneNodeCopy::containsChanges() const { - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); return m_sceneNodeClone->differsFrom(sceneNode); } void SceneNodeCopy::onCommitTransaction() { - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); sceneNode->onCommitTransaction(m_scene); } @@ -64,11 +64,11 @@ SceneNodeTransform::SceneNodeTransform(Scene* inScene, SceneNode* inSceneNode) std::unique_ptr SceneNodeTransform::exec() { - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); - auto redoTransaction = std::make_unique(m_scene, sceneNode); - sceneNode->transform = m_savedTransform; - return redoTransaction; + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto redoTransaction = std::make_unique(m_scene, sceneNode); + sceneNode->transform = m_savedTransform; + return redoTransaction; } bool SceneNodeTransform::merge(SnapshotBase* command) @@ -89,8 +89,8 @@ bool SceneNodeTransform::merge(SnapshotBase* command) bool SceneNodeTransform::containsChanges() const { - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); return sceneNode->transform.position != m_savedTransform.position || sceneNode->transform.rotation != m_savedTransform.rotation || @@ -99,8 +99,8 @@ bool SceneNodeTransform::containsChanges() const void SceneNodeTransform::onCommitTransaction() { - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); sceneNode->onCommitTransaction(m_scene); } diff --git a/src/commands/nodeSnapshot.hpp b/src/commands/nodeSnapshot.hpp index e8f1591..ffdc3fb 100644 --- a/src/commands/nodeSnapshot.hpp +++ b/src/commands/nodeSnapshot.hpp @@ -46,4 +46,4 @@ class SceneNodeTransform final : public SnapshotBase Transform m_savedTransform; }; -}; +} diff --git a/src/commands/scopedTransaction.hpp b/src/commands/scopedTransaction.hpp index da24492..10f1e0b 100644 --- a/src/commands/scopedTransaction.hpp +++ b/src/commands/scopedTransaction.hpp @@ -24,13 +24,13 @@ class ScopedTransaction CommandManager* m_commandManager; }; -template +template class TScopedTransaction : public ScopedTransaction { public: - template + template TScopedTransaction(CommandManager* commandManager, Scene* scene, TArgs&&... args) : ScopedTransaction(commandManager, std::make_unique(scene, std::forward(args)...)) { } -}; \ No newline at end of file +}; diff --git a/src/commands/snapshotBase.hpp b/src/commands/snapshotBase.hpp index 0e3c2c0..e80e232 100644 --- a/src/commands/snapshotBase.hpp +++ b/src/commands/snapshotBase.hpp @@ -5,29 +5,27 @@ class SnapshotBase : public CommandBase { public: + void setAllowMerging(bool value) { m_allowMerging = value; } - void setAllowMerging(bool value) { m_allowMerging = value; } - - [[nodiscard]] - bool getAllowMerging() const { return m_allowMerging; } + [[nodiscard]] + bool getAllowMerging() const { return m_allowMerging; } protected: - friend class CommandManager; - - // Try to merge 2 snapshots, this would effectively invalidate the passed-in snapshot. - // Would return true if snapshots are eligible for merge and were successfully merged. - virtual bool merge(SnapshotBase* command) = 0; + friend class CommandManager; - // Whether this snapshot contains relevant changes - If a new state of data is not identical to the old. - [[nodiscard]] - virtual bool containsChanges() const = 0; + // Try to merge 2 snapshots, this would effectively invalidate the passed-in snapshot. + // Would return true if snapshots are eligible for merge and were successfully merged. + virtual bool merge(SnapshotBase* command) = 0; - // Notify the underlying node that the transaction has been completed - this represents a logical 'cutoff point', - // after which it would make sense to actualize the state of the nodes or perform heavy operations. - virtual void onCommitTransaction() = 0; + // Whether this snapshot contains relevant changes - If a new state of data is not identical to the old. + [[nodiscard]] + virtual bool containsChanges() const = 0; - // Whether this command could be merged with identical or similar snapshots - - // the exact specifics are dictated by 'merge' and 'containsChanges'. - bool m_allowMerging : 1 = true; + // Notify the underlying node that the transaction has been completed - this represents a logical 'cutoff point', + // after which it would make sense to actualize the state of the nodes or perform heavy operations. + virtual void onCommitTransaction() = 0; + // Whether this command could be merged with identical or similar snapshots - + // the exact specifics are dictated by 'merge' and 'containsChanges'. + bool m_allowMerging : 1 = true; }; diff --git a/src/scene.cpp b/src/scene.cpp index 05d8a57..496b9cf 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -11,59 +11,71 @@ Scene::Scene(std::string name) { - this->name = name; -} - -SceneNodeHandle Scene::getHandleOfNode(SceneNode *node) -{ - if (auto prim = dynamic_cast(node)) - { - const auto it = std::ranges::find_if( - m_primitives, [prim](const std::pair &x) { return x.second == prim; }); - if (it != m_primitives.end()) - { - return it->first; - } - } - if (auto light = dynamic_cast(node)) - { - const auto it = std::ranges::find_if( - m_lights, [light](const std::pair &x) { return x.second == light; }); - if (it != m_lights.end()) - { - return it->first; - } - } - if (auto camera = dynamic_cast(node)) - { - const auto it = std::ranges::find_if( - m_cameras, [camera](const std::pair &x) { return x.second == camera; }); - if (it != m_cameras.end()) - { - return it->first; - } - } - return SceneNodeHandle::invalidHandle(); -} - -SceneNode *Scene::getNodeByHandle(SceneNodeHandle handle) -{ - if (auto it = m_primitives.find(handle); it != m_primitives.end()) - { - return it->second; - } - if (auto it = m_lights.find(handle); it != m_lights.end()) - { - return it->second; - } - if (auto it = m_cameras.find(handle); it != m_cameras.end()) - { - return it->second; - } - return nullptr; -} - -SceneNode * Scene::getRootNode() + this->name = name; +} + +SceneNodeHandle Scene::getHandleOfNode(SceneNode* node) +{ + if (auto prim = dynamic_cast(node)) + { + const auto it = std::ranges::find_if( + m_primitives, + [prim](const std::pair& x) + { + return x.second == prim; + }); + if (it != m_primitives.end()) + { + return it->first; + } + } + if (auto light = dynamic_cast(node)) + { + const auto it = std::ranges::find_if( + m_lights, + [light](const std::pair& x) + { + return x.second == light; + }); + if (it != m_lights.end()) + { + return it->first; + } + } + if (auto camera = dynamic_cast(node)) + { + const auto it = std::ranges::find_if( + m_cameras, + [camera](const std::pair& x) + { + return x.second == camera; + }); + if (it != m_cameras.end()) + { + return it->first; + } + } + return SceneNodeHandle::invalidHandle(); +} + +SceneNode* Scene::getNodeByHandle(SceneNodeHandle handle) +{ + if (auto it = m_primitives.find(handle); it != m_primitives.end()) + { + return it->second; + } + if (auto it = m_lights.find(handle); it != m_lights.end()) + { + return it->second; + } + if (auto it = m_cameras.find(handle); it != m_cameras.end()) + { + return it->second; + } + return nullptr; +} + +SceneNode* Scene::getRootNode() { return children.front().get(); } @@ -75,7 +87,7 @@ bool Scene::isNameUsed(std::string name) uint32_t& Scene::getNameCounter(std::string name) { - return m_nodeNames[name]; + return m_nodeNames[name]; } void Scene::addPrimitive(Primitive* primitive) @@ -90,22 +102,22 @@ void Scene::addLight(Light* light) m_lights.emplace(SceneNodeHandle::generateHandle(), light); } -std::unordered_map& Scene::getPrimitives() +SceneUnorderedMap& Scene::getPrimitives() { return m_primitives; } Primitive* Scene::getPrimitiveByID(size_t id) { - auto it = m_primitives.find(SceneNodeHandle{ static_cast(id) }); - if (it != m_primitives.end()) - { - return it->second; - } + auto it = m_primitives.find(SceneNodeHandle{static_cast(id)}); + if (it != m_primitives.end()) + { + return it->second; + } return nullptr; } -std::unordered_map&Scene::getLights() +SceneUnorderedMap& Scene::getLights() { return m_lights; } @@ -159,13 +171,13 @@ int32_t Scene::getActivePrimitiveID() { if (auto activePrim = dynamic_cast(m_activeNode)) { - for (auto& [handle, prim] : m_primitives) - { - if (activePrim == prim) - { - return static_cast(handle); - } - } + for (auto& [handle, prim] : m_primitives) + { + if (activePrim == prim) + { + return static_cast(handle); + } + } } return -1; } @@ -203,7 +215,7 @@ bool Scene::isNodeSelected(SceneNode* node) void Scene::addMaterial(std::shared_ptr&& material) { - m_materials.emplace(material->name, material); + m_materials.emplace(material->name, material); } bool Scene::areLightsDirty() @@ -233,24 +245,24 @@ void Scene::deleteNode(SceneNode* node) m_activeNode = nullptr; } m_selectedNodes.erase(node); - SceneNodeHandle nodeHandle = getHandleOfNode(node); + SceneNodeHandle nodeHandle = getHandleOfNode(node); if (dynamic_cast(node)) { - m_primitives.erase(nodeHandle); + m_primitives.erase(nodeHandle); markSceneBVHDirty(); } if (dynamic_cast(node)) { - m_lights.erase(nodeHandle); + m_lights.erase(nodeHandle); setLightsDirty(); } if (auto camera = dynamic_cast(node)) { if (m_cameras.size() < 1) { - return; + return; } - m_cameras.erase(nodeHandle); + m_cameras.erase(nodeHandle); if (m_activeCamera == camera) { m_activeCamera = nullptr; @@ -258,65 +270,33 @@ void Scene::deleteNode(SceneNode* node) } } -SceneNode* Scene::duplicateNode(SceneNode* node) +SceneNode* Scene::adoptClonedNode( + std::unique_ptr&& clonedNode, + SceneNodeHandle preferredHandle +) { - SceneNode* nodeDuplicate = nullptr; - if (node->parent) + if (!preferredHandle.isValid()) { - std::unique_ptr newNode = node->clone(); - // The pointer above would be moved, so let's copy it - nodeDuplicate = newNode.get(); - validateName(nodeDuplicate); - node->parent->addChild(std::move(newNode)); - SceneNodeHandle preferredHandle = SceneNodeHandle::generateHandle(); - if (auto newPrim = dynamic_cast(nodeDuplicate)) - { - m_primitives.emplace(preferredHandle, newPrim); - markSceneBVHDirty(); - } - if (auto newLight = dynamic_cast(nodeDuplicate)) - { - m_lights.emplace(preferredHandle, newLight); - setLightsDirty(); - } - if (auto newCamera = dynamic_cast(nodeDuplicate)) - { - m_cameras.emplace(preferredHandle, newCamera); - } + preferredHandle = SceneNodeHandle::generateHandle(); } - return nodeDuplicate; -} - -SceneNode* Scene::adoptClonedNode( - std::unique_ptr&& clonedNode, - SceneNodeHandle preferredHandle - ) -{ - if (!preferredHandle.isValid()) - { - preferredHandle = SceneNodeHandle::generateHandle(); - } // the parent of a cloned node must be nullptr assert(clonedNode->parent == nullptr); addChild(std::move(clonedNode)); - SceneNode* nodeClone = nullptr; - if (auto prim = dynamic_cast(children.back().get())) + SceneNode* nodeClone = children.back().get(); + if (auto prim = dynamic_cast(nodeClone)) { m_primitives.emplace(preferredHandle, prim); markSceneBVHDirty(); - nodeClone = prim; } - if (auto light = dynamic_cast(children.back().get())) + if (auto light = dynamic_cast(nodeClone)) { m_lights.emplace(preferredHandle, light); setLightsDirty(); - nodeClone = light; } - if (auto camera = dynamic_cast(children.back().get())) + if (auto camera = dynamic_cast(nodeClone)) { m_cameras.emplace(preferredHandle, camera); - nodeClone = camera; } setActiveNode(nodeClone); return nodeClone; @@ -341,20 +321,22 @@ void Scene::buildSceneBVH() m_primBboxes.resize(primCount); m_primCenters.resize(primCount); - int32_t i = 0; - for (auto& [handle, prim] : m_primitives) + int32_t i = 0; + for (auto& [handle, prim] : m_primitives) { glm::mat4 worldMatrix = prim->getWorldMatrix(); m_primBboxes[i] = prim->getWorldBBox(worldMatrix); m_primCenters[i] = m_primBboxes[i].get_center(); - ++i; + ++i; } - m_sceneBVH = std::make_unique(bvh::v2::DefaultBuilder::build(*m_threadPool.get(), m_primBboxes, m_primCenters)); + m_sceneBVH = std::make_unique( + bvh::v2::DefaultBuilder::build(*m_threadPool.get(), m_primBboxes, m_primCenters)); m_sceneBVHDirty = false; auto endTime = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(endTime - startTime).count(); - std::cout << "Built scene BVH with " << m_sceneBVH->nodes.size() << " nodes for " << primCount << " primitives in " << duration << " ms." << std::endl; + std::cout << "Built scene BVH with " << m_sceneBVH->nodes.size() << " nodes for " << primCount << " primitives in " + << duration << " ms." << std::endl; } void Scene::markSceneBVHDirty() diff --git a/src/scene.hpp b/src/scene.hpp index 51dc019..8fa957e 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -22,10 +22,10 @@ using BBox = bvh::v2::BBox; using Node = bvh::v2::Node; using Bvh = bvh::v2::Bvh; -template +template using SceneUnorderedMap = std::unordered_map; -template +template using StringUnorderedMap = std::unordered_map; class Scene : public SceneNode @@ -34,8 +34,8 @@ class Scene : public SceneNode Scene(std::string name = "Default Scene"); ~Scene() = default; - SceneNodeHandle getHandleOfNode(SceneNode* node); - SceneNode* getNodeByHandle(SceneNodeHandle handle); + SceneNodeHandle getHandleOfNode(SceneNode* node); + SceneNode* getNodeByHandle(SceneNodeHandle handle); SceneNode* getRootNode(); bool isNameUsed(std::string name); uint32_t& getNameCounter(std::string name); @@ -68,10 +68,9 @@ class Scene : public SceneNode void setActiveCamera(Camera* camera); void deleteNode(SceneNode* node); - SceneNode* duplicateNode(SceneNode* node); SceneNode* adoptClonedNode( - std::unique_ptr&& clonedNode, - SceneNodeHandle preferredHandle = SceneNodeHandle::invalidHandle()); + std::unique_ptr&& clonedNode, + SceneNodeHandle preferredHandle = SceneNodeHandle::invalidHandle()); Camera* getActiveCamera(); void buildSceneBVH(); @@ -80,13 +79,14 @@ class Scene : public SceneNode void rebuildSceneBVHIfDirty(); void validateName(SceneNode* node); const Bvh* getSceneBVH() const; + private: SceneNode m_rootNode; SceneUnorderedMap m_primitives; - SceneUnorderedMap m_lights; - SceneUnorderedMap m_cameras; - StringUnorderedMap> m_textures; // path + actual texture - StringUnorderedMap> m_materials; // path + actual material + SceneUnorderedMap m_lights; + SceneUnorderedMap m_cameras; + StringUnorderedMap> m_textures; // path + actual texture + StringUnorderedMap> m_materials; // path + actual material Camera* m_activeCamera = nullptr; SceneNode* m_activeNode = nullptr; @@ -102,5 +102,4 @@ class Scene : public SceneNode std::vector m_primCenters; // Centers of each primitive bbox bool m_sceneBVHDirty = true; std::unique_ptr m_threadPool; - -}; \ No newline at end of file +}; diff --git a/src/sceneNode.cpp b/src/sceneNode.cpp index 94f8ab8..acfd9bb 100644 --- a/src/sceneNode.cpp +++ b/src/sceneNode.cpp @@ -7,8 +7,11 @@ std::atomic_int32_t SceneNodeHandle::s_handleGenerator = 0; SceneNode::SceneNode(SceneNode&& other) noexcept - : transform(other.transform), children(std::move(other.children)), visible(other.visible), dirty(other.dirty), - movable(other.movable) + : transform(other.transform) + , children(std::move(other.children)) + , visible(other.visible) + , dirty(other.dirty) + , movable(other.movable) { this->parent = std::move(other.parent); other.parent = nullptr; @@ -59,6 +62,13 @@ bool SceneNode::differsFrom(const SceneNode* node) const movable != node->movable; } +std::unique_ptr SceneNode::clone() const +{ + std::unique_ptr newNode = std::make_unique(this->name); + newNode->copyFrom(this); + return newNode; +} + void SceneNode::addChild(std::unique_ptr&& child) { if (!child) @@ -72,7 +82,7 @@ void SceneNode::addChild(std::unique_ptr&& child) } child->parent = this; - SceneNode* childPtr = child.get(); // Get a raw pointer before moving + SceneNode* childPtr = child.get(); // Get a raw pointer before moving children.push_back(std::move(child)); if (dynamic_cast(this)) @@ -107,26 +117,19 @@ void SceneNode::addChild(std::unique_ptr&& child) childPtr->transform.updateMatrix(); } -std::unique_ptr SceneNode::clone() const -{ - std::unique_ptr newNode = std::make_unique(this->name); - newNode->copyFrom(this); - return newNode; -} - std::unique_ptr SceneNode::removeChild(SceneNode* child) { if (child->parent != this) return nullptr; - glm::mat4 childWorldMatrix = child->getWorldMatrix(); - child->parent = nullptr; - auto it = std::find_if(children.begin(), children.end(), - [child](const std::unique_ptr& ptr) { return ptr.get() == child; }); - + auto it = std::ranges::find_if(children, + [child](const std::unique_ptr& ptr) + { + return ptr.get() == child; + }); if (it == children.end()) { std::cerr << "Error: Child not found in parent's children list." << std::endl; @@ -149,7 +152,6 @@ std::unique_ptr SceneNode::removeChild(SceneNode* child) return removedChild; } - glm::mat4 SceneNode::getWorldMatrix() { transform.updateMatrix(); @@ -159,4 +161,3 @@ glm::mat4 SceneNode::getWorldMatrix() } return transform.matrix; }; - diff --git a/src/sceneNode.hpp b/src/sceneNode.hpp index 2912212..6c93251 100644 --- a/src/sceneNode.hpp +++ b/src/sceneNode.hpp @@ -34,4 +34,4 @@ class SceneNode void addChild(std::unique_ptr&& child); std::unique_ptr removeChild(SceneNode* child); glm::mat4 getWorldMatrix(); -}; \ No newline at end of file +}; diff --git a/src/sceneNodeHandle.hpp b/src/sceneNodeHandle.hpp index aa7a593..3c8aaaf 100644 --- a/src/sceneNodeHandle.hpp +++ b/src/sceneNodeHandle.hpp @@ -13,19 +13,20 @@ class SceneNodeHandle static constexpr int32_t c_valueInvalid = 0; SceneNodeHandle() = default; + explicit SceneNodeHandle(int32_t inHandle) : handle(inHandle) {} explicit operator int32_t() const { return handle; } bool operator==(const SceneNodeHandle& other) const { return handle == other.handle; } bool operator!=(const SceneNodeHandle& other) const { return !(*this == other); } - static SceneNodeHandle generateHandle() { return SceneNodeHandle{ ++s_handleGenerator }; } - static SceneNodeHandle invalidHandle() { return SceneNodeHandle{ c_valueInvalid }; } + static SceneNodeHandle generateHandle() { return SceneNodeHandle{++s_handleGenerator}; } + static SceneNodeHandle invalidHandle() { return SceneNodeHandle{c_valueInvalid}; } bool isValid() const { return handle != c_valueInvalid; } }; -template<> +template <> struct std::hash { std::size_t operator()(const SceneNodeHandle& s) const noexcept From ff05c2db9eb9b540b358139be8cd7900417d8934 Mon Sep 17 00:00:00 2001 From: oreyg Date: Fri, 26 Dec 2025 04:15:19 +0100 Subject: [PATCH 3/7] Add .vs and .idea folders to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index df4ed61..cbba944 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ *.d build/ .vscode/ +.vs/ +.idea/ # Large assets (use Git LFS or download separately) *.glb From c1b13f4f7569722b5a77825132b6a2f9ba0b488c Mon Sep 17 00:00:00 2001 From: oreyg Date: Fri, 26 Dec 2025 04:47:26 +0100 Subject: [PATCH 4/7] Don't copy hierarchy in SceneNode::copyFrom; Make some node constructors explicit; Obey rule of 4; Pass refs instead of pointers to indicate that values are not-null. --- src/camera.cpp | 12 +++++----- src/camera.hpp | 8 +++---- src/commands/nodeCommand.cpp | 4 ++-- src/commands/nodeSnapshot.cpp | 12 +++++----- src/light.cpp | 15 ++++++------- src/light.hpp | 6 ++--- src/primitive.cpp | 28 +++++++++--------------- src/primitive.hpp | 10 ++++----- src/scene.cpp | 6 ++--- src/scene.hpp | 25 ++++++++++----------- src/sceneNode.cpp | 41 ++++++++++++++--------------------- src/sceneNode.hpp | 9 ++++---- 12 files changed, 79 insertions(+), 97 deletions(-) diff --git a/src/camera.cpp b/src/camera.cpp index 86b36e5..fcbe94e 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -108,25 +108,25 @@ void Camera::processOrbit() transform.rotation.x = -89.0f; } -void Camera::onCommitTransaction(Scene* scene) +void Camera::onCommitTransaction(Scene& scene) { updateCameraVectors(); } -void Camera::copyFrom(const SceneNode* node) +void Camera::copyFrom(const SceneNode& node) { SceneNode::copyFrom(node); - if (const auto cameraNode = dynamic_cast(node)) + if (const auto cameraNode = dynamic_cast(&node)) { fov = cameraNode->fov; } } -bool Camera::differsFrom(const SceneNode* node) const +bool Camera::differsFrom(const SceneNode& node) const { if (!SceneNode::differsFrom(node)) { - if (const auto cameraNode = dynamic_cast(node)) + if (const auto cameraNode = dynamic_cast(&node)) { return fov != cameraNode->fov; } @@ -137,7 +137,7 @@ bool Camera::differsFrom(const SceneNode* node) const std::unique_ptr Camera::clone() const { std::unique_ptr cameraNode = std::make_unique(transform.position); - cameraNode->copyFrom(this); + cameraNode->copyFrom(*this); return cameraNode; } diff --git a/src/camera.hpp b/src/camera.hpp index d4e43d8..b8e6dbf 100644 --- a/src/camera.hpp +++ b/src/camera.hpp @@ -17,7 +17,7 @@ class Camera : public SceneNode float fov; float distanceToOrbitPivot; - Camera(glm::vec3 pos); + explicit Camera(glm::vec3 pos); void processMovementControls(); glm::mat4 getViewMatrix(); @@ -30,8 +30,8 @@ class Camera : public SceneNode // void focusOn(Primitive* primitive); - virtual void onCommitTransaction(Scene* scene) override; - virtual void copyFrom(const SceneNode* node) override; - virtual bool differsFrom(const SceneNode* node) const override; + virtual void onCommitTransaction(Scene& scene) override; + virtual void copyFrom(const SceneNode& node) override; + virtual bool differsFrom(const SceneNode& node) const override; virtual std::unique_ptr clone() const; }; \ No newline at end of file diff --git a/src/commands/nodeCommand.cpp b/src/commands/nodeCommand.cpp index c17360f..37df697 100644 --- a/src/commands/nodeCommand.cpp +++ b/src/commands/nodeCommand.cpp @@ -12,7 +12,7 @@ DuplicateSceneNode::DuplicateSceneNode( SceneNode* inSceneNode, bool reuseNodeHandle) : m_scene(inScene) - , m_nodeHandle(reuseNodeHandle ? inScene->getHandleOfNode(inSceneNode) : SceneNodeHandle::invalidHandle()) + , m_nodeHandle(reuseNodeHandle ? inScene->findHandleOfNode(inSceneNode) : SceneNodeHandle::invalidHandle()) , m_validateName(!reuseNodeHandle) { assert(inSceneNode); @@ -31,7 +31,7 @@ std::unique_ptr DuplicateSceneNode::exec() RemoveSceneNode::RemoveSceneNode(Scene* inScene, SceneNode* inSceneNode) : m_scene(inScene) - , m_nodeHandle(inScene->getHandleOfNode(inSceneNode)) + , m_nodeHandle(inScene->findHandleOfNode(inSceneNode)) { assert(inSceneNode); } diff --git a/src/commands/nodeSnapshot.cpp b/src/commands/nodeSnapshot.cpp index ffd489d..61dcb71 100644 --- a/src/commands/nodeSnapshot.cpp +++ b/src/commands/nodeSnapshot.cpp @@ -9,7 +9,7 @@ namespace Snapshot SceneNodeCopy::SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode) : m_scene(inScene) - , m_nodeHandle(inScene->getHandleOfNode(inSceneNode)) + , m_nodeHandle(inScene->findHandleOfNode(inSceneNode)) { assert(inSceneNode); m_sceneNodeClone = inSceneNode->clone(); @@ -20,7 +20,7 @@ std::unique_ptr SceneNodeCopy::exec() SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); assert(sceneNode); auto redoTransaction = std::make_unique(m_scene, sceneNode); - sceneNode->copyFrom(m_sceneNodeClone.get()); + sceneNode->copyFrom(*m_sceneNodeClone); return redoTransaction; } @@ -45,19 +45,19 @@ bool SceneNodeCopy::containsChanges() const { SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); assert(sceneNode); - return m_sceneNodeClone->differsFrom(sceneNode); + return m_sceneNodeClone->differsFrom(*sceneNode); } void SceneNodeCopy::onCommitTransaction() { SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); assert(sceneNode); - sceneNode->onCommitTransaction(m_scene); + sceneNode->onCommitTransaction(*m_scene); } SceneNodeTransform::SceneNodeTransform(Scene* inScene, SceneNode* inSceneNode) : m_scene(inScene) - , m_nodeHandle(inScene->getHandleOfNode(inSceneNode)) + , m_nodeHandle(inScene->findHandleOfNode(inSceneNode)) { m_savedTransform = inSceneNode->transform; } @@ -101,7 +101,7 @@ void SceneNodeTransform::onCommitTransaction() { SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); assert(sceneNode); - sceneNode->onCommitTransaction(m_scene); + sceneNode->onCommitTransaction(*m_scene); } } diff --git a/src/light.cpp b/src/light.cpp index 0eaa2a3..1ef1c0d 100644 --- a/src/light.cpp +++ b/src/light.cpp @@ -29,16 +29,15 @@ LightData Light::getLightData() return data; } -void Light::onCommitTransaction(Scene* scene) +void Light::onCommitTransaction(Scene& scene) { - dirty = true; - scene->setLightsDirty(true); + scene.setLightsDirty(true); } -void Light::copyFrom(const SceneNode* node) +void Light::copyFrom(const SceneNode& node) { SceneNode::copyFrom(node); - if (const auto lightNode = dynamic_cast(node)) + if (const auto lightNode = dynamic_cast(&node)) { intensity = lightNode->intensity; color = lightNode->color; @@ -50,11 +49,11 @@ void Light::copyFrom(const SceneNode* node) } } -bool Light::differsFrom(const SceneNode* node) const +bool Light::differsFrom(const SceneNode& node) const { if (!SceneNode::differsFrom(node)) { - if (const auto lightNode = dynamic_cast(node)) + if (const auto lightNode = dynamic_cast(&node)) { return intensity != lightNode->intensity || @@ -72,6 +71,6 @@ bool Light::differsFrom(const SceneNode* node) const std::unique_ptr Light::clone() const { std::unique_ptr lightNode = std::make_unique(type, transform.position, name); - lightNode->copyFrom(this); + lightNode->copyFrom(*this); return lightNode; } diff --git a/src/light.hpp b/src/light.hpp index 926611e..afd6fe8 100644 --- a/src/light.hpp +++ b/src/light.hpp @@ -38,9 +38,9 @@ class Light : public SceneNode float radius = 1.0f; LightType type = POINT_LIGHT; - virtual void onCommitTransaction(Scene* scene) override; - virtual void copyFrom(const SceneNode* node) override; - virtual bool differsFrom(const SceneNode* node) const override; + virtual void onCommitTransaction(Scene& scene) override; + virtual void copyFrom(const SceneNode& node) override; + virtual bool differsFrom(const SceneNode& node) const override; virtual std::unique_ptr clone() const override; }; \ No newline at end of file diff --git a/src/primitive.cpp b/src/primitive.cpp index ecef9eb..32e6112 100644 --- a/src/primitive.cpp +++ b/src/primitive.cpp @@ -141,32 +141,24 @@ const BBox* Primitive::getLocalBBox() const return m_sharedData->bbox.get(); } -void Primitive::copyFrom(const SceneNode* node) +void Primitive::copyFrom(const SceneNode& node) { - assert(node); + name = node.name; + transform = node.transform; + visible = node.visible; + dirty = node.dirty; + movable = node.movable; - name = node->name; - transform = node->transform; - visible = node->visible; - dirty = node->dirty; - movable = node->movable; - - if (auto primitiveNode = dynamic_cast(node)) + if (auto primitiveNode = dynamic_cast(&node)) { m_sharedData = primitiveNode->m_sharedData; material = primitiveNode->material; } - - for (const auto& child : node->children) - { - std::unique_ptr childClone = child->clone(); - addChild(std::move(childClone)); - } } -bool Primitive::differsFrom(const SceneNode* node) const +bool Primitive::differsFrom(const SceneNode& node) const { - if (const Primitive* primitiveNode = dynamic_cast(node)) + if (const Primitive* primitiveNode = dynamic_cast(&node)) { return SceneNode::differsFrom(node) || material != primitiveNode->material; } @@ -176,7 +168,7 @@ bool Primitive::differsFrom(const SceneNode* node) const std::unique_ptr Primitive::clone() const { std::unique_ptr primitive = std::make_unique(m_device); - primitive->copyFrom(this); + primitive->copyFrom(*this); return primitive; } diff --git a/src/primitive.hpp b/src/primitive.hpp index 93ea393..f339a48 100644 --- a/src/primitive.hpp +++ b/src/primitive.hpp @@ -45,11 +45,11 @@ class Primitive : public SceneNode { public: explicit Primitive(ComPtr device); - ~Primitive() = default; + ~Primitive() override = default; Primitive(const Primitive&) = delete; - Primitive& operator=(const Primitive&) = delete; Primitive(Primitive&&) = default; - Primitive& operator=(Primitive&&) = default; + Primitive& operator=(const Primitive&) = delete; + Primitive& operator=(Primitive&&) = delete; void setVertexData(std::vector&& vertexData); void setIndexData(std::vector&& indexData); @@ -62,8 +62,8 @@ class Primitive : public SceneNode const Bvh* getBVH() const; BBox getWorldBBox(glm::mat4 worldMatrix) const; const BBox* getLocalBBox() const; - virtual void copyFrom(const SceneNode* node) override; - virtual bool differsFrom(const SceneNode* node) const override; + virtual void copyFrom(const SceneNode& node) override; + virtual bool differsFrom(const SceneNode& node) const override; virtual std::unique_ptr clone() const override; void setSharedPrimitiveData(std::shared_ptr sharedData); diff --git a/src/scene.cpp b/src/scene.cpp index 496b9cf..1c35fe3 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -9,12 +9,12 @@ #include "material.hpp" #include "camera.hpp" -Scene::Scene(std::string name) +Scene::Scene(std::string_view name) { this->name = name; } -SceneNodeHandle Scene::getHandleOfNode(SceneNode* node) +SceneNodeHandle Scene::findHandleOfNode(SceneNode* node) { if (auto prim = dynamic_cast(node)) { @@ -245,7 +245,7 @@ void Scene::deleteNode(SceneNode* node) m_activeNode = nullptr; } m_selectedNodes.erase(node); - SceneNodeHandle nodeHandle = getHandleOfNode(node); + SceneNodeHandle nodeHandle = findHandleOfNode(node); if (dynamic_cast(node)) { m_primitives.erase(nodeHandle); diff --git a/src/scene.hpp b/src/scene.hpp index 8fa957e..d3c5922 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -31,10 +31,10 @@ using StringUnorderedMap = std::unordered_map; class Scene : public SceneNode { public: - Scene(std::string name = "Default Scene"); - ~Scene() = default; + explicit Scene(std::string_view name = "Default Scene"); + ~Scene() override = default; - SceneNodeHandle getHandleOfNode(SceneNode* node); + SceneNodeHandle findHandleOfNode(SceneNode* node); SceneNode* getNodeByHandle(SceneNodeHandle handle); SceneNode* getRootNode(); bool isNameUsed(std::string name); @@ -62,7 +62,6 @@ class Scene : public SceneNode void clearSelectedNodes(); bool isNodeSelected(SceneNode* node); - bool areLightsDirty(); void setLightsDirty(bool dirty = true); void setActiveCamera(Camera* camera); @@ -87,19 +86,19 @@ class Scene : public SceneNode SceneUnorderedMap m_cameras; StringUnorderedMap> m_textures; // path + actual texture StringUnorderedMap> m_materials; // path + actual material - Camera* m_activeCamera = nullptr; - - SceneNode* m_activeNode = nullptr; - - std::unordered_map m_selectedNodes; - - bool m_lightsAreDirty = false; - StringUnorderedMap m_nodeNames; std::unique_ptr m_sceneBVH; std::vector m_primBboxes; // World-space bboxes for each primitive std::vector m_primCenters; // Centers of each primitive bbox - bool m_sceneBVHDirty = true; + std::unique_ptr m_threadPool; + + std::unordered_map m_selectedNodes; + + Camera* m_activeCamera = nullptr; + SceneNode* m_activeNode = nullptr; + + bool m_lightsAreDirty = false; + bool m_sceneBVHDirty = true; }; diff --git a/src/sceneNode.cpp b/src/sceneNode.cpp index acfd9bb..00798bc 100644 --- a/src/sceneNode.cpp +++ b/src/sceneNode.cpp @@ -22,50 +22,41 @@ SceneNode::SceneNode(SceneNode&& other) noexcept other.children.clear(); } -SceneNode::SceneNode(std::string name) : parent(nullptr) +SceneNode::SceneNode(std::string name) + : parent(nullptr) { this->name = name; } SceneNode::~SceneNode() = default; -void SceneNode::onCommitTransaction(Scene* scene) +void SceneNode::onCommitTransaction(Scene& scene) { - scene->markSceneBVHDirty(); + scene.markSceneBVHDirty(); } -void SceneNode::copyFrom(const SceneNode* node) +void SceneNode::copyFrom(const SceneNode& node) { - assert(node); - - transform = node->transform; - visible = node->visible; - movable = node->movable; - name = node->name; - - children.clear(); - for (const auto& child : this->children) - { - std::unique_ptr childClone = child->clone(); - addChild(std::move(childClone)); - } + transform = node.transform; + visible = node.visible; + movable = node.movable; + name = node.name; } -bool SceneNode::differsFrom(const SceneNode* node) const +bool SceneNode::differsFrom(const SceneNode& node) const { - assert(node); return - transform.position != node->transform.position || - transform.rotation != node->transform.rotation || - transform.scale != node->transform.scale || - visible != node->visible || - movable != node->movable; + transform.position != node.transform.position || + transform.rotation != node.transform.rotation || + transform.scale != node.transform.scale || + visible != node.visible || + movable != node.movable; } std::unique_ptr SceneNode::clone() const { std::unique_ptr newNode = std::make_unique(this->name); - newNode->copyFrom(this); + newNode->copyFrom(*this); return newNode; } diff --git a/src/sceneNode.hpp b/src/sceneNode.hpp index 6c93251..938a6bb 100644 --- a/src/sceneNode.hpp +++ b/src/sceneNode.hpp @@ -23,13 +23,14 @@ class SceneNode SceneNode(std::string name = "SceneNode"); SceneNode(const SceneNode&) = delete; - SceneNode& operator=(const SceneNode&) = delete; SceneNode(SceneNode&& other) noexcept; + SceneNode& operator=(const SceneNode&) = delete; + SceneNode& operator=(SceneNode&&) = delete; virtual ~SceneNode(); - virtual void onCommitTransaction(Scene* scene); - virtual void copyFrom(const SceneNode* node); - virtual bool differsFrom(const SceneNode* node) const; + virtual void onCommitTransaction(Scene& scene); + virtual void copyFrom(const SceneNode& node); + virtual bool differsFrom(const SceneNode& node) const; virtual std::unique_ptr clone() const; void addChild(std::unique_ptr&& child); std::unique_ptr removeChild(SceneNode* child); From 509ab4790d2dc29744302e8fa52bf2ab8c5f0309 Mon Sep 17 00:00:00 2001 From: oreyg Date: Fri, 26 Dec 2025 04:59:25 +0100 Subject: [PATCH 5/7] Assert if a preferred handle is already taken --- src/scene.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/scene.cpp b/src/scene.cpp index 1c35fe3..b17f9dd 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -280,6 +280,8 @@ SceneNode* Scene::adoptClonedNode( preferredHandle = SceneNodeHandle::generateHandle(); } + // it's an error if a preferred handle is taken + assert(getNodeByHandle(preferredHandle) == nullptr); // the parent of a cloned node must be nullptr assert(clonedNode->parent == nullptr); addChild(std::move(clonedNode)); From b701fc4e6b23b53dad376a621c80d475d4a16740 Mon Sep 17 00:00:00 2001 From: oreyg Date: Fri, 26 Dec 2025 05:01:45 +0100 Subject: [PATCH 6/7] Formatting - indent in namespaces --- src/commands/nodeCommand.cpp | 64 +++++++-------- src/commands/nodeCommand.hpp | 60 +++++++------- src/commands/nodeSnapshot.cpp | 150 +++++++++++++++++----------------- src/commands/nodeSnapshot.hpp | 70 ++++++++-------- 4 files changed, 172 insertions(+), 172 deletions(-) diff --git a/src/commands/nodeCommand.cpp b/src/commands/nodeCommand.cpp index 37df697..e0031d3 100644 --- a/src/commands/nodeCommand.cpp +++ b/src/commands/nodeCommand.cpp @@ -7,42 +7,42 @@ namespace Command { -DuplicateSceneNode::DuplicateSceneNode( - Scene* inScene, - SceneNode* inSceneNode, - bool reuseNodeHandle) - : m_scene(inScene) - , m_nodeHandle(reuseNodeHandle ? inScene->findHandleOfNode(inSceneNode) : SceneNodeHandle::invalidHandle()) - , m_validateName(!reuseNodeHandle) -{ - assert(inSceneNode); - m_sceneNodeClone = inSceneNode->clone(); -} + DuplicateSceneNode::DuplicateSceneNode( + Scene* inScene, + SceneNode* inSceneNode, + bool reuseNodeHandle) + : m_scene(inScene) + , m_nodeHandle(reuseNodeHandle ? inScene->findHandleOfNode(inSceneNode) : SceneNodeHandle::invalidHandle()) + , m_validateName(!reuseNodeHandle) + { + assert(inSceneNode); + m_sceneNodeClone = inSceneNode->clone(); + } -std::unique_ptr DuplicateSceneNode::exec() -{ - if (m_validateName) + std::unique_ptr DuplicateSceneNode::exec() { - m_scene->validateName(m_sceneNodeClone.get()); + if (m_validateName) + { + m_scene->validateName(m_sceneNodeClone.get()); + } + SceneNode* clonedNode = m_scene->adoptClonedNode(std::move(m_sceneNodeClone), m_nodeHandle); + return std::make_unique(m_scene, clonedNode); } - SceneNode* clonedNode = m_scene->adoptClonedNode(std::move(m_sceneNodeClone), m_nodeHandle); - return std::make_unique(m_scene, clonedNode); -} -RemoveSceneNode::RemoveSceneNode(Scene* inScene, SceneNode* inSceneNode) - : m_scene(inScene) - , m_nodeHandle(inScene->findHandleOfNode(inSceneNode)) -{ - assert(inSceneNode); -} + RemoveSceneNode::RemoveSceneNode(Scene* inScene, SceneNode* inSceneNode) + : m_scene(inScene) + , m_nodeHandle(inScene->findHandleOfNode(inSceneNode)) + { + assert(inSceneNode); + } -std::unique_ptr RemoveSceneNode::exec() -{ - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); - auto createCommand = std::make_unique(m_scene, sceneNode, true); - m_scene->deleteNode(sceneNode); - return createCommand; -} + std::unique_ptr RemoveSceneNode::exec() + { + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto createCommand = std::make_unique(m_scene, sceneNode, true); + m_scene->deleteNode(sceneNode); + return createCommand; + } } diff --git a/src/commands/nodeCommand.hpp b/src/commands/nodeCommand.hpp index 8ed021d..5f4dc7b 100644 --- a/src/commands/nodeCommand.hpp +++ b/src/commands/nodeCommand.hpp @@ -9,35 +9,35 @@ class Scene; namespace Command { -class DuplicateSceneNode final : public CommandBase -{ -public: - DuplicateSceneNode( - Scene* inScene, - SceneNode* inSceneNode, - bool reuseNodeHandle = false); - -protected: - virtual std::unique_ptr exec() override; - - Scene* m_scene = nullptr; - SceneNodeHandle m_nodeHandle; - std::unique_ptr m_sceneNodeClone = nullptr; - bool m_validateName = true; -}; - -class RemoveSceneNode final : public CommandBase -{ -public: - RemoveSceneNode( - Scene* inScene, - SceneNode* inSceneNode); - -protected: - virtual std::unique_ptr exec() override; - - Scene* m_scene = nullptr; - SceneNodeHandle m_nodeHandle; -}; + class DuplicateSceneNode final : public CommandBase + { + public: + DuplicateSceneNode( + Scene* inScene, + SceneNode* inSceneNode, + bool reuseNodeHandle = false); + + protected: + virtual std::unique_ptr exec() override; + + Scene* m_scene = nullptr; + SceneNodeHandle m_nodeHandle; + std::unique_ptr m_sceneNodeClone = nullptr; + bool m_validateName = true; + }; + + class RemoveSceneNode final : public CommandBase + { + public: + RemoveSceneNode( + Scene* inScene, + SceneNode* inSceneNode); + + protected: + virtual std::unique_ptr exec() override; + + Scene* m_scene = nullptr; + SceneNodeHandle m_nodeHandle; + }; }; diff --git a/src/commands/nodeSnapshot.cpp b/src/commands/nodeSnapshot.cpp index 61dcb71..f7a1e03 100644 --- a/src/commands/nodeSnapshot.cpp +++ b/src/commands/nodeSnapshot.cpp @@ -7,101 +7,101 @@ namespace Snapshot { -SceneNodeCopy::SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode) - : m_scene(inScene) - , m_nodeHandle(inScene->findHandleOfNode(inSceneNode)) -{ - assert(inSceneNode); - m_sceneNodeClone = inSceneNode->clone(); -} + SceneNodeCopy::SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode) + : m_scene(inScene) + , m_nodeHandle(inScene->findHandleOfNode(inSceneNode)) + { + assert(inSceneNode); + m_sceneNodeClone = inSceneNode->clone(); + } -std::unique_ptr SceneNodeCopy::exec() -{ - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); - auto redoTransaction = std::make_unique(m_scene, sceneNode); - sceneNode->copyFrom(*m_sceneNodeClone); - return redoTransaction; -} + std::unique_ptr SceneNodeCopy::exec() + { + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto redoTransaction = std::make_unique(m_scene, sceneNode); + sceneNode->copyFrom(*m_sceneNodeClone); + return redoTransaction; + } -bool SceneNodeCopy::merge(SnapshotBase* command) -{ - if (getAllowMerging() && command->getAllowMerging()) + bool SceneNodeCopy::merge(SnapshotBase* command) { - if (auto* castCommand = dynamic_cast(command)) + if (getAllowMerging() && command->getAllowMerging()) { - if (castCommand->m_nodeHandle == m_nodeHandle) + if (auto* castCommand = dynamic_cast(command)) { - m_sceneNodeClone = std::move(castCommand->m_sceneNodeClone); - castCommand->m_sceneNodeClone = nullptr; - return true; + if (castCommand->m_nodeHandle == m_nodeHandle) + { + m_sceneNodeClone = std::move(castCommand->m_sceneNodeClone); + castCommand->m_sceneNodeClone = nullptr; + return true; + } } } + return false; } - return false; -} -bool SceneNodeCopy::containsChanges() const -{ - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); - return m_sceneNodeClone->differsFrom(*sceneNode); -} + bool SceneNodeCopy::containsChanges() const + { + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + return m_sceneNodeClone->differsFrom(*sceneNode); + } -void SceneNodeCopy::onCommitTransaction() -{ - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); - sceneNode->onCommitTransaction(*m_scene); -} + void SceneNodeCopy::onCommitTransaction() + { + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + sceneNode->onCommitTransaction(*m_scene); + } -SceneNodeTransform::SceneNodeTransform(Scene* inScene, SceneNode* inSceneNode) - : m_scene(inScene) - , m_nodeHandle(inScene->findHandleOfNode(inSceneNode)) -{ - m_savedTransform = inSceneNode->transform; -} + SceneNodeTransform::SceneNodeTransform(Scene* inScene, SceneNode* inSceneNode) + : m_scene(inScene) + , m_nodeHandle(inScene->findHandleOfNode(inSceneNode)) + { + m_savedTransform = inSceneNode->transform; + } -std::unique_ptr SceneNodeTransform::exec() -{ - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); - auto redoTransaction = std::make_unique(m_scene, sceneNode); - sceneNode->transform = m_savedTransform; - return redoTransaction; -} + std::unique_ptr SceneNodeTransform::exec() + { + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto redoTransaction = std::make_unique(m_scene, sceneNode); + sceneNode->transform = m_savedTransform; + return redoTransaction; + } -bool SceneNodeTransform::merge(SnapshotBase* command) -{ - if (getAllowMerging() && command->getAllowMerging()) + bool SceneNodeTransform::merge(SnapshotBase* command) { - if (const auto* castCommand = dynamic_cast(command)) + if (getAllowMerging() && command->getAllowMerging()) { - if (castCommand->m_nodeHandle == m_nodeHandle) + if (const auto* castCommand = dynamic_cast(command)) { - m_savedTransform = castCommand->m_savedTransform; - return true; + if (castCommand->m_nodeHandle == m_nodeHandle) + { + m_savedTransform = castCommand->m_savedTransform; + return true; + } } } + return false; } - return false; -} -bool SceneNodeTransform::containsChanges() const -{ - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); - return - sceneNode->transform.position != m_savedTransform.position || - sceneNode->transform.rotation != m_savedTransform.rotation || - sceneNode->transform.scale != m_savedTransform.scale; -} + bool SceneNodeTransform::containsChanges() const + { + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + return + sceneNode->transform.position != m_savedTransform.position || + sceneNode->transform.rotation != m_savedTransform.rotation || + sceneNode->transform.scale != m_savedTransform.scale; + } -void SceneNodeTransform::onCommitTransaction() -{ - SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); - assert(sceneNode); - sceneNode->onCommitTransaction(*m_scene); -} + void SceneNodeTransform::onCommitTransaction() + { + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + sceneNode->onCommitTransaction(*m_scene); + } } diff --git a/src/commands/nodeSnapshot.hpp b/src/commands/nodeSnapshot.hpp index ffdc3fb..2087a6f 100644 --- a/src/commands/nodeSnapshot.hpp +++ b/src/commands/nodeSnapshot.hpp @@ -10,40 +10,40 @@ class Scene; namespace Snapshot { -// This command would save a snapshot of the scene node. -// This allows you to encompass arbitrary property changes. -// Be careful, as it might be excessively expensive to do this for some node types. -class SceneNodeCopy final : public SnapshotBase -{ -public: - SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode); - -protected: - virtual std::unique_ptr exec() override; - virtual bool merge(SnapshotBase* command) override; - virtual bool containsChanges() const override; - virtual void onCommitTransaction() override; - - Scene* m_scene = nullptr; - SceneNodeHandle m_nodeHandle; - std::unique_ptr m_sceneNodeClone = nullptr; -}; - -// This command would only save a snapshot of the scene node transform. -class SceneNodeTransform final : public SnapshotBase -{ -public: - SceneNodeTransform(Scene* inScene, SceneNode* inSceneNode); - -protected: - virtual std::unique_ptr exec() override; - virtual bool merge(SnapshotBase* command) override; - virtual bool containsChanges() const override; - virtual void onCommitTransaction() override; - - Scene* m_scene = nullptr; - SceneNodeHandle m_nodeHandle; - Transform m_savedTransform; -}; + // This command would save a snapshot of the scene node. + // This allows you to encompass arbitrary property changes. + // Be careful, as it might be excessively expensive to do this for some node types. + class SceneNodeCopy final : public SnapshotBase + { + public: + SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode); + + protected: + virtual std::unique_ptr exec() override; + virtual bool merge(SnapshotBase* command) override; + virtual bool containsChanges() const override; + virtual void onCommitTransaction() override; + + Scene* m_scene = nullptr; + SceneNodeHandle m_nodeHandle; + std::unique_ptr m_sceneNodeClone = nullptr; + }; + + // This command would only save a snapshot of the scene node transform. + class SceneNodeTransform final : public SnapshotBase + { + public: + SceneNodeTransform(Scene* inScene, SceneNode* inSceneNode); + + protected: + virtual std::unique_ptr exec() override; + virtual bool merge(SnapshotBase* command) override; + virtual bool containsChanges() const override; + virtual void onCommitTransaction() override; + + Scene* m_scene = nullptr; + SceneNodeHandle m_nodeHandle; + Transform m_savedTransform; + }; } From 798d55060ec4e067a56a9c8b5567b368df92783c Mon Sep 17 00:00:00 2001 From: oreyg Date: Fri, 26 Dec 2025 05:28:49 +0100 Subject: [PATCH 7/7] Add camera to the map of cameras --- src/renderer.cpp | 1 + src/scene.cpp | 6 ++++++ src/scene.hpp | 2 ++ 3 files changed, 9 insertions(+) diff --git a/src/renderer.cpp b/src/renderer.cpp index 223e12d..e43dc88 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -39,6 +39,7 @@ Renderer::Renderer(const HWND& hwnd) std::unique_ptr pointLight = std::make_unique(POINT_LIGHT, glm::vec3(0.0f, 1.0f, 1.0f)); m_scene->addLight(pointLight.get()); m_scene->addChild(std::move(pointLight)); + m_scene->addCamera(m_camera.get()); m_scene->setActiveCamera(m_camera.get()); m_scene->addChild(std::move(m_camera)); // m_scene->buildSceneBVH(); diff --git a/src/scene.cpp b/src/scene.cpp index b17f9dd..619f03a 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -122,6 +122,12 @@ SceneUnorderedMap& Scene::getLights() return m_lights; } +void Scene::addCamera(Camera* camera) +{ + validateName(camera); + m_cameras.emplace(SceneNodeHandle::generateHandle(), camera); +} + size_t Scene::getPrimitiveCount() { return m_primitives.size(); diff --git a/src/scene.hpp b/src/scene.hpp index d3c5922..d426dee 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -48,6 +48,8 @@ class Scene : public SceneNode void addLight(Light* light); SceneUnorderedMap& getLights(); + void addCamera(Camera* camera); + std::shared_ptr getTexture(std::string name); void addTexture(std::shared_ptr&& texture);