Create ISceneManager interface and fix GetSceneManager return type
- Created ISceneManager interface with Editor需要的 SceneManager 方法 - SceneManager now implements ISceneManager - IEditorContext::GetSceneManager() now returns ISceneManager& instead of void* - Removed SceneManager::GetSceneManagerConcrete() method - Updated HierarchyPanel and InspectorPanel to use ISceneManager interface
This commit is contained in:
144
docs/issues/RHI_OpenGL_State_Not_Applied.md
Normal file
144
docs/issues/RHI_OpenGL_State_Not_Applied.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# RHI 模块严重问题:OpenGL 后端 RHI 状态不生效
|
||||
|
||||
## 问题严重程度
|
||||
|
||||
**严重级别**: 🔴 Critical Bug
|
||||
|
||||
## 问题定位
|
||||
|
||||
`OpenGLPipelineState` 维护了两套状态系统,但它们之间没有关联。
|
||||
|
||||
### 两套状态系统
|
||||
|
||||
**RHI 抽象层状态** (`OpenGLPipelineState.h:131-133`):
|
||||
|
||||
```cpp
|
||||
RasterizerDesc m_rasterizerDesc;
|
||||
BlendDesc m_blendDesc;
|
||||
DepthStencilStateDesc m_depthStencilDesc;
|
||||
```
|
||||
|
||||
**OpenGL 特定状态** (`OpenGLPipelineState.h:141-143`):
|
||||
|
||||
```cpp
|
||||
OpenGLDepthStencilState m_glDepthStencilState;
|
||||
OpenGLBlendState m_glBlendState;
|
||||
OpenGLRasterizerState m_glRasterizerState;
|
||||
```
|
||||
|
||||
## 问题详述
|
||||
|
||||
### RHI 接口设置的状态只存储不生效
|
||||
|
||||
```cpp
|
||||
// OpenGLPipelineState.cpp:19-21
|
||||
void OpenGLPipelineState::SetRasterizerState(const RasterizerDesc& state) {
|
||||
m_rasterizerDesc = state; // ❌ 只存储到 m_rasterizerDesc
|
||||
}
|
||||
|
||||
void OpenGLPipelineState::SetBlendState(const BlendDesc& state) {
|
||||
m_blendDesc = state; // ❌ 只存储到 m_blendDesc
|
||||
}
|
||||
|
||||
void OpenGLPipelineState::SetDepthStencilState(const DepthStencilStateDesc& state) {
|
||||
m_depthStencilDesc = state; // ❌ 只存储到 m_depthStencilDesc
|
||||
}
|
||||
```
|
||||
|
||||
### Apply() 使用的是另一套状态
|
||||
|
||||
```cpp
|
||||
// OpenGLPipelineState.cpp:133-149
|
||||
void OpenGLPipelineState::ApplyRasterizer() {
|
||||
if (m_glRasterizerState.cullFaceEnable) { // ✅ 使用的是 m_glRasterizerState
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(static_cast<GLenum>(m_glRasterizerState.cullFace));
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLPipelineState::ApplyBlend() {
|
||||
if (m_glBlendState.blendEnable) { // ✅ 使用的是 m_glBlendState
|
||||
glEnable(GL_BLEND);
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLPipelineState::ApplyDepthStencil() {
|
||||
if (m_glDepthStencilState.depthTestEnable) { // ✅ 使用的是 m_glDepthStencilState
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 导致的后果
|
||||
|
||||
**通过 RHI 抽象层设置的状态完全不会生效!**
|
||||
|
||||
```cpp
|
||||
// 上层代码
|
||||
RasterizerDesc raster;
|
||||
raster.cullMode = 2; // 设置剔除模式
|
||||
pso->SetRasterizerState(raster); // 通过 RHI 接口设置
|
||||
|
||||
// 实际渲染时
|
||||
pso->Bind(); // 调用 ApplyRasterizer()
|
||||
// ❌ m_glRasterizerState.cullMode 仍然是默认值,不是设置的值!
|
||||
```
|
||||
|
||||
## 对比 D3D12 后端
|
||||
|
||||
D3D12 后端正确地在 `CreateD3D12PSO()` 中使用了 RHI 状态:
|
||||
|
||||
```cpp
|
||||
// D3D12PipelineState.cpp:157-161
|
||||
desc.RasterizerState.FillMode = static_cast<D3D12_FILL_MODE>(m_rasterizerDesc.fillMode);
|
||||
desc.RasterizerState.CullMode = static_cast<D3D12_CULL_MODE>(m_rasterizerDesc.cullMode);
|
||||
// ✅ 直接使用 m_rasterizerDesc
|
||||
```
|
||||
|
||||
## 根本原因
|
||||
|
||||
OpenGL 后端有两个 `SetRasterizerState` 重载:
|
||||
|
||||
```cpp
|
||||
// RHI 接口 - 只存储
|
||||
void SetRasterizerState(const RasterizerDesc& state);
|
||||
|
||||
// OpenGL 特定接口 - 会生效
|
||||
void SetRasterizerState(const OpenGLRasterizerState& state);
|
||||
```
|
||||
|
||||
RHI 接口的实现缺少状态转换逻辑。
|
||||
|
||||
## 修复方案
|
||||
|
||||
在 `SetRasterizerState(const RasterizerDesc& state)` 中添加转换逻辑:
|
||||
|
||||
```cpp
|
||||
void OpenGLPipelineState::SetRasterizerState(const RasterizerDesc& state) {
|
||||
m_rasterizerDesc = state;
|
||||
|
||||
// 转换到 OpenGL 状态
|
||||
m_glRasterizerState.cullFaceEnable = (state.cullMode != 0); // CullMode::None
|
||||
m_glRasterizerState.cullFace = static_cast<CullMode>(state.cullMode);
|
||||
m_glRasterizerState.frontFace = static_cast<FrontFace>(state.frontFace);
|
||||
m_glRasterizerState.polygonMode = static_cast<FillMode>(state.fillMode);
|
||||
m_glRasterizerState.depthClipEnable = state.depthClipEnable;
|
||||
// ... 其他字段
|
||||
}
|
||||
```
|
||||
|
||||
同样需要修复 `SetBlendState` 和 `SetDepthStencilState`。
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `engine/include/XCEngine/RHI/OpenGL/OpenGLPipelineState.h`
|
||||
- `engine/src/RHI/OpenGL/OpenGLPipelineState.cpp`
|
||||
|
||||
## 影响范围
|
||||
|
||||
1. **所有通过 RHI 抽象层设置的状态都不会生效**
|
||||
2. **OpenGL 集成测试能工作是因为使用了 OpenGL 特定 API**
|
||||
3. **上层代码如果使用 RHI 接口,行为会与预期不符**
|
||||
@@ -1,174 +0,0 @@
|
||||
# RHI 模块严重问题:PipelineState 与 Shader 关系设计混乱
|
||||
|
||||
## 问题严重程度
|
||||
|
||||
**严重级别**: 🔴 架构级缺陷(Critical)
|
||||
|
||||
## 问题定位
|
||||
|
||||
根据核心设计理念:
|
||||
> - **求同存异**:优先提取 API 共性作为核心抽象
|
||||
> - **核心约束原则**:面向 D3D12/Vulkan 设计,参考 Unity RHI
|
||||
|
||||
当前设计存在一个**根本性的架构缺陷**。
|
||||
|
||||
## 问题详述
|
||||
|
||||
### `RHIDevice::CreatePipelineState()` 接口设计错误
|
||||
|
||||
```cpp
|
||||
// engine/include/XCEngine/RHI/RHIDevice.h:41
|
||||
virtual RHIPipelineState* CreatePipelineState(const GraphicsPipelineDesc& desc) = 0;
|
||||
```
|
||||
|
||||
### `GraphicsPipelineDesc` 缺少 Shader 字段
|
||||
|
||||
```cpp
|
||||
// engine/include/XCEngine/RHI/RHITypes.h:309-320
|
||||
struct GraphicsPipelineDesc {
|
||||
InputLayoutDesc inputLayout;
|
||||
RasterizerDesc rasterizerState;
|
||||
BlendDesc blendState;
|
||||
DepthStencilStateDesc depthStencilState;
|
||||
|
||||
uint32_t topologyType = 0;
|
||||
uint32_t renderTargetCount = 1;
|
||||
uint32_t renderTargetFormats[8] = { 0 };
|
||||
uint32_t depthStencilFormat = 0;
|
||||
uint32_t sampleCount = 1;
|
||||
// ❌ 缺少 Shader 字段!
|
||||
};
|
||||
```
|
||||
|
||||
## 导致的严重后果
|
||||
|
||||
### 1. D3D12 PSO 创建后立即无效
|
||||
|
||||
```cpp
|
||||
// engine/src/RHI/D3D12/D3D12Device.cpp:434-444
|
||||
RHIPipelineState* D3D12Device::CreatePipelineState(const GraphicsPipelineDesc& desc) {
|
||||
auto* pso = new D3D12PipelineState(m_device.Get());
|
||||
pso->SetInputLayout(desc.inputLayout);
|
||||
// ... 设置各种状态
|
||||
return pso; // ❌ 此时 m_vsBytecode 和 m_psBytecode 都是空的!
|
||||
}
|
||||
```
|
||||
|
||||
当调用 `EnsureValid()` 时:
|
||||
|
||||
```cpp
|
||||
// engine/src/RHI/D3D12/D3D12PipelineState.cpp:143-146
|
||||
bool D3D12PipelineState::CreateD3D12PSO() {
|
||||
if (!m_vsBytecode.pShaderBytecode || !m_psBytecode.pShaderBytecode) {
|
||||
return false; // ❌ 直接失败!
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 后端行为不一致,违反"求同存异"原则
|
||||
|
||||
| 后端 | `CreatePipelineState()` 返回后 `IsValid()` |
|
||||
|------|------------------------------------------|
|
||||
| D3D12 | `false` (需要额外设置 shader) |
|
||||
| OpenGL | `true` (OpenGL 不需要预编译 PSO) |
|
||||
|
||||
这完全违反了核心设计原则。
|
||||
|
||||
### 3. 运行时状态污染
|
||||
|
||||
Shader 通过 `SetShaderBytecodes()` 在运行时注入:
|
||||
|
||||
```cpp
|
||||
// 需要外部代码手动调用
|
||||
pso->SetShaderBytecodes(vsBytecode, psBytecode); // ❌ 语义混乱
|
||||
pso->EnsureValid(); // ❌ 现在才能创建真正的 D3D12 PSO
|
||||
```
|
||||
|
||||
## 设计根源分析
|
||||
|
||||
### 参考 Unity RHI 的正确做法
|
||||
|
||||
Unity 的 `PipelineState` 是 **Shader + RenderState** 的不可变组合:
|
||||
- 创建时必须提供完整的 shader 和 state
|
||||
- 创建后不可修改
|
||||
- `IsValid()` 的语义是确定的
|
||||
|
||||
### 当前设计的错误假设
|
||||
|
||||
把 Shader 和 PipelineState 分离,试图让 PSO 成为"可配置的 render state 容器",但这与 D3D12/Vulkan 的 PSO 概念完全冲突——在这些 API 中,**PSO 是 shader + state 的编译后不可变对象**。
|
||||
|
||||
## 影响范围
|
||||
|
||||
1. **无法正确实现 SRP**:上层渲染管线无法可靠地预编译 PSO
|
||||
2. **资源管理混乱**:PSO 何时真正创建?何时可以释放?语义不明确
|
||||
3. **测试困难**:后端行为不一致,单元测试无法统一编写
|
||||
4. **扩展性差**:未来添加 Vulkan 后端会遇到同样的问题
|
||||
|
||||
## 建议修复方案
|
||||
|
||||
### 方案 A:在 `GraphicsPipelineDesc` 中添加 Shader 字段
|
||||
|
||||
```cpp
|
||||
struct GraphicsPipelineDesc {
|
||||
// ... 现有字段
|
||||
|
||||
RHIShader* vertexShader = nullptr;
|
||||
RHIShader* pixelShader = nullptr;
|
||||
RHIShader* geometryShader = nullptr;
|
||||
RHIShader* hullShader = nullptr;
|
||||
RHIShader* domainShader = nullptr;
|
||||
};
|
||||
```
|
||||
|
||||
### 方案 B:创建专用的 ShaderProgram 类型
|
||||
|
||||
```cpp
|
||||
struct ShaderProgramDesc {
|
||||
RHIShader* vertexShader = nullptr;
|
||||
RHIShader* pixelShader = nullptr;
|
||||
// ...
|
||||
};
|
||||
|
||||
struct GraphicsPipelineDesc {
|
||||
ShaderProgramDesc shaders; // 包含所有 shader
|
||||
InputLayoutDesc inputLayout;
|
||||
RasterizerDesc rasterizerState;
|
||||
BlendDesc blendState;
|
||||
DepthStencilStateDesc depthStencilState;
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
### 方案 C:分离 PSO 创建接口
|
||||
|
||||
```cpp
|
||||
// 独立的 Compute PSO 创建
|
||||
RHIPipelineState* CreateComputePipelineState(RHIShader* computeShader);
|
||||
|
||||
// Graphics PSO 创建时必须提供 shader
|
||||
RHIPipelineState* CreateGraphicsPipelineState(
|
||||
const GraphicsPipelineDesc& desc,
|
||||
RHIShader* vertexShader,
|
||||
RHIShader* pixelShader
|
||||
);
|
||||
```
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `engine/include/XCEngine/RHI/RHIDevice.h`
|
||||
- `engine/include/XCEngine/RHI/RHITypes.h`
|
||||
- `engine/include/XCEngine/RHI/RHIPipelineState.h`
|
||||
- `engine/src/RHI/D3D12/D3D12Device.cpp`
|
||||
- `engine/src/RHI/D3D12/D3D12PipelineState.cpp`
|
||||
- `engine/src/RHI/OpenGL/OpenGLDevice.cpp`
|
||||
|
||||
## 总结
|
||||
|
||||
**`GraphicsPipelineDesc` 缺少 Shader 字段** 导致:
|
||||
|
||||
1. 抽象层无法正确表达 D3D12/Vulkan 的 PSO 概念
|
||||
2. 后端行为不一致,违反"求同存异"
|
||||
3. PSO 生命周期管理语义混乱
|
||||
|
||||
这是整个 RHI 模块**最严重的架构级缺陷**,应该优先修复。
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "EventBus.h"
|
||||
#include "SelectionManager.h"
|
||||
#include "IProjectManager.h"
|
||||
#include "ISceneManager.h"
|
||||
#include "Managers/SceneManager.h"
|
||||
#include "Managers/ProjectManager.h"
|
||||
#include <string>
|
||||
@@ -30,11 +31,7 @@ public:
|
||||
return *m_selectionManager;
|
||||
}
|
||||
|
||||
void* GetSceneManager() override {
|
||||
return m_sceneManager.get();
|
||||
}
|
||||
|
||||
SceneManager& GetSceneManagerConcrete() {
|
||||
ISceneManager& GetSceneManager() override {
|
||||
return *m_sceneManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Editor {
|
||||
class EventBus;
|
||||
class ISelectionManager;
|
||||
class IProjectManager;
|
||||
class ISceneManager;
|
||||
|
||||
class IEditorContext {
|
||||
public:
|
||||
@@ -16,7 +17,7 @@ public:
|
||||
|
||||
virtual EventBus& GetEventBus() = 0;
|
||||
virtual ISelectionManager& GetSelectionManager() = 0;
|
||||
virtual void* GetSceneManager() = 0;
|
||||
virtual ISceneManager& GetSceneManager() = 0;
|
||||
virtual IProjectManager& GetProjectManager() = 0;
|
||||
|
||||
virtual void SetProjectPath(const std::string& path) = 0;
|
||||
|
||||
29
editor/src/Core/ISceneManager.h
Normal file
29
editor/src/Core/ISceneManager.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class ISceneManager {
|
||||
public:
|
||||
virtual ~ISceneManager() = default;
|
||||
|
||||
virtual ::XCEngine::Components::GameObject* CreateEntity(const std::string& name, ::XCEngine::Components::GameObject* parent = nullptr) = 0;
|
||||
virtual void DeleteEntity(::XCEngine::Components::GameObject::ID id) = 0;
|
||||
virtual void RenameEntity(::XCEngine::Components::GameObject::ID id, const std::string& newName) = 0;
|
||||
virtual ::XCEngine::Components::GameObject* GetEntity(::XCEngine::Components::GameObject::ID id) = 0;
|
||||
virtual const std::vector<::XCEngine::Components::GameObject*>& GetRootEntities() const = 0;
|
||||
virtual void CopyEntity(::XCEngine::Components::GameObject::ID id) = 0;
|
||||
virtual ::XCEngine::Components::GameObject::ID PasteEntity(::XCEngine::Components::GameObject::ID parent = 0) = 0;
|
||||
virtual ::XCEngine::Components::GameObject::ID DuplicateEntity(::XCEngine::Components::GameObject::ID id) = 0;
|
||||
virtual void MoveEntity(::XCEngine::Components::GameObject::ID id, ::XCEngine::Components::GameObject::ID newParent) = 0;
|
||||
virtual bool HasClipboardData() const = 0;
|
||||
virtual void CreateDemoScene() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,14 @@
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include "Core/ISelectionManager.h"
|
||||
#include "Core/ISceneManager.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class ISelectionManager;
|
||||
|
||||
class SceneManager {
|
||||
class SceneManager : public ISceneManager {
|
||||
public:
|
||||
SceneManager();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "HierarchyPanel.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Core/ISceneManager.h"
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Core/Math/Quaternion.h>
|
||||
#include <imgui.h>
|
||||
@@ -15,8 +16,8 @@ HierarchyPanel::~HierarchyPanel() {
|
||||
}
|
||||
|
||||
void HierarchyPanel::OnAttach() {
|
||||
auto* sceneManager = static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
sceneManager->CreateDemoScene();
|
||||
auto& sceneManager = m_context->GetSceneManager();
|
||||
sceneManager.CreateDemoScene();
|
||||
}
|
||||
|
||||
void HierarchyPanel::Render() {
|
||||
@@ -32,7 +33,7 @@ void HierarchyPanel::Render() {
|
||||
|
||||
ImGui::BeginChild("EntityList");
|
||||
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto& sceneManager = m_context->GetSceneManager();
|
||||
auto rootEntities = sceneManager.GetRootEntities();
|
||||
SortEntities(const_cast<std::vector<::XCEngine::Components::GameObject*>&>(rootEntities));
|
||||
|
||||
@@ -56,7 +57,7 @@ void HierarchyPanel::Render() {
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_PTR")) {
|
||||
::XCEngine::Components::GameObject* sourceGameObject = *(::XCEngine::Components::GameObject**)payload->Data;
|
||||
if (sourceGameObject && sourceGameObject->GetParent() != nullptr) {
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto& sceneManager = m_context->GetSceneManager();
|
||||
sceneManager.MoveEntity(sourceGameObject->GetID(), 0);
|
||||
}
|
||||
}
|
||||
@@ -108,7 +109,7 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
if (ImGui::InputText("##Rename", m_renameBuffer, sizeof(m_renameBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
|
||||
if (strlen(m_renameBuffer) > 0) {
|
||||
static_cast<SceneManager*>(m_context->GetSceneManager())->RenameEntity(gameObject->GetID(), m_renameBuffer);
|
||||
m_context->GetSceneManager().RenameEntity(gameObject->GetID(), m_renameBuffer);
|
||||
}
|
||||
m_renaming = false;
|
||||
m_renamingEntity = nullptr;
|
||||
@@ -116,7 +117,7 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
||||
|
||||
if (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0)) {
|
||||
if (strlen(m_renameBuffer) > 0) {
|
||||
static_cast<SceneManager*>(m_context->GetSceneManager())->RenameEntity(gameObject->GetID(), m_renameBuffer);
|
||||
m_context->GetSceneManager().RenameEntity(gameObject->GetID(), m_renameBuffer);
|
||||
}
|
||||
m_renaming = false;
|
||||
m_renamingEntity = nullptr;
|
||||
@@ -161,7 +162,7 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
||||
}
|
||||
|
||||
void HierarchyPanel::RenderContextMenu(::XCEngine::Components::GameObject* gameObject) {
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto& sceneManager = m_context->GetSceneManager();
|
||||
auto& selectionManager = m_context->GetSelectionManager();
|
||||
|
||||
if (ImGui::BeginMenu("Create")) {
|
||||
@@ -213,7 +214,7 @@ void HierarchyPanel::RenderContextMenu(::XCEngine::Components::GameObject* gameO
|
||||
}
|
||||
|
||||
void HierarchyPanel::RenderCreateMenu(::XCEngine::Components::GameObject* parent) {
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto& sceneManager = m_context->GetSceneManager();
|
||||
auto& selectionManager = m_context->GetSelectionManager();
|
||||
|
||||
if (ImGui::MenuItem("Empty Object")) {
|
||||
@@ -256,7 +257,7 @@ void HierarchyPanel::RenderCreateMenu(::XCEngine::Components::GameObject* parent
|
||||
}
|
||||
|
||||
void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObject) {
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto& sceneManager = m_context->GetSceneManager();
|
||||
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
|
||||
ImGui::SetDragDropPayload("ENTITY_PTR", &gameObject, sizeof(::XCEngine::Components::GameObject*));
|
||||
@@ -297,7 +298,7 @@ void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObje
|
||||
}
|
||||
|
||||
void HierarchyPanel::HandleKeyboardShortcuts() {
|
||||
auto& sceneManager = *static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto& sceneManager = m_context->GetSceneManager();
|
||||
auto& selectionManager = m_context->GetSelectionManager();
|
||||
|
||||
::XCEngine::Components::GameObject* selectedGameObject = sceneManager.GetEntity(selectionManager.GetSelectedEntity());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "InspectorPanel.h"
|
||||
#include "Core/EditorContext.h"
|
||||
#include "Managers/SceneManager.h"
|
||||
#include "Core/ISceneManager.h"
|
||||
#include "UI/UI.h"
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <imgui.h>
|
||||
@@ -38,8 +38,8 @@ void InspectorPanel::Render() {
|
||||
m_selectedEntityId = m_context->GetSelectionManager().GetSelectedEntity();
|
||||
|
||||
if (m_selectedEntityId) {
|
||||
auto* sceneManager = static_cast<SceneManager*>(m_context->GetSceneManager());
|
||||
auto* gameObject = sceneManager->GetEntity(m_selectedEntityId);
|
||||
auto& sceneManager = m_context->GetSceneManager();
|
||||
auto* gameObject = sceneManager.GetEntity(m_selectedEntityId);
|
||||
if (gameObject) {
|
||||
RenderGameObject(gameObject);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user