Added a scene graph

This commit is contained in:
Diren D Bharwani 2022-09-11 22:47:30 +08:00
parent 759e414657
commit d4944ef920
6 changed files with 625 additions and 2 deletions

View File

@ -183,6 +183,7 @@
<ClInclude Include="src\Resource\Handle.h" />
<ClInclude Include="src\Resource\ResourceLibrary.h" />
<ClInclude Include="src\Resource\SparseSet.h" />
<ClInclude Include="src\Scene\SHSceneGraph.h" />
<ClInclude Include="src\SHpch.h" />
<ClInclude Include="src\Scene\SHScene.h" />
<ClInclude Include="src\Scene\SHSceneManager.h" />
@ -253,6 +254,7 @@
<ClCompile Include="src\Math\Vector\SHVec2.cpp" />
<ClCompile Include="src\Math\Vector\SHVec3.cpp" />
<ClCompile Include="src\Math\Vector\SHVec4.cpp" />
<ClCompile Include="src\Scene\SHSceneGraph.cpp" />
<ClCompile Include="src\SHpch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>

View File

@ -353,6 +353,7 @@
<ClInclude Include="src\Math\Vector\SHVec2.h" />
<ClInclude Include="src\Math\Vector\SHVec3.h" />
<ClInclude Include="src\Math\Vector\SHVec4.h" />
<ClInclude Include="src\Scene\SHSceneGraph.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Engine\ECS_Base\Components\SHComponent.cpp">
@ -536,5 +537,6 @@
<ClCompile Include="src\Math\Vector\SHVec2.cpp" />
<ClCompile Include="src\Math\Vector\SHVec3.cpp" />
<ClCompile Include="src\Math\Vector\SHVec4.cpp" />
<ClCompile Include="src\Scene\SHSceneGraph.cpp" />
</ItemGroup>
</Project>

View File

@ -34,7 +34,7 @@ namespace SHADE
//The Container of all Componentgroups
static std::vector<SHComponentGroup> componentGroups;
friend class SHSceneNode;
friend struct SHSceneNode;

View File

@ -22,6 +22,7 @@
#include <string>
#include <algorithm>
#include <array>
#include <ranges>
#include <utility>
#include <unordered_map>
#include <map>

View File

@ -0,0 +1,475 @@
/****************************************************************************************
* \file SHSceneGraph.cpp
* \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Implementation for a Scene Graph & Scene Nodes.
*
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
* disclosure of this file or its contents without the prior written consent
* of DigiPen Institute of Technology is prohibited.
****************************************************************************************/
#include <SHpch.h>
// Primary Header
#include "SHSceneGraph.h"
// Project Headers
#include "Engine/ECS_Base/System/SHEntityManager.h"
#include "Tools/SHLogger.h"
#include "Tools/SHException.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Constructors & Destructor Definitions */
/*-----------------------------------------------------------------------------------*/
SHSceneNode::SHSceneNode(EntityID eid, SHSceneNode* parent) noexcept
: isActive { true }
, entityID { eid }
, parent { parent }
{}
SHSceneNode::SHSceneNode(const SHSceneNode& rhs) noexcept
: isActive { rhs.isActive }
, entityID { rhs.entityID }
, parent { rhs.parent }
{
std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children));
}
SHSceneNode::SHSceneNode(SHSceneNode&& rhs) noexcept
: isActive { rhs.isActive }
, entityID { rhs.entityID }
, parent { rhs.parent }
{
std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children));
}
SHSceneNode& SHSceneNode::operator=(const SHSceneNode& rhs) noexcept
{
if (this == &rhs)
return *this;
isActive = rhs.isActive;
entityID = rhs.entityID;
parent = rhs.parent;
children.clear();
std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children));
return *this;
}
SHSceneNode& SHSceneNode::operator=(SHSceneNode&& rhs) noexcept
{
isActive = rhs.isActive;
entityID = rhs.entityID;
parent = rhs.parent;
children.clear();
std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children));
return *this;
}
SHSceneGraph::SHSceneGraph() noexcept
: root { nullptr }
{}
SHSceneGraph::~SHSceneGraph() noexcept
{
SHASSERT(root != nullptr, "Unable to destroy a Scene without a root node!")
#ifdef _DEBUG
SHLOG_INFO("Destroying Scene Graph...")
#endif
// Go through the map and release all the nodes
for (auto* node : entityNodeMap | std::views::values)
ReleaseNode(node);
#ifdef _DEBUG
SHLOG_INFO("Scene Graph Destroyed Successfully!")
#endif
}
/*-----------------------------------------------------------------------------------*/
/* Getter Function Definitions */
/*-----------------------------------------------------------------------------------*/
SHSceneNode* SHSceneNode::GetChild(EntityID childID) const noexcept
{
// Error handling
{
if (!SHEntityManager::IsValidEID(childID))
{
SHLOG_ERROR("Child Entity {} is invalid! Unable to get child from Entity {}", childID, entityID)
return nullptr;
}
if (children.empty())
{
SHLOG_WARNING("Entity {} has no children!", entityID)
return nullptr;
}
}
// Find child
const auto ENTITY_MATCH = [&](const SHSceneNode* node) { return node->GetEntityID() == childID; };
const auto CHILD_ITER = std::ranges::find_if(children.begin(), children.end(),ENTITY_MATCH);
if (CHILD_ITER == children.end())
{
SHLOG_WARNING("Entity {} is not a child of Entity {}! Unable to retrieve child node!", childID, entityID)
return nullptr;
}
return *CHILD_ITER;
}
SHSceneNode* SHSceneGraph::GetRoot() const noexcept
{
if (root != nullptr)
return root;
SHLOG_WARNING("Scene has no root object!")
return nullptr;
}
SHSceneNode* SHSceneGraph::GetNode(EntityID entityID) const noexcept
{
if (!SHEntityManager::IsValidEID(entityID))
{
SHLOG_ERROR("Entity {} is invalid! Unable to Get Scene node!", entityID)
return nullptr;
}
const auto NODE_ITER = entityNodeMap.find(entityID);
if (NODE_ITER == entityNodeMap.end())
{
SHLOG_WARNING("Entity {} cannot be found in the scene! Unable to Get Scene node!", entityID)
return nullptr;
}
return NODE_ITER->second;
}
SHSceneNode* SHSceneGraph::GetParent(EntityID entityID) const noexcept
{
if (!SHEntityManager::IsValidEID(entityID))
{
SHLOG_ERROR("Entity {} is invalid! Unable to get Parent node!", entityID)
return nullptr;
}
const auto NODE_ITER = entityNodeMap.find(entityID);
if (NODE_ITER == entityNodeMap.end())
{
SHLOG_WARNING("Entity {} cannot be found in the scene! Unable to get Parent node!", entityID)
return nullptr;
}
return NODE_ITER->second->GetParent();
}
SHSceneNode* SHSceneGraph::GetChild(EntityID entityID, SHSceneNode* childNode) const noexcept
{
// Error Handling
if (!SHEntityManager::IsValidEID(entityID))
{
SHLOG_ERROR("Entity {} is invalid!", entityID)
return nullptr;
}
const auto NODE_ITER = entityNodeMap.find(entityID);
if (NODE_ITER == entityNodeMap.end())
{
SHLOG_WARNING("Entity {} cannot be found in the scene!", entityID)
return nullptr;
}
const auto& children = NODE_ITER->second->GetChildren();
if (children.empty())
{
SHLOG_WARNING("Entity {} has no children!", entityID)
return nullptr;
}
const auto CHILD_ITER = std::ranges::find(children.begin(), children.end(), childNode);
if (CHILD_ITER == children.end())
{
SHLOG_WARNING("Entity {} is not a child of Entity {}!", childNode->GetEntityID(), entityID)
return nullptr;
}
return *CHILD_ITER;
}
SHSceneNode* SHSceneGraph::GetChild(EntityID entityID, EntityID childEntityID) const noexcept
{
if (!SHEntityManager::IsValidEID(entityID))
{
SHLOG_ERROR("Entity {} is invalid!", entityID)
return nullptr;
}
const auto NODE_ITER = entityNodeMap.find(entityID);
if (NODE_ITER == entityNodeMap.end())
{
SHLOG_WARNING("Entity {} cannot be found in the scene!", entityID)
return nullptr;
}
return NODE_ITER->second->GetChild(childEntityID);
}
const std::vector<SHSceneNode*>& SHSceneGraph::GetChildren(EntityID entityID) const noexcept
{
// TODO(Diren): Discuss with team best way to handle this
if (!SHEntityManager::IsValidEID(entityID))
{
SHLOG_ERROR("Entity {} is invalid!", entityID)
return root->GetChildren();
}
const auto NODE_ITER = entityNodeMap.find(entityID);
if (NODE_ITER == entityNodeMap.end())
{
SHLOG_WARNING("Entity {} cannot be found in the scene!", entityID)
return root->GetChildren();
}
return NODE_ITER->second->GetChildren();
}
/*-----------------------------------------------------------------------------------*/
/* Setter Function Definitions */
/*-----------------------------------------------------------------------------------*/
void SHSceneNode::SetParent(SHSceneNode* parentNode) noexcept
{
if (parentNode == nullptr)
SHLOG_WARNING("Removing Entity {}'s parent", entityID)
if (parentNode == parent)
return;
parent = parentNode;
// Update parent's children
parent->AddChild(this);
}
void SHSceneGraph::SetParent(EntityID entityID, SHSceneNode* parent) const noexcept
{
if (!SHEntityManager::IsValidEID(entityID))
{
SHLOG_ERROR("Entity {} is invalid!", entityID)
return;
}
const auto NODE_ITER = entityNodeMap.find(entityID);
if (NODE_ITER == entityNodeMap.end())
{
SHLOG_WARNING("Entity {} cannot be found in the scene!", entityID)
return;
}
NODE_ITER->second->SetParent(parent);
}
void SHSceneGraph::SetParent(EntityID entityID, EntityID parent) const noexcept
{
if (!SHEntityManager::IsValidEID(entityID))
{
SHLOG_ERROR("Entity {} is invalid! Unable to set parent of an invalid entity!", entityID)
return;
}
if (!SHEntityManager::IsValidEID(parent))
{
SHLOG_ERROR("Parent Entity {} is invalid! Unable to set Entity {}'s parent!", parent, entityID)
return;
}
auto NODE_ITER = entityNodeMap.find(entityID);
if (NODE_ITER == entityNodeMap.end())
{
SHLOG_WARNING("Entity {} cannot be found in the scene! Unable to set parent!", entityID)
return;
}
auto PARENT_ITER = entityNodeMap.find(entityID);
if (PARENT_ITER == entityNodeMap.end())
{
SHLOG_WARNING("Entity {} cannot be found in the scene! Unable to parent to Entity {}", parent, entityID)
return;
}
SHSceneNode* currentNode = NODE_ITER->second;
currentNode->SetParent(PARENT_ITER->second);
}
/*-----------------------------------------------------------------------------------*/
/* Public Function Member Definitions */
/*-----------------------------------------------------------------------------------*/
void SHSceneNode::AddChild(SHSceneNode* newChild) noexcept
{
if (newChild == nullptr)
{
SHLOG_WARNING("Attempting to add a non-existent child to an entity!")
return;
}
children.emplace_back(newChild);
}
bool SHSceneNode::RemoveChild(EntityID childID) noexcept
{
if (!SHEntityManager::IsValidEID(childID))
{
SHLOG_ERROR("Entity {} is invalid!", childID)
return false;
}
SHSceneNode* removedChild = nullptr;
const auto ENTITY_MATCH = [&](SHSceneNode* node)
{
if (node->GetEntityID() == childID)
{
removedChild = node;
return true;
}
return false;
};
children.end() = std::remove_if(children.begin(), children.end(), ENTITY_MATCH);
removedChild->parent = nullptr;
return removedChild == nullptr;
}
bool SHSceneNode::RemoveChild(SHSceneNode* childToRemove) noexcept
{
if (childToRemove == nullptr)
{
SHLOG_WARNING("Attempting to remove non-existent child from Entity {}", entityID)
return false;
}
children.end() = std::remove(children.begin(), children.end(), childToRemove);
childToRemove->parent = nullptr;
return true;
}
void SHSceneNode::RemoveAllChildren() noexcept
{
for (const auto child : children)
child->parent = nullptr;
children.clear();
}
SHSceneNode* SHSceneGraph::AddNode(EntityID entityID, SHSceneNode* parent)
{
if (!SHEntityManager::IsValidEID(entityID))
{
SHLOG_ERROR("Entity {} is invalid!", entityID)
return nullptr;
}
if (auto NODE_ITER = entityNodeMap.find(entityID); NODE_ITER != entityNodeMap.end())
{
SHLOG_WARNING("Entity {} already exists in the scene!", entityID)
return NODE_ITER->second;
}
SHSceneNode* newNode = AllocateNode(entityID);
newNode->SetParent(parent);
return newNode;
}
bool SHSceneGraph::RemoveNode(EntityID entityID) noexcept
{
if (!SHEntityManager::IsValidEID(entityID))
{
SHLOG_ERROR("Entity {} is invalid!", entityID)
return false;
}
auto NODE_ITER = entityNodeMap.find(entityID);
if (NODE_ITER == entityNodeMap.end())
{
SHLOG_WARNING("Entity {} does not exist in the scene!", entityID)
return false;
}
// Remove reference of current node from parent
SHSceneNode* currentNode = NODE_ITER->second;
SHSceneNode* parent = currentNode->GetParent();
if (parent != nullptr)
parent->RemoveChild(currentNode);
ReleaseNode(currentNode);
return true;
}
bool SHSceneGraph::RemoveNode(SHSceneNode* nodeToRemove) noexcept
{
// Remove reference of current node from parent
SHSceneNode* parent = nodeToRemove->GetParent();
if (parent != nullptr)
parent->RemoveChild(nodeToRemove);
ReleaseNode(nodeToRemove);
return true;
}
void SHSceneGraph::Reset() noexcept
{
for (auto* node : entityNodeMap | std::views::values)
ReleaseNode(node);
}
/*-----------------------------------------------------------------------------------*/
/* Private Function Member Definitions */
/*-----------------------------------------------------------------------------------*/
SHSceneNode* SHSceneGraph::AllocateNode(EntityID entityID)
{
SHSceneNode* newNode = new SHSceneNode{entityID};
#ifdef _DEBUG
SHLOG_INFO("Allocated a new Scene Node for Entity {}!", entityID)
#endif
entityNodeMap.emplace(entityID, newNode);
return newNode;
}
void SHSceneGraph::ReleaseNode(SHSceneNode* node) noexcept
{
SHASSERT(node != nullptr, "Attempting to release Invalid Node!")
// Remove parent's reference to this node if there is a parent
if (node->GetParent() != nullptr)
node->GetParent()->RemoveChild(node);
// Remove child's references to this node. Children end up as floating nodes.
for (auto* child : node->GetChildren())
{
child->SetParent(nullptr);
}
entityNodeMap.erase(node->GetEntityID());
delete node;
}
} // namespace SHADE

View File

@ -0,0 +1,143 @@
/****************************************************************************************
* \file SHSceneGraph.h
* \author Diren D Bharwani, diren.dbharwani, 390002520
* \brief Interface for a Scene Graph & Scene Nodes.
*
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
* disclosure of this file or its contents without the prior written consent
* of DigiPen Institute of Technology is prohibited.
****************************************************************************************/
#pragma once
#include <vector>
// Project Headers
#include "Engine/ECS_Base/Entity/SHEntity.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Type Definitions */
/*-----------------------------------------------------------------------------------*/
class SHSceneNode
{
public:
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
bool isActive;
/*---------------------------------------------------------------------------------*/
/* Constructors & Destructor */
/*---------------------------------------------------------------------------------*/
~SHSceneNode () = default;
SHSceneNode (EntityID eid, SHSceneNode* parent = nullptr) noexcept;
SHSceneNode (const SHSceneNode& rhs) noexcept;
SHSceneNode (SHSceneNode&& rhs) noexcept;
SHSceneNode& operator= (const SHSceneNode& rhs) noexcept;
SHSceneNode& operator= (SHSceneNode&& rhs) noexcept;
/*---------------------------------------------------------------------------------*/
/* Getter Functions */
/*---------------------------------------------------------------------------------*/
[[nodiscard]] EntityID GetEntityID () const noexcept { return entityID ;}
[[nodiscard]] SHSceneNode* GetParent () const noexcept { return parent; }
[[nodiscard]] std::vector<SHSceneNode*>& GetChildren () noexcept { return children; }
[[nodiscard]] SHSceneNode* GetChild (EntityID childID) const noexcept;
/*---------------------------------------------------------------------------------*/
/* Setter Functions */
/*---------------------------------------------------------------------------------*/
void SetParent (SHSceneNode* parentNode) noexcept;
/*---------------------------------------------------------------------------------*/
/* Function Members */
/*---------------------------------------------------------------------------------*/
void AddChild (SHSceneNode* newChild) noexcept;
bool RemoveChild (EntityID childID) noexcept;
bool RemoveChild (SHSceneNode* childToRemove) noexcept;
void RemoveAllChildren () noexcept;
private:
EntityID entityID;
SHSceneNode* parent;
std::vector<SHSceneNode*> children;
};
class SHSceneGraph
{
public:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
using EntityNodeMap = std::unordered_map<EntityID, SHSceneNode*>;
/*---------------------------------------------------------------------------------*/
/* Constructors & Destructor */
/*---------------------------------------------------------------------------------*/
SHSceneGraph () noexcept;
~SHSceneGraph () noexcept;
SHSceneGraph (const SHSceneGraph&) = delete;
SHSceneGraph (SHSceneGraph&&) = delete;
SHSceneGraph& operator= (const SHSceneGraph&) = delete;
SHSceneGraph& operator= (SHSceneGraph&&) = delete;
/*---------------------------------------------------------------------------------*/
/* Getter Functions */
/*---------------------------------------------------------------------------------*/
[[nodiscard]] SHSceneNode* GetRoot () const noexcept;
[[nodiscard]] SHSceneNode* GetNode (EntityID entityID) const noexcept;
[[nodiscard]] SHSceneNode* GetParent (EntityID entityID) const noexcept;
[[nodiscard]] SHSceneNode* GetChild (EntityID entityID, SHSceneNode* childNode) const noexcept;
[[nodiscard]] SHSceneNode* GetChild (EntityID entityID, EntityID childEntityID) const noexcept;
[[nodiscard]] const std::vector<SHSceneNode*>& GetChildren (EntityID entityID) const noexcept;
/*---------------------------------------------------------------------------------*/
/* Setter Functions */
/*---------------------------------------------------------------------------------*/
void SetParent (EntityID entityID, SHSceneNode* parent) const noexcept;
void SetParent (EntityID entityID, EntityID parent) const noexcept;
/*---------------------------------------------------------------------------------*/
/* Function Members */
/*---------------------------------------------------------------------------------*/
SHSceneNode* AddNode (EntityID entityID, SHSceneNode* parent = nullptr);
bool RemoveNode (EntityID entityID) noexcept;
bool RemoveNode (SHSceneNode* nodeToRemove) noexcept;
void Reset () noexcept;
private:
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
SHSceneNode* root;
EntityNodeMap entityNodeMap;
/*---------------------------------------------------------------------------------*/
/* Function Members */
/*---------------------------------------------------------------------------------*/
SHSceneNode* AllocateNode (EntityID entityID);
void ReleaseNode (SHSceneNode* node) noexcept;
};
} // namespace SHADE