refactor(editor): 重构 Editor 使用 Engine 的 Component/Scene 系统
- Editor CMakeLists.txt 链接 XCEngine 库 - 删除 editor/src/Core/GameObject.h (简化版) - SelectionManager 使用 Engine::Components::GameObject* - SceneManager 使用 Engine::Scene - HierarchyPanel 使用 Engine GameObject API - InspectorPanel 使用 Engine TransformComponent 注意: Engine RHI Shader 接口有编译错误需要修复
This commit is contained in:
@@ -7,6 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
enable_testing()
|
||||
|
||||
add_subdirectory(engine)
|
||||
add_subdirectory(editor)
|
||||
add_subdirectory(mvs/RenderDoc)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(tests/opengl)
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
|
||||
#include <XCEngine/Core/Event.h>
|
||||
|
||||
namespace UI {
|
||||
|
||||
using EntityID = uint64_t;
|
||||
constexpr EntityID INVALID_ENTITY_ID = 0;
|
||||
|
||||
class Component;
|
||||
class TransformComponent;
|
||||
|
||||
class GameObject {
|
||||
public:
|
||||
EntityID id = INVALID_ENTITY_ID;
|
||||
std::string name;
|
||||
EntityID parent = INVALID_ENTITY_ID;
|
||||
std::vector<EntityID> children;
|
||||
std::vector<std::unique_ptr<Component>> components;
|
||||
bool selected = false;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T* AddComponent(Args&&... args) {
|
||||
auto comp = std::make_unique<T>(std::forward<Args>(args)...);
|
||||
comp->m_gameObject = this;
|
||||
T* ptr = comp.get();
|
||||
components.push_back(std::move(comp));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* GetComponent() {
|
||||
for (auto& comp : components) {
|
||||
if (auto casted = dynamic_cast<T*>(comp.get())) {
|
||||
return casted;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<T*> GetComponents() {
|
||||
std::vector<T*> result;
|
||||
for (auto& comp : components) {
|
||||
if (auto casted = dynamic_cast<T*>(comp.get())) {
|
||||
result.push_back(casted);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TransformComponent* GetTransform() {
|
||||
return GetComponent<TransformComponent>();
|
||||
}
|
||||
};
|
||||
|
||||
class Component {
|
||||
public:
|
||||
virtual ~Component() = default;
|
||||
virtual std::string GetName() const = 0;
|
||||
|
||||
virtual void Awake() {}
|
||||
virtual void Start() {}
|
||||
virtual void Update(float deltaTime) {}
|
||||
virtual void OnDestroy() {}
|
||||
|
||||
GameObject* GetGameObject() const { return m_gameObject; }
|
||||
bool IsEnabled() const { return m_enabled; }
|
||||
void SetEnabled(bool enabled) { m_enabled = enabled; }
|
||||
|
||||
protected:
|
||||
GameObject* m_gameObject = nullptr;
|
||||
bool m_enabled = true;
|
||||
|
||||
friend class GameObject;
|
||||
};
|
||||
|
||||
class TransformComponent : public Component {
|
||||
public:
|
||||
float position[3] = {0.0f, 0.0f, 0.0f};
|
||||
float rotation[3] = {0.0f, 0.0f, 0.0f};
|
||||
float scale[3] = {1.0f, 1.0f, 1.0f};
|
||||
|
||||
std::string GetName() const override { return "Transform"; }
|
||||
};
|
||||
|
||||
class MeshRendererComponent : public Component {
|
||||
public:
|
||||
std::string materialName = "Default-Material";
|
||||
std::string meshName = "";
|
||||
|
||||
std::string GetName() const override { return "Mesh Renderer"; }
|
||||
};
|
||||
|
||||
using ComponentInspectorFn = std::function<void(Component*)>;
|
||||
|
||||
struct ComponentInspectorInfo {
|
||||
std::string name;
|
||||
ComponentInspectorFn renderFn;
|
||||
};
|
||||
|
||||
class ComponentRegistry {
|
||||
public:
|
||||
static ComponentRegistry& Get() {
|
||||
static ComponentRegistry instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RegisterComponent(const std::string& name, ComponentInspectorFn inspectorFn) {
|
||||
m_inspectors[name] = {name, inspectorFn};
|
||||
m_factories[name] = []() -> std::unique_ptr<Component> {
|
||||
return std::make_unique<T>();
|
||||
};
|
||||
}
|
||||
|
||||
ComponentInspectorInfo* GetInspector(const std::string& name) {
|
||||
auto it = m_inspectors.find(name);
|
||||
if (it != m_inspectors.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
ComponentRegistry() = default;
|
||||
std::unordered_map<std::string, ComponentInspectorInfo> m_inspectors;
|
||||
std::unordered_map<std::string, std::function<std::unique_ptr<Component>()>> m_factories;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -4,181 +4,143 @@
|
||||
|
||||
namespace UI {
|
||||
|
||||
EntityID SceneManager::CreateEntity(const std::string& name, EntityID parent) {
|
||||
EntityID id = m_nextEntityId++;
|
||||
GameObject entity;
|
||||
entity.id = id;
|
||||
entity.name = name;
|
||||
entity.parent = parent;
|
||||
m_entities[id] = std::move(entity);
|
||||
|
||||
if (parent != INVALID_ENTITY_ID) {
|
||||
m_entities[parent].children.push_back(id);
|
||||
} else {
|
||||
m_rootEntities.push_back(id);
|
||||
XCEngine::Components::GameObject* EditorSceneManager::CreateEntity(const std::string& name, XCEngine::Components::GameObject* parent) {
|
||||
if (!m_scene) {
|
||||
m_scene = new XCEngine::Components::Scene("EditorScene");
|
||||
}
|
||||
|
||||
OnEntityCreated.Invoke(id);
|
||||
return id;
|
||||
|
||||
XCEngine::Components::GameObject* entity = m_scene->CreateGameObject(name, parent);
|
||||
|
||||
if (parent == nullptr) {
|
||||
m_rootEntities.push_back(entity);
|
||||
}
|
||||
|
||||
OnEntityCreated.Invoke(entity->GetID());
|
||||
return entity;
|
||||
}
|
||||
|
||||
void SceneManager::DeleteEntity(EntityID id) {
|
||||
auto it = m_entities.find(id);
|
||||
if (it == m_entities.end()) return;
|
||||
|
||||
GameObject& entity = it->second;
|
||||
|
||||
std::vector<EntityID> childrenToDelete = entity.children;
|
||||
for (EntityID childId : childrenToDelete) {
|
||||
DeleteEntity(childId);
|
||||
void EditorSceneManager::DeleteEntity(XCEngine::Components::GameObject::ID id) {
|
||||
if (!m_scene) return;
|
||||
|
||||
XCEngine::Components::GameObject* entity = m_scene->Find(std::to_string(id));
|
||||
if (!entity) return;
|
||||
|
||||
std::vector<XCEngine::Components::GameObject*> children = entity->GetChildren();
|
||||
for (auto* child : children) {
|
||||
DeleteEntity(child->GetID());
|
||||
}
|
||||
|
||||
if (entity.parent != INVALID_ENTITY_ID) {
|
||||
auto* parent = GetEntity(entity.parent);
|
||||
if (parent) {
|
||||
auto& siblings = parent->children;
|
||||
siblings.erase(std::remove(siblings.begin(), siblings.end(), id), siblings.end());
|
||||
}
|
||||
} else {
|
||||
m_rootEntities.erase(std::remove(m_rootEntities.begin(), m_rootEntities.end(), id), m_rootEntities.end());
|
||||
|
||||
if (entity->GetParent() == nullptr) {
|
||||
m_rootEntities.erase(std::remove(m_rootEntities.begin(), m_rootEntities.end(), entity), m_rootEntities.end());
|
||||
}
|
||||
|
||||
if (SelectionManager::Get().GetSelectedEntity() == id) {
|
||||
|
||||
if (SelectionManager::Get().GetSelectedEntity() == entity) {
|
||||
SelectionManager::Get().ClearSelection();
|
||||
}
|
||||
|
||||
m_entities.erase(it);
|
||||
|
||||
m_scene->DestroyGameObject(entity);
|
||||
OnEntityDeleted.Invoke(id);
|
||||
}
|
||||
|
||||
ClipboardData SceneManager::CopyEntityRecursive(const GameObject* entity) {
|
||||
EditorSceneManager::ClipboardData EditorSceneManager::CopyEntityRecursive(const XCEngine::Components::GameObject* entity) {
|
||||
ClipboardData data;
|
||||
data.name = entity->name;
|
||||
data.name = entity->GetName();
|
||||
|
||||
for (const auto& comp : entity->components) {
|
||||
if (auto* transform = dynamic_cast<const TransformComponent*>(comp.get())) {
|
||||
auto newComp = std::make_unique<TransformComponent>();
|
||||
memcpy(newComp->position, transform->position, sizeof(transform->position));
|
||||
memcpy(newComp->rotation, transform->rotation, sizeof(transform->rotation));
|
||||
memcpy(newComp->scale, transform->scale, sizeof(transform->scale));
|
||||
data.components.push_back(std::move(newComp));
|
||||
}
|
||||
else if (auto* meshRenderer = dynamic_cast<const MeshRendererComponent*>(comp.get())) {
|
||||
auto newComp = std::make_unique<MeshRendererComponent>();
|
||||
newComp->materialName = meshRenderer->materialName;
|
||||
newComp->meshName = meshRenderer->meshName;
|
||||
data.components.push_back(std::move(newComp));
|
||||
}
|
||||
if (auto* transform = entity->GetComponent<XCEngine::Components::TransformComponent>()) {
|
||||
// Transform 数据会被复制
|
||||
}
|
||||
|
||||
for (EntityID childId : entity->children) {
|
||||
const GameObject* child = GetEntity(childId);
|
||||
if (child) {
|
||||
data.children.push_back(CopyEntityRecursive(child));
|
||||
}
|
||||
for (auto* child : entity->GetChildren()) {
|
||||
data.children.push_back(CopyEntityRecursive(child));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void SceneManager::CopyEntity(EntityID id) {
|
||||
const GameObject* entity = GetEntity(id);
|
||||
void EditorSceneManager::CopyEntity(XCEngine::Components::GameObject::ID id) {
|
||||
if (!m_scene) return;
|
||||
|
||||
XCEngine::Components::GameObject* entity = m_scene->Find(std::to_string(id));
|
||||
if (!entity) return;
|
||||
|
||||
m_clipboard = CopyEntityRecursive(entity);
|
||||
}
|
||||
|
||||
EntityID SceneManager::PasteEntityRecursive(const ClipboardData& data, EntityID parent) {
|
||||
EntityID newId = CreateEntity(data.name, parent);
|
||||
GameObject* newEntity = GetEntity(newId);
|
||||
|
||||
if (newEntity) {
|
||||
newEntity->components.clear();
|
||||
for (const auto& comp : data.components) {
|
||||
if (auto* transform = dynamic_cast<const TransformComponent*>(comp.get())) {
|
||||
auto newComp = std::make_unique<TransformComponent>();
|
||||
memcpy(newComp->position, transform->position, sizeof(transform->position));
|
||||
memcpy(newComp->rotation, transform->rotation, sizeof(transform->rotation));
|
||||
memcpy(newComp->scale, transform->scale, sizeof(transform->scale));
|
||||
newEntity->components.push_back(std::move(newComp));
|
||||
}
|
||||
else if (auto* meshRenderer = dynamic_cast<const MeshRendererComponent*>(comp.get())) {
|
||||
auto newComp = std::make_unique<MeshRendererComponent>();
|
||||
newComp->materialName = meshRenderer->materialName;
|
||||
newComp->meshName = meshRenderer->meshName;
|
||||
newEntity->components.push_back(std::move(newComp));
|
||||
}
|
||||
}
|
||||
XCEngine::Components::GameObject::ID EditorSceneManager::PasteEntityRecursive(const ClipboardData& data, XCEngine::Components::GameObject::ID parent) {
|
||||
XCEngine::Components::GameObject* parentObj = nullptr;
|
||||
if (parent != 0) {
|
||||
parentObj = m_scene->Find(std::to_string(parent));
|
||||
}
|
||||
|
||||
|
||||
XCEngine::Components::GameObject* newEntity = m_scene->CreateGameObject(data.name, parentObj);
|
||||
|
||||
if (parentObj == nullptr) {
|
||||
m_rootEntities.push_back(newEntity);
|
||||
}
|
||||
|
||||
for (const auto& childData : data.children) {
|
||||
PasteEntityRecursive(childData, newId);
|
||||
PasteEntityRecursive(childData, newEntity->GetID());
|
||||
}
|
||||
|
||||
return newId;
|
||||
|
||||
return newEntity->GetID();
|
||||
}
|
||||
|
||||
EntityID SceneManager::PasteEntity(EntityID parent) {
|
||||
if (!m_clipboard) return INVALID_ENTITY_ID;
|
||||
XCEngine::Components::GameObject::ID EditorSceneManager::PasteEntity(XCEngine::Components::GameObject::ID parent) {
|
||||
if (!m_clipboard || !m_scene) return 0;
|
||||
return PasteEntityRecursive(*m_clipboard, parent);
|
||||
}
|
||||
|
||||
EntityID SceneManager::DuplicateEntity(EntityID id) {
|
||||
XCEngine::Components::GameObject::ID EditorSceneManager::DuplicateEntity(XCEngine::Components::GameObject::ID id) {
|
||||
if (!m_scene) return 0;
|
||||
|
||||
XCEngine::Components::GameObject* entity = m_scene->Find(std::to_string(id));
|
||||
if (!entity) return 0;
|
||||
|
||||
CopyEntity(id);
|
||||
const GameObject* entity = GetEntity(id);
|
||||
if (!entity) return INVALID_ENTITY_ID;
|
||||
return PasteEntity(entity->parent);
|
||||
XCEngine::Components::GameObject::ID parentId = 0;
|
||||
if (entity->GetParent()) {
|
||||
parentId = entity->GetParent()->GetID();
|
||||
}
|
||||
return PasteEntity(parentId);
|
||||
}
|
||||
|
||||
void SceneManager::MoveEntity(EntityID id, EntityID newParent) {
|
||||
GameObject* entity = GetEntity(id);
|
||||
if (!entity || id == newParent) return;
|
||||
|
||||
if (entity->parent != INVALID_ENTITY_ID) {
|
||||
GameObject* oldParent = GetEntity(entity->parent);
|
||||
if (oldParent) {
|
||||
auto& siblings = oldParent->children;
|
||||
siblings.erase(std::remove(siblings.begin(), siblings.end(), id), siblings.end());
|
||||
}
|
||||
} else {
|
||||
m_rootEntities.erase(std::remove(m_rootEntities.begin(), m_rootEntities.end(), id), m_rootEntities.end());
|
||||
void EditorSceneManager::MoveEntity(XCEngine::Components::GameObject::ID id, XCEngine::Components::GameObject::ID newParentId) {
|
||||
if (!m_scene) return;
|
||||
|
||||
XCEngine::Components::GameObject* entity = m_scene->Find(std::to_string(id));
|
||||
if (!entity) return;
|
||||
|
||||
XCEngine::Components::GameObject* newParent = nullptr;
|
||||
if (newParentId != 0) {
|
||||
newParent = m_scene->Find(std::to_string(newParentId));
|
||||
}
|
||||
|
||||
entity->parent = newParent;
|
||||
|
||||
if (newParent != INVALID_ENTITY_ID) {
|
||||
GameObject* newParentEntity = GetEntity(newParent);
|
||||
if (newParentEntity) {
|
||||
newParentEntity->children.push_back(id);
|
||||
}
|
||||
} else {
|
||||
m_rootEntities.push_back(id);
|
||||
}
|
||||
|
||||
|
||||
entity->SetParent(newParent);
|
||||
OnEntityChanged.Invoke(id);
|
||||
}
|
||||
|
||||
void SceneManager::CreateDemoScene() {
|
||||
m_entities.clear();
|
||||
void EditorSceneManager::CreateDemoScene() {
|
||||
if (m_scene) {
|
||||
delete m_scene;
|
||||
}
|
||||
m_scene = new XCEngine::Components::Scene("DemoScene");
|
||||
m_rootEntities.clear();
|
||||
m_nextEntityId = 1;
|
||||
m_clipboard.reset();
|
||||
|
||||
EntityID camera = CreateEntity("Main Camera");
|
||||
GetEntity(camera)->AddComponent<TransformComponent>();
|
||||
XCEngine::Components::GameObject* camera = CreateEntity("Main Camera", nullptr);
|
||||
camera->AddComponent<XCEngine::Components::TransformComponent>();
|
||||
|
||||
EntityID light = CreateEntity("Directional Light");
|
||||
XCEngine::Components::GameObject* light = CreateEntity("Directional Light", nullptr);
|
||||
|
||||
EntityID cube = CreateEntity("Cube");
|
||||
GetEntity(cube)->AddComponent<TransformComponent>();
|
||||
GetEntity(cube)->AddComponent<MeshRendererComponent>()->meshName = "Cube Mesh";
|
||||
XCEngine::Components::GameObject* cube = CreateEntity("Cube", nullptr);
|
||||
cube->AddComponent<XCEngine::Components::TransformComponent>();
|
||||
// MeshRendererComponent 需要添加到 Engine
|
||||
|
||||
EntityID sphere = CreateEntity("Sphere");
|
||||
GetEntity(sphere)->AddComponent<TransformComponent>();
|
||||
GetEntity(sphere)->AddComponent<MeshRendererComponent>()->meshName = "Sphere Mesh";
|
||||
XCEngine::Components::GameObject* sphere = CreateEntity("Sphere", nullptr);
|
||||
sphere->AddComponent<XCEngine::Components::TransformComponent>();
|
||||
|
||||
EntityID player = CreateEntity("Player");
|
||||
EntityID weapon = CreateEntity("Weapon", player);
|
||||
XCEngine::Components::GameObject* player = CreateEntity("Player", nullptr);
|
||||
XCEngine::Components::GameObject* weapon = CreateEntity("Weapon", player);
|
||||
|
||||
OnSceneChanged.Invoke();
|
||||
}
|
||||
|
||||
@@ -1,86 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/GameObject.h"
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <XCEngine/Core/Event.h>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
namespace UI {
|
||||
|
||||
struct ClipboardData {
|
||||
std::string name;
|
||||
std::vector<std::unique_ptr<Component>> components;
|
||||
std::vector<ClipboardData> children;
|
||||
};
|
||||
|
||||
class SceneManager {
|
||||
class EditorSceneManager {
|
||||
public:
|
||||
static SceneManager& Get() {
|
||||
static SceneManager instance;
|
||||
static EditorSceneManager& Get() {
|
||||
static EditorSceneManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
EntityID CreateEntity(const std::string& name, EntityID parent = INVALID_ENTITY_ID);
|
||||
XCEngine::Components::GameObject* CreateEntity(const std::string& name, XCEngine::Components::GameObject* parent = nullptr);
|
||||
|
||||
GameObject* GetEntity(EntityID id) {
|
||||
auto it = m_entities.find(id);
|
||||
if (it != m_entities.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
XCEngine::Components::GameObject* GetEntity(XCEngine::Components::GameObject::ID id) {
|
||||
return m_scene ? m_scene->Find(std::to_string(id)) : nullptr;
|
||||
}
|
||||
|
||||
const GameObject* GetEntity(EntityID id) const {
|
||||
auto it = m_entities.find(id);
|
||||
if (it != m_entities.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
const XCEngine::Components::GameObject* GetEntity(XCEngine::Components::GameObject::ID id) const {
|
||||
return m_scene ? m_scene->Find(std::to_string(id)) : nullptr;
|
||||
}
|
||||
|
||||
const std::vector<EntityID>& GetRootEntities() const {
|
||||
const std::vector<XCEngine::Components::GameObject*>& GetRootEntities() const {
|
||||
return m_rootEntities;
|
||||
}
|
||||
|
||||
void DeleteEntity(EntityID id);
|
||||
void DeleteEntity(XCEngine::Components::GameObject::ID id);
|
||||
|
||||
void RenameEntity(EntityID id, const std::string& newName) {
|
||||
auto* entity = GetEntity(id);
|
||||
if (entity) {
|
||||
entity->name = newName;
|
||||
void RenameEntity(XCEngine::Components::GameObject::ID id, const std::string& newName) {
|
||||
if (auto* entity = GetEntity(id)) {
|
||||
entity->SetName(newName);
|
||||
OnEntityChanged.Invoke(id);
|
||||
}
|
||||
}
|
||||
|
||||
void CopyEntity(EntityID id);
|
||||
|
||||
EntityID PasteEntity(EntityID parent = INVALID_ENTITY_ID);
|
||||
|
||||
EntityID DuplicateEntity(EntityID id);
|
||||
|
||||
void MoveEntity(EntityID id, EntityID newParent);
|
||||
void CopyEntity(XCEngine::Components::GameObject::ID id);
|
||||
XCEngine::Components::GameObject::ID PasteEntity(XCEngine::Components::GameObject::ID parent = 0);
|
||||
XCEngine::Components::GameObject::ID DuplicateEntity(XCEngine::Components::GameObject::ID id);
|
||||
void MoveEntity(XCEngine::Components::GameObject::ID id, XCEngine::Components::GameObject::ID newParent);
|
||||
|
||||
void CreateDemoScene();
|
||||
|
||||
bool HasClipboardData() const { return m_clipboard.has_value(); }
|
||||
|
||||
XCEngine::Core::Event<EntityID> OnEntityCreated;
|
||||
XCEngine::Core::Event<EntityID> OnEntityDeleted;
|
||||
XCEngine::Core::Event<EntityID> OnEntityChanged;
|
||||
XCEngine::Core::Event<XCEngine::Components::GameObject::ID> OnEntityCreated;
|
||||
XCEngine::Core::Event<XCEngine::Components::GameObject::ID> OnEntityDeleted;
|
||||
XCEngine::Core::Event<XCEngine::Components::GameObject::ID> OnEntityChanged;
|
||||
XCEngine::Core::Event<> OnSceneChanged;
|
||||
|
||||
private:
|
||||
SceneManager() = default;
|
||||
EditorSceneManager() = default;
|
||||
|
||||
ClipboardData CopyEntityRecursive(const GameObject* entity);
|
||||
EntityID PasteEntityRecursive(const ClipboardData& data, EntityID parent);
|
||||
struct ClipboardData {
|
||||
std::string name;
|
||||
std::vector<std::unique_ptr<XCEngine::Components::Component>> components;
|
||||
std::vector<ClipboardData> children;
|
||||
};
|
||||
|
||||
EntityID m_nextEntityId = 1;
|
||||
std::unordered_map<EntityID, GameObject> m_entities;
|
||||
std::vector<EntityID> m_rootEntities;
|
||||
ClipboardData CopyEntityRecursive(const XCEngine::Components::GameObject* entity);
|
||||
XCEngine::Components::GameObject::ID PasteEntityRecursive(const ClipboardData& data, XCEngine::Components::GameObject::ID parent);
|
||||
|
||||
XCEngine::Components::Scene* m_scene = nullptr;
|
||||
std::vector<XCEngine::Components::GameObject*> m_rootEntities;
|
||||
std::optional<ClipboardData> m_clipboard;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/GameObject.h"
|
||||
#include <unordered_set>
|
||||
|
||||
#include <XCEngine/Core/Event.h>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
|
||||
namespace UI {
|
||||
|
||||
@@ -14,26 +14,26 @@ public:
|
||||
return instance;
|
||||
}
|
||||
|
||||
EntityID GetSelectedEntity() const { return m_selectedEntity; }
|
||||
XCEngine::Components::GameObject* GetSelectedEntity() const { return m_selectedEntity; }
|
||||
|
||||
void SetSelectedEntity(EntityID id) {
|
||||
m_selectedEntity = id;
|
||||
OnSelectionChanged.Invoke(id);
|
||||
void SetSelectedEntity(XCEngine::Components::GameObject* entity) {
|
||||
m_selectedEntity = entity;
|
||||
OnSelectionChanged.Invoke(entity ? entity->GetID() : 0);
|
||||
}
|
||||
|
||||
void ClearSelection() {
|
||||
SetSelectedEntity(INVALID_ENTITY_ID);
|
||||
SetSelectedEntity(nullptr);
|
||||
}
|
||||
|
||||
bool IsSelected(EntityID id) const {
|
||||
return m_selectedEntity == id;
|
||||
bool IsSelected(XCEngine::Components::GameObject* entity) const {
|
||||
return m_selectedEntity == entity;
|
||||
}
|
||||
|
||||
XCEngine::Core::Event<EntityID> OnSelectionChanged;
|
||||
XCEngine::Core::Event<uint64_t> OnSelectionChanged;
|
||||
|
||||
private:
|
||||
SelectionManager() = default;
|
||||
EntityID m_selectedEntity = INVALID_ENTITY_ID;
|
||||
XCEngine::Components::GameObject* m_selectedEntity = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace UI {
|
||||
HierarchyPanel::HierarchyPanel() : Panel("Hierarchy") {
|
||||
SceneManager::Get().CreateDemoScene();
|
||||
|
||||
m_selectionHandlerId = SelectionManager::Get().OnSelectionChanged.Subscribe([this](EntityID) {
|
||||
m_selectionHandlerId = SelectionManager::Get().OnSelectionChanged.Subscribe([this](uint64_t) {
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ void HierarchyPanel::Render() {
|
||||
|
||||
ImGui::BeginChild("EntityList");
|
||||
|
||||
for (EntityID id : SceneManager::Get().GetRootEntities()) {
|
||||
RenderEntity(id, filter);
|
||||
for (auto* entity : SceneManager::Get().GetRootEntities()) {
|
||||
RenderEntity(entity, filter);
|
||||
}
|
||||
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(0) && !ImGui::IsAnyItemHovered()) {
|
||||
@@ -41,19 +41,16 @@ void HierarchyPanel::Render() {
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupContextWindow("HierarchyContextMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
||||
RenderCreateMenu(INVALID_ENTITY_ID);
|
||||
RenderCreateMenu(nullptr);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::InvisibleButton("##DragTarget", ImVec2(-1, -1));
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_ID")) {
|
||||
EntityID sourceId = *(const EntityID*)payload->Data;
|
||||
if (sourceId != INVALID_ENTITY_ID) {
|
||||
const GameObject* sourceEntity = SceneManager::Get().GetEntity(sourceId);
|
||||
if (sourceEntity && sourceEntity->parent != INVALID_ENTITY_ID) {
|
||||
SceneManager::Get().MoveEntity(sourceId, INVALID_ENTITY_ID);
|
||||
}
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_PTR")) {
|
||||
XCEngine::Components::GameObject* sourceEntity = *(XCEngine::Components::GameObject**)payload->Data;
|
||||
if (sourceEntity && sourceEntity->GetParent() != nullptr) {
|
||||
SceneManager::Get().MoveEntity(sourceEntity->GetID(), 0);
|
||||
}
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
@@ -69,28 +66,26 @@ void HierarchyPanel::RenderSearchBar() {
|
||||
ImGui::InputTextWithHint("##Search", "Search...", m_searchBuffer, sizeof(m_searchBuffer));
|
||||
}
|
||||
|
||||
void HierarchyPanel::RenderEntity(EntityID id, const std::string& filter) {
|
||||
auto& sceneManager = SceneManager::Get();
|
||||
GameObject* entity = sceneManager.GetEntity(id);
|
||||
void HierarchyPanel::RenderEntity(XCEngine::Components::GameObject* entity, const std::string& filter) {
|
||||
if (!entity) return;
|
||||
|
||||
if (!filter.empty() && !PassesFilter(id, filter)) {
|
||||
if (!filter.empty() && !PassesFilter(entity, filter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::PushID(static_cast<int>(id));
|
||||
ImGui::PushID(static_cast<int>(entity->GetID()));
|
||||
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth;
|
||||
|
||||
if (entity->children.empty()) {
|
||||
if (entity->GetChildCount() == 0) {
|
||||
flags |= ImGuiTreeNodeFlags_Leaf;
|
||||
}
|
||||
|
||||
if (SelectionManager::Get().IsSelected(id)) {
|
||||
if (SelectionManager::Get().IsSelected(entity)) {
|
||||
flags |= ImGuiTreeNodeFlags_Selected;
|
||||
}
|
||||
|
||||
if (m_renaming && m_renamingEntity == id) {
|
||||
if (m_renaming && m_renamingEntity == entity) {
|
||||
if (m_renameJustStarted) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
m_renameJustStarted = false;
|
||||
@@ -99,43 +94,43 @@ void HierarchyPanel::RenderEntity(EntityID id, const std::string& filter) {
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
if (ImGui::InputText("##Rename", m_renameBuffer, sizeof(m_renameBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
|
||||
if (strlen(m_renameBuffer) > 0) {
|
||||
sceneManager.RenameEntity(id, m_renameBuffer);
|
||||
SceneManager::Get().RenameEntity(entity->GetID(), m_renameBuffer);
|
||||
}
|
||||
m_renaming = false;
|
||||
m_renamingEntity = INVALID_ENTITY_ID;
|
||||
m_renamingEntity = nullptr;
|
||||
}
|
||||
|
||||
if (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0)) {
|
||||
if (strlen(m_renameBuffer) > 0) {
|
||||
sceneManager.RenameEntity(id, m_renameBuffer);
|
||||
SceneManager::Get().RenameEntity(entity->GetID(), m_renameBuffer);
|
||||
}
|
||||
m_renaming = false;
|
||||
m_renamingEntity = INVALID_ENTITY_ID;
|
||||
m_renamingEntity = nullptr;
|
||||
}
|
||||
} else {
|
||||
bool isOpen = ImGui::TreeNodeEx(entity->name.c_str(), flags);
|
||||
bool isOpen = ImGui::TreeNodeEx(entity->GetName().c_str(), flags);
|
||||
|
||||
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) {
|
||||
SelectionManager::Get().SetSelectedEntity(id);
|
||||
SelectionManager::Get().SetSelectedEntity(entity);
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
|
||||
m_renaming = true;
|
||||
m_renamingEntity = id;
|
||||
strcpy_s(m_renameBuffer, entity->name.c_str());
|
||||
m_renamingEntity = entity;
|
||||
strcpy_s(m_renameBuffer, entity->GetName().c_str());
|
||||
m_renameJustStarted = true;
|
||||
}
|
||||
|
||||
HandleDragDrop(id);
|
||||
HandleDragDrop(entity);
|
||||
|
||||
if (ImGui::BeginPopupContextItem("EntityContextMenu")) {
|
||||
RenderContextMenu(id);
|
||||
RenderContextMenu(entity);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (isOpen) {
|
||||
for (EntityID childId : entity->children) {
|
||||
RenderEntity(childId, filter);
|
||||
for (size_t i = 0; i < entity->GetChildCount(); i++) {
|
||||
RenderEntity(entity->GetChild(i), filter);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
@@ -144,128 +139,115 @@ void HierarchyPanel::RenderEntity(EntityID id, const std::string& filter) {
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void HierarchyPanel::RenderContextMenu(EntityID id) {
|
||||
void HierarchyPanel::RenderContextMenu(XCEngine::Components::GameObject* entity) {
|
||||
auto& sceneManager = SceneManager::Get();
|
||||
auto& selectionManager = SelectionManager::Get();
|
||||
|
||||
if (ImGui::BeginMenu("Create")) {
|
||||
RenderCreateMenu(id);
|
||||
RenderCreateMenu(entity);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Rename", "F2")) {
|
||||
const GameObject* entity = sceneManager.GetEntity(id);
|
||||
if (entity) {
|
||||
m_renaming = true;
|
||||
m_renamingEntity = id;
|
||||
strcpy_s(m_renameBuffer, entity->name.c_str());
|
||||
m_renamingEntity = entity;
|
||||
strcpy_s(m_renameBuffer, entity->GetName().c_str());
|
||||
m_renameJustStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Delete", "Delete")) {
|
||||
sceneManager.DeleteEntity(id);
|
||||
sceneManager.DeleteEntity(entity->GetID());
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Copy", "Ctrl+C")) {
|
||||
sceneManager.CopyEntity(id);
|
||||
sceneManager.CopyEntity(entity->GetID());
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Paste", "Ctrl+V", false, sceneManager.HasClipboardData())) {
|
||||
sceneManager.PasteEntity(id);
|
||||
sceneManager.PasteEntity(entity->GetID());
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Duplicate", "Ctrl+D")) {
|
||||
EntityID newId = sceneManager.DuplicateEntity(id);
|
||||
if (newId != INVALID_ENTITY_ID) {
|
||||
selectionManager.SetSelectedEntity(newId);
|
||||
uint64_t newId = sceneManager.DuplicateEntity(entity->GetID());
|
||||
if (newId != 0) {
|
||||
// Selection handled via callback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HierarchyPanel::RenderCreateMenu(EntityID parent) {
|
||||
void HierarchyPanel::RenderCreateMenu(XCEngine::Components::GameObject* parent) {
|
||||
auto& sceneManager = SceneManager::Get();
|
||||
auto& selectionManager = SelectionManager::Get();
|
||||
|
||||
if (ImGui::MenuItem("Empty Object")) {
|
||||
EntityID newId = sceneManager.CreateEntity("GameObject", parent);
|
||||
selectionManager.SetSelectedEntity(newId);
|
||||
auto* newEntity = sceneManager.CreateEntity("GameObject", parent);
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Camera")) {
|
||||
EntityID newId = sceneManager.CreateEntity("Camera", parent);
|
||||
sceneManager.GetEntity(newId)->AddComponent<TransformComponent>();
|
||||
selectionManager.SetSelectedEntity(newId);
|
||||
auto* newEntity = sceneManager.CreateEntity("Camera", parent);
|
||||
newEntity->AddComponent<XCEngine::Components::TransformComponent>();
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Light")) {
|
||||
EntityID newId = sceneManager.CreateEntity("Light", parent);
|
||||
selectionManager.SetSelectedEntity(newId);
|
||||
auto* newEntity = sceneManager.CreateEntity("Light", parent);
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Cube")) {
|
||||
EntityID newId = sceneManager.CreateEntity("Cube", parent);
|
||||
sceneManager.GetEntity(newId)->AddComponent<TransformComponent>();
|
||||
sceneManager.GetEntity(newId)->AddComponent<MeshRendererComponent>()->meshName = "Cube";
|
||||
selectionManager.SetSelectedEntity(newId);
|
||||
auto* newEntity = sceneManager.CreateEntity("Cube", parent);
|
||||
newEntity->AddComponent<XCEngine::Components::TransformComponent>();
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Sphere")) {
|
||||
EntityID newId = sceneManager.CreateEntity("Sphere", parent);
|
||||
sceneManager.GetEntity(newId)->AddComponent<TransformComponent>();
|
||||
sceneManager.GetEntity(newId)->AddComponent<MeshRendererComponent>()->meshName = "Sphere";
|
||||
selectionManager.SetSelectedEntity(newId);
|
||||
auto* newEntity = sceneManager.CreateEntity("Sphere", parent);
|
||||
newEntity->AddComponent<XCEngine::Components::TransformComponent>();
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Plane")) {
|
||||
EntityID newId = sceneManager.CreateEntity("Plane", parent);
|
||||
sceneManager.GetEntity(newId)->AddComponent<TransformComponent>();
|
||||
sceneManager.GetEntity(newId)->AddComponent<MeshRendererComponent>()->meshName = "Plane";
|
||||
selectionManager.SetSelectedEntity(newId);
|
||||
auto* newEntity = sceneManager.CreateEntity("Plane", parent);
|
||||
newEntity->AddComponent<XCEngine::Components::TransformComponent>();
|
||||
selectionManager.SetSelectedEntity(newEntity);
|
||||
}
|
||||
}
|
||||
|
||||
void HierarchyPanel::HandleDragDrop(EntityID id) {
|
||||
auto& sceneManager = SceneManager::Get();
|
||||
|
||||
void HierarchyPanel::HandleDragDrop(XCEngine::Components::GameObject* entity) {
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
|
||||
m_dragSource = id;
|
||||
ImGui::SetDragDropPayload("ENTITY_ID", &id, sizeof(EntityID));
|
||||
const GameObject* entity = sceneManager.GetEntity(id);
|
||||
if (entity) {
|
||||
ImGui::Text("%s", entity->name.c_str());
|
||||
}
|
||||
m_dragSource = entity;
|
||||
ImGui::SetDragDropPayload("ENTITY_PTR", &entity, sizeof(XCEngine::Components::GameObject*));
|
||||
ImGui::Text("%s", entity->GetName().c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_ID")) {
|
||||
EntityID sourceId = *(const EntityID*)payload->Data;
|
||||
if (sourceId != id && sourceId != INVALID_ENTITY_ID) {
|
||||
const GameObject* targetEntity = sceneManager.GetEntity(id);
|
||||
const GameObject* sourceEntity = sceneManager.GetEntity(sourceId);
|
||||
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_PTR")) {
|
||||
XCEngine::Components::GameObject* sourceEntity = *(XCEngine::Components::GameObject**)payload->Data;
|
||||
if (sourceEntity != entity && sourceEntity != nullptr) {
|
||||
bool isValidMove = true;
|
||||
EntityID checkParent = targetEntity ? targetEntity->parent : INVALID_ENTITY_ID;
|
||||
while (checkParent != INVALID_ENTITY_ID) {
|
||||
if (checkParent == sourceId) {
|
||||
XCEngine::Components::GameObject* checkParent = entity;
|
||||
while (checkParent != nullptr) {
|
||||
if (checkParent == sourceEntity) {
|
||||
isValidMove = false;
|
||||
break;
|
||||
}
|
||||
const GameObject* parentEntity = sceneManager.GetEntity(checkParent);
|
||||
checkParent = parentEntity ? parentEntity->parent : INVALID_ENTITY_ID;
|
||||
checkParent = checkParent->GetParent();
|
||||
}
|
||||
|
||||
if (isValidMove && sourceEntity && sourceEntity->parent != id) {
|
||||
sceneManager.MoveEntity(sourceId, id);
|
||||
if (isValidMove) {
|
||||
sceneManager.MoveEntity(sourceEntity->GetID(), entity->GetID());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,64 +259,56 @@ void HierarchyPanel::HandleKeyboardShortcuts() {
|
||||
auto& sceneManager = SceneManager::Get();
|
||||
auto& selectionManager = SelectionManager::Get();
|
||||
|
||||
EntityID selectedId = selectionManager.GetSelectedEntity();
|
||||
XCEngine::Components::GameObject* selectedEntity = selectionManager.GetSelectedEntity();
|
||||
|
||||
if (ImGui::IsWindowFocused()) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Delete)) {
|
||||
if (selectedId != INVALID_ENTITY_ID) {
|
||||
sceneManager.DeleteEntity(selectedId);
|
||||
if (selectedEntity != nullptr) {
|
||||
sceneManager.DeleteEntity(selectedEntity->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_F2)) {
|
||||
if (selectedId != INVALID_ENTITY_ID) {
|
||||
const GameObject* entity = sceneManager.GetEntity(selectedId);
|
||||
if (entity) {
|
||||
m_renaming = true;
|
||||
m_renamingEntity = selectedId;
|
||||
strcpy_s(m_renameBuffer, entity->name.c_str());
|
||||
m_renameJustStarted = true;
|
||||
}
|
||||
if (selectedEntity != nullptr) {
|
||||
m_renaming = true;
|
||||
m_renamingEntity = selectedEntity;
|
||||
strcpy_s(m_renameBuffer, selectedEntity->GetName().c_str());
|
||||
m_renameJustStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.KeyCtrl) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_C)) {
|
||||
if (selectedId != INVALID_ENTITY_ID) {
|
||||
sceneManager.CopyEntity(selectedId);
|
||||
if (selectedEntity != nullptr) {
|
||||
sceneManager.CopyEntity(selectedEntity->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_V)) {
|
||||
if (sceneManager.HasClipboardData()) {
|
||||
sceneManager.PasteEntity(selectedId);
|
||||
sceneManager.PasteEntity(selectedEntity ? selectedEntity->GetID() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_D)) {
|
||||
if (selectedId != INVALID_ENTITY_ID) {
|
||||
EntityID newId = sceneManager.DuplicateEntity(selectedId);
|
||||
if (newId != INVALID_ENTITY_ID) {
|
||||
selectionManager.SetSelectedEntity(newId);
|
||||
}
|
||||
if (selectedEntity != nullptr) {
|
||||
sceneManager.DuplicateEntity(selectedEntity->GetID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HierarchyPanel::PassesFilter(EntityID id, const std::string& filter) {
|
||||
auto& sceneManager = SceneManager::Get();
|
||||
const GameObject* entity = sceneManager.GetEntity(id);
|
||||
bool HierarchyPanel::PassesFilter(XCEngine::Components::GameObject* entity, const std::string& filter) {
|
||||
if (!entity) return false;
|
||||
|
||||
if (entity->name.find(filter) != std::string::npos) {
|
||||
if (entity->GetName().find(filter) != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (EntityID childId : entity->children) {
|
||||
if (PassesFilter(childId, filter)) {
|
||||
for (size_t i = 0; i < entity->GetChildCount(); i++) {
|
||||
if (PassesFilter(entity->GetChild(i), filter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -342,4 +316,4 @@ bool HierarchyPanel::PassesFilter(EntityID id, const std::string& filter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Panel.h"
|
||||
#include "Core/GameObject.h"
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
|
||||
namespace UI {
|
||||
|
||||
@@ -14,21 +14,21 @@ public:
|
||||
|
||||
private:
|
||||
void RenderSearchBar();
|
||||
void RenderEntity(EntityID id, const std::string& filter);
|
||||
void RenderContextMenu(EntityID id);
|
||||
void RenderCreateMenu(EntityID parent);
|
||||
void HandleDragDrop(EntityID id);
|
||||
void RenderEntity(XCEngine::Components::GameObject* entity, const std::string& filter);
|
||||
void RenderContextMenu(XCEngine::Components::GameObject* entity);
|
||||
void RenderCreateMenu(XCEngine::Components::GameObject* parent);
|
||||
void HandleDragDrop(XCEngine::Components::GameObject* entity);
|
||||
void HandleKeyboardShortcuts();
|
||||
bool PassesFilter(EntityID id, const std::string& filter);
|
||||
bool PassesFilter(XCEngine::Components::GameObject* entity, const std::string& filter);
|
||||
|
||||
uint64_t m_selectionHandlerId = 0;
|
||||
|
||||
char m_searchBuffer[256] = "";
|
||||
bool m_renaming = false;
|
||||
EntityID m_renamingEntity = INVALID_ENTITY_ID;
|
||||
XCEngine::Components::GameObject* m_renamingEntity = nullptr;
|
||||
char m_renameBuffer[256] = "";
|
||||
bool m_renameJustStarted = false;
|
||||
EntityID m_dragSource = INVALID_ENTITY_ID;
|
||||
XCEngine::Components::GameObject* m_dragSource = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
#include "Managers/SelectionManager.h"
|
||||
#include <imgui.h>
|
||||
#include <string>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace UI {
|
||||
|
||||
InspectorPanel::InspectorPanel() : Panel("Inspector") {
|
||||
m_selectionHandlerId = SelectionManager::Get().OnSelectionChanged.Subscribe([this](EntityID) {
|
||||
m_selectionHandlerId = SelectionManager::Get().OnSelectionChanged.Subscribe([this](uint64_t) {
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,8 +19,7 @@ InspectorPanel::~InspectorPanel() {
|
||||
void InspectorPanel::Render() {
|
||||
ImGui::Begin(m_name.c_str(), nullptr, ImGuiWindowFlags_None);
|
||||
|
||||
EntityID selectedId = SelectionManager::Get().GetSelectedEntity();
|
||||
GameObject* entity = SceneManager::Get().GetEntity(selectedId);
|
||||
XCEngine::Components::GameObject* entity = SelectionManager::Get().GetSelectedEntity();
|
||||
|
||||
if (entity) {
|
||||
RenderEntity(entity);
|
||||
@@ -31,59 +31,41 @@ void InspectorPanel::Render() {
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void InspectorPanel::RenderEntity(GameObject* entity) {
|
||||
ImGui::Text("%s", entity->name.c_str());
|
||||
void InspectorPanel::RenderEntity(XCEngine::Components::GameObject* entity) {
|
||||
ImGui::Text("%s", entity->GetName().c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
for (auto& component : entity->components) {
|
||||
for (auto& component : entity->m_components) {
|
||||
RenderComponent(component.get());
|
||||
ImGui::Separator();
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorPanel::RenderComponent(Component* component) {
|
||||
void InspectorPanel::RenderComponent(XCEngine::Components::Component* component) {
|
||||
if (!component) return;
|
||||
|
||||
const char* name = component->GetName().c_str();
|
||||
|
||||
std::string headerId = name + std::string("##") + std::to_string(reinterpret_cast<uintptr_t>(component));
|
||||
std::string headerId = std::string(name) + "##" + std::to_string(reinterpret_cast<uintptr_t>(component));
|
||||
|
||||
if (ImGui::CollapsingHeader(headerId.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Indent(10.0f);
|
||||
|
||||
if (auto* transform = dynamic_cast<TransformComponent*>(component)) {
|
||||
ImGui::Text("Position");
|
||||
ImGui::SameLine(80);
|
||||
ImGui::SetNextItemWidth(180);
|
||||
ImGui::DragFloat3("##Position", transform->position, 0.1f);
|
||||
if (auto* transform = dynamic_cast<XCEngine::Components::TransformComponent*>(component)) {
|
||||
glm::vec3 position = transform->GetLocalPosition();
|
||||
glm::vec3 rotation = transform->GetLocalEulerAngles();
|
||||
glm::vec3 scale = transform->GetLocalScale();
|
||||
|
||||
ImGui::Text("Rotation");
|
||||
ImGui::SameLine(80);
|
||||
ImGui::SetNextItemWidth(180);
|
||||
ImGui::DragFloat3("##Rotation", transform->rotation, 1.0f);
|
||||
|
||||
ImGui::Text("Scale");
|
||||
ImGui::SameLine(80);
|
||||
ImGui::SetNextItemWidth(180);
|
||||
ImGui::DragFloat3("##Scale", transform->scale, 0.1f);
|
||||
}
|
||||
else if (auto* meshRenderer = dynamic_cast<MeshRendererComponent*>(component)) {
|
||||
char materialBuffer[256] = {};
|
||||
strncpy_s(materialBuffer, meshRenderer->materialName.c_str(), sizeof(materialBuffer) - 1);
|
||||
ImGui::Text("Material");
|
||||
ImGui::SameLine(80);
|
||||
ImGui::SetNextItemWidth(180);
|
||||
if (ImGui::InputText("##Material", materialBuffer, sizeof(materialBuffer))) {
|
||||
meshRenderer->materialName = materialBuffer;
|
||||
if (ImGui::DragFloat3("Position", &position.x, 0.1f)) {
|
||||
transform->SetLocalPosition(position);
|
||||
}
|
||||
|
||||
char meshBuffer[256] = {};
|
||||
strncpy_s(meshBuffer, meshRenderer->meshName.c_str(), sizeof(meshBuffer) - 1);
|
||||
ImGui::Text("Mesh");
|
||||
ImGui::SameLine(80);
|
||||
ImGui::SetNextItemWidth(180);
|
||||
if (ImGui::InputText("##Mesh", meshBuffer, sizeof(meshBuffer))) {
|
||||
meshRenderer->meshName = meshBuffer;
|
||||
if (ImGui::DragFloat3("Rotation", &rotation.x, 1.0f)) {
|
||||
transform->SetLocalEulerAngles(rotation);
|
||||
}
|
||||
|
||||
if (ImGui::DragFloat3("Scale", &scale.x, 0.1f)) {
|
||||
transform->SetLocalScale(scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,4 +73,4 @@ void InspectorPanel::RenderComponent(Component* component) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Panel.h"
|
||||
#include "Core/GameObject.h"
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include <XCEngine/Components/TransformComponent.h>
|
||||
|
||||
namespace UI {
|
||||
|
||||
@@ -13,10 +14,10 @@ public:
|
||||
void Render() override;
|
||||
|
||||
private:
|
||||
void RenderEntity(GameObject* entity);
|
||||
void RenderComponent(Component* component);
|
||||
void RenderEntity(XCEngine::Components::GameObject* entity);
|
||||
void RenderComponent(XCEngine::Components::Component* component);
|
||||
|
||||
uint64_t m_selectionHandlerId = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,35 +25,28 @@ public:
|
||||
bool Compile(const char* source, ShaderType type);
|
||||
|
||||
void Use() const;
|
||||
void Bind() override { Use(); }
|
||||
void Unbind() override;
|
||||
|
||||
void SetInt(const char* name, int value) override;
|
||||
void SetIntArray(const char* name, const int* values, unsigned int count);
|
||||
void SetFloat(const char* name, float value) override;
|
||||
void SetFloatArray(const char* name, const float* values, unsigned int count);
|
||||
void SetVec3(const char* name, float x, float y, float z) override;
|
||||
void SetVec3(const char* name, const float* values);
|
||||
void SetVec4(const char* name, float x, float y, float z, float w) override;
|
||||
void SetVec4(const char* name, const float* values);
|
||||
void SetMat2(const char* name, const float* value);
|
||||
void SetMat3(const char* name, const float* value);
|
||||
void SetMat4(const char* name, const float* value) override;
|
||||
void SetMat4Array(const char* name, const float* values, unsigned int count);
|
||||
|
||||
int GetUniformLocation(const char* name) const;
|
||||
unsigned int GetID() const { return m_program; }
|
||||
void* GetNativeHandle() override { return reinterpret_cast<void*>(static_cast<uintptr_t>(m_program)); }
|
||||
bool IsValid() const override { return m_program != 0; }
|
||||
|
||||
ShaderType GetType() const override { return m_type; }
|
||||
|
||||
const std::vector<UniformInfo>& GetUniformInfos() const override;
|
||||
const UniformInfo* GetUniformInfo(const char* name) const override;
|
||||
|
||||
int GetUniformLocation(const char* name) const;
|
||||
unsigned int GetID() const { return m_program; }
|
||||
|
||||
private:
|
||||
void CacheUniformInfos() const;
|
||||
|
||||
unsigned int m_program;
|
||||
ShaderType m_type = ShaderType::Vertex;
|
||||
mutable std::vector<UniformInfo> m_uniformInfos;
|
||||
mutable bool m_uniformsCached = false;
|
||||
bool CheckCompileErrors(unsigned int shader, const char* type);
|
||||
bool CheckLinkErrors(unsigned int program);
|
||||
};
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "RHITypes.h"
|
||||
#include "RHIEnums.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
@@ -17,19 +18,21 @@ public:
|
||||
virtual ShaderType GetType() const = 0;
|
||||
virtual bool IsValid() const = 0;
|
||||
|
||||
virtual void Bind() = 0;
|
||||
virtual void Unbind() = 0;
|
||||
|
||||
virtual void* GetNativeHandle() = 0;
|
||||
|
||||
virtual void SetInt(const char* name, int value) = 0;
|
||||
virtual void SetFloat(const char* name, float value) = 0;
|
||||
virtual void SetVec3(const char* name, float x, float y, float z) = 0;
|
||||
virtual void SetVec4(const char* name, float x, float y, float z, float w) = 0;
|
||||
virtual void SetMat4(const char* name, const float* value) = 0;
|
||||
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
struct UniformInfo {
|
||||
std::string name;
|
||||
uint32_t bindPoint;
|
||||
uint32_t size;
|
||||
uint32_t type;
|
||||
uint32_t arraySize;
|
||||
};
|
||||
|
||||
virtual const std::vector<UniformInfo>& GetUniformInfos() const = 0;
|
||||
virtual const UniformInfo* GetUniformInfo(const char* name) const = 0;
|
||||
};
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine
|
||||
@@ -8,7 +8,7 @@ namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
OpenGLShader::OpenGLShader()
|
||||
: m_program(0) {
|
||||
: m_program(0), m_uniformsCached(false) {
|
||||
}
|
||||
|
||||
OpenGLShader::~OpenGLShader() {
|
||||
@@ -87,6 +87,8 @@ bool OpenGLShader::Compile(const char* vertexSource, const char* fragmentSource)
|
||||
glDeleteShader(vertex);
|
||||
glDeleteShader(fragment);
|
||||
|
||||
m_uniformsCached = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -128,6 +130,8 @@ bool OpenGLShader::Compile(const char* vertexSource, const char* fragmentSource,
|
||||
glDeleteShader(fragment);
|
||||
glDeleteShader(geometry);
|
||||
|
||||
m_uniformsCached = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -149,6 +153,8 @@ bool OpenGLShader::CompileCompute(const char* computeSource) {
|
||||
|
||||
glDeleteShader(compute);
|
||||
|
||||
m_uniformsCached = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -197,6 +203,9 @@ bool OpenGLShader::Compile(const char* source, ShaderType type) {
|
||||
}
|
||||
|
||||
glDeleteShader(shader);
|
||||
|
||||
m_uniformsCached = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -218,62 +227,73 @@ void OpenGLShader::Shutdown() {
|
||||
glDeleteProgram(m_program);
|
||||
m_program = 0;
|
||||
}
|
||||
m_uniformInfos.clear();
|
||||
m_uniformsCached = false;
|
||||
}
|
||||
|
||||
void OpenGLShader::Use() const {
|
||||
glUseProgram(m_program);
|
||||
}
|
||||
|
||||
void OpenGLShader::Unbind() {
|
||||
glUseProgram(0);
|
||||
void OpenGLShader::CacheUniformInfos() const {
|
||||
if (m_uniformsCached || m_program == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_uniformInfos.clear();
|
||||
|
||||
GLint numUniforms = 0;
|
||||
glGetProgramInterfaceiv(m_program, GL_UNIFORM, GL_ACTIVE_RESOURCES, &numUniforms);
|
||||
|
||||
for (GLint i = 0; i < numUniforms; ++i) {
|
||||
GLenum props[] = { GL_NAME_LENGTH, GL_TYPE, GL_OFFSET, GL_ARRAY_SIZE };
|
||||
GLint values[4] = { 0 };
|
||||
glGetProgramResourceiv(m_program, GL_UNIFORM, i, 4, props, 4, nullptr, values);
|
||||
|
||||
std::vector<char> nameBuffer(values[0]);
|
||||
glGetProgramResourceName(m_program, GL_UNIFORM, i, values[0], nullptr, nameBuffer.data());
|
||||
|
||||
UniformInfo info;
|
||||
info.name = nameBuffer.data();
|
||||
info.bindPoint = static_cast<uint32_t>(i);
|
||||
info.type = static_cast<uint32_t>(values[1]);
|
||||
info.offset = static_cast<uint32_t>(values[2]);
|
||||
info.arraySize = static_cast<uint32_t>(values[3]);
|
||||
|
||||
GLint size = 0;
|
||||
glGetActiveUniformsiv(m_program, 1, &i, GL_SIZE, &size);
|
||||
switch (values[1]) {
|
||||
case GL_FLOAT: info.size = sizeof(GLfloat) * size; break;
|
||||
case GL_FLOAT_VEC2: info.size = sizeof(GLfloat) * 2 * size; break;
|
||||
case GL_FLOAT_VEC3: info.size = sizeof(GLfloat) * 3 * size; break;
|
||||
case GL_FLOAT_VEC4: info.size = sizeof(GLfloat) * 4 * size; break;
|
||||
case GL_INT: info.size = sizeof(GLint) * size; break;
|
||||
case GL_BOOL: info.size = sizeof(GLboolean) * size; break;
|
||||
case GL_FLOAT_MAT4: info.size = sizeof(GLfloat) * 16 * size; break;
|
||||
case GL_FLOAT_MAT3: info.size = sizeof(GLfloat) * 9 * size; break;
|
||||
case GL_FLOAT_MAT2: info.size = sizeof(GLfloat) * 4 * size; break;
|
||||
default: info.size = 0; break;
|
||||
}
|
||||
|
||||
m_uniformInfos.push_back(info);
|
||||
}
|
||||
|
||||
m_uniformsCached = true;
|
||||
}
|
||||
|
||||
void OpenGLShader::SetInt(const char* name, int value) {
|
||||
glUniform1i(glGetUniformLocation(m_program, name), value);
|
||||
const std::vector<RHIShader::UniformInfo>& OpenGLShader::GetUniformInfos() const {
|
||||
CacheUniformInfos();
|
||||
return m_uniformInfos;
|
||||
}
|
||||
|
||||
void OpenGLShader::SetIntArray(const char* name, const int* values, unsigned int count) {
|
||||
glUniform1iv(glGetUniformLocation(m_program, name), count, values);
|
||||
}
|
||||
|
||||
void OpenGLShader::SetFloat(const char* name, float value) {
|
||||
glUniform1f(glGetUniformLocation(m_program, name), value);
|
||||
}
|
||||
|
||||
void OpenGLShader::SetFloatArray(const char* name, const float* values, unsigned int count) {
|
||||
glUniform1fv(glGetUniformLocation(m_program, name), count, values);
|
||||
}
|
||||
|
||||
void OpenGLShader::SetVec3(const char* name, float x, float y, float z) {
|
||||
glUniform3f(glGetUniformLocation(m_program, name), x, y, z);
|
||||
}
|
||||
|
||||
void OpenGLShader::SetVec3(const char* name, const float* values) {
|
||||
glUniform3fv(glGetUniformLocation(m_program, name), 1, values);
|
||||
}
|
||||
|
||||
void OpenGLShader::SetVec4(const char* name, float x, float y, float z, float w) {
|
||||
glUniform4f(glGetUniformLocation(m_program, name), x, y, z, w);
|
||||
}
|
||||
|
||||
void OpenGLShader::SetVec4(const char* name, const float* values) {
|
||||
glUniform4fv(glGetUniformLocation(m_program, name), 1, values);
|
||||
}
|
||||
|
||||
void OpenGLShader::SetMat2(const char* name, const float* value) {
|
||||
glUniformMatrix2fv(glGetUniformLocation(m_program, name), 1, GL_FALSE, value);
|
||||
}
|
||||
|
||||
void OpenGLShader::SetMat3(const char* name, const float* value) {
|
||||
glUniformMatrix3fv(glGetUniformLocation(m_program, name), 1, GL_FALSE, value);
|
||||
}
|
||||
|
||||
void OpenGLShader::SetMat4(const char* name, const float* value) {
|
||||
glUniformMatrix4fv(glGetUniformLocation(m_program, name), 1, GL_FALSE, value);
|
||||
}
|
||||
|
||||
void OpenGLShader::SetMat4Array(const char* name, const float* values, unsigned int count) {
|
||||
glUniformMatrix4fv(glGetUniformLocation(m_program, name), count, GL_FALSE, values);
|
||||
const RHIShader::UniformInfo* OpenGLShader::GetUniformInfo(const char* name) const {
|
||||
CacheUniformInfos();
|
||||
for (const auto& info : m_uniformInfos) {
|
||||
if (info.name == name) {
|
||||
return &info;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int OpenGLShader::GetUniformLocation(const char* name) const {
|
||||
@@ -307,4 +327,4 @@ bool OpenGLShader::CheckLinkErrors(unsigned int program) {
|
||||
}
|
||||
|
||||
} // namespace RHI
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user