feat(editor): unify component registration pipeline
This commit is contained in:
@@ -33,6 +33,7 @@ add_executable(${PROJECT_NAME} WIN32
|
|||||||
src/Application.cpp
|
src/Application.cpp
|
||||||
src/Theme.cpp
|
src/Theme.cpp
|
||||||
src/Core/UndoManager.cpp
|
src/Core/UndoManager.cpp
|
||||||
|
src/ComponentEditors/ComponentEditorRegistry.cpp
|
||||||
src/Managers/SceneManager.cpp
|
src/Managers/SceneManager.cpp
|
||||||
src/Managers/ProjectManager.cpp
|
src/Managers/ProjectManager.cpp
|
||||||
src/Core/EditorConsoleSink.cpp
|
src/Core/EditorConsoleSink.cpp
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IComponentEditor.h"
|
#include "IComponentEditor.h"
|
||||||
|
#include "Core/IUndoManager.h"
|
||||||
#include "UI/UI.h"
|
#include "UI/UI.h"
|
||||||
|
|
||||||
#include <XCEngine/Components/CameraComponent.h>
|
#include <XCEngine/Components/CameraComponent.h>
|
||||||
@@ -10,12 +11,12 @@ namespace Editor {
|
|||||||
|
|
||||||
class CameraComponentEditor : public IComponentEditor {
|
class CameraComponentEditor : public IComponentEditor {
|
||||||
public:
|
public:
|
||||||
const char* GetDisplayName() const override {
|
const char* GetComponentTypeName() const override {
|
||||||
return "Camera";
|
return "Camera";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanEdit(::XCEngine::Components::Component* component) const override {
|
const char* GetDisplayName() const override {
|
||||||
return dynamic_cast<::XCEngine::Components::CameraComponent*>(component) != nullptr;
|
return "Camera";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override {
|
bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override {
|
||||||
@@ -119,10 +120,6 @@ public:
|
|||||||
return gameObject->GetComponent<::XCEngine::Components::CameraComponent>() ? "Already Added" : nullptr;
|
return gameObject->GetComponent<::XCEngine::Components::CameraComponent>() ? "Already Added" : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
::XCEngine::Components::Component* AddTo(::XCEngine::Components::GameObject* gameObject) const override {
|
|
||||||
return gameObject ? gameObject->AddComponent<::XCEngine::Components::CameraComponent>() : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CanRemove(::XCEngine::Components::Component* component) const override {
|
bool CanRemove(::XCEngine::Components::Component* component) const override {
|
||||||
return CanEdit(component);
|
return CanEdit(component);
|
||||||
}
|
}
|
||||||
|
|||||||
45
editor/src/ComponentEditors/ComponentEditorRegistry.cpp
Normal file
45
editor/src/ComponentEditors/ComponentEditorRegistry.cpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#include "ComponentEditors/ComponentEditorRegistry.h"
|
||||||
|
|
||||||
|
#include "ComponentEditors/CameraComponentEditor.h"
|
||||||
|
#include "ComponentEditors/LightComponentEditor.h"
|
||||||
|
#include "ComponentEditors/TransformComponentEditor.h"
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Editor {
|
||||||
|
|
||||||
|
ComponentEditorRegistry& ComponentEditorRegistry::Get() {
|
||||||
|
static ComponentEditorRegistry registry;
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentEditorRegistry::ComponentEditorRegistry() {
|
||||||
|
RegisterEditor(std::make_unique<TransformComponentEditor>());
|
||||||
|
RegisterEditor(std::make_unique<CameraComponentEditor>());
|
||||||
|
RegisterEditor(std::make_unique<LightComponentEditor>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentEditorRegistry::RegisterEditor(std::unique_ptr<IComponentEditor> editor) {
|
||||||
|
if (!editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IComponentEditor* editorPtr = editor.get();
|
||||||
|
m_editorsByType[editor->GetComponentTypeName()] = editorPtr;
|
||||||
|
m_editors.push_back(std::move(editor));
|
||||||
|
}
|
||||||
|
|
||||||
|
IComponentEditor* ComponentEditorRegistry::FindEditor(::XCEngine::Components::Component* component) const {
|
||||||
|
return component ? FindEditorByTypeName(component->GetName()) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
IComponentEditor* ComponentEditorRegistry::FindEditorByTypeName(const std::string& componentTypeName) const {
|
||||||
|
const auto it = m_editorsByType.find(componentTypeName);
|
||||||
|
return it != m_editorsByType.end() ? it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::unique_ptr<IComponentEditor>>& ComponentEditorRegistry::GetEditors() const {
|
||||||
|
return m_editors;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Editor
|
||||||
|
} // namespace XCEngine
|
||||||
30
editor/src/ComponentEditors/ComponentEditorRegistry.h
Normal file
30
editor/src/ComponentEditors/ComponentEditorRegistry.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IComponentEditor.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Editor {
|
||||||
|
|
||||||
|
class ComponentEditorRegistry {
|
||||||
|
public:
|
||||||
|
static ComponentEditorRegistry& Get();
|
||||||
|
|
||||||
|
void RegisterEditor(std::unique_ptr<IComponentEditor> editor);
|
||||||
|
IComponentEditor* FindEditor(::XCEngine::Components::Component* component) const;
|
||||||
|
IComponentEditor* FindEditorByTypeName(const std::string& componentTypeName) const;
|
||||||
|
const std::vector<std::unique_ptr<IComponentEditor>>& GetEditors() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComponentEditorRegistry();
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<IComponentEditor>> m_editors;
|
||||||
|
std::unordered_map<std::string, IComponentEditor*> m_editorsByType;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Editor
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEngine/Components/ComponentFactoryRegistry.h>
|
||||||
#include <XCEngine/Components/Component.h>
|
#include <XCEngine/Components/Component.h>
|
||||||
#include <XCEngine/Components/GameObject.h>
|
#include <XCEngine/Components/GameObject.h>
|
||||||
|
|
||||||
@@ -12,8 +13,11 @@ class IComponentEditor {
|
|||||||
public:
|
public:
|
||||||
virtual ~IComponentEditor() = default;
|
virtual ~IComponentEditor() = default;
|
||||||
|
|
||||||
|
virtual const char* GetComponentTypeName() const = 0;
|
||||||
virtual const char* GetDisplayName() const = 0;
|
virtual const char* GetDisplayName() const = 0;
|
||||||
virtual bool CanEdit(::XCEngine::Components::Component* component) const = 0;
|
virtual bool CanEdit(::XCEngine::Components::Component* component) const {
|
||||||
|
return component && component->GetName() == GetComponentTypeName();
|
||||||
|
}
|
||||||
virtual bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) = 0;
|
virtual bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) = 0;
|
||||||
|
|
||||||
virtual bool ShowInAddComponentMenu() const { return true; }
|
virtual bool ShowInAddComponentMenu() const { return true; }
|
||||||
@@ -23,8 +27,9 @@ public:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
virtual ::XCEngine::Components::Component* AddTo(::XCEngine::Components::GameObject* gameObject) const {
|
virtual ::XCEngine::Components::Component* AddTo(::XCEngine::Components::GameObject* gameObject) const {
|
||||||
(void)gameObject;
|
return gameObject
|
||||||
return nullptr;
|
? ::XCEngine::Components::ComponentFactoryRegistry::Get().CreateComponent(gameObject, GetComponentTypeName())
|
||||||
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool CanRemove(::XCEngine::Components::Component* component) const {
|
virtual bool CanRemove(::XCEngine::Components::Component* component) const {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IComponentEditor.h"
|
#include "IComponentEditor.h"
|
||||||
|
#include "Core/IUndoManager.h"
|
||||||
#include "UI/UI.h"
|
#include "UI/UI.h"
|
||||||
|
|
||||||
#include <XCEngine/Components/LightComponent.h>
|
#include <XCEngine/Components/LightComponent.h>
|
||||||
@@ -10,12 +11,12 @@ namespace Editor {
|
|||||||
|
|
||||||
class LightComponentEditor : public IComponentEditor {
|
class LightComponentEditor : public IComponentEditor {
|
||||||
public:
|
public:
|
||||||
const char* GetDisplayName() const override {
|
const char* GetComponentTypeName() const override {
|
||||||
return "Light";
|
return "Light";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanEdit(::XCEngine::Components::Component* component) const override {
|
const char* GetDisplayName() const override {
|
||||||
return dynamic_cast<::XCEngine::Components::LightComponent*>(component) != nullptr;
|
return "Light";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override {
|
bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override {
|
||||||
@@ -103,10 +104,6 @@ public:
|
|||||||
return gameObject->GetComponent<::XCEngine::Components::LightComponent>() ? "Already Added" : nullptr;
|
return gameObject->GetComponent<::XCEngine::Components::LightComponent>() ? "Already Added" : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
::XCEngine::Components::Component* AddTo(::XCEngine::Components::GameObject* gameObject) const override {
|
|
||||||
return gameObject ? gameObject->AddComponent<::XCEngine::Components::LightComponent>() : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CanRemove(::XCEngine::Components::Component* component) const override {
|
bool CanRemove(::XCEngine::Components::Component* component) const override {
|
||||||
return CanEdit(component);
|
return CanEdit(component);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IComponentEditor.h"
|
#include "IComponentEditor.h"
|
||||||
|
#include "Core/IUndoManager.h"
|
||||||
#include "UI/UI.h"
|
#include "UI/UI.h"
|
||||||
|
|
||||||
#include <XCEngine/Components/TransformComponent.h>
|
#include <XCEngine/Components/TransformComponent.h>
|
||||||
@@ -10,12 +11,12 @@ namespace Editor {
|
|||||||
|
|
||||||
class TransformComponentEditor : public IComponentEditor {
|
class TransformComponentEditor : public IComponentEditor {
|
||||||
public:
|
public:
|
||||||
const char* GetDisplayName() const override {
|
const char* GetComponentTypeName() const override {
|
||||||
return "Transform";
|
return "Transform";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanEdit(::XCEngine::Components::Component* component) const override {
|
const char* GetDisplayName() const override {
|
||||||
return dynamic_cast<::XCEngine::Components::TransformComponent*>(component) != nullptr;
|
return "Transform";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override {
|
bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
#include "SceneManager.h"
|
#include "SceneManager.h"
|
||||||
#include "Core/EventBus.h"
|
#include "Core/EventBus.h"
|
||||||
#include "Core/EditorEvents.h"
|
#include "Core/EditorEvents.h"
|
||||||
|
#include <XCEngine/Components/ComponentFactoryRegistry.h>
|
||||||
#include <XCEngine/Components/CameraComponent.h>
|
#include <XCEngine/Components/CameraComponent.h>
|
||||||
#include <XCEngine/Components/LightComponent.h>
|
#include <XCEngine/Components/LightComponent.h>
|
||||||
#include <XCEngine/Components/AudioSourceComponent.h>
|
|
||||||
#include <XCEngine/Components/AudioListenerComponent.h>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -21,29 +20,6 @@ std::pair<std::string, std::string> SerializeComponent(const ::XCEngine::Compone
|
|||||||
return { component->GetName(), payload.str() };
|
return { component->GetName(), payload.str() };
|
||||||
}
|
}
|
||||||
|
|
||||||
::XCEngine::Components::Component* CreateComponentByName(
|
|
||||||
::XCEngine::Components::GameObject* gameObject,
|
|
||||||
const std::string& componentName) {
|
|
||||||
if (!gameObject) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (componentName == "Camera") {
|
|
||||||
return gameObject->AddComponent<::XCEngine::Components::CameraComponent>();
|
|
||||||
}
|
|
||||||
if (componentName == "Light") {
|
|
||||||
return gameObject->AddComponent<::XCEngine::Components::LightComponent>();
|
|
||||||
}
|
|
||||||
if (componentName == "AudioSource") {
|
|
||||||
return gameObject->AddComponent<::XCEngine::Components::AudioSourceComponent>();
|
|
||||||
}
|
|
||||||
if (componentName == "AudioListener") {
|
|
||||||
return gameObject->AddComponent<::XCEngine::Components::AudioListenerComponent>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
SceneManager::SceneManager(EventBus* eventBus)
|
SceneManager::SceneManager(EventBus* eventBus)
|
||||||
@@ -152,7 +128,7 @@ void SceneManager::CopyEntity(::XCEngine::Components::GameObject::ID id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& componentData : data.components) {
|
for (const auto& componentData : data.components) {
|
||||||
if (auto* component = CreateComponentByName(newEntity, componentData.first)) {
|
if (auto* component = ::XCEngine::Components::ComponentFactoryRegistry::Get().CreateComponent(newEntity, componentData.first)) {
|
||||||
if (!componentData.second.empty()) {
|
if (!componentData.second.empty()) {
|
||||||
std::istringstream payloadStream(componentData.second);
|
std::istringstream payloadStream(componentData.second);
|
||||||
component->Deserialize(payloadStream);
|
component->Deserialize(payloadStream);
|
||||||
|
|||||||
@@ -5,10 +5,8 @@
|
|||||||
#include "Core/IUndoManager.h"
|
#include "Core/IUndoManager.h"
|
||||||
#include "Core/EventBus.h"
|
#include "Core/EventBus.h"
|
||||||
#include "Core/EditorEvents.h"
|
#include "Core/EditorEvents.h"
|
||||||
#include "ComponentEditors/CameraComponentEditor.h"
|
#include "ComponentEditors/ComponentEditorRegistry.h"
|
||||||
#include "ComponentEditors/IComponentEditor.h"
|
#include "ComponentEditors/IComponentEditor.h"
|
||||||
#include "ComponentEditors/LightComponentEditor.h"
|
|
||||||
#include "ComponentEditors/TransformComponentEditor.h"
|
|
||||||
#include "Utils/UndoUtils.h"
|
#include "Utils/UndoUtils.h"
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -16,9 +14,7 @@
|
|||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
InspectorPanel::InspectorPanel() : Panel("Inspector") {
|
InspectorPanel::InspectorPanel() : Panel("Inspector") {}
|
||||||
RegisterDefaultComponentEditors();
|
|
||||||
}
|
|
||||||
|
|
||||||
InspectorPanel::~InspectorPanel() {
|
InspectorPanel::~InspectorPanel() {
|
||||||
if (m_context) {
|
if (m_context) {
|
||||||
@@ -33,34 +29,6 @@ void InspectorPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
|
|||||||
m_selectedEntityId = event.primarySelection;
|
m_selectedEntityId = event.primarySelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InspectorPanel::RegisterDefaultComponentEditors() {
|
|
||||||
RegisterComponentEditor(std::make_unique<TransformComponentEditor>());
|
|
||||||
RegisterComponentEditor(std::make_unique<CameraComponentEditor>());
|
|
||||||
RegisterComponentEditor(std::make_unique<LightComponentEditor>());
|
|
||||||
}
|
|
||||||
|
|
||||||
void InspectorPanel::RegisterComponentEditor(std::unique_ptr<IComponentEditor> editor) {
|
|
||||||
if (!editor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_componentEditors.push_back(std::move(editor));
|
|
||||||
}
|
|
||||||
|
|
||||||
IComponentEditor* InspectorPanel::GetEditorFor(::XCEngine::Components::Component* component) const {
|
|
||||||
if (!component) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& editor : m_componentEditors) {
|
|
||||||
if (editor && editor->CanEdit(component)) {
|
|
||||||
return editor.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InspectorPanel::Render() {
|
void InspectorPanel::Render() {
|
||||||
ImGui::Begin(m_name.c_str(), nullptr, ImGuiWindowFlags_None);
|
ImGui::Begin(m_name.c_str(), nullptr, ImGuiWindowFlags_None);
|
||||||
|
|
||||||
@@ -126,7 +94,7 @@ void InspectorPanel::RenderAddComponentPopup(::XCEngine::Components::GameObject*
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
bool drewAnyEntry = false;
|
bool drewAnyEntry = false;
|
||||||
for (const auto& editor : m_componentEditors) {
|
for (const auto& editor : ComponentEditorRegistry::Get().GetEditors()) {
|
||||||
if (!editor || !editor->ShowInAddComponentMenu()) {
|
if (!editor || !editor->ShowInAddComponentMenu()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -167,7 +135,7 @@ void InspectorPanel::RenderAddComponentPopup(::XCEngine::Components::GameObject*
|
|||||||
void InspectorPanel::RenderComponent(::XCEngine::Components::Component* component, ::XCEngine::Components::GameObject* gameObject) {
|
void InspectorPanel::RenderComponent(::XCEngine::Components::Component* component, ::XCEngine::Components::GameObject* gameObject) {
|
||||||
if (!component) return;
|
if (!component) return;
|
||||||
|
|
||||||
IComponentEditor* editor = GetEditorFor(component);
|
IComponentEditor* editor = ComponentEditorRegistry::Get().FindEditor(component);
|
||||||
|
|
||||||
const char* name = component->GetName().c_str();
|
const char* name = component->GetName().c_str();
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
#include "Panel.h"
|
#include "Panel.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Components {
|
namespace Components {
|
||||||
@@ -14,8 +12,6 @@ class GameObject;
|
|||||||
|
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
class IComponentEditor;
|
|
||||||
|
|
||||||
class InspectorPanel : public Panel {
|
class InspectorPanel : public Panel {
|
||||||
public:
|
public:
|
||||||
InspectorPanel();
|
InspectorPanel();
|
||||||
@@ -24,9 +20,6 @@ public:
|
|||||||
void Render() override;
|
void Render() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RegisterDefaultComponentEditors();
|
|
||||||
void RegisterComponentEditor(std::unique_ptr<IComponentEditor> editor);
|
|
||||||
IComponentEditor* GetEditorFor(::XCEngine::Components::Component* component) const;
|
|
||||||
void RenderGameObject(::XCEngine::Components::GameObject* gameObject);
|
void RenderGameObject(::XCEngine::Components::GameObject* gameObject);
|
||||||
void RenderAddComponentPopup(::XCEngine::Components::GameObject* gameObject);
|
void RenderAddComponentPopup(::XCEngine::Components::GameObject* gameObject);
|
||||||
void RenderComponent(::XCEngine::Components::Component* component, ::XCEngine::Components::GameObject* gameObject);
|
void RenderComponent(::XCEngine::Components::Component* component, ::XCEngine::Components::GameObject* gameObject);
|
||||||
@@ -35,7 +28,6 @@ private:
|
|||||||
|
|
||||||
uint64_t m_selectionHandlerId = 0;
|
uint64_t m_selectionHandlerId = 0;
|
||||||
uint64_t m_selectedEntityId = 0;
|
uint64_t m_selectedEntityId = 0;
|
||||||
std::vector<std::unique_ptr<IComponentEditor>> m_componentEditors;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,6 +247,7 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/LightComponent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/LightComponent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/AudioSourceComponent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/AudioSourceComponent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/AudioListenerComponent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/AudioListenerComponent.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/ComponentFactoryRegistry.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/Component.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/Component.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/TransformComponent.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/TransformComponent.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/GameObject.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/GameObject.cpp
|
||||||
@@ -254,6 +255,7 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/LightComponent.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/LightComponent.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/AudioSourceComponent.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/AudioSourceComponent.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/AudioListenerComponent.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/AudioListenerComponent.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/ComponentFactoryRegistry.cpp
|
||||||
|
|
||||||
# Scene
|
# Scene
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/Scene.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/Scene.h
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
class Component;
|
||||||
|
class GameObject;
|
||||||
|
|
||||||
|
class ComponentFactoryRegistry {
|
||||||
|
public:
|
||||||
|
using CreateComponentFn = Component* (*)(GameObject* gameObject);
|
||||||
|
|
||||||
|
static ComponentFactoryRegistry& Get();
|
||||||
|
|
||||||
|
void RegisterFactory(const std::string& typeName, CreateComponentFn createFn);
|
||||||
|
Component* CreateComponent(GameObject* gameObject, const std::string& typeName) const;
|
||||||
|
bool IsRegistered(const std::string& typeName) const;
|
||||||
|
const std::vector<std::string>& GetRegisteredTypes() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComponentFactoryRegistry();
|
||||||
|
|
||||||
|
std::unordered_map<std::string, CreateComponentFn> m_factories;
|
||||||
|
std::vector<std::string> m_registrationOrder;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
67
engine/src/Components/ComponentFactoryRegistry.cpp
Normal file
67
engine/src/Components/ComponentFactoryRegistry.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include "Components/ComponentFactoryRegistry.h"
|
||||||
|
|
||||||
|
#include "Components/AudioListenerComponent.h"
|
||||||
|
#include "Components/AudioSourceComponent.h"
|
||||||
|
#include "Components/CameraComponent.h"
|
||||||
|
#include "Components/GameObject.h"
|
||||||
|
#include "Components/LightComponent.h"
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Component* CreateBuiltInComponent(GameObject* gameObject) {
|
||||||
|
return gameObject ? gameObject->AddComponent<T>() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ComponentFactoryRegistry& ComponentFactoryRegistry::Get() {
|
||||||
|
static ComponentFactoryRegistry registry;
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentFactoryRegistry::ComponentFactoryRegistry() {
|
||||||
|
RegisterFactory("Camera", &CreateBuiltInComponent<CameraComponent>);
|
||||||
|
RegisterFactory("Light", &CreateBuiltInComponent<LightComponent>);
|
||||||
|
RegisterFactory("AudioSource", &CreateBuiltInComponent<AudioSourceComponent>);
|
||||||
|
RegisterFactory("AudioListener", &CreateBuiltInComponent<AudioListenerComponent>);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentFactoryRegistry::RegisterFactory(const std::string& typeName, CreateComponentFn createFn) {
|
||||||
|
if (typeName.empty() || !createFn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto [it, inserted] = m_factories.insert_or_assign(typeName, createFn);
|
||||||
|
(void)it;
|
||||||
|
if (inserted) {
|
||||||
|
m_registrationOrder.push_back(typeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component* ComponentFactoryRegistry::CreateComponent(GameObject* gameObject, const std::string& typeName) const {
|
||||||
|
if (!gameObject) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto it = m_factories.find(typeName);
|
||||||
|
if (it == m_factories.end() || !it->second) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->second(gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComponentFactoryRegistry::IsRegistered(const std::string& typeName) const {
|
||||||
|
return m_factories.find(typeName) != m_factories.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string>& ComponentFactoryRegistry::GetRegisteredTypes() const {
|
||||||
|
return m_registrationOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
#include "Scene/Scene.h"
|
#include "Scene/Scene.h"
|
||||||
|
#include "Components/ComponentFactoryRegistry.h"
|
||||||
#include "Components/GameObject.h"
|
#include "Components/GameObject.h"
|
||||||
#include "Components/TransformComponent.h"
|
#include "Components/TransformComponent.h"
|
||||||
#include "Components/CameraComponent.h"
|
|
||||||
#include "Components/LightComponent.h"
|
|
||||||
#include "Components/AudioSourceComponent.h"
|
|
||||||
#include "Components/AudioListenerComponent.h"
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -66,27 +63,6 @@ std::string UnescapeString(const std::string& value) {
|
|||||||
return unescaped;
|
return unescaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
Component* CreateComponentByType(GameObject* gameObject, const std::string& type) {
|
|
||||||
if (!gameObject) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == "Camera") {
|
|
||||||
return gameObject->AddComponent<CameraComponent>();
|
|
||||||
}
|
|
||||||
if (type == "Light") {
|
|
||||||
return gameObject->AddComponent<LightComponent>();
|
|
||||||
}
|
|
||||||
if (type == "AudioSource") {
|
|
||||||
return gameObject->AddComponent<AudioSourceComponent>();
|
|
||||||
}
|
|
||||||
if (type == "AudioListener") {
|
|
||||||
return gameObject->AddComponent<AudioListenerComponent>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SerializeGameObjectRecursive(std::ostream& os, GameObject* gameObject) {
|
void SerializeGameObjectRecursive(std::ostream& os, GameObject* gameObject) {
|
||||||
if (!gameObject) {
|
if (!gameObject) {
|
||||||
return;
|
return;
|
||||||
@@ -345,7 +321,7 @@ void Scene::DeserializeFromString(const std::string& data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const PendingComponentData& componentData : pending.components) {
|
for (const PendingComponentData& componentData : pending.components) {
|
||||||
if (Component* component = CreateComponentByType(go.get(), componentData.type)) {
|
if (Component* component = ComponentFactoryRegistry::Get().CreateComponent(go.get(), componentData.type)) {
|
||||||
if (!componentData.payload.empty()) {
|
if (!componentData.payload.empty()) {
|
||||||
std::istringstream componentStream(componentData.payload);
|
std::istringstream componentStream(componentData.payload);
|
||||||
component->Deserialize(componentStream);
|
component->Deserialize(componentStream);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ project(XCEngine_ComponentsTests)
|
|||||||
|
|
||||||
set(COMPONENTS_TEST_SOURCES
|
set(COMPONENTS_TEST_SOURCES
|
||||||
test_component.cpp
|
test_component.cpp
|
||||||
|
test_component_factory_registry.cpp
|
||||||
test_transform_component.cpp
|
test_transform_component.cpp
|
||||||
test_game_object.cpp
|
test_game_object.cpp
|
||||||
test_camera_light_component.cpp
|
test_camera_light_component.cpp
|
||||||
|
|||||||
36
tests/Components/test_component_factory_registry.cpp
Normal file
36
tests/Components/test_component_factory_registry.cpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <XCEngine/Components/AudioListenerComponent.h>
|
||||||
|
#include <XCEngine/Components/AudioSourceComponent.h>
|
||||||
|
#include <XCEngine/Components/CameraComponent.h>
|
||||||
|
#include <XCEngine/Components/ComponentFactoryRegistry.h>
|
||||||
|
#include <XCEngine/Components/GameObject.h>
|
||||||
|
#include <XCEngine/Components/LightComponent.h>
|
||||||
|
|
||||||
|
using namespace XCEngine::Components;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(ComponentFactoryRegistry_Test, BuiltInTypesAreRegistered) {
|
||||||
|
auto& registry = ComponentFactoryRegistry::Get();
|
||||||
|
|
||||||
|
EXPECT_TRUE(registry.IsRegistered("Camera"));
|
||||||
|
EXPECT_TRUE(registry.IsRegistered("Light"));
|
||||||
|
EXPECT_TRUE(registry.IsRegistered("AudioSource"));
|
||||||
|
EXPECT_TRUE(registry.IsRegistered("AudioListener"));
|
||||||
|
EXPECT_FALSE(registry.IsRegistered("Transform"));
|
||||||
|
EXPECT_FALSE(registry.IsRegistered("MissingComponent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ComponentFactoryRegistry_Test, CreateBuiltInComponentsByTypeName) {
|
||||||
|
GameObject gameObject("FactoryTarget");
|
||||||
|
auto& registry = ComponentFactoryRegistry::Get();
|
||||||
|
|
||||||
|
EXPECT_NE(dynamic_cast<CameraComponent*>(registry.CreateComponent(&gameObject, "Camera")), nullptr);
|
||||||
|
EXPECT_NE(dynamic_cast<LightComponent*>(registry.CreateComponent(&gameObject, "Light")), nullptr);
|
||||||
|
EXPECT_NE(dynamic_cast<AudioSourceComponent*>(registry.CreateComponent(&gameObject, "AudioSource")), nullptr);
|
||||||
|
EXPECT_NE(dynamic_cast<AudioListenerComponent*>(registry.CreateComponent(&gameObject, "AudioListener")), nullptr);
|
||||||
|
EXPECT_EQ(registry.CreateComponent(&gameObject, "MissingComponent"), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <XCEngine/Components/AudioListenerComponent.h>
|
||||||
|
#include <XCEngine/Components/AudioSourceComponent.h>
|
||||||
#include <XCEngine/Scene/Scene.h>
|
#include <XCEngine/Scene/Scene.h>
|
||||||
#include <XCEngine/Components/CameraComponent.h>
|
#include <XCEngine/Components/CameraComponent.h>
|
||||||
#include <XCEngine/Components/GameObject.h>
|
#include <XCEngine/Components/GameObject.h>
|
||||||
@@ -262,6 +264,30 @@ TEST_F(SceneTest, Save_ContainsGameObjectData) {
|
|||||||
std::filesystem::remove(scenePath);
|
std::filesystem::remove(scenePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneTest, Save_And_Load_PreservesAudioComponents) {
|
||||||
|
GameObject* listenerObject = testScene->CreateGameObject("Listener");
|
||||||
|
GameObject* sourceObject = testScene->CreateGameObject("Source");
|
||||||
|
|
||||||
|
listenerObject->AddComponent<AudioListenerComponent>();
|
||||||
|
sourceObject->AddComponent<AudioSourceComponent>();
|
||||||
|
|
||||||
|
const std::filesystem::path scenePath = GetTempScenePath("test_scene_audio_components.xc");
|
||||||
|
testScene->Save(scenePath.string());
|
||||||
|
|
||||||
|
Scene loadedScene;
|
||||||
|
loadedScene.Load(scenePath.string());
|
||||||
|
|
||||||
|
GameObject* loadedListenerObject = loadedScene.Find("Listener");
|
||||||
|
GameObject* loadedSourceObject = loadedScene.Find("Source");
|
||||||
|
ASSERT_NE(loadedListenerObject, nullptr);
|
||||||
|
ASSERT_NE(loadedSourceObject, nullptr);
|
||||||
|
|
||||||
|
EXPECT_NE(loadedListenerObject->GetComponent<AudioListenerComponent>(), nullptr);
|
||||||
|
EXPECT_NE(loadedSourceObject->GetComponent<AudioSourceComponent>(), nullptr);
|
||||||
|
|
||||||
|
std::filesystem::remove(scenePath);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SceneTest, Save_And_Load_PreservesHierarchyAndComponents) {
|
TEST_F(SceneTest, Save_And_Load_PreservesHierarchyAndComponents) {
|
||||||
testScene->SetName("Serialized Scene");
|
testScene->SetName("Serialized Scene");
|
||||||
testScene->SetActive(false);
|
testScene->SetActive(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user