diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index 27e46d98..5d7b6a81 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -48,15 +48,28 @@ namespace SHADE if (Begin()) { + if(skipFrame) + { + ImGui::End(); + skipFrame = false; + return; + } DrawMenuBar(); auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); - if(const auto root = sceneGraph.GetRoot()) + + if (const auto root = sceneGraph.GetRoot()) { auto const& children = root->GetChildren(); - + for (const auto child : children) { - RecursivelyDrawEntityNode(child); + if(child) + RecursivelyDrawEntityNode(child); + if(skipFrame) + { + ImGui::End(); + return; + } } } else @@ -120,8 +133,10 @@ namespace SHADE } } - ImRect SHHierarchyPanel::RecursivelyDrawEntityNode(SHSceneNode* currentNode) + ImRect SHHierarchyPanel::RecursivelyDrawEntityNode(SHSceneNode* const currentNode) { + if(currentNode == nullptr) + return {}; auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); //Get node data (Children, eid, selected) @@ -193,10 +208,16 @@ namespace SHADE if(ImGui::Selectable("Paste")) { SetScrollTo(SHSerialization::DeserializeEntitiesFromString(SHClipboardUtilities::GetDataFromClipboard())); + skipFrame = true; + ImGui::EndPopup(); + if(isNodeOpen) + ImGui::TreePop(); + return nodeRect; } if(ImGui::Selectable("Paste as Child")) { SetScrollTo(SHSerialization::DeserializeEntitiesFromString(SHClipboardUtilities::GetDataFromClipboard(), eid)); + skipFrame = true; } if(ImGui::Selectable(std::format("{} Delete", ICON_MD_DELETE).data())) { diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h index 0cfe6474..8fae5d6d 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h @@ -26,10 +26,12 @@ namespace SHADE void SetScrollTo(EntityID eid); private: void DrawMenuBar() const noexcept; - ImRect RecursivelyDrawEntityNode(SHSceneNode*); + ImRect RecursivelyDrawEntityNode(SHSceneNode* const); void CreateChildEntity(EntityID parentEID) const noexcept; void ParentSelectedEntities(EntityID parentEID) const noexcept; void SelectRangeOfEntities(EntityID beginEID, EntityID EndEID); + + bool skipFrame = false; std::string filter; bool isAnyNodeSelected = false; EntityID scrollTo = MAX_EID; diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index 3e569690..31b15409 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -85,6 +85,8 @@ namespace SHADE // Deserialise scripts if (node[ScriptsNode]) SHSystemManager::GetSystem()->DeserialiseScripts(eid, node[ScriptsNode]); + + return eid; } void SHSerialization::DeserializeSceneFromFile(std::filesystem::path const& path) @@ -192,9 +194,9 @@ namespace SHADE { components[rttr::type::get().get_name().data()] = SHSerializationHelper::SerializeComponentToNode(rigidbody); } - if (const auto collisionComp = SHComponentManager::GetComponent_s(eid)) + if (auto collisionComp = SHComponentManager::GetComponent_s(eid)) { - components[rttr::type::get().get_name().data()] = SHSerializationHelper::SerializeComponentToNode(collisionComp); + components[rttr::type::get().get_name().data()] = YAML::convert::encode(*collisionComp); } node[ComponentsNode] = components; @@ -254,6 +256,10 @@ namespace SHADE if (id.has_value()) componentIDList.push_back(id.value()); + id = GetComponentID(componentsNode); + if(id.has_value()) + componentIDList.push_back(id.value()); + return componentIDList; } @@ -263,5 +269,7 @@ namespace SHADE if (!componentsNode) return; SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); + SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); + SHSerializationHelper::InitializeComponentFromNodeData(componentsNode, eid); } } diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index f8b55d7e..6d52df7d 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -35,13 +35,13 @@ namespace YAML } static bool decode(Node const& node, SHVec4& rhs) { - if(node[x]) + if (node[x]) rhs.x = node[x].as(); - if(node[y]) + if (node[y]) rhs.y = node[y].as(); - if(node[z]) + if (node[z]) rhs.z = node[z].as(); - if(node[w]) + if (node[w]) rhs.w = node[w].as(); return true; } @@ -65,11 +65,11 @@ namespace YAML } static bool decode(Node const& node, SHVec3& rhs) { - if(node[x]) + if (node[x]) rhs.x = node[x].as(); - if(node[y]) + if (node[y]) rhs.y = node[y].as(); - if(node[z]) + if (node[z]) rhs.z = node[z].as(); return true; } @@ -91,13 +91,162 @@ namespace YAML } static bool decode(Node const& node, SHVec2& rhs) { - if(node[x]) + if (node[x]) rhs.x = node[x].as(); - if(node[y]) + if (node[y]) rhs.y = node[y].as(); return true; } }; + + template<> + struct convert + { + static constexpr const char* IsTrigger = "Is Trigger"; + + static constexpr const char* Type = "Type"; + static constexpr const char* HalfExtents = "Half Extents"; + static constexpr const char* Radius = "Radius"; + + static constexpr const char* Friction = "Friction"; + static constexpr const char* Bounciness = "Bounciness"; + static constexpr const char* Density = "Density"; + static constexpr const char* PositionOffset = "Position Offset"; + + static Node encode(SHCollider& rhs) + { + Node node; + node[IsTrigger] = rhs.IsTrigger(); + + rttr::type const shapeRttrType = rttr::type::get(); + rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); + SHCollider::Type colliderType = rhs.GetType(); + + node[Type] = enumAlign.value_to_name(colliderType).data(); + + switch (colliderType) + { + case SHCollider::Type::BOX: + { + auto const bb = reinterpret_cast(rhs.GetShape()); + node[HalfExtents] = bb->GetHalfExtents(); + } + break; + case SHCollider::Type::SPHERE: + { + auto const bs = reinterpret_cast(rhs.GetShape()); + node[Radius] = bs->GetRadius(); + } + break; + case SHCollider::Type::CAPSULE: break; + default:; + } + + node[Friction] = rhs.GetFriction(); + node[Bounciness] = rhs.GetBounciness(); + node[Density] = rhs.GetDensity(); + node[PositionOffset] = rhs.GetPositionOffset(); + + return node; + } + static bool decode(Node const& node, SHCollider& rhs) + { + if (node[IsTrigger]) + rhs.SetIsTrigger(node[IsTrigger].as()); + if (!node[Type]) + return false; + rttr::type const shapeRttrType = rttr::type::get(); + rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); + bool ok; + const SHCollider::Type colliderType = enumAlign.name_to_value(node[Type].as()).convert(&ok); + if (!ok) + return false; + switch (colliderType) + { + case SHCollider::Type::BOX: + { + if(auto const bb = dynamic_cast(rhs.GetShape()); bb) + { + if (node[HalfExtents]) + { + bb->SetHalfExtents(node[HalfExtents].as()); + } + } + } + break; + case SHCollider::Type::SPHERE: + { + if(auto const bs = dynamic_cast(rhs.GetShape()); bs) + { + if (node[Radius]) + { + bs->SetRadius(node[Radius].as()); + } + } + } + break; + case SHCollider::Type::CAPSULE: break; + default:; + } + if (node[Friction]) + rhs.SetFriction(node[Friction].as()); + if (node[Bounciness]) + rhs.SetBounciness(rhs.GetBounciness()); + if (node[Density]) + rhs.SetDensity(node[Density].as()); + if (node[PositionOffset]) + rhs.SetPositionOffset(node[PositionOffset].as()); + + return true; + } + }; + + template<> + struct convert + { + static constexpr const char* Colliders = "Colliders"; + static Node encode(SHColliderComponent& rhs) + { + Node node, collidersNode; + auto const& colliders = rhs.GetColliders(); + int const numColliders = static_cast(colliders.size()); + for (int i = 0; i < numColliders; ++i) + { + auto& collider = rhs.GetCollider(i); + Node colliderNode = convert::encode(collider); + if (colliderNode.IsDefined()) + collidersNode[i] = colliderNode; + } + node[Colliders] = collidersNode; + return node; + } + static bool decode(Node const& node, SHColliderComponent& rhs) + { + if(node.IsNull()) + return false; + if (node[Colliders]) + { + int numColliders{}; + for (auto const& colliderNode : node[Colliders]) + { + rttr::type const shapeRttrType = rttr::type::get(); + rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); + bool ok; + const SHCollider::Type colliderType = enumAlign.name_to_value(colliderNode[convert::Type].as()).convert(&ok); + if (!ok) + return false; + switch (colliderType) + { + case SHCollider::Type::BOX: rhs.AddBoundingBox(); break; + case SHCollider::Type::SPHERE: rhs.AddBoundingSphere(); break; + case SHCollider::Type::CAPSULE: break; + default:; + } + rhs.GetCollider(numColliders++) = colliderNode.as(); + } + } + } + }; } namespace SHADE @@ -110,11 +259,11 @@ namespace SHADE { YAML::Node node; auto varType = var.get_type(); - if(varType.is_sequential_container()) + if (varType.is_sequential_container()) { - for(auto const& elem : var.create_sequential_view()) + for (auto const& elem : var.create_sequential_view()) { - node.push_back(RTTRToNode(elem)); + node.push_back(RTTRToNode(elem)); } } if (varType == rttr::type::get()) @@ -178,7 +327,7 @@ namespace SHADE else { auto properties = var.get_type().get_properties(); - for (auto property : properties) + for (auto const& property : properties) { node[property.get_name().data()] = RTTRToNode(property.get_value(var)); } @@ -186,7 +335,7 @@ namespace SHADE return node; } - template , bool> = true> + template , bool> = true> static std::string SerializeComponentToString(ComponentType* component) { return std::string(); @@ -216,17 +365,17 @@ namespace SHADE auto propType = prop.get_type(); if (propType == rttr::type::get()) { - SHVec4 vec{ propertyNode["X"].as(), propertyNode["Y"].as(), propertyNode["Z"].as(), propertyNode["W"].as() }; + SHVec4 vec = propertyNode.as(); prop.set_value(component, vec); } else if (propType == rttr::type::get()) { - SHVec3 vec{ propertyNode["X"].as(), propertyNode["Y"].as(), propertyNode["Z"].as() }; + SHVec3 vec = propertyNode.as(); prop.set_value(component, vec); } else if (propType == rttr::type::get()) { - SHVec2 vec{ propertyNode["X"].as(), propertyNode["Y"].as() }; + SHVec2 vec = propertyNode.as(); prop.set_value(component, vec); } else if (propType.is_arithmetic()) @@ -263,7 +412,7 @@ namespace SHADE else { auto properties = propType.get_properties(); - for (auto property : properties) + for (auto const& property : properties) { InitializeProperty(component, property, propertyNode[property.get_name().data()]); } @@ -273,7 +422,7 @@ namespace SHADE template , bool> = true> static void InitializeComponentFromNode(YAML::Node const& componentsNode, EntityID const& eid) { - auto component = SHComponentManager::GetComponent_s(eid); + ComponentType* component = SHComponentManager::GetComponent_s(eid); if (componentsNode.IsNull() && !component) return; auto rttrType = rttr::type::get(); @@ -283,12 +432,26 @@ namespace SHADE auto properties = rttrType.get_properties(); for (auto const& prop : properties) { - if (componentNode[prop.get_name().data()]) + + if (componentNode[prop.get_name().data()].IsDefined()) { InitializeProperty(component, prop, componentNode[prop.get_name().data()]); } } } + template , bool> = true> + static void InitializeComponentFromNodeData(YAML::Node const& componentsNode, EntityID const& eid) + { + ComponentType* component = SHComponentManager::GetComponent_s(eid); + if (componentsNode.IsNull() && !component) + return; + auto rttrType = rttr::type::get(); + auto componentNode = componentsNode[rttrType.get_name().data()]; + if (componentsNode.IsNull()) + return; + YAML::convert::decode(componentNode, *component); + } + }; }