Added SceneGraph interaction functions in GameObject

This commit is contained in:
Kah Wei 2022-11-08 18:32:13 +08:00
parent 78db3305d4
commit 9be58e7e5d
5 changed files with 372 additions and 21 deletions

View File

@ -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()));
}
}
}

View File

@ -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 */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// Static class that caches all the lists of children for GameObjects.
/// </summary>
private ref class ChildListCache abstract sealed
{
public:
/*-----------------------------------------------------------------------------*/
/* Type Definitions */
/*-----------------------------------------------------------------------------*/
using ChildList = System::Collections::Generic::List<GameObject>;
using ChildEnumerable = System::Collections::Generic::IEnumerable<GameObject>;
using ListMap = System::Collections::Generic::Dictionary<Entity, ChildList^>;
internal:
/*-----------------------------------------------------------------------------*/
/* Static Usage Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Retrieves the children list for the specified Entity.
/// </summary>
/// <returns>
/// Enumerable read only list of an Entity's children. Null if entity is invalid
/// or there are no children.
/// </returns>
static ChildEnumerable^ GetChildList(Entity entity);
/// <summary>
/// Updates the children list for the specified Entity if it exists.
/// </summary>
static void UpdateChildList(Entity entity);
/*-----------------------------------------------------------------------------*/
/* Event Handling Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// To be
/// </summary>
static void OnChildrenChanged(EntityID entity);
private:
/*-----------------------------------------------------------------------------*/
/* Static Data Members */
/*-----------------------------------------------------------------------------*/
static ListMap^ cachedLists = gcnew ListMap();
/*-----------------------------------------------------------------------------*/
/* Helper Functions */
/*-----------------------------------------------------------------------------*/
static void updateChildList(ChildList^ list, const SHSceneNode* sceneNode);
};
}

View File

@ -20,7 +20,7 @@ namespace SHADE
{
/// <summary>
/// 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.
/// </summary>
private ref class EngineInterface abstract sealed
{

View File

@ -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<int>(NODE->GetChildren().size());
}
/*---------------------------------------------------------------------------------*/
@ -215,6 +229,88 @@ namespace SHADE
ScriptStore::RemoveScript<T>(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>^ 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);
}
/*---------------------------------------------------------------------------------*/

View File

@ -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
/// <summary>
/// The parent entity for this GameObject.
/// </summary>
property GameObject^ Parent
property GameObject Parent
{
GameObject^ get();
void set(GameObject^);
GameObject get();
void set(GameObject);
}
/// <summary>
/// Number of Children held by this GameObject
/// </summary>
property int ChildCount
{
int get();
}
/*-----------------------------------------------------------------------------*/
@ -120,8 +127,7 @@ namespace SHADE
/// <param name="active">
/// Whether to activate or deactivate this GameObject.
/// </param>
void SetActive(bool active);
void SetActive(bool active);
/*-----------------------------------------------------------------------------*/
/* Component Access Functions */
@ -214,6 +220,82 @@ namespace SHADE
generic<typename T> where T : ref class, Script
void RemoveScript();
/*-----------------------------------------------------------------------------*/
/* Scene Graph Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Unparents all children. Useful if you want to destroy the root of a hierarchy
/// without destroying the children.
/// </summary>
void DetachChildren();
/// <summary>
/// Returns a child by index.
/// </summary>
/// <param name="index">Index of the child GameObject to retrieve.</param>
/// <returns>
/// Handle to the GameObject if the index is valid. Invalid GameObject otherwise.
/// </returns>
GameObject GetChild(int index);
/// <summary>
/// Returns a cached enumerable container of child GameObjects of this
/// GameObject.
/// </summary>
/// <returns>
/// Enumerable container of child GameObjects of this GameObject. Null if
/// ChildCount is 0.
/// </returns>
System::Collections::Generic::IEnumerable<GameObject>^ GetChildren();
/// <summary>
/// Gets the sibling index. Use GetSiblingIndex to find out the GameObjects
/// place in this hierarchy. When the sibling index of a GameObject is changed,
/// its order in the Hierarchy window will also change.
/// </summary>
/// <returns>
/// Index of this GameObject among the parent GameObject's children.
/// </returns>
[System::ObsoleteAttribute("Not yet implemented.", true)]
int GetSiblingIndex();
/// <summary>
/// Checks if this GameObject a direct or indirect child of the specified
/// GameObject.
/// </summary>
/// <returns>
/// True if this GameObject is a child, deep child (child of a child) or
/// identical to this GameObject, otherwise false.
/// </returns>
bool IsChildOf(GameObject gameObj);
/// <summary>
/// Move the GameObject to the start of the parent GameObject's children list.
/// </summary>
[System::ObsoleteAttribute("Not yet implemented.", true)]
void SetAsFirstSibling();
/// <summary>
/// Move the GameObject to the end of the parent GameObject's children list.
/// </summary>
[System::ObsoleteAttribute("Not yet implemented.", true)]
void SetAsLastSibling();
/// <summary>
/// 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).
/// </summary>
/// <param name="index">
/// Position to place this GameObject at in the hierarchy. Clamped to between
/// [0, parent.ChildCount].
/// </param>
[System::ObsoleteAttribute("Not yet implemented.", true)]
void SetSiblingIndex(int index);
/*-----------------------------------------------------------------------------*/
/* Operator Overloads */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Implicit conversion operator to enable checking if a GameObject is valid.
/// </summary>
/// <param name="gameObj">GameObjects to check.</param>
/// <returns>True if the GameObject is valid.</returns>
static operator bool(GameObject gameObj);
internal:
/*-----------------------------------------------------------------------------*/
/* Constructors */
@ -249,13 +331,13 @@ namespace SHADE
SHEntity& GetNativeEntity();
/*-----------------------------------------------------------------------------*/
/* Operator Overloads */
/* Helper Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Implicit conversion operator to enable checking if a GameObject is valid.
/// Retrieves the SceneNode for this GameObject's referenced entity.
/// </summary>
/// <param name="gameObj">GameObjects to check.</param>
static operator bool(GameObject gameObj);
/// <returns>Pointer to the SceneNode for this GameObject..</returns>
SHSceneNode* GetSceneNode();
private:
/*-----------------------------------------------------------------------------*/