#include "MetaCoreScene/MetaCoreScene.h" #include "MetaCoreFoundation/MetaCoreId.h" #define GLM_ENABLE_EXPERIMENTAL #include #include #include #include #include #include #include namespace MetaCore { namespace { glm::mat4 MetaCoreBuildTransformMatrix(const MetaCoreTransformComponent& transform) { const glm::mat4 translation = glm::translate(glm::mat4(1.0F), transform.Position); const glm::mat4 rotation = glm::yawPitchRoll( glm::radians(transform.RotationEulerDegrees.y), glm::radians(transform.RotationEulerDegrees.x), glm::radians(transform.RotationEulerDegrees.z) ); const glm::mat4 scale = glm::scale(glm::mat4(1.0F), transform.Scale); return translation * rotation * scale; } void MetaCoreApplyMatrixToTransform(const glm::mat4& matrix, MetaCoreTransformComponent& transform) { glm::vec3 skew{}; glm::vec4 perspective{}; glm::quat rotation{}; glm::decompose(matrix, transform.Scale, rotation, transform.Position, skew, perspective); transform.RotationEulerDegrees = glm::degrees(glm::eulerAngles(rotation)); } std::string MetaCoreBuildCopyName(const std::string& name) { return name + " Copy"; } } // namespace MetaCoreGameObject& MetaCoreScene::CreateGameObject(const std::string& name, MetaCoreId parentId) { GameObjects_.push_back(MetaCoreGameObject{ MetaCoreIdGenerator::Generate(), parentId, name, MetaCoreTransformComponent{}, std::nullopt, std::nullopt, std::nullopt }); return GameObjects_.back(); } MetaCoreGameObject* MetaCoreScene::FindGameObject(MetaCoreId objectId) { for (MetaCoreGameObject& object : GameObjects_) { if (object.Id == objectId) { return &object; } } return nullptr; } const MetaCoreGameObject* MetaCoreScene::FindGameObject(MetaCoreId objectId) const { for (const MetaCoreGameObject& object : GameObjects_) { if (object.Id == objectId) { return &object; } } return nullptr; } std::vector& MetaCoreScene::GetGameObjects() { return GameObjects_; } const std::vector& MetaCoreScene::GetGameObjects() const { return GameObjects_; } std::vector MetaCoreScene::GetRootObjectIds() const { std::vector rootIds; for (const MetaCoreGameObject& object : GameObjects_) { if (object.ParentId == 0) { rootIds.push_back(object.Id); } } return rootIds; } std::vector MetaCoreScene::GetChildrenOf(MetaCoreId parentId) const { std::vector childIds; for (const MetaCoreGameObject& object : GameObjects_) { if (object.ParentId == parentId) { childIds.push_back(object.Id); } } return childIds; } void MetaCoreScene::CollectPreorder(MetaCoreId objectId, std::vector& output) const { if (FindGameObject(objectId) == nullptr) { 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; } const MetaCoreGameObject* currentObject = FindGameObject(objectId); while (currentObject != nullptr && currentObject->ParentId != 0) { if (currentObject->ParentId == ancestorId) { return true; } currentObject = FindGameObject(currentObject->ParentId); } return false; } bool MetaCoreScene::RenameGameObject(MetaCoreId objectId, const std::string& name) { MetaCoreGameObject* object = FindGameObject(objectId); if (object == nullptr || name.empty()) { return false; } object->Name = name; return true; } std::vector MetaCoreScene::DeleteGameObjects(const std::vector& objectIds) { std::unordered_set selectedIds; for (MetaCoreId objectId : objectIds) { if (FindGameObject(objectId) != nullptr) { selectedIds.insert(objectId); } } if (selectedIds.empty()) { return {}; } std::vector rootIds; rootIds.reserve(selectedIds.size()); for (MetaCoreId objectId : selectedIds) { const MetaCoreGameObject* object = FindGameObject(objectId); bool hasSelectedAncestor = false; while (object != nullptr && object->ParentId != 0) { if (selectedIds.contains(object->ParentId)) { hasSelectedAncestor = true; break; } object = FindGameObject(object->ParentId); } 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()); std::erase_if(GameObjects_, [&](const MetaCoreGameObject& object) { if (!idsToDelete.contains(object.Id)) { return false; } deletedIds.push_back(object.Id); return true; }); return deletedIds; } std::vector MetaCoreScene::DuplicateGameObjects(const std::vector& objectIds) { std::unordered_set selectedIds; for (MetaCoreId objectId : objectIds) { if (FindGameObject(objectId) != nullptr) { selectedIds.insert(objectId); } } if (selectedIds.empty()) { return {}; } std::vector orderedIds = BuildHierarchyPreorder(); std::vector rootIds; for (MetaCoreId objectId : orderedIds) { if (!selectedIds.contains(objectId)) { continue; } const MetaCoreGameObject* object = FindGameObject(objectId); bool hasSelectedAncestor = false; while (object != nullptr && object->ParentId != 0) { if (selectedIds.contains(object->ParentId)) { hasSelectedAncestor = true; break; } object = FindGameObject(object->ParentId); } if (!hasSelectedAncestor) { rootIds.push_back(objectId); } } std::vector clonedObjects; std::vector duplicatedRootIds; const auto cloneSubtree = [&](const auto& self, MetaCoreId sourceId, MetaCoreId duplicatedParentId, bool isRoot) -> void { const MetaCoreGameObject* sourceObject = FindGameObject(sourceId); if (sourceObject == nullptr) { return; } MetaCoreGameObject clonedObject = *sourceObject; clonedObject.Id = MetaCoreIdGenerator::Generate(); clonedObject.ParentId = duplicatedParentId; if (isRoot) { clonedObject.Name = MetaCoreBuildCopyName(sourceObject->Name); duplicatedRootIds.push_back(clonedObject.Id); } clonedObjects.push_back(std::move(clonedObject)); const MetaCoreId newObjectId = clonedObjects.back().Id; for (MetaCoreId childId : GetChildrenOf(sourceId)) { self(self, childId, newObjectId, false); } }; for (MetaCoreId rootId : rootIds) { const MetaCoreGameObject* rootObject = FindGameObject(rootId); if (rootObject == nullptr) { continue; } cloneSubtree(cloneSubtree, rootId, rootObject->ParentId, true); } GameObjects_.insert(GameObjects_.end(), clonedObjects.begin(), clonedObjects.end()); return duplicatedRootIds; } bool MetaCoreScene::ReparentGameObjects(const std::vector& objectIds, MetaCoreId newParentId, bool keepWorldTransform) { if (newParentId != 0 && FindGameObject(newParentId) == nullptr) { return false; } std::unordered_set selectedIds; for (MetaCoreId objectId : objectIds) { if (FindGameObject(objectId) != nullptr) { selectedIds.insert(objectId); } } if (selectedIds.empty()) { return false; } std::vector orderedIds = BuildHierarchyPreorder(); std::vector rootIds; for (MetaCoreId objectId : orderedIds) { if (!selectedIds.contains(objectId)) { continue; } const MetaCoreGameObject* object = FindGameObject(objectId); bool hasSelectedAncestor = false; while (object != nullptr && object->ParentId != 0) { if (selectedIds.contains(object->ParentId)) { hasSelectedAncestor = true; break; } object = FindGameObject(object->ParentId); } 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 { const MetaCoreGameObject* object = FindGameObject(objectId); if (object == nullptr) { return glm::mat4(1.0F); } const glm::mat4 localMatrix = MetaCoreBuildTransformMatrix(object->Transform); if (object->ParentId == 0) { return localMatrix; } return self(self, object->ParentId) * 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 != nullptr) { object->ParentId = 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 == nullptr) { continue; } const auto matrixIterator = worldMatrices.find(rootId); if (matrixIterator == worldMatrices.end()) { continue; } const glm::mat4 newLocalMatrix = inverseParentWorldMatrix * matrixIterator->second; MetaCoreApplyMatrixToTransform(newLocalMatrix, object->Transform); } } return true; } MetaCoreSceneSnapshot MetaCoreScene::CaptureSnapshot() const { MetaCoreSceneSnapshot snapshot; snapshot.GameObjects = GameObjects_; return snapshot; } void MetaCoreScene::RestoreSnapshot(const MetaCoreSceneSnapshot& snapshot) { GameObjects_ = snapshot.GameObjects; MetaCoreId maxId = 0; for (const MetaCoreGameObject& object : GameObjects_) { maxId = std::max(maxId, object.Id); } MetaCoreIdGenerator::EnsureAbove(maxId); } MetaCoreScene MetaCoreCreateDefaultScene() { MetaCoreScene scene; MetaCoreGameObject& mainCamera = scene.CreateGameObject("Main Camera"); mainCamera.Camera = MetaCoreCameraComponent{}; mainCamera.Camera->IsPrimary = true; mainCamera.Transform.Position = glm::vec3(0.0F, 2.2F, 6.5F); mainCamera.Transform.RotationEulerDegrees = glm::vec3(-15.0F, 0.0F, 0.0F); MetaCoreGameObject& directionalLight = scene.CreateGameObject("Directional Light"); directionalLight.Light = MetaCoreLightComponent{}; directionalLight.Transform.RotationEulerDegrees = glm::vec3(-45.0F, 30.0F, 0.0F); MetaCoreGameObject& cube = scene.CreateGameObject("Cube"); cube.MeshRenderer = MetaCoreMeshRendererComponent{}; cube.Transform.Position = glm::vec3(0.0F, 0.5F, 0.0F); MetaCoreGameObject& valve = scene.CreateGameObject("Valve"); valve.MeshRenderer = MetaCoreMeshRendererComponent{}; valve.Transform.Position = glm::vec3(-2.2F, 0.5F, 0.0F); valve.MeshRenderer->BaseColor = glm::vec3(0.85F, 0.45F, 0.20F); MetaCoreGameObject& tank = scene.CreateGameObject("Tank"); tank.MeshRenderer = MetaCoreMeshRendererComponent{}; tank.Transform.Position = glm::vec3(2.2F, 0.5F, 0.0F); tank.Transform.Scale = glm::vec3(1.4F, 1.4F, 1.4F); tank.MeshRenderer->BaseColor = glm::vec3(0.35F, 0.65F, 0.90F); MetaCoreGameObject& alarmBeacon = scene.CreateGameObject("Alarm Beacon"); alarmBeacon.Light = MetaCoreLightComponent{}; alarmBeacon.Light->Intensity = 0.0F; alarmBeacon.Light->Color = glm::vec3(1.0F, 0.15F, 0.1F); alarmBeacon.Transform.Position = glm::vec3(0.0F, 2.5F, 0.0F); return scene; } } // namespace MetaCore