From 9be58e7e5d961df6c90f60fbc20a4f8ea38a927d Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 8 Nov 2022 18:32:13 +0800 Subject: [PATCH] Added SceneGraph interaction functions in GameObject --- SHADE_Managed/src/Engine/ChildListCache.cxx | 89 ++++++++++++++ SHADE_Managed/src/Engine/ChildListCache.hxx | 80 +++++++++++++ SHADE_Managed/src/Engine/EngineInterface.hxx | 2 +- SHADE_Managed/src/Engine/GameObject.cxx | 120 +++++++++++++++++-- SHADE_Managed/src/Engine/GameObject.hxx | 102 ++++++++++++++-- 5 files changed, 372 insertions(+), 21 deletions(-) create mode 100644 SHADE_Managed/src/Engine/ChildListCache.cxx create mode 100644 SHADE_Managed/src/Engine/ChildListCache.hxx diff --git a/SHADE_Managed/src/Engine/ChildListCache.cxx b/SHADE_Managed/src/Engine/ChildListCache.cxx new file mode 100644 index 00000000..b183646f --- /dev/null +++ b/SHADE_Managed/src/Engine/ChildListCache.cxx @@ -0,0 +1,89 @@ +/************************************************************************************//*! +\file ChildListCache.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 11, 2022 +\brief Contains the definition of the functions for the ChildListCache managed + class. + + Note: This file is written in C++17/CLI. + +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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "ChildListCache.hxx" +// External Dependencies +#include "Scene/SHSceneManager.h" +// Project Headers +#include "Utility/Debug.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Static Usage Functions */ + /*---------------------------------------------------------------------------------*/ + ChildListCache::ChildEnumerable^ ChildListCache::GetChildList(Entity entity) + { + // Ignore if invalid + if (entity == MAX_EID) + return nullptr; + + // Check if in cache + if (cachedLists->ContainsKey(entity)) + return cachedLists[entity]; + + // Grab the native child list + auto node = GameObject(entity).GetSceneNode(); + if (!node || node->GetChildren().empty()) + return nullptr; + + // Otherwise + // - Create the list + ChildList^ list = gcnew ChildList(); + updateChildList(list, node); + // - Cache it + cachedLists[entity] = list; + + return list; + } + + void ChildListCache::UpdateChildList(Entity entity) + { + // Ignore if invalid + if (entity == MAX_EID) + return; + + // Check if in cache + if (!cachedLists->ContainsKey(entity)) + return; + + // Update + updateChildList(cachedLists[entity], GameObject(entity).GetSceneNode()); + } + + /*---------------------------------------------------------------------------------*/ + /* Event Handling Functions */ + /*---------------------------------------------------------------------------------*/ + void ChildListCache::OnChildrenChanged(EntityID entity) + { + SAFE_NATIVE_CALL_BEGIN + UpdateChildList(entity); + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ChildListCache") + } + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + void ChildListCache::updateChildList(ChildList^ list, const SHSceneNode* sceneNode) + { + list->Clear(); + for (auto node : sceneNode->GetChildren()) + { + list->Add(GameObject(node->GetEntityID())); + } + } +} diff --git a/SHADE_Managed/src/Engine/ChildListCache.hxx b/SHADE_Managed/src/Engine/ChildListCache.hxx new file mode 100644 index 00000000..1a2637d3 --- /dev/null +++ b/SHADE_Managed/src/Engine/ChildListCache.hxx @@ -0,0 +1,80 @@ +/************************************************************************************//*! +\file ChildListCache.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 11, 2022 +\brief Contains the definition of the ChildListCache managed class. + + Note: This file is written in C++17/CLI. + +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 + +// Project Includes +#include "GameObject.hxx" + +namespace SHADE { } + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*---------------------------------------------------------------------------------*/ + class SHSceneNode; + + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Static class that caches all the lists of children for GameObjects. + /// + private ref class ChildListCache abstract sealed + { + public: + /*-----------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------*/ + using ChildList = System::Collections::Generic::List; + using ChildEnumerable = System::Collections::Generic::IEnumerable; + using ListMap = System::Collections::Generic::Dictionary; + + internal: + /*-----------------------------------------------------------------------------*/ + /* Static Usage Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Retrieves the children list for the specified Entity. + /// + /// + /// Enumerable read only list of an Entity's children. Null if entity is invalid + /// or there are no children. + /// + static ChildEnumerable^ GetChildList(Entity entity); + /// + /// Updates the children list for the specified Entity if it exists. + /// + static void UpdateChildList(Entity entity); + + /*-----------------------------------------------------------------------------*/ + /* Event Handling Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// To be + /// + static void OnChildrenChanged(EntityID entity); + + private: + /*-----------------------------------------------------------------------------*/ + /* Static Data Members */ + /*-----------------------------------------------------------------------------*/ + static ListMap^ cachedLists = gcnew ListMap(); + + /*-----------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------*/ + static void updateChildList(ChildList^ list, const SHSceneNode* sceneNode); + }; +} \ No newline at end of file diff --git a/SHADE_Managed/src/Engine/EngineInterface.hxx b/SHADE_Managed/src/Engine/EngineInterface.hxx index 4fd8f7b3..37ded4eb 100644 --- a/SHADE_Managed/src/Engine/EngineInterface.hxx +++ b/SHADE_Managed/src/Engine/EngineInterface.hxx @@ -20,7 +20,7 @@ namespace SHADE { /// /// Static class that contains the functions for interfacing with the core - /// PlushieEngine written in C++ for managing the lifecycle of managed code. + /// SHADE Engine written in C++ for managing the lifecycle of managed code. /// private ref class EngineInterface abstract sealed { diff --git a/SHADE_Managed/src/Engine/GameObject.cxx b/SHADE_Managed/src/Engine/GameObject.cxx index 9f15c6c9..ece163f9 100644 --- a/SHADE_Managed/src/Engine/GameObject.cxx +++ b/SHADE_Managed/src/Engine/GameObject.cxx @@ -23,6 +23,7 @@ of DigiPen Institute of Technology is prohibited. #include "Utility/Convert.hxx" #include "Scripts/ScriptStore.hxx" #include "Utility/Debug.hxx" +#include "ChildListCache.hxx" namespace SHADE { @@ -87,30 +88,43 @@ namespace SHADE throw gcnew System::NullReferenceException(); return entity; } - GameObject^ GameObject::Parent::get() + GameObject GameObject::Parent::get() { if (!valid) throw gcnew System::NullReferenceException(); + const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); const auto* ROOT = SCENE_GRAPH.GetRoot(); - const auto* NODE = SCENE_GRAPH.GetNode(entity); if (NODE == nullptr) throw gcnew System::InvalidOperationException("Unable to retrieve SceneGraphNode for Entity " + entity.ToString()); const auto* PARENT = NODE->GetParent(); - return PARENT != ROOT ? gcnew GameObject(PARENT->GetEntityID()) : nullptr; + return PARENT != ROOT ? GameObject(PARENT->GetEntityID()) : GameObject(); } - void GameObject::Parent::set(GameObject^ newParent) + void GameObject::Parent::set(GameObject newParent) { if (!valid) throw gcnew System::NullReferenceException(); const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); - if (newParent == nullptr) - SCENE_GRAPH.SetParent(entity, nullptr); + if (newParent) + SCENE_GRAPH.SetParent(entity, newParent.EntityId); else - SCENE_GRAPH.SetParent(entity, newParent->EntityId); + SCENE_GRAPH.SetParent(entity, nullptr); + } + int GameObject::ChildCount::get() + { + if (!valid) + throw gcnew System::NullReferenceException(); + + const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); + const auto* ROOT = SCENE_GRAPH.GetRoot(); + const auto* NODE = SCENE_GRAPH.GetNode(entity); + if (NODE == nullptr) + throw gcnew System::InvalidOperationException("Unable to retrieve SceneGraphNode for Entity " + entity.ToString()); + + return static_cast(NODE->GetChildren().size()); } /*---------------------------------------------------------------------------------*/ @@ -215,6 +229,88 @@ namespace SHADE ScriptStore::RemoveScript(entity); } + /*---------------------------------------------------------------------------------*/ + /* Scene Graph Functions */ + /*---------------------------------------------------------------------------------*/ + void GameObject::DetachChildren() + { + // Validity Checks + if (!valid) + throw gcnew System::NullReferenceException(); + auto node = GetSceneNode(); + if (!node) + throw gcnew System::NullReferenceException(); + + // Unparent all children to the root + for (auto child : node->GetChildren()) + { + child->SetParent(nullptr); + ChildListCache::UpdateChildList(child->GetEntityID()); + } + ChildListCache::UpdateChildList(entity); + } + + GameObject GameObject::GetChild(int index) + { + // Validity Checks + if (!valid) + throw gcnew System::NullReferenceException(); + auto node = GetSceneNode(); + if (!node) + throw gcnew System::NullReferenceException(); + + auto child = node->GetChild(index); + return child ? GameObject(child->GetEntityID()) : GameObject(); + } + + System::Collections::Generic::IEnumerable^ GameObject::GetChildren() + { + return ChildListCache::GetChildList(entity); + } + + int GameObject::GetSiblingIndex() + { + throw gcnew System::NotImplementedException(); + } + + bool GameObject::IsChildOf(GameObject gameObj) + { + // Search parents recursively + auto node = GetSceneNode(); + while (node != nullptr) + { + if (node->GetEntityID() == gameObj.entity) + return true; + + // Go up higher + node = node->GetParent(); + } + return false; + } + + void GameObject::SetAsFirstSibling() + { + throw gcnew System::NotImplementedException(); + } + + void GameObject::SetAsLastSibling() + { + throw gcnew System::NotImplementedException(); + } + + void GameObject::SetSiblingIndex(int index) + { + throw gcnew System::NotImplementedException(); + } + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + GameObject::operator bool(GameObject gameObj) + { + return gameObj.valid; + } + /*---------------------------------------------------------------------------------*/ /* Constructors */ /*---------------------------------------------------------------------------------*/ @@ -245,11 +341,15 @@ namespace SHADE } /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ + /* Helper Functions */ /*---------------------------------------------------------------------------------*/ - GameObject::operator bool(GameObject gameObj) + SHSceneNode* GameObject::GetSceneNode() { - return gameObj.valid; + const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); + const auto* ROOT = SCENE_GRAPH.GetRoot(); + if (!ROOT) + return nullptr; + return SCENE_GRAPH.GetNode(entity); } /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Engine/GameObject.hxx b/SHADE_Managed/src/Engine/GameObject.hxx index 030b917c..ec75f9d9 100644 --- a/SHADE_Managed/src/Engine/GameObject.hxx +++ b/SHADE_Managed/src/Engine/GameObject.hxx @@ -20,7 +20,7 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { /*---------------------------------------------------------------------------------*/ - /* Forward Declarations */ + /* Forward Declarations */ /*---------------------------------------------------------------------------------*/ ref class Script; ref class BaseComponent; @@ -97,10 +97,17 @@ namespace SHADE /// /// The parent entity for this GameObject. /// - property GameObject^ Parent + property GameObject Parent { - GameObject^ get(); - void set(GameObject^); + GameObject get(); + void set(GameObject); + } + /// + /// Number of Children held by this GameObject + /// + property int ChildCount + { + int get(); } /*-----------------------------------------------------------------------------*/ @@ -120,8 +127,7 @@ namespace SHADE /// /// Whether to activate or deactivate this GameObject. /// - void SetActive(bool active); - + void SetActive(bool active); /*-----------------------------------------------------------------------------*/ /* Component Access Functions */ @@ -214,6 +220,82 @@ namespace SHADE generic where T : ref class, Script void RemoveScript(); + /*-----------------------------------------------------------------------------*/ + /* Scene Graph Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Unparents all children. Useful if you want to destroy the root of a hierarchy + /// without destroying the children. + /// + void DetachChildren(); + /// + /// Returns a child by index. + /// + /// Index of the child GameObject to retrieve. + /// + /// Handle to the GameObject if the index is valid. Invalid GameObject otherwise. + /// + GameObject GetChild(int index); + /// + /// Returns a cached enumerable container of child GameObjects of this + /// GameObject. + /// + /// + /// Enumerable container of child GameObjects of this GameObject. Null if + /// ChildCount is 0. + /// + System::Collections::Generic::IEnumerable^ GetChildren(); + /// + /// Gets the sibling index. Use GetSiblingIndex to find out the GameObject’s + /// place in this hierarchy. When the sibling index of a GameObject is changed, + /// its order in the Hierarchy window will also change. + /// + /// + /// Index of this GameObject among the parent GameObject's children. + /// + [System::ObsoleteAttribute("Not yet implemented.", true)] + int GetSiblingIndex(); + /// + /// Checks if this GameObject a direct or indirect child of the specified + /// GameObject. + /// + /// + /// True if this GameObject is a child, deep child (child of a child) or + /// identical to this GameObject, otherwise false. + /// + bool IsChildOf(GameObject gameObj); + /// + /// Move the GameObject to the start of the parent GameObject's children list. + /// + [System::ObsoleteAttribute("Not yet implemented.", true)] + void SetAsFirstSibling(); + /// + /// Move the GameObject to the end of the parent GameObject's children list. + /// + [System::ObsoleteAttribute("Not yet implemented.", true)] + void SetAsLastSibling(); + /// + /// Move the GameObject to the specified position in the parent GameObject's + /// children list. An existing object at that position if any, will be pushed + /// to the next index (existing element will be at index + 1). + /// + /// + /// Position to place this GameObject at in the hierarchy. Clamped to between + /// [0, parent.ChildCount]. + /// + [System::ObsoleteAttribute("Not yet implemented.", true)] + void SetSiblingIndex(int index); + + /*-----------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-----------------------------------------------------------------------------*/ + /// + /// Implicit conversion operator to enable checking if a GameObject is valid. + /// + /// GameObjects to check. + /// True if the GameObject is valid. + static operator bool(GameObject gameObj); + internal: /*-----------------------------------------------------------------------------*/ /* Constructors */ @@ -249,13 +331,13 @@ namespace SHADE SHEntity& GetNativeEntity(); /*-----------------------------------------------------------------------------*/ - /* Operator Overloads */ + /* Helper Functions */ /*-----------------------------------------------------------------------------*/ /// - /// Implicit conversion operator to enable checking if a GameObject is valid. + /// Retrieves the SceneNode for this GameObject's referenced entity. /// - /// GameObjects to check. - static operator bool(GameObject gameObj); + /// Pointer to the SceneNode for this GameObject.. + SHSceneNode* GetSceneNode(); private: /*-----------------------------------------------------------------------------*/