#include "Scene/EditorSceneBridge.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace XCEngine::UI::Editor::App { namespace { using ::XCEngine::Components::Component; using ::XCEngine::Components::ComponentFactoryRegistry; using ::XCEngine::Components::GameObject; using ::XCEngine::Components::Scene; using ::XCEngine::Components::SceneManager; using ::XCEngine::Components::TransformComponent; using ::XCEngine::Resources::ResourceManager; struct ClipboardNode { std::string name = {}; std::string transformPayload = {}; std::vector> components = {}; std::vector children = {}; }; Scene* ResolvePrimaryScene() { SceneManager& sceneManager = SceneManager::Get(); if (Scene* activeScene = sceneManager.GetActiveScene(); activeScene != nullptr) { return activeScene; } const std::vector scenes = sceneManager.GetAllScenes(); if (!scenes.empty() && scenes.front() != nullptr) { sceneManager.SetActiveScene(scenes.front()); return scenes.front(); } return nullptr; } std::pair SerializeComponent( const Component* component) { std::ostringstream payload = {}; component->Serialize(payload); return { component->GetName(), payload.str() }; } ClipboardNode CopyGameObjectRecursive(const GameObject& gameObject) { ClipboardNode node = {}; node.name = gameObject.GetName(); if (const TransformComponent* transform = gameObject.GetTransform(); transform != nullptr) { std::ostringstream payload = {}; transform->Serialize(payload); node.transformPayload = payload.str(); } const auto components = gameObject.GetComponents(); for (const Component* component : components) { if (component == nullptr || component == gameObject.GetTransform()) { continue; } node.components.push_back(SerializeComponent(component)); } for (GameObject* child : gameObject.GetChildren()) { if (child == nullptr) { continue; } node.children.push_back(CopyGameObjectRecursive(*child)); } return node; } GameObject* PasteGameObjectRecursive( Scene& scene, const ClipboardNode& node, GameObject* parent) { GameObject* gameObject = scene.CreateGameObject(node.name, parent); if (gameObject == nullptr) { return nullptr; } if (!node.transformPayload.empty()) { if (TransformComponent* transform = gameObject->GetTransform(); transform != nullptr) { std::istringstream transformStream(node.transformPayload); transform->Deserialize(transformStream); } } for (const auto& componentData : node.components) { Component* component = ComponentFactoryRegistry::Get().CreateComponent( gameObject, componentData.first); if (component == nullptr || componentData.second.empty()) { continue; } std::istringstream payloadStream(componentData.second); component->Deserialize(payloadStream); } for (const ClipboardNode& child : node.children) { PasteGameObjectRecursive(scene, child, gameObject); } return gameObject; } bool WouldCreateCycle( const GameObject& source, const GameObject& targetParent) { const GameObject* current = &targetParent; while (current != nullptr) { if (current == &source) { return true; } current = current->GetParent(); } return false; } } // namespace EditorStartupSceneResult EnsureEditorStartupScene( const std::filesystem::path& projectRoot) { EditorStartupSceneResult result = {}; if (projectRoot.empty()) { return result; } ResourceManager::Get().SetResourceRoot(projectRoot.string().c_str()); if (Scene* activeScene = ResolvePrimaryScene(); activeScene != nullptr) { result.ready = true; result.sceneName = activeScene->GetName(); return result; } const std::filesystem::path startupScenePath = (projectRoot / "Assets" / "Scenes" / "Main.xc").lexically_normal(); SceneManager& sceneManager = SceneManager::Get(); if (std::filesystem::exists(startupScenePath) && std::filesystem::is_regular_file(startupScenePath)) { sceneManager.LoadScene(startupScenePath.string()); Scene* loadedScene = sceneManager.GetScene(startupScenePath.stem().string()); if (loadedScene == nullptr) { loadedScene = ResolvePrimaryScene(); } else { sceneManager.SetActiveScene(loadedScene); } if (loadedScene != nullptr) { result.ready = true; result.loadedFromDisk = true; result.scenePath = startupScenePath; result.sceneName = loadedScene->GetName(); return result; } } if (Scene* scene = sceneManager.CreateScene("Main"); scene != nullptr) { sceneManager.SetActiveScene(scene); result.ready = true; result.sceneName = scene->GetName(); } return result; } Scene* GetActiveEditorScene() { return ResolvePrimaryScene(); } bool OpenEditorSceneAsset(const std::filesystem::path& scenePath) { if (scenePath.empty()) { return false; } std::error_code errorCode = {}; if (!std::filesystem::exists(scenePath, errorCode) || errorCode || !std::filesystem::is_regular_file(scenePath, errorCode)) { return false; } SceneManager& sceneManager = SceneManager::Get(); sceneManager.LoadScene(scenePath.string()); Scene* loadedScene = sceneManager.GetScene(scenePath.stem().string()); if (loadedScene == nullptr) { loadedScene = ResolvePrimaryScene(); } else { sceneManager.SetActiveScene(loadedScene); } return loadedScene != nullptr; } std::string MakeEditorGameObjectItemId(GameObject::ID id) { return id == GameObject::INVALID_ID ? std::string() : std::to_string(id); } std::optional ParseEditorGameObjectItemId( std::string_view itemId) { if (itemId.empty()) { return std::nullopt; } GameObject::ID parsedId = GameObject::INVALID_ID; const char* first = itemId.data(); const char* last = itemId.data() + itemId.size(); const std::from_chars_result result = std::from_chars(first, last, parsedId); if (result.ec != std::errc() || result.ptr != last || parsedId == GameObject::INVALID_ID) { return std::nullopt; } return parsedId; } GameObject* FindEditorGameObject(std::string_view itemId) { Scene* scene = ResolvePrimaryScene(); const std::optional gameObjectId = ParseEditorGameObjectItemId(itemId); if (scene == nullptr || !gameObjectId.has_value()) { return nullptr; } return scene->FindByID(gameObjectId.value()); } bool RenameEditorGameObject( std::string_view itemId, std::string_view newName) { GameObject* gameObject = FindEditorGameObject(itemId); if (gameObject == nullptr) { return false; } gameObject->SetName(std::string(newName)); return true; } bool DeleteEditorGameObject(std::string_view itemId) { Scene* scene = ResolvePrimaryScene(); GameObject* gameObject = FindEditorGameObject(itemId); if (scene == nullptr || gameObject == nullptr) { return false; } scene->DestroyGameObject(gameObject); return true; } std::string DuplicateEditorGameObject(std::string_view itemId) { Scene* scene = ResolvePrimaryScene(); GameObject* gameObject = FindEditorGameObject(itemId); if (scene == nullptr || gameObject == nullptr) { return {}; } const ClipboardNode clipboard = CopyGameObjectRecursive(*gameObject); GameObject* duplicate = PasteGameObjectRecursive(*scene, clipboard, gameObject->GetParent()); return duplicate != nullptr ? MakeEditorGameObjectItemId(duplicate->GetID()) : std::string(); } bool ReparentEditorGameObject( std::string_view itemId, std::string_view parentItemId) { GameObject* gameObject = FindEditorGameObject(itemId); GameObject* newParent = FindEditorGameObject(parentItemId); if (gameObject == nullptr || newParent == nullptr || gameObject == newParent || gameObject->GetParent() == newParent || WouldCreateCycle(*gameObject, *newParent)) { return false; } gameObject->SetParent(newParent); return true; } bool MoveEditorGameObjectToRoot(std::string_view itemId) { GameObject* gameObject = FindEditorGameObject(itemId); if (gameObject == nullptr || gameObject->GetParent() == nullptr) { return false; } gameObject->SetParent(nullptr); return true; } } // namespace XCEngine::UI::Editor::App