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 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/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/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 b2dfc93..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 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..e0031d3 100644 --- a/src/commands/nodeCommand.cpp +++ b/src/commands/nodeCommand.cpp @@ -7,14 +7,16 @@ namespace Command { - DuplicateSceneNode::DuplicateSceneNode(Scene* inScene, SceneNode* inSceneNode, bool inValidateName) + DuplicateSceneNode::DuplicateSceneNode( + Scene* inScene, + SceneNode* inSceneNode, + bool reuseNodeHandle) : m_scene(inScene) - , m_sceneNode(inSceneNode) - , m_validateName(inValidateName) + , m_nodeHandle(reuseNodeHandle ? inScene->findHandleOfNode(inSceneNode) : SceneNodeHandle::invalidHandle()) + , m_validateName(!reuseNodeHandle) { assert(inSceneNode); - m_breakHistory = true; - m_sceneNodeClone = m_sceneNode->clone(); + m_sceneNodeClone = inSceneNode->clone(); } std::unique_ptr DuplicateSceneNode::exec() @@ -23,25 +25,24 @@ namespace Command { m_scene->validateName(m_sceneNodeClone.get()); } - SceneNode* clonedNode = m_scene->adoptClonedNode(std::move(m_sceneNodeClone)); + 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_sceneNode(inSceneNode) + , m_nodeHandle(inScene->findHandleOfNode(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); + 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..5f4dc7b 100644 --- a/src/commands/nodeCommand.hpp +++ b/src/commands/nodeCommand.hpp @@ -1,11 +1,13 @@ #pragma once +#include "sceneNodeHandle.hpp" #include "commandBase.hpp" class SceneNode; class Scene; -namespace Command { +namespace Command +{ class DuplicateSceneNode final : public CommandBase { @@ -13,13 +15,13 @@ namespace Command { DuplicateSceneNode( Scene* inScene, SceneNode* inSceneNode, - bool inValidateName = true); + bool reuseNodeHandle = false); protected: virtual std::unique_ptr exec() override; Scene* m_scene = nullptr; - SceneNode* m_sceneNode = nullptr; + SceneNodeHandle m_nodeHandle; std::unique_ptr m_sceneNodeClone = nullptr; bool m_validateName = true; }; @@ -34,9 +36,8 @@ namespace Command { protected: virtual std::unique_ptr exec() override; - // TO-DO: switch from pointers to handles Scene* m_scene = nullptr; - SceneNode* m_sceneNode = nullptr; + SceneNodeHandle m_nodeHandle; }; }; diff --git a/src/commands/nodeSnapshot.cpp b/src/commands/nodeSnapshot.cpp index a6de985..f7a1e03 100644 --- a/src/commands/nodeSnapshot.cpp +++ b/src/commands/nodeSnapshot.cpp @@ -1,6 +1,7 @@ #include "nodeSnapshot.hpp" +#include "scene.hpp" #include "sceneNode.hpp" namespace Snapshot @@ -8,16 +9,18 @@ namespace Snapshot SceneNodeCopy::SceneNodeCopy(Scene* inScene, SceneNode* inSceneNode) : m_scene(inScene) - , m_sceneNode(inSceneNode) + , m_nodeHandle(inScene->findHandleOfNode(inSceneNode)) { assert(inSceneNode); - m_sceneNodeClone = m_sceneNode->clone(); + m_sceneNodeClone = inSceneNode->clone(); } std::unique_ptr SceneNodeCopy::exec() { - auto redoTransaction = std::make_unique(m_scene, m_sceneNode); - m_sceneNode->copyFrom(m_sceneNodeClone.get()); + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto redoTransaction = std::make_unique(m_scene, sceneNode); + sceneNode->copyFrom(*m_sceneNodeClone); return redoTransaction; } @@ -27,7 +30,7 @@ namespace Snapshot { if (auto* castCommand = dynamic_cast(command)) { - if (castCommand->m_sceneNode == m_sceneNode) + if (castCommand->m_nodeHandle == m_nodeHandle) { m_sceneNodeClone = std::move(castCommand->m_sceneNodeClone); castCommand->m_sceneNodeClone = nullptr; @@ -40,25 +43,31 @@ namespace Snapshot bool SceneNodeCopy::containsChanges() const { - return m_sceneNodeClone->differsFrom(m_sceneNode); + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + return m_sceneNodeClone->differsFrom(*sceneNode); } void SceneNodeCopy::onCommitTransaction() { - m_sceneNode->onCommitTransaction(m_scene); + 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_nodeHandle(inScene->findHandleOfNode(inSceneNode)) { - m_savedTransform = m_sceneNode->transform; + m_savedTransform = inSceneNode->transform; } std::unique_ptr SceneNodeTransform::exec() { - auto redoTransaction = std::make_unique(m_scene, m_sceneNode); - m_sceneNode->transform = m_savedTransform; + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); + auto redoTransaction = std::make_unique(m_scene, sceneNode); + sceneNode->transform = m_savedTransform; return redoTransaction; } @@ -68,7 +77,7 @@ namespace Snapshot { if (const auto* castCommand = dynamic_cast(command)) { - if (castCommand->m_sceneNode == m_sceneNode) + if (castCommand->m_nodeHandle == m_nodeHandle) { m_savedTransform = castCommand->m_savedTransform; return true; @@ -80,15 +89,19 @@ namespace Snapshot bool SceneNodeTransform::containsChanges() const { + SceneNode* sceneNode = m_scene->getNodeByHandle(m_nodeHandle); + assert(sceneNode); return - m_sceneNode->transform.position != m_savedTransform.position || - m_sceneNode->transform.rotation != m_savedTransform.rotation || - m_sceneNode->transform.scale != m_savedTransform.scale; + 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); + 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..2087a6f 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/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 a934df8..e80e232 100644 --- a/src/commands/snapshotBase.hpp +++ b/src/commands/snapshotBase.hpp @@ -5,7 +5,6 @@ class SnapshotBase : public CommandBase { public: - void setAllowMerging(bool value) { m_allowMerging = value; } [[nodiscard]] @@ -14,20 +13,19 @@ class SnapshotBase : public CommandBase protected: 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. + // 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; - // Notify the underlying node that transaction has been completed - this represents a logical 'cutoff point', + // 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/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/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/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/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 920d9a1..619f03a 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -9,11 +9,72 @@ #include "material.hpp" #include "camera.hpp" -Scene::Scene(std::string name) +Scene::Scene(std::string_view name) { this->name = name; } +SceneNodeHandle Scene::findHandleOfNode(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(); @@ -21,7 +82,7 @@ SceneNode* Scene::getRootNode() 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) @@ -32,30 +93,41 @@ uint32_t& Scene::getNameCounter(std::string 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() +SceneUnorderedMap& 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() +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(); @@ -103,13 +175,13 @@ 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++) + for (auto& [handle, prim] : m_primitives) { - if (m_primitives[i] == prim) + if (activePrim == prim) { - return static_cast(i); + return static_cast(handle); } } } @@ -144,12 +216,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 +241,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,23 +251,16 @@ void Scene::deleteNode(SceneNode* node) m_activeNode = nullptr; } m_selectedNodes.erase(node); - if (auto prim = dynamic_cast(node)) + SceneNodeHandle nodeHandle = findHandleOfNode(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)) { @@ -203,11 +268,7 @@ void Scene::deleteNode(SceneNode* node) { return; } - auto it = std::find(m_cameras.begin(), m_cameras.end(), camera); - if (it != m_cameras.end()) - { - m_cameras.erase(it); - } + m_cameras.erase(nodeHandle); if (m_activeCamera == camera) { m_activeCamera = nullptr; @@ -215,60 +276,35 @@ 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(); - validateName(newNode.get()); - node->parent->addChild(std::move(newNode)); - if (auto prim = dynamic_cast(node)) - { - Primitive* newPrim = dynamic_cast(node->parent->children.back().get()); - m_primitives.push_back(newPrim); - markSceneBVHDirty(); - nodeDuplicate = newPrim; - } - if (auto light = dynamic_cast(node)) - { - Light* newLight = dynamic_cast(node->parent->children.back().get()); - m_lights.push_back(newLight); - setLightsDirty(); - nodeDuplicate = newLight; - } - if (auto camera = dynamic_cast(node)) - { - Camera* newCamera = dynamic_cast(node->parent->children.back().get()); - m_cameras.push_back(newCamera); - nodeDuplicate = newCamera; - } + preferredHandle = SceneNodeHandle::generateHandle(); } - return nodeDuplicate; -} -SceneNode* Scene::adoptClonedNode(std::unique_ptr&& clonedNode) -{ - // parent of a cloned node must be nullptr + // 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)); - SceneNode* nodeClone = nullptr; - if (auto prim = dynamic_cast(children.back().get())) + SceneNode* nodeClone = children.back().get(); + if (auto prim = dynamic_cast(nodeClone)) { - m_primitives.push_back(prim); + m_primitives.emplace(preferredHandle, prim); markSceneBVHDirty(); - nodeClone = prim; } - if (auto light = dynamic_cast(children.back().get())) + if (auto light = dynamic_cast(nodeClone)) { - m_lights.push_back(light); + m_lights.emplace(preferredHandle, light); setLightsDirty(); - nodeClone = light; } - if (auto camera = dynamic_cast(children.back().get())) + if (auto camera = dynamic_cast(nodeClone)) { - m_cameras.push_back(camera); - nodeClone = camera; + m_cameras.emplace(preferredHandle, camera); } setActiveNode(nodeClone); return nodeClone; @@ -293,19 +329,22 @@ 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)); + 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 c0ea789..d426dee 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,33 @@ 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; + explicit Scene(std::string_view name = "Default Scene"); + ~Scene() override = default; + SceneNodeHandle findHandleOfNode(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(); + + void addCamera(Camera* camera); std::shared_ptr getTexture(std::string name); void addTexture(std::shared_ptr&& texture); @@ -50,14 +64,14 @@ class Scene : public SceneNode void clearSelectedNodes(); bool isNodeSelected(SceneNode* node); - bool areLightsDirty(); void setLightsDirty(bool dirty = true); void setActiveCamera(Camera* camera); 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(); @@ -66,27 +80,27 @@ class Scene : public SceneNode void rebuildSceneBVHIfDirty(); void validateName(SceneNode* node); 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; - Camera* m_activeCamera = nullptr; - - SceneNode* m_activeNode = nullptr; - - std::unordered_map m_selectedNodes; - - bool m_lightsAreDirty; - - std::unordered_map m_nodeNames; + SceneUnorderedMap m_primitives; + SceneUnorderedMap m_lights; + SceneUnorderedMap m_cameras; + StringUnorderedMap> m_textures; // path + actual texture + StringUnorderedMap> m_materials; // path + actual material + 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; -}; \ No newline at end of file + 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 99634a1..00798bc 100644 --- a/src/sceneNode.cpp +++ b/src/sceneNode.cpp @@ -4,10 +4,14 @@ #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), - 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; @@ -18,44 +22,42 @@ 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); + return newNode; } void SceneNode::addChild(std::unique_ptr&& child) @@ -71,7 +73,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)) @@ -106,26 +108,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; @@ -148,7 +143,6 @@ std::unique_ptr SceneNode::removeChild(SceneNode* child) return removedChild; } - glm::mat4 SceneNode::getWorldMatrix() { transform.updateMatrix(); @@ -158,4 +152,3 @@ glm::mat4 SceneNode::getWorldMatrix() } return transform.matrix; }; - diff --git a/src/sceneNode.hpp b/src/sceneNode.hpp index bc050c5..938a6bb 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 @@ -21,15 +23,16 @@ 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); glm::mat4 getWorldMatrix(); -}; \ No newline at end of file +}; diff --git a/src/sceneNodeHandle.hpp b/src/sceneNodeHandle.hpp new file mode 100644 index 0000000..3c8aaaf --- /dev/null +++ b/src/sceneNodeHandle.hpp @@ -0,0 +1,36 @@ +#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); + } +};