#include "MetaCoreScene/MetaCoreScene.h" #include "MetaCoreFoundation/MetaCoreId.h" #include "MetaCoreFoundation/MetaCoreAssetRegistry.h" #define GLM_ENABLE_EXPERIMENTAL #include #include #include #include #include #include #include #include "MetaCoreScene/MetaCoreTransformUtils.h" namespace MetaCore { namespace { std::string MetaCoreBuildCopyName(const std::string& name) { return name + " Copy"; } } // namespace MetaCoreGameObject MetaCoreScene::CreateGameObject(const std::string& name, MetaCoreId parentId) { return CreateGameObjectWithId(MetaCoreIdGenerator::Generate(), name, parentId); } MetaCoreGameObject MetaCoreScene::CreateGameObjectWithId(MetaCoreId id, const std::string& name, MetaCoreId parentId) { entt::entity entity = Registry_.create(); IdToEntity_[id] = entity; MetaCoreGameObject obj(entity, this); obj.AddComponent().Id = id; obj.AddComponent().Name = name; obj.AddComponent().ParentId = parentId; obj.AddComponent(); IncrementRevision(); return obj; } MetaCoreGameObject MetaCoreScene::FindGameObject(MetaCoreId objectId) { auto it = IdToEntity_.find(objectId); if (it != IdToEntity_.end()) { return MetaCoreGameObject(it->second, this); } return MetaCoreGameObject(); } MetaCoreGameObject MetaCoreScene::FindGameObject(MetaCoreId objectId) const { auto it = IdToEntity_.find(objectId); if (it != IdToEntity_.end()) { return MetaCoreGameObject(it->second, const_cast(this)); } return MetaCoreGameObject(); } std::vector MetaCoreScene::GetGameObjects() { std::vector objects; objects.reserve(IdToEntity_.size()); for (auto [id, entity] : IdToEntity_) { objects.emplace_back(entity, this); } return objects; } std::vector MetaCoreScene::GetGameObjects() const { std::vector objects; objects.reserve(IdToEntity_.size()); for (auto [id, entity] : IdToEntity_) { objects.emplace_back(entity, const_cast(this)); } return objects; } std::vector MetaCoreScene::GetRootObjectIds() const { std::vector rootIds; auto view = Registry_.view(); for (auto entity : view) { if (view.get(entity).ParentId == 0) { rootIds.push_back(view.get(entity).Id); } } return rootIds; } std::vector MetaCoreScene::GetChildrenOf(MetaCoreId parentId) const { std::vector childIds; auto view = Registry_.view(); for (auto entity : view) { if (view.get(entity).ParentId == parentId) { childIds.push_back(view.get(entity).Id); } } return childIds; } void MetaCoreScene::CollectPreorder(MetaCoreId objectId, std::vector& output) const { if (!FindGameObject(objectId)) { return; } output.push_back(objectId); for (MetaCoreId childId : GetChildrenOf(objectId)) { CollectPreorder(childId, output); } } std::vector MetaCoreScene::BuildHierarchyPreorder() const { std::vector orderedIds; for (MetaCoreId rootId : GetRootObjectIds()) { CollectPreorder(rootId, orderedIds); } return orderedIds; } std::vector MetaCoreScene::GetSubtreeObjectIds(MetaCoreId rootId) const { std::vector subtreeIds; CollectPreorder(rootId, subtreeIds); return subtreeIds; } bool MetaCoreScene::IsDescendantOf(MetaCoreId objectId, MetaCoreId ancestorId) const { if (objectId == 0 || ancestorId == 0 || objectId == ancestorId) { return false; } MetaCoreGameObject currentObject = FindGameObject(objectId); while (currentObject && currentObject.GetParentId() != 0) { if (currentObject.GetParentId() == ancestorId) { return true; } currentObject = FindGameObject(currentObject.GetParentId()); } return false; } bool MetaCoreScene::RenameGameObject(MetaCoreId objectId, const std::string& name) { MetaCoreGameObject object = FindGameObject(objectId); if (!object || name.empty()) { return false; } object.GetName() = name; IncrementRevision(); return true; } std::vector MetaCoreScene::DeleteGameObjects(const std::vector& objectIds) { std::unordered_set selectedIds; for (MetaCoreId objectId : objectIds) { if (FindGameObject(objectId)) { selectedIds.insert(objectId); } } if (selectedIds.empty()) { return {}; } std::vector rootIds; rootIds.reserve(selectedIds.size()); for (MetaCoreId objectId : selectedIds) { MetaCoreGameObject object = FindGameObject(objectId); bool hasSelectedAncestor = false; while (object && object.GetParentId() != 0) { if (selectedIds.contains(object.GetParentId())) { hasSelectedAncestor = true; break; } object = FindGameObject(object.GetParentId()); } if (!hasSelectedAncestor) { rootIds.push_back(objectId); } } std::unordered_set idsToDelete; for (MetaCoreId rootId : rootIds) { for (MetaCoreId subtreeId : GetSubtreeObjectIds(rootId)) { idsToDelete.insert(subtreeId); } } std::vector deletedIds; deletedIds.reserve(idsToDelete.size()); for (MetaCoreId id : idsToDelete) { auto it = IdToEntity_.find(id); if (it != IdToEntity_.end()) { Registry_.destroy(it->second); IdToEntity_.erase(it); deletedIds.push_back(id); } } if (!deletedIds.empty()) { IncrementRevision(); } return deletedIds; } std::vector MetaCoreScene::DuplicateGameObjects(const std::vector& objectIds) { std::unordered_set selectedIds; for (MetaCoreId objectId : objectIds) { if (FindGameObject(objectId)) { selectedIds.insert(objectId); } } if (selectedIds.empty()) { return {}; } std::vector orderedIds = BuildHierarchyPreorder(); std::vector rootIds; for (MetaCoreId objectId : orderedIds) { if (!selectedIds.contains(objectId)) { continue; } MetaCoreGameObject object = FindGameObject(objectId); bool hasSelectedAncestor = false; while (object && object.GetParentId() != 0) { if (selectedIds.contains(object.GetParentId())) { hasSelectedAncestor = true; break; } object = FindGameObject(object.GetParentId()); } if (!hasSelectedAncestor) { rootIds.push_back(objectId); } } std::vector duplicatedRootIds; const auto cloneSubtree = [&](const auto& self, MetaCoreId sourceId, MetaCoreId duplicatedParentId, bool isRoot) -> void { MetaCoreGameObject sourceObject = FindGameObject(sourceId); if (!sourceObject) { return; } MetaCoreId newId = MetaCoreIdGenerator::Generate(); std::string newName = isRoot ? MetaCoreBuildCopyName(sourceObject.GetName()) : sourceObject.GetName(); MetaCoreGameObject clonedObject = CreateGameObjectWithId(newId, newName, duplicatedParentId); clonedObject.GetComponent() = sourceObject.GetComponent(); if (sourceObject.HasComponent()) clonedObject.AddComponent(sourceObject.GetComponent()); if (sourceObject.HasComponent()) clonedObject.AddComponent(sourceObject.GetComponent()); if (sourceObject.HasComponent()) clonedObject.AddComponent(sourceObject.GetComponent()); if (sourceObject.HasComponent()) clonedObject.AddComponent(sourceObject.GetComponent()); if (isRoot) { duplicatedRootIds.push_back(newId); } for (MetaCoreId childId : GetChildrenOf(sourceId)) { self(self, childId, newId, false); } }; for (MetaCoreId rootId : rootIds) { MetaCoreGameObject rootObject = FindGameObject(rootId); if (!rootObject) { continue; } cloneSubtree(cloneSubtree, rootId, rootObject.GetParentId(), true); } return duplicatedRootIds; } bool MetaCoreScene::ReparentGameObjects(const std::vector& objectIds, MetaCoreId newParentId, bool keepWorldTransform) { if (newParentId != 0 && !FindGameObject(newParentId)) { return false; } std::unordered_set selectedIds; for (MetaCoreId objectId : objectIds) { if (FindGameObject(objectId)) { selectedIds.insert(objectId); } } if (selectedIds.empty()) { return false; } std::vector orderedIds = BuildHierarchyPreorder(); std::vector rootIds; for (MetaCoreId objectId : orderedIds) { if (!selectedIds.contains(objectId)) { continue; } MetaCoreGameObject object = FindGameObject(objectId); bool hasSelectedAncestor = false; while (object && object.GetParentId() != 0) { if (selectedIds.contains(object.GetParentId())) { hasSelectedAncestor = true; break; } object = FindGameObject(object.GetParentId()); } if (!hasSelectedAncestor) { rootIds.push_back(objectId); } } if (rootIds.empty()) { return false; } for (MetaCoreId objectId : rootIds) { if (objectId == newParentId || IsDescendantOf(newParentId, objectId)) { return false; } } std::unordered_map worldMatrices; if (keepWorldTransform) { const auto buildWorldMatrix = [&](const auto& self, MetaCoreId objectId) -> glm::mat4 { MetaCoreGameObject object = FindGameObject(objectId); if (!object) { return glm::mat4(1.0F); } const glm::mat4 localMatrix = MetaCoreBuildTransformMatrix(object.GetComponent()); if (object.GetParentId() == 0) { return localMatrix; } return self(self, object.GetParentId()) * localMatrix; }; for (MetaCoreId rootId : rootIds) { worldMatrices.emplace(rootId, buildWorldMatrix(buildWorldMatrix, rootId)); } if (newParentId != 0) { worldMatrices.emplace(newParentId, buildWorldMatrix(buildWorldMatrix, newParentId)); } } for (MetaCoreId rootId : rootIds) { MetaCoreGameObject object = FindGameObject(rootId); if (object) { object.SetParentId(newParentId); } } if (keepWorldTransform) { const glm::mat4 newParentWorldMatrix = newParentId == 0 ? glm::mat4(1.0F) : worldMatrices[newParentId]; const glm::mat4 inverseParentWorldMatrix = glm::inverse(newParentWorldMatrix); for (MetaCoreId rootId : rootIds) { MetaCoreGameObject object = FindGameObject(rootId); if (!object) { continue; } const auto matrixIterator = worldMatrices.find(rootId); if (matrixIterator == worldMatrices.end()) { continue; } const glm::mat4 newLocalMatrix = inverseParentWorldMatrix * matrixIterator->second; MetaCoreApplyMatrixToTransform(newLocalMatrix, object.GetComponent()); } } IncrementRevision(); return true; } MetaCoreSceneSnapshot MetaCoreScene::CaptureSnapshot() const { MetaCoreSceneSnapshot snapshot; auto objects = GetGameObjects(); snapshot.GameObjects.reserve(objects.size()); for (auto obj : objects) { MetaCoreGameObjectData data; data.Id = obj.GetId(); data.ParentId = obj.GetParentId(); data.Name = obj.GetName(); data.Transform = obj.GetComponent(); if (obj.HasComponent()) data.Camera = obj.GetComponent(); if (obj.HasComponent()) data.MeshRenderer = obj.GetComponent(); if (obj.HasComponent()) data.Light = obj.GetComponent(); if (obj.HasComponent()) data.PrefabInstance = obj.GetComponent(); if (obj.HasComponent()) data.ModelRootTag = obj.GetComponent(); snapshot.GameObjects.push_back(std::move(data)); } return snapshot; } void MetaCoreScene::RestoreSnapshot(const MetaCoreSceneSnapshot& snapshot) { Registry_.clear(); IdToEntity_.clear(); MetaCoreId maxId = 0; for (const MetaCoreGameObjectData& data : snapshot.GameObjects) { maxId = std::max(maxId, data.Id); MetaCoreGameObject obj = CreateGameObjectWithId(data.Id, data.Name, data.ParentId); obj.GetComponent() = data.Transform; if (data.Camera.has_value()) obj.AddComponent(data.Camera.value()); if (data.MeshRenderer.has_value()) obj.AddComponent(data.MeshRenderer.value()); if (data.Light.has_value()) obj.AddComponent(data.Light.value()); if (data.PrefabInstance.has_value()) obj.AddComponent(data.PrefabInstance.value()); if (data.ModelRootTag.has_value()) obj.AddComponent(data.ModelRootTag.value()); } MetaCoreIdGenerator::EnsureAbove(maxId); IncrementRevision(); } MetaCoreScene MetaCoreCreateDefaultScene() { MetaCoreScene scene; MetaCoreGameObject mainCamera = scene.CreateGameObject("Main Camera"); mainCamera.AddComponent().IsPrimary = true; mainCamera.GetComponent().Position = glm::vec3(0.0F, 2.2F, 6.5F); mainCamera.GetComponent().RotationEulerDegrees = glm::vec3(-15.0F, 0.0F, 0.0F); MetaCoreGameObject directionalLight = scene.CreateGameObject("Directional Light"); directionalLight.AddComponent(); directionalLight.GetComponent().RotationEulerDegrees = glm::vec3(-45.0F, 30.0F, 0.0F); MetaCoreGameObject cube = scene.CreateGameObject("box1.glb"); auto& meshRenderer = cube.AddComponent(); meshRenderer.MeshSource = MetaCoreMeshSourceKind::Asset; meshRenderer.SourceModelPath = "Assets/Models/box1.glb"; meshRenderer.SourceModelAssetGuid = MetaCoreAssetRegistry::Get().ResolvePathToGuid("Assets/Models/box1.glb"); cube.AddComponent(MetaCoreModelRootTag{ "Assets/Models/box1.glb" }); cube.GetComponent().Position = glm::vec3(0.0F, 0.5F, 0.0F); MetaCoreGameObject alarmBeacon = scene.CreateGameObject("Alarm Beacon"); auto& beaconLight = alarmBeacon.AddComponent(); beaconLight.Intensity = 0.0F; beaconLight.Color = glm::vec3(1.0F, 0.15F, 0.1F); alarmBeacon.GetComponent().Position = glm::vec3(0.0F, 2.5F, 0.0F); return scene; } } // namespace MetaCore