MetaCore/Source/MetaCoreScene/Private/MetaCoreScene.cpp
2026-06-24 16:32:25 +08:00

516 lines
18 KiB
C++

#include "MetaCoreScene/MetaCoreScene.h"
#include "MetaCoreFoundation/MetaCoreId.h"
#include "MetaCoreFoundation/MetaCoreAssetRegistry.h"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/ext/matrix_transform.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include <algorithm>
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include "MetaCoreScene/MetaCoreTransformUtils.h"
namespace MetaCore {
namespace {
std::string MetaCoreBuildCopyName(const std::string& name) {
return name + " Copy";
}
[[nodiscard]] bool MetaCoreEnvironmentSettingsEqual(
const MetaCoreSceneEnvironmentSettings& lhs,
const MetaCoreSceneEnvironmentSettings& rhs
) {
return lhs.Source == rhs.Source &&
lhs.IblKtxPath == rhs.IblKtxPath &&
lhs.SkyboxKtxPath == rhs.SkyboxKtxPath &&
lhs.SkyboxVisible == rhs.SkyboxVisible &&
lhs.RotationDegrees == rhs.RotationDegrees &&
lhs.IndirectLightIntensity == rhs.IndirectLightIntensity &&
lhs.SkyboxIntensity == rhs.SkyboxIntensity;
}
} // 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<MetaCoreIdComponent>().Id = id;
obj.AddComponent<MetaCoreNameComponent>().Name = name;
obj.AddComponent<MetaCoreHierarchyComponent>().ParentId = parentId;
obj.AddComponent<MetaCoreTransformComponent>();
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<MetaCoreScene*>(this));
}
return MetaCoreGameObject();
}
std::vector<MetaCoreGameObject> MetaCoreScene::GetGameObjects() {
std::vector<MetaCoreGameObject> objects;
objects.reserve(IdToEntity_.size());
for (auto [id, entity] : IdToEntity_) {
objects.emplace_back(entity, this);
}
std::sort(objects.begin(), objects.end(), [](const MetaCoreGameObject& lhs, const MetaCoreGameObject& rhs) {
return lhs.GetId() < rhs.GetId();
});
return objects;
}
std::vector<MetaCoreGameObject> MetaCoreScene::GetGameObjects() const {
std::vector<MetaCoreGameObject> objects;
objects.reserve(IdToEntity_.size());
for (auto [id, entity] : IdToEntity_) {
objects.emplace_back(entity, const_cast<MetaCoreScene*>(this));
}
std::sort(objects.begin(), objects.end(), [](const MetaCoreGameObject& lhs, const MetaCoreGameObject& rhs) {
return lhs.GetId() < rhs.GetId();
});
return objects;
}
std::vector<MetaCoreId> MetaCoreScene::GetRootObjectIds() const {
std::vector<MetaCoreId> rootIds;
auto view = Registry_.view<MetaCoreIdComponent, MetaCoreHierarchyComponent>();
for (auto entity : view) {
if (view.get<MetaCoreHierarchyComponent>(entity).ParentId == 0) {
rootIds.push_back(view.get<MetaCoreIdComponent>(entity).Id);
}
}
return rootIds;
}
std::vector<MetaCoreId> MetaCoreScene::GetChildrenOf(MetaCoreId parentId) const {
std::vector<MetaCoreId> childIds;
auto view = Registry_.view<MetaCoreIdComponent, MetaCoreHierarchyComponent>();
for (auto entity : view) {
if (view.get<MetaCoreHierarchyComponent>(entity).ParentId == parentId) {
childIds.push_back(view.get<MetaCoreIdComponent>(entity).Id);
}
}
return childIds;
}
void MetaCoreScene::CollectPreorder(MetaCoreId objectId, std::vector<MetaCoreId>& output) const {
if (!FindGameObject(objectId)) {
return;
}
output.push_back(objectId);
for (MetaCoreId childId : GetChildrenOf(objectId)) {
CollectPreorder(childId, output);
}
}
std::vector<MetaCoreId> MetaCoreScene::BuildHierarchyPreorder() const {
std::vector<MetaCoreId> orderedIds;
for (MetaCoreId rootId : GetRootObjectIds()) {
CollectPreorder(rootId, orderedIds);
}
return orderedIds;
}
std::vector<MetaCoreId> MetaCoreScene::GetSubtreeObjectIds(MetaCoreId rootId) const {
std::vector<MetaCoreId> 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<MetaCoreId> MetaCoreScene::DeleteGameObjects(const std::vector<MetaCoreId>& objectIds) {
std::unordered_set<MetaCoreId> selectedIds;
for (MetaCoreId objectId : objectIds) {
if (FindGameObject(objectId)) {
selectedIds.insert(objectId);
}
}
if (selectedIds.empty()) {
return {};
}
std::vector<MetaCoreId> 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<MetaCoreId> idsToDelete;
for (MetaCoreId rootId : rootIds) {
for (MetaCoreId subtreeId : GetSubtreeObjectIds(rootId)) {
idsToDelete.insert(subtreeId);
}
}
std::vector<MetaCoreId> 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<MetaCoreId> MetaCoreScene::DuplicateGameObjects(const std::vector<MetaCoreId>& objectIds) {
std::unordered_set<MetaCoreId> selectedIds;
for (MetaCoreId objectId : objectIds) {
if (FindGameObject(objectId)) {
selectedIds.insert(objectId);
}
}
if (selectedIds.empty()) {
return {};
}
std::vector<MetaCoreId> orderedIds = BuildHierarchyPreorder();
std::vector<MetaCoreId> 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<MetaCoreId> 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<MetaCoreTransformComponent>() = sourceObject.GetComponent<MetaCoreTransformComponent>();
if (sourceObject.HasComponent<MetaCoreCameraComponent>())
clonedObject.AddComponent<MetaCoreCameraComponent>(sourceObject.GetComponent<MetaCoreCameraComponent>());
if (sourceObject.HasComponent<MetaCoreMeshRendererComponent>())
clonedObject.AddComponent<MetaCoreMeshRendererComponent>(sourceObject.GetComponent<MetaCoreMeshRendererComponent>());
if (sourceObject.HasComponent<MetaCoreLightComponent>())
clonedObject.AddComponent<MetaCoreLightComponent>(sourceObject.GetComponent<MetaCoreLightComponent>());
if (sourceObject.HasComponent<MetaCoreRotatorComponent>())
clonedObject.AddComponent<MetaCoreRotatorComponent>(sourceObject.GetComponent<MetaCoreRotatorComponent>());
if (sourceObject.HasComponent<MetaCoreScriptComponent>())
clonedObject.AddComponent<MetaCoreScriptComponent>(sourceObject.GetComponent<MetaCoreScriptComponent>());
if (sourceObject.HasComponent<MetaCorePrefabInstanceMetadata>())
clonedObject.AddComponent<MetaCorePrefabInstanceMetadata>(sourceObject.GetComponent<MetaCorePrefabInstanceMetadata>());
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<MetaCoreId>& objectIds, MetaCoreId newParentId, bool keepWorldTransform) {
if (newParentId != 0 && !FindGameObject(newParentId)) {
return false;
}
std::unordered_set<MetaCoreId> selectedIds;
for (MetaCoreId objectId : objectIds) {
if (FindGameObject(objectId)) {
selectedIds.insert(objectId);
}
}
if (selectedIds.empty()) {
return false;
}
std::vector<MetaCoreId> orderedIds = BuildHierarchyPreorder();
std::vector<MetaCoreId> 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<MetaCoreId, glm::mat4> 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<MetaCoreTransformComponent>());
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<MetaCoreTransformComponent>());
}
}
IncrementRevision();
return true;
}
bool MetaCoreScene::SetEnvironmentSettings(const MetaCoreSceneEnvironmentSettings& settings) {
if (MetaCoreEnvironmentSettingsEqual(EnvironmentSettings_, settings)) {
return false;
}
EnvironmentSettings_ = settings;
IncrementRevision();
return true;
}
MetaCoreSceneSnapshot MetaCoreScene::CaptureSnapshot() const {
MetaCoreSceneSnapshot snapshot;
snapshot.Environment = EnvironmentSettings_;
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<MetaCoreTransformComponent>();
if (obj.HasComponent<MetaCoreCameraComponent>())
data.Camera = obj.GetComponent<MetaCoreCameraComponent>();
if (obj.HasComponent<MetaCoreMeshRendererComponent>())
data.MeshRenderer = obj.GetComponent<MetaCoreMeshRendererComponent>();
if (obj.HasComponent<MetaCoreLightComponent>())
data.Light = obj.GetComponent<MetaCoreLightComponent>();
if (obj.HasComponent<MetaCoreRotatorComponent>())
data.Rotator = obj.GetComponent<MetaCoreRotatorComponent>();
if (obj.HasComponent<MetaCoreScriptComponent>())
data.Script = obj.GetComponent<MetaCoreScriptComponent>();
if (obj.HasComponent<MetaCorePrefabInstanceMetadata>())
data.PrefabInstance = obj.GetComponent<MetaCorePrefabInstanceMetadata>();
if (obj.HasComponent<MetaCoreModelRootTag>())
data.ModelRootTag = obj.GetComponent<MetaCoreModelRootTag>();
snapshot.GameObjects.push_back(std::move(data));
}
return snapshot;
}
void MetaCoreScene::RestoreSnapshot(const MetaCoreSceneSnapshot& snapshot) {
Registry_.clear();
IdToEntity_.clear();
EnvironmentSettings_ = snapshot.Environment;
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<MetaCoreTransformComponent>() = data.Transform;
if (data.Camera.has_value())
obj.AddComponent<MetaCoreCameraComponent>(data.Camera.value());
if (data.MeshRenderer.has_value())
obj.AddComponent<MetaCoreMeshRendererComponent>(data.MeshRenderer.value());
if (data.Light.has_value())
obj.AddComponent<MetaCoreLightComponent>(data.Light.value());
if (data.Rotator.has_value())
obj.AddComponent<MetaCoreRotatorComponent>(data.Rotator.value());
if (data.Script.has_value())
obj.AddComponent<MetaCoreScriptComponent>(data.Script.value());
if (data.PrefabInstance.has_value())
obj.AddComponent<MetaCorePrefabInstanceMetadata>(data.PrefabInstance.value());
if (data.ModelRootTag.has_value())
obj.AddComponent<MetaCoreModelRootTag>(data.ModelRootTag.value());
}
MetaCoreIdGenerator::EnsureAbove(maxId);
IncrementRevision();
}
MetaCoreScene MetaCoreCreateDefaultScene() {
MetaCoreScene scene;
MetaCoreGameObject mainCamera = scene.CreateGameObject("Main Camera");
mainCamera.AddComponent<MetaCoreCameraComponent>().IsPrimary = true;
mainCamera.GetComponent<MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 2.2F, 6.5F);
mainCamera.GetComponent<MetaCoreTransformComponent>().RotationEulerDegrees = glm::vec3(-15.0F, 0.0F, 0.0F);
MetaCoreGameObject directionalLight = scene.CreateGameObject("Directional Light");
MetaCoreLightComponent& keyLight = directionalLight.AddComponent<MetaCoreLightComponent>();
keyLight.Color = glm::vec3(1.0F, 0.96F, 0.9F);
keyLight.Intensity = 1.25F;
directionalLight.GetComponent<MetaCoreTransformComponent>().RotationEulerDegrees = glm::vec3(-45.0F, 30.0F, 0.0F);
MetaCoreGameObject fillLight = scene.CreateGameObject("Fill Light");
MetaCoreLightComponent& fillLightComponent = fillLight.AddComponent<MetaCoreLightComponent>();
fillLightComponent.Color = glm::vec3(0.72F, 0.82F, 1.0F);
fillLightComponent.Intensity = 0.18F;
fillLight.GetComponent<MetaCoreTransformComponent>().RotationEulerDegrees = glm::vec3(-20.0F, -135.0F, 0.0F);
return scene;
}
} // namespace MetaCore