diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.cpp index 57762324..c5511606 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.cpp @@ -34,8 +34,11 @@ namespace SHADE void SHRenderable::OnDestroy() { // Remove from SuperBatch - Handle superBatch = sharedMaterial->GetBaseMaterial()->GetPipeline()->GetPipelineState().GetSubpass()->GetSuperBatch(); - superBatch->Remove(this); + if (sharedMaterial) + { + Handle superBatch = sharedMaterial->GetBaseMaterial()->GetPipeline()->GetPipelineState().GetSubpass()->GetSuperBatch(); + superBatch->Remove(this); + } // Free resources if (material) diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index ce406c77..d66a7506 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -288,7 +288,15 @@ namespace YAML { YAML::Node node; node[MESH_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMesh()).value_or(0); - node[MAT_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMaterial()->GetBaseMaterial()).value_or(0); + auto mat = rhs.GetMaterial(); + if (mat) + { + node[MAT_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMaterial()->GetBaseMaterial()).value_or(0); + } + else + { + node[MAT_YAML_TAG.data()] = 0; + } return node; } static bool decode(YAML::Node const& node, SHRenderable& rhs) diff --git a/SHADE_Managed/src/Engine/ECS.cxx b/SHADE_Managed/src/Engine/ECS.cxx index 80f070e2..76a6a5e2 100644 --- a/SHADE_Managed/src/Engine/ECS.cxx +++ b/SHADE_Managed/src/Engine/ECS.cxx @@ -27,6 +27,7 @@ of DigiPen Institute of Technology is prohibited. #include "Scene/SHSceneManager.h" #include "Scene/SHSceneGraph.h" #include "Tools/SHLog.h" +#include "Graphics\MiddleEnd\Interface\SHRenderable.h" // Project Headers #include "Utility/Convert.hxx" #include "Utility/Debug.hxx" @@ -36,6 +37,7 @@ of DigiPen Institute of Technology is prohibited. #include "Components/Camera.hxx" #include "Components/CameraArm.hxx" #include "Components/Light.hxx" +#include "Components\Renderable.hxx" namespace SHADE { @@ -166,6 +168,70 @@ namespace SHADE return T(); } + generic + System::Collections::Generic::IEnumerable^ ECS::GetComponentsInChildren(EntityID entity) + { + System::Type^ componentType = T::typeid; + + // Check if entity is correct + if (!SHEntityManager::IsValidEID(entity)) + { + std::ostringstream oss; + oss << "[ECS] Attempted to retrieve Component \"" + << Convert::ToNative(componentType->Name) + << "\" from invalid Entity."; + Debug::LogError(oss.str()); + return nullptr; + } + + // Search all elements via a iterative breadth first search + System::Collections::Generic::List^ results; + System::Collections::Generic::Queue^ searchSpace = gcnew System::Collections::Generic::Queue(); + // Start off with direct children + SHSceneNode* entityNode = SHSceneManager::GetCurrentSceneGraph().GetNode(entity); + if (entityNode == nullptr) + { + std::ostringstream oss; + oss << "[ScriptStore] Failed to retrieve SceneGraphNode of entity #" << entity << ". This should not happen!"; + SHLog::Warning(oss.str()); + } + for (const auto& child : entityNode->GetChildren()) + { + searchSpace->Enqueue(child->GetEntityID()); + } + // Continue with all subsequent children + while (searchSpace->Count > 0) + { + // Check if this entity has the component we need + Entity curr = searchSpace->Dequeue(); + T component = GetComponent(curr); + if (component != nullptr) + { + // We only construct if we need to + if (results == nullptr) + results = gcnew System::Collections::Generic::List(); + results->Add(component); + } + + // Add children to the queue + SHSceneNode* sceneGraphNode = SHSceneManager::GetCurrentSceneGraph().GetNode(curr); + if (sceneGraphNode == nullptr) + { + std::ostringstream oss; + oss << "[ECS_CLI] Failed to retrieve SceneGraphNode of entity #" << entity << ". This should not happen!"; + SHLog::Warning(oss.str()); + continue; + } + for (const auto& child : sceneGraphNode->GetChildren()) + { + searchSpace->Enqueue(child->GetEntityID()); + } + } + + // None here + return results; + } + generic T ECS::EnsureComponent(EntityID entity) { @@ -249,6 +315,7 @@ namespace SHADE static ECS::ECS() { componentMap.Add(createComponentSet()); + componentMap.Add(createComponentSet()); componentMap.Add(createComponentSet()); componentMap.Add(createComponentSet()); componentMap.Add(createComponentSet()); diff --git a/SHADE_Managed/src/Engine/ECS.hxx b/SHADE_Managed/src/Engine/ECS.hxx index 0563f678..18acf30d 100644 --- a/SHADE_Managed/src/Engine/ECS.hxx +++ b/SHADE_Managed/src/Engine/ECS.hxx @@ -51,9 +51,9 @@ namespace SHADE /// specified Component. /// generic where T : BaseComponent - static T GetComponent(EntityID entity); + static T GetComponent(EntityID entity); /// - /// Retrieves the first Component from the specified GameObjectt's children that + /// Retrieves the first Component from the specified GameObject's children that /// matches the specified type. /// /// Type of the Component to get. @@ -65,6 +65,20 @@ namespace SHADE generic where T : BaseComponent static T GetComponentInChildren(EntityID entity); /// + /// Retrieves a list of Components from the specified GameObject's children that + /// matches the specified type. + /// This function performs allocations. If expecting only 1 component, use + /// GetComponentInChildren() instead. + /// This does not search the specified entity. + /// + /// Type of the Component to get. + /// Entity object to get the Component from. + /// + /// Newly allocated List of components. Will be null if no components are found. + /// + generic where T : BaseComponent + static System::Collections::Generic::IEnumerable^ GetComponentsInChildren(EntityID entity); + /// /// Ensures a Component on the specified Entity. /// /// Type of the Component to ensure. diff --git a/SHADE_Managed/src/Engine/GameObject.cxx b/SHADE_Managed/src/Engine/GameObject.cxx index 017366fe..200b2079 100644 --- a/SHADE_Managed/src/Engine/GameObject.cxx +++ b/SHADE_Managed/src/Engine/GameObject.cxx @@ -170,6 +170,14 @@ namespace SHADE return ECS::GetComponentInChildren(entity); } + generic + System::Collections::Generic::IEnumerable^ GameObject::GetComponentsInChildren() + { + if (!valid) + throw gcnew System::NullReferenceException(); + return ECS::GetComponentsInChildren(entity); + } + generic T GameObject::EnsureComponent() { @@ -212,6 +220,13 @@ namespace SHADE throw gcnew System::NullReferenceException(); return ScriptStore::GetScriptInChildren(entity); } + generic + System::Collections::Generic::IEnumerable^ GameObject::GetScriptsInChildren() + { + if (!valid) + throw gcnew System::NullReferenceException(); + return ScriptStore::GetScriptsInChildren(entity); + } generic System::Collections::Generic::IEnumerable^ GameObject::GetScripts() diff --git a/SHADE_Managed/src/Engine/GameObject.hxx b/SHADE_Managed/src/Engine/GameObject.hxx index 2e0f360c..64d1b428 100644 --- a/SHADE_Managed/src/Engine/GameObject.hxx +++ b/SHADE_Managed/src/Engine/GameObject.hxx @@ -153,6 +153,7 @@ namespace SHADE /// /// Retrieves the first Component from this GameObject's children that matches /// the specified type. + /// Unlike Unity, we do not search this GameObject, only the children. /// /// Type of the Component to get. /// @@ -162,6 +163,19 @@ namespace SHADE generic where T : BaseComponent T GetComponentInChildren(); /// + /// Retrieves a list of Components from this GameObject's children that matches + /// the specified type. + /// This function performs allocations. If expecting only 1 component, use + /// GetComponentInChildren() instead. + /// Unlike Unity, we do not search this GameObject, only the children. + /// + /// Type of the Component to get. + /// + /// Newly allocated List of components. Will be null if no components are found. + /// + generic where T : BaseComponent + System::Collections::Generic::IEnumerable^ GetComponentsInChildren(); + /// /// Ensures a Component on this GameObject. /// /// Type of the Component to ensure. @@ -201,12 +215,26 @@ namespace SHADE /// Retrieves a Script of the specified type from child GameObjects. /// If multiple Scripts of the same specified type are added on the same /// child GameObject, this will retrieve the first one added. + /// Unlike Unity, we do not search this GameObject, only the children. /// /// Type of Script to retrieve. /// Reference to the Script to retrieve. generic where T : ref class, Script T GetScriptInChildren(); /// + /// Retrieves a list of Scripts from this GameObject's children that matches + /// the specified type. + /// This function performs allocations. If expecting only 1 component, use + /// GetComponentInChildren() instead. + /// Unlike Unity, we do not search this GameObject, only the children. + /// + /// Type of the Component to get. + /// + /// Newly allocated List of components. Will be null if no components are found. + /// + generic where T : ref class, Script + System::Collections::Generic::IEnumerable^ GetScriptsInChildren(); + /// /// Retrieves a immutable list of Scripts of the specified type from this /// GameObject. /// diff --git a/SHADE_Managed/src/Scripts/Script.cxx b/SHADE_Managed/src/Scripts/Script.cxx index 017242d6..a2af38a3 100644 --- a/SHADE_Managed/src/Scripts/Script.cxx +++ b/SHADE_Managed/src/Scripts/Script.cxx @@ -42,6 +42,12 @@ namespace SHADE return owner.GetComponentInChildren(); } + generic + System::Collections::Generic::IEnumerable^ Script::GetComponentsInChildren() + { + return owner.GetComponentsInChildren(); + } + generic T Script::EnsureComponent() { @@ -72,6 +78,11 @@ namespace SHADE { return ScriptStore::GetScriptInChildren(owner.GetEntity()); } + generic + System::Collections::Generic::IEnumerable^ Script::GetScriptsInChildren() + { + return ScriptStore::GetScriptsInChildren(owner.GetEntity()); + } generic System::Collections::Generic::IEnumerable^ Script::GetScripts() diff --git a/SHADE_Managed/src/Scripts/Script.hxx b/SHADE_Managed/src/Scripts/Script.hxx index fb564d27..46736245 100644 --- a/SHADE_Managed/src/Scripts/Script.hxx +++ b/SHADE_Managed/src/Scripts/Script.hxx @@ -69,6 +69,7 @@ namespace SHADE /// /// Retrieves the first Component from this GameObject's children that matches /// the specified type. + /// Unlike Unity, we do not search this GameObject, only the children. /// /// /// Type of the Component to get. Must be derived from BaseComponent. @@ -77,6 +78,19 @@ namespace SHADE generic where T : BaseComponent T GetComponentInChildren(); /// + /// Retrieves a list of Components from this GameObject's children that + /// matches the specified type. + /// This function performs allocations. If expecting only 1 component, use + /// GetComponentInChildren() instead. + /// Unlike Unity, we do not search this GameObject, only the children. + /// + /// Type of the Component to get. + /// + /// Newly allocated List of components. Will be null if no components are found. + /// + generic where T : BaseComponent + System::Collections::Generic::IEnumerable^ GetComponentsInChildren(); + /// /// Ensures a Component on the GameObject that this Script belongs to. /// /// @@ -121,6 +135,7 @@ namespace SHADE /// /// Retrieves the first Script from this GameObject's children that matches the /// specified type. + /// Unlike Unity, we do not search this GameObject, only the children. /// /// /// Type of script to get. @@ -130,6 +145,19 @@ namespace SHADE generic where T : ref class, Script T GetScriptInChildren(); /// + /// Retrieves a list of Scripts from this GameObject's children that matches + /// the specified type. + /// This function performs allocations. If expecting only 1 component, use + /// GetComponentInChildren() instead. + /// Unlike Unity, we do not search this GameObject, only the children. + /// + /// Type of the Component to get. + /// + /// Newly allocated List of components. Will be null if no components are found. + /// + generic where T : ref class, Script + System::Collections::Generic::IEnumerable^ GetScriptsInChildren(); + /// /// Retrieves a immutable list of scripts from the specified Entity that /// matches the specified type. ///
diff --git a/SHADE_Managed/src/Scripts/ScriptStore.cxx b/SHADE_Managed/src/Scripts/ScriptStore.cxx index 318f5839..2b1540b6 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.cxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.cxx @@ -211,6 +211,70 @@ namespace SHADE return T(); } + generic + System::Collections::Generic::IEnumerable^ ScriptStore::GetScriptsInChildren(Entity entity) + { + System::Type^ componentType = T::typeid; + + // Check if entity is correct + if (!SHEntityManager::IsValidEID(entity)) + { + std::ostringstream oss; + oss << "[ScriptStore] Attempted to retrieve Script \"" + << Convert::ToNative(componentType->Name) + << "\" from invalid Entity."; + Debug::LogError(oss.str()); + return nullptr; + } + + // Search all elements via a iterative breadth first search + System::Collections::Generic::List^ results; + System::Collections::Generic::Queue^ searchSpace = gcnew System::Collections::Generic::Queue(); + // Start off with direct children + SHSceneNode* entityNode = SHSceneManager::GetCurrentSceneGraph().GetNode(entity); + if (entityNode == nullptr) + { + std::ostringstream oss; + oss << "[ScriptStore] Failed to retrieve SceneGraphNode of entity #" << entity << ". This should not happen!"; + SHLog::Warning(oss.str()); + } + for (const auto& child : entityNode->GetChildren()) + { + searchSpace->Enqueue(child->GetEntityID()); + } + // Continue with all subsequent children + while (searchSpace->Count > 0) + { + // Check if this entity has the component we need + Entity curr = searchSpace->Dequeue(); + T script = GetScript(curr); + if (script != nullptr) + { + // We only construct if we need to + if (results == nullptr) + results = gcnew System::Collections::Generic::List(); + results->Add(script); + } + + // Add children to the queue + SHSceneNode* sceneGraphNode = SHSceneManager::GetCurrentSceneGraph().GetNode(curr); + if (sceneGraphNode == nullptr) + { + std::ostringstream oss; + oss << "[ScriptStore] Failed to retrieve SceneGraphNode of entity #" << entity << ". This should not happen!"; + SHLog::Warning(oss.str()); + continue; + } + for (const auto& child : sceneGraphNode->GetChildren()) + { + searchSpace->Enqueue(child->GetEntityID()); + } + } + + // None here + return results; + } + generic System::Collections::Generic::IEnumerable^ ScriptStore::GetScripts(Entity entity) { diff --git a/SHADE_Managed/src/Scripts/ScriptStore.hxx b/SHADE_Managed/src/Scripts/ScriptStore.hxx index 62e3003a..78f8c787 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.hxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.hxx @@ -137,6 +137,29 @@ namespace SHADE generic where T : ref class, Script static T GetScriptInChildren(Entity entity); /// + /// Retrieves the list of Scripts from the specified Entity and the Entity's + /// children that matches the specified type. + /// This function performs allocations. If expecting only 1 component, use + /// GetScriptInChildren() instead. + /// This does not search the specified entity. + /// + /// + /// Type of script to get. + /// This needs to be a default constructable Script. + /// + /// + /// The entity which the script to retrieve is attached. + /// + /// + /// Reference to the script. This can be null if no script of the specified + /// type is attached. + /// + /// + /// If the specified Entity is invalid. + /// + generic where T : ref class, Script + static System::Collections::Generic::IEnumerable^ GetScriptsInChildren(Entity entity); + /// /// Retrieves a immutable list of scripts from the specified Entity that /// matches the specified type. ///
diff --git a/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ b/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ index 3e756ce4..dde6705a 100644 --- a/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ +++ b/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ @@ -28,7 +28,47 @@ namespace SHADE template bool SerialisationUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode) { - return varInsertYamlInternal(fieldInfo->GetValue(object), fieldNode); + // Handle null objects + System::Object^ fieldObject = fieldInfo->GetValue(object); + if (fieldObject == nullptr) + { + // Default construct if null + if (fieldInfo->FieldType == FieldType::typeid) + { + if constexpr (std::is_same_v) + { + fieldNode = 0; + } + else if constexpr (std::is_same_v) + { + fieldNode = ""; + } + else if constexpr (std::is_same_v) + { + fieldNode.SetStyle(YAML::EmitterStyle::Flow); + fieldNode.push_back(0.0f); + fieldNode.push_back(0.0f); + } + else if constexpr (std::is_same_v) + { + fieldNode.SetStyle(YAML::EmitterStyle::Flow); + fieldNode.push_back(0.0f); + fieldNode.push_back(0.0f); + fieldNode.push_back(0.0f); + } + else if constexpr (std::is_same_v) + { + fieldNode = MAX_EID; + } + else + { + fieldNode = FieldType(); + } + return true; + } + return false; + } + return varInsertYamlInternal(fieldObject, fieldNode); } template bool SerialisationUtilities::varInsertYamlInternal(System::Object^ object, YAML::Node& fieldNode)