chore: sync editor worktree changes
This commit is contained in:
184
editor/README.md
184
editor/README.md
@@ -1,8 +1,10 @@
|
|||||||
# XCEditor
|
# XCEditor
|
||||||
|
|
||||||
`editor/` 是 XCEngine 当前随仓库维护的桌面编辑器模块。它不是一套独立渲染器,而是 `D3D12` 宿主应用,用来承接引擎 `Rendering + RHI + Scene + Scripting` 主链。
|
`editor/` 是 XCEngine 当前随仓库维护的桌面编辑器模块。它不是第二套独立渲染器,而是 `D3D12` 宿主应用,用来承接引擎 `Rendering + RHI + Scene + Scripting` 主链。
|
||||||
|
|
||||||
当前 editor 已经具备:
|
如果你想了解整个工作区,先看仓库根目录的 [README.md](../README.md);如果你要直接改 editor 或相关引擎接线,再看 [AGENT.md](../AGENT.md)。
|
||||||
|
|
||||||
|
## 当前能力
|
||||||
|
|
||||||
- Scene / Game viewport 离屏渲染接入
|
- Scene / Game viewport 离屏渲染接入
|
||||||
- object-id picking 与选中描边
|
- object-id picking 与选中描边
|
||||||
@@ -10,20 +12,22 @@
|
|||||||
- 项目根目录解析与 `Project.xcproject` 加载
|
- 项目根目录解析与 `Project.xcproject` 加载
|
||||||
- `Assets + .meta + Library` 风格项目目录接入
|
- `Assets + .meta + Library` 风格项目目录接入
|
||||||
- `ScriptComponent` 的脚本类与字段编辑入口
|
- `ScriptComponent` 的脚本类与字段编辑入口
|
||||||
|
- 项目脚本程序集重建与运行时重载入口
|
||||||
|
|
||||||
## 当前定位
|
## 当前定位
|
||||||
|
|
||||||
如果你想理解当前 editor,先把它当成三层:
|
理解当前 editor,推荐把它拆成四层:
|
||||||
|
|
||||||
1. `Win32 + D3D12` 宿主窗口与 ImGui backend
|
1. `Win32 + D3D12` 宿主窗口与 ImGui backend
|
||||||
2. `ViewportHostService` 对引擎渲染链路的接线
|
2. `ViewportHostService` 以及 Scene View helper 对引擎渲染链路的接线
|
||||||
3. `panels/`、`Managers/`、`ComponentEditors/` 这些编辑器业务层
|
3. `panels/`、`Managers/`、`ComponentEditors/` 这些编辑器业务层
|
||||||
|
4. `Scripting/` 对项目脚本程序集和运行时状态的桥接
|
||||||
|
|
||||||
当前不应再把 editor 视为旧式“UI sample”。它已经是引擎工作区的正式入口之一。
|
当前不要再把 editor 当成“旧式 UI sample”。它已经是引擎工作区的正式入口之一。
|
||||||
|
|
||||||
## 构建
|
## 构建
|
||||||
|
|
||||||
推荐直接在仓库根目录构建,而不是单独进入 `editor/` 目录。
|
推荐始终在仓库根目录构建,而不是单独进入 `editor/` 目录。
|
||||||
|
|
||||||
### 前置要求
|
### 前置要求
|
||||||
|
|
||||||
@@ -32,19 +36,13 @@
|
|||||||
- CMake 3.15+
|
- CMake 3.15+
|
||||||
- Vulkan SDK
|
- Vulkan SDK
|
||||||
|
|
||||||
如果需要启用 Mono 脚本运行时,还需要:
|
如果要启用 Mono 脚本运行时,还需要:
|
||||||
|
|
||||||
- .NET SDK
|
- .NET SDK
|
||||||
- `参考/Fermion/Fermion/external/mono` 下的 Mono 依赖
|
- `参考/Fermion/Fermion/external/mono` 下可用的 Mono 依赖
|
||||||
|
|
||||||
### 配置
|
### 配置
|
||||||
|
|
||||||
```bash
|
|
||||||
cmake -S .. -B ..\build -A x64
|
|
||||||
```
|
|
||||||
|
|
||||||
更常见的做法是直接在仓库根目录运行:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cmake -S . -B build -A x64
|
cmake -S . -B build -A x64
|
||||||
```
|
```
|
||||||
@@ -85,48 +83,99 @@ cmake --build build --config Debug --target XCEditor
|
|||||||
cmake --build build --config Debug --target xcengine_project_managed_assemblies
|
cmake --build build --config Debug --target xcengine_project_managed_assemblies
|
||||||
```
|
```
|
||||||
|
|
||||||
该 target 会把程序集放到:
|
成功重建后,项目脚本程序集目录通常应包含:
|
||||||
|
|
||||||
- `project/Library/ScriptAssemblies/XCEngine.ScriptCore.dll`
|
- `project/Library/ScriptAssemblies/XCEngine.ScriptCore.dll`
|
||||||
- `project/Library/ScriptAssemblies/GameScripts.dll`
|
- `project/Library/ScriptAssemblies/GameScripts.dll`
|
||||||
- `project/Library/ScriptAssemblies/mscorlib.dll`
|
- `project/Library/ScriptAssemblies/mscorlib.dll`
|
||||||
|
|
||||||
|
当前工作树如果暂时缺少 `XCEngine.ScriptCore.dll`,通常说明项目脚本程序集还没有按最新状态完成重建,而不是 editor 只需要两个程序集。
|
||||||
|
|
||||||
## 当前目录结构
|
## 当前目录结构
|
||||||
|
|
||||||
```text
|
```text
|
||||||
editor/
|
editor/
|
||||||
├── CMakeLists.txt
|
|- CMakeLists.txt
|
||||||
├── README.md
|
|- README.md
|
||||||
├── resources/
|
|- bin/ # 输出目录,产物名仍为 XCEngine.exe
|
||||||
│ └── Icons/
|
|- resources/
|
||||||
├── src/
|
| `- Icons/
|
||||||
│ ├── Actions/ # 编辑器动作路由
|
`- src/
|
||||||
│ ├── Commands/ # 命令与实体操作
|
|- Actions/ # 编辑器动作路由
|
||||||
│ ├── ComponentEditors/ # Inspector 组件编辑器
|
|- Commands/ # 命令与实体操作
|
||||||
│ ├── Core/ # 应用生命周期、日志、项目根解析、撤销等
|
|- ComponentEditors/ # Inspector 组件编辑器
|
||||||
│ ├── Layers/ # EditorLayer 等高层组装
|
|- Core/ # 生命周期、日志、项目根解析、撤销、play session
|
||||||
│ ├── Layout/
|
|- Layers/ # EditorLayer 等高层组装
|
||||||
│ ├── Managers/ # SceneManager / ProjectManager
|
|- Layout/
|
||||||
│ ├── panels/ # Hierarchy / Scene / Game / Inspector / Project / Console
|
|- Managers/ # SceneManager / ProjectManager / SelectionManager
|
||||||
│ ├── Platform/ # Win32 host、D3D12 backend 辅助
|
|- panels/
|
||||||
│ ├── UI/ # ImGui bridge 与通用 widget
|
| |- ConsolePanel.cpp
|
||||||
│ ├── Utils/
|
| |- ConsolePanel.h
|
||||||
│ ├── Viewport/
|
| |- GameViewPanel.cpp
|
||||||
│ │ ├── Passes/ # editor viewport overlay pass
|
| |- GameViewPanel.h
|
||||||
│ │ ├── SceneViewportOverlayBuilder.*
|
| |- HierarchyPanel.cpp
|
||||||
│ │ ├── SceneViewportPicker.*
|
| |- HierarchyPanel.h
|
||||||
│ │ ├── SceneViewportMoveGizmo.*
|
| |- InspectorPanel.cpp
|
||||||
│ │ ├── SceneViewportRotateGizmo.*
|
| |- InspectorPanel.h
|
||||||
│ │ ├── SceneViewportScaleGizmo.*
|
| |- MenuBar.cpp
|
||||||
│ │ ├── ViewportHostRenderFlowUtils.h
|
| |- MenuBar.h
|
||||||
│ │ └── ViewportHostService.h
|
| |- Panel.cpp
|
||||||
│ ├── Application.cpp
|
| |- Panel.h
|
||||||
│ ├── Application.h
|
| |- PanelCollection.h
|
||||||
│ ├── EditorApp.rc
|
| |- ProjectPanel.cpp
|
||||||
│ ├── Theme.cpp
|
| |- ProjectPanel.h
|
||||||
│ ├── Theme.h
|
| |- SceneViewPanel.cpp
|
||||||
│ └── main.cpp
|
| |- SceneViewPanel.h
|
||||||
└── bin/
|
| `- ViewportPanelContent.h
|
||||||
|
|- Platform/ # Win32 host、D3D12 backend 辅助
|
||||||
|
|- Scripting/ # 项目脚本程序集构建与运行时状态
|
||||||
|
|- UI/ # ImGui bridge 与通用 widget
|
||||||
|
|- Utils/
|
||||||
|
|- Viewport/
|
||||||
|
| |- Passes/
|
||||||
|
| | |- SceneViewportEditorOverlayPass.*
|
||||||
|
| | |- SceneViewportGridPass.*
|
||||||
|
| | `- SceneViewportSelectionOutlinePass.*
|
||||||
|
| |- IViewportHostService.h
|
||||||
|
| |- SceneViewportCameraController.h
|
||||||
|
| |- SceneViewportChrome.*
|
||||||
|
| |- SceneViewportEditorModes.h
|
||||||
|
| |- SceneViewportEditorOverlayData.h
|
||||||
|
| |- SceneViewportHudOverlay.*
|
||||||
|
| |- SceneViewportInteractionActions.*
|
||||||
|
| |- SceneViewportInteractionFrame.h
|
||||||
|
| |- SceneViewportInteractionResolver.*
|
||||||
|
| |- SceneViewportMath.h
|
||||||
|
| |- SceneViewportMoveGizmo.*
|
||||||
|
| |- SceneViewportNavigation.h
|
||||||
|
| |- SceneViewportOrientationGizmo.*
|
||||||
|
| |- SceneViewportOverlayBuilder.*
|
||||||
|
| |- SceneViewportOverlayFrameCache.*
|
||||||
|
| |- SceneViewportOverlayHandleBuilder.h
|
||||||
|
| |- SceneViewportOverlayHitTester.h
|
||||||
|
| |- SceneViewportOverlayProviders.*
|
||||||
|
| |- SceneViewportOverlaySpriteResources.*
|
||||||
|
| |- SceneViewportPassSpecs.h
|
||||||
|
| |- SceneViewportPicker.*
|
||||||
|
| |- SceneViewportRenderPlan.h
|
||||||
|
| |- SceneViewportResourcePaths.h
|
||||||
|
| |- SceneViewportRotateGizmo.*
|
||||||
|
| |- SceneViewportScaleGizmo.*
|
||||||
|
| |- SceneViewportShaderPaths.h # 兼容 include,真实 owner 已转到 ResourcePaths
|
||||||
|
| |- SceneViewportTransformGizmoCoordinator.*
|
||||||
|
| |- SceneViewportTransformGizmoFrameBuilder.h
|
||||||
|
| |- ViewportHostRenderFlowUtils.h
|
||||||
|
| |- ViewportHostRenderTargets.h
|
||||||
|
| |- ViewportHostService.h
|
||||||
|
| |- ViewportHostSurfaceUtils.h
|
||||||
|
| `- ViewportObjectIdPicker.h
|
||||||
|
|- Application.cpp
|
||||||
|
|- Application.h
|
||||||
|
|- EditorApp.rc
|
||||||
|
|- EditorResources.h
|
||||||
|
|- Theme.cpp
|
||||||
|
|- Theme.h
|
||||||
|
`- main.cpp
|
||||||
```
|
```
|
||||||
|
|
||||||
## 关键模块
|
## 关键模块
|
||||||
@@ -140,7 +189,7 @@ editor/
|
|||||||
|
|
||||||
- editor 初始化与关闭
|
- editor 初始化与关闭
|
||||||
- resource root 设置
|
- resource root 设置
|
||||||
- scripting runtime 初始化
|
- scripting runtime 初始化与重载
|
||||||
- ImGui backend 初始化
|
- ImGui backend 初始化
|
||||||
- `ViewportHostService` 接线
|
- `ViewportHostService` 接线
|
||||||
|
|
||||||
@@ -148,25 +197,35 @@ editor/
|
|||||||
|
|
||||||
- `src/Core/ProjectRootResolver.h`
|
- `src/Core/ProjectRootResolver.h`
|
||||||
- `src/Utils/ProjectFileUtils.h`
|
- `src/Utils/ProjectFileUtils.h`
|
||||||
|
- `src/Managers/ProjectManager.*`
|
||||||
|
|
||||||
负责:
|
负责:
|
||||||
|
|
||||||
- 自动识别仓库内 `project/`
|
- 自动识别仓库内 `project/`
|
||||||
- 解析 `--project`
|
- 解析 `--project`
|
||||||
- 读写 `Project.xcproject`
|
- 读写 `Project.xcproject`
|
||||||
|
- 当前 Project 面板目录与资源操作
|
||||||
|
|
||||||
### Viewport
|
### Viewport
|
||||||
|
|
||||||
- `src/Viewport/ViewportHostService.h`
|
- `src/Viewport/ViewportHostService.h`
|
||||||
- `src/Viewport/ViewportHostRenderFlowUtils.h`
|
- `src/Viewport/SceneViewportChrome.h`
|
||||||
|
- `src/Viewport/SceneViewportInteractionFrame.h`
|
||||||
|
- `src/Viewport/SceneViewportNavigation.h`
|
||||||
|
- `src/Viewport/SceneViewportTransformGizmoCoordinator.h`
|
||||||
|
- `src/Viewport/SceneViewportRenderPlan.h`
|
||||||
- `src/Viewport/SceneViewportOverlayBuilder.*`
|
- `src/Viewport/SceneViewportOverlayBuilder.*`
|
||||||
|
- `src/Viewport/SceneViewportOverlaySpriteResources.*`
|
||||||
|
- `src/Viewport/SceneViewportResourcePaths.h`
|
||||||
- `src/Viewport/Passes/SceneViewportEditorOverlayPass.*`
|
- `src/Viewport/Passes/SceneViewportEditorOverlayPass.*`
|
||||||
|
|
||||||
负责:
|
负责:
|
||||||
|
|
||||||
- 组装 scene/game viewport 渲染请求
|
- 组装 Scene / Game viewport 渲染请求
|
||||||
- 把 editor overlay 接入 `CameraRenderRequest::overlayPasses`
|
- 把 editor overlay 接入 `CameraRenderRequest::overlayPasses`
|
||||||
- object-id picking、outline、overlay pass 等 editor 视口能力
|
- 处理 Scene View 的工具切换、hover / click 解析与导航输入
|
||||||
|
- 维护 gizmo overlay state、handle hit-test、HUD overlay、sprite 资源缓存与 presentation 刷新
|
||||||
|
- 提供 object-id picking、outline、grid 等 editor 视口能力
|
||||||
|
|
||||||
### Panels
|
### Panels
|
||||||
|
|
||||||
@@ -181,12 +240,29 @@ editor/
|
|||||||
|
|
||||||
### Component Editors
|
### Component Editors
|
||||||
|
|
||||||
`ComponentEditors/` 当前不仅负责基础组件,也已经包含 `ScriptComponent` 的 Inspector 编辑入口。
|
`ComponentEditors/` 不只负责基础组件,也已经包含:
|
||||||
|
|
||||||
|
- `MeshFilterComponentEditor`
|
||||||
|
- `MeshRendererComponentEditor`
|
||||||
|
- `ScriptComponentEditor`
|
||||||
|
- `AssetReferenceEditorUtils`
|
||||||
|
|
||||||
|
### Scripting
|
||||||
|
|
||||||
|
- `src/Scripting/EditorScriptAssemblyBuilder.*`
|
||||||
|
- `src/Scripting/EditorScriptAssemblyBuilderUtils.h`
|
||||||
|
- `src/Scripting/EditorScriptRuntimeStatus.h`
|
||||||
|
|
||||||
|
负责:
|
||||||
|
|
||||||
|
- 从项目脚本资产构建程序集
|
||||||
|
- 汇报脚本运行时状态
|
||||||
|
- 为 Inspector / 菜单动作提供“能否重建脚本程序集”的判断依据
|
||||||
|
|
||||||
## 开发约束
|
## 开发约束
|
||||||
|
|
||||||
- editor 是宿主,不是第二套 renderer。
|
- editor 是宿主,不是第二套 renderer。
|
||||||
- 新的世界空间 overlay / gizmo,不应继续堆到 ImGui world draw 路径。
|
- 新的世界空间 overlay / gizmo,不应继续堆到旧的 ImGui world draw 路径。
|
||||||
- viewport 相关问题优先检查 `engine/Rendering`、`RenderSurface` 与 `ViewportHostService` 的接线,而不是直接在 panel 里复制渲染逻辑。
|
- viewport 相关问题优先检查 `engine/Rendering`、`RenderSurface` 与 `ViewportHostService` 的接线,而不是直接在 panel 里复制渲染逻辑。
|
||||||
- 与项目资源、脚本程序集、`.meta`、`Library` 相关的问题,不要假设 editor 仍处于“无工程状态”的旧结构。
|
- 与项目资源、脚本程序集、`.meta`、`Library` 相关的问题,不要假设 editor 仍处于“无工程状态”的旧结构。
|
||||||
|
|
||||||
@@ -197,7 +273,7 @@ cmake --build build --config Debug --target editor_tests
|
|||||||
cmake --build build --config Debug --target rendering_phase_regression
|
cmake --build build --config Debug --target rendering_phase_regression
|
||||||
```
|
```
|
||||||
|
|
||||||
如果改动影响脚本类发现或 Inspector 脚本字段编辑,再补:
|
如果改动影响脚本类发现、脚本程序集重建或 Inspector 脚本字段编辑,再补:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cmake --build build --config Debug --target xcengine_project_managed_assemblies
|
cmake --build build --config Debug --target xcengine_project_managed_assemblies
|
||||||
|
|||||||
23
editor/resources/xcui_demo/xcui_demo_theme.xctheme
Normal file
23
editor/resources/xcui_demo/xcui_demo_theme.xctheme
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<Theme name="XCUIDemoTheme">
|
||||||
|
<Token name="color.surface" type="color" value="#11161D" />
|
||||||
|
<Token name="color.surface.elevated" type="color" value="#1A212B" />
|
||||||
|
<Token name="color.surface.debug" type="color" value="#121820" />
|
||||||
|
<Token name="color.text.primary" type="color" value="#F2F5F8" />
|
||||||
|
<Token name="color.text.muted" type="color" value="#9AA8B7" />
|
||||||
|
<Token name="color.outline" type="color" value="#485666" />
|
||||||
|
<Token name="color.accent" type="color" value="#49B5FF" />
|
||||||
|
<Token name="color.accent.alt" type="color" value="#FF9A52" />
|
||||||
|
<Token name="color.button.text" type="color" value="#0D131A" />
|
||||||
|
<Token name="space.compact" type="float" value="6" />
|
||||||
|
<Token name="space.regular" type="float" value="10" />
|
||||||
|
<Token name="space.loose" type="float" value="14" />
|
||||||
|
<Token name="padding.panel" type="thickness" value="18" />
|
||||||
|
<Token name="padding.card" type="thickness" value="14" />
|
||||||
|
<Token name="padding.button" type="thickness" value="12,10,12,10" />
|
||||||
|
<Token name="radius.card" type="corner-radius" value="10" />
|
||||||
|
<Token name="radius.button" type="corner-radius" value="8" />
|
||||||
|
<Token name="font.body" type="float" value="14" />
|
||||||
|
<Token name="font.title" type="float" value="20" />
|
||||||
|
<Token name="font.metric" type="float" value="16" />
|
||||||
|
<Token name="line.default" type="float" value="1" />
|
||||||
|
</Theme>
|
||||||
33
editor/resources/xcui_demo/xcui_demo_view.xcui
Normal file
33
editor/resources/xcui_demo/xcui_demo_view.xcui
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<View name="XCUI Demo" theme="xcui_demo_theme.xctheme" style="Root">
|
||||||
|
<Column id="root" gap="12" padding="18">
|
||||||
|
<Card id="hero" style="HeroCard">
|
||||||
|
<Row id="heroRow" gap="12">
|
||||||
|
<StatusDot id="statusDot" />
|
||||||
|
<Column id="heroCopy" gap="4" width="stretch">
|
||||||
|
<Text id="title" text="XCUI Demo" style="HeroTitle" />
|
||||||
|
<Text id="subtitle" text="Markup -> Tree -> Layout -> Style -> Input -> DrawData" style="HeroSubtitle" />
|
||||||
|
</Column>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
<Row id="metrics" gap="12">
|
||||||
|
<Card id="metricPipeline" style="MetricCard" width="stretch">
|
||||||
|
<Text text="Pipeline" style="MetricLabel" />
|
||||||
|
<Text text="True XCUI path" style="MetricValue" />
|
||||||
|
</Card>
|
||||||
|
<Card id="metricBackend" style="MetricCard" width="stretch">
|
||||||
|
<Text text="Backend" style="MetricLabel" />
|
||||||
|
<Text text="ImGui transition" style="MetricValue" />
|
||||||
|
</Card>
|
||||||
|
</Row>
|
||||||
|
<Button id="toggleAccent" style="AccentButton" action="demo.toggleAccent">
|
||||||
|
<Text id="toggleLabel" text="Toggle Accent" style="ButtonLabel" />
|
||||||
|
</Button>
|
||||||
|
<Card id="debugCard" style="DebugCard">
|
||||||
|
<Column id="debugStack" gap="4">
|
||||||
|
<Text id="focusStatus" style="Meta" />
|
||||||
|
<Text id="treeStatus" style="Meta" />
|
||||||
|
<Text id="commandStatus" style="Meta" />
|
||||||
|
</Column>
|
||||||
|
</Card>
|
||||||
|
</Column>
|
||||||
|
</View>
|
||||||
@@ -32,8 +32,16 @@ inline ActionBinding MakeRebuildScriptsAction(bool enabled = true) {
|
|||||||
return MakeAction("Rebuild Script Assemblies", nullptr, false, enabled);
|
return MakeAction("Rebuild Script Assemblies", nullptr, false, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ActionBinding MakeMigrateSceneAssetReferencesAction(bool enabled = true) {
|
inline ActionBinding MakeReimportSelectedAssetAction(bool enabled = true) {
|
||||||
return MakeAction("Migrate Scene AssetRefs", nullptr, false, enabled);
|
return MakeAction("Reimport Selected Asset", nullptr, false, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ActionBinding MakeReimportAllAssetsAction(bool enabled = true) {
|
||||||
|
return MakeAction("Reimport All Assets", nullptr, false, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ActionBinding MakeClearLibraryAction(bool enabled = true) {
|
||||||
|
return MakeAction("Clear Library", nullptr, false, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ActionBinding MakeNewSceneAction(bool enabled = true) {
|
inline ActionBinding MakeNewSceneAction(bool enabled = true) {
|
||||||
|
|||||||
@@ -38,8 +38,16 @@ inline void ExecuteRebuildScriptAssemblies(IEditorContext& context) {
|
|||||||
Commands::RebuildScriptAssemblies(context);
|
Commands::RebuildScriptAssemblies(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void ExecuteMigrateSceneAssetReferences(IEditorContext& context) {
|
inline void ExecuteReimportSelectedAsset(IEditorContext& context) {
|
||||||
Commands::MigrateSceneAssetReferences(context);
|
Commands::ReimportSelectedAsset(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ExecuteReimportAllAssets(IEditorContext& context) {
|
||||||
|
Commands::ReimportAllAssets(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ExecuteClearLibrary(IEditorContext& context) {
|
||||||
|
Commands::ClearLibrary(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void ExecuteOpenScene(IEditorContext& context) {
|
inline void ExecuteOpenScene(IEditorContext& context) {
|
||||||
@@ -151,10 +159,6 @@ inline void DrawFileMenuActions(IEditorContext& context) {
|
|||||||
DrawMenuAction(MakeSaveSceneAction(canEditDocuments), [&]() { ExecuteSaveScene(context); });
|
DrawMenuAction(MakeSaveSceneAction(canEditDocuments), [&]() { ExecuteSaveScene(context); });
|
||||||
DrawMenuAction(MakeSaveSceneAsAction(canEditDocuments), [&]() { ExecuteSaveSceneAs(context); });
|
DrawMenuAction(MakeSaveSceneAsAction(canEditDocuments), [&]() { ExecuteSaveSceneAs(context); });
|
||||||
DrawMenuSeparator();
|
DrawMenuSeparator();
|
||||||
DrawMenuAction(
|
|
||||||
MakeMigrateSceneAssetReferencesAction(Commands::CanMigrateSceneAssetReferences(context)),
|
|
||||||
[&]() { ExecuteMigrateSceneAssetReferences(context); });
|
|
||||||
DrawMenuSeparator();
|
|
||||||
DrawMenuAction(MakeExitAction(), [&]() { RequestEditorExit(context); });
|
DrawMenuAction(MakeExitAction(), [&]() { RequestEditorExit(context); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +182,19 @@ inline void DrawScriptsMenuActions(IEditorContext& context) {
|
|||||||
[&]() { ExecuteRebuildScriptAssemblies(context); });
|
[&]() { ExecuteRebuildScriptAssemblies(context); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void DrawAssetsMenuActions(IEditorContext& context) {
|
||||||
|
DrawMenuAction(
|
||||||
|
MakeReimportSelectedAssetAction(Commands::CanReimportSelectedAsset(context)),
|
||||||
|
[&]() { ExecuteReimportSelectedAsset(context); });
|
||||||
|
DrawMenuAction(
|
||||||
|
MakeReimportAllAssetsAction(Commands::CanReimportAllAssets(context)),
|
||||||
|
[&]() { ExecuteReimportAllAssets(context); });
|
||||||
|
DrawMenuSeparator();
|
||||||
|
DrawMenuAction(
|
||||||
|
MakeClearLibraryAction(Commands::CanClearLibrary(context)),
|
||||||
|
[&]() { ExecuteClearLibrary(context); });
|
||||||
|
}
|
||||||
|
|
||||||
inline void DrawViewMenuActions(IEditorContext& context) {
|
inline void DrawViewMenuActions(IEditorContext& context) {
|
||||||
DrawMenuAction(MakeResetLayoutAction(), [&]() { RequestDockLayoutReset(context); });
|
DrawMenuAction(MakeResetLayoutAction(), [&]() { RequestDockLayoutReset(context); });
|
||||||
}
|
}
|
||||||
@@ -201,6 +218,9 @@ inline void DrawMainMenuBar(IEditorContext& context, UI::DeferredPopupState& abo
|
|||||||
UI::DrawMenuScope("Edit", [&]() {
|
UI::DrawMenuScope("Edit", [&]() {
|
||||||
DrawEditActions(context);
|
DrawEditActions(context);
|
||||||
});
|
});
|
||||||
|
UI::DrawMenuScope("Assets", [&]() {
|
||||||
|
DrawAssetsMenuActions(context);
|
||||||
|
});
|
||||||
UI::DrawMenuScope("Run", [&]() {
|
UI::DrawMenuScope("Run", [&]() {
|
||||||
DrawRunMenuActions(context);
|
DrawRunMenuActions(context);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -145,6 +145,40 @@ bool Application::RebuildScriptingAssemblies() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Application::CanReimportProjectAsset(const std::string& assetPath) const {
|
||||||
|
if (assetPath.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::XCEngine::Resources::ResourceManager::Get().CanReimportProjectAsset(assetPath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::ReimportProjectAsset(const std::string& assetPath) {
|
||||||
|
if (assetPath.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& resourceManager = ::XCEngine::Resources::ResourceManager::Get();
|
||||||
|
resourceManager.Initialize();
|
||||||
|
return resourceManager.ReimportProjectAsset(assetPath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::ReimportAllProjectAssets() {
|
||||||
|
auto& resourceManager = ::XCEngine::Resources::ResourceManager::Get();
|
||||||
|
resourceManager.Initialize();
|
||||||
|
return resourceManager.RebuildProjectAssetCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::ClearProjectLibrary() {
|
||||||
|
auto& resourceManager = ::XCEngine::Resources::ResourceManager::Get();
|
||||||
|
resourceManager.Initialize();
|
||||||
|
return resourceManager.ClearProjectLibraryCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Application::GetProjectLibraryRoot() const {
|
||||||
|
return ::XCEngine::Resources::ResourceManager::Get().GetProjectLibraryRoot().CStr();
|
||||||
|
}
|
||||||
|
|
||||||
bool Application::InitializeWindowRenderer(HWND hwnd) {
|
bool Application::InitializeWindowRenderer(HWND hwnd) {
|
||||||
RECT clientRect = {};
|
RECT clientRect = {};
|
||||||
if (!GetClientRect(hwnd, &clientRect)) {
|
if (!GetClientRect(hwnd, &clientRect)) {
|
||||||
@@ -227,13 +261,13 @@ void Application::RenderEditorFrame() {
|
|||||||
|
|
||||||
m_imguiBackend.BeginFrame();
|
m_imguiBackend.BeginFrame();
|
||||||
m_viewportHostService.BeginFrame();
|
m_viewportHostService.BeginFrame();
|
||||||
m_layerStack.onImGuiRender();
|
m_layerStack.onUIRender();
|
||||||
UpdateWindowTitle();
|
UpdateWindowTitle();
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
m_windowRenderer.Render(
|
m_windowRenderer.Render(
|
||||||
m_imguiBackend,
|
m_imguiBackend,
|
||||||
kClearColor,
|
kClearColor,
|
||||||
[this](const Rendering::RenderContext& renderContext) {
|
[this](const Rendering::RenderContext& renderContext, const Rendering::RenderSurface&) {
|
||||||
if (m_editorContext) {
|
if (m_editorContext) {
|
||||||
m_viewportHostService.RenderRequestedViewports(*m_editorContext, renderContext);
|
m_viewportHostService.RenderRequestedViewports(*m_editorContext, renderContext);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Viewport/ViewportHostService.h"
|
#include "Viewport/ViewportHostService.h"
|
||||||
|
|
||||||
#include <XCEngine/Rendering/RenderContext.h>
|
#include <XCEngine/Rendering/RenderContext.h>
|
||||||
|
#include <XCEngine/Rendering/RenderSurface.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -42,8 +43,14 @@ public:
|
|||||||
bool SwitchProject(const std::string& projectPath);
|
bool SwitchProject(const std::string& projectPath);
|
||||||
bool ReloadScriptingRuntime();
|
bool ReloadScriptingRuntime();
|
||||||
bool RebuildScriptingAssemblies();
|
bool RebuildScriptingAssemblies();
|
||||||
|
bool CanReimportProjectAsset(const std::string& assetPath) const;
|
||||||
|
bool ReimportProjectAsset(const std::string& assetPath);
|
||||||
|
bool ReimportAllProjectAssets();
|
||||||
|
bool ClearProjectLibrary();
|
||||||
|
std::string GetProjectLibraryRoot() const;
|
||||||
void SaveProjectState();
|
void SaveProjectState();
|
||||||
Rendering::RenderContext GetMainRenderContext() const { return m_windowRenderer.GetRenderContext(); }
|
Rendering::RenderContext GetMainRenderContext() const { return m_windowRenderer.GetRenderContext(); }
|
||||||
|
const Rendering::RenderSurface* GetMainRenderSurface() const { return m_windowRenderer.GetCurrentRenderSurface(); }
|
||||||
RHI::RHIDevice* GetMainRHIDevice() const { return m_windowRenderer.GetRHIDevice(); }
|
RHI::RHIDevice* GetMainRHIDevice() const { return m_windowRenderer.GetRHIDevice(); }
|
||||||
RHI::RHISwapChain* GetMainSwapChain() const { return m_windowRenderer.GetSwapChain(); }
|
RHI::RHISwapChain* GetMainSwapChain() const { return m_windowRenderer.GetSwapChain(); }
|
||||||
IViewportHostService& GetViewportHostService() { return m_viewportHostService; }
|
IViewportHostService& GetViewportHostService() { return m_viewportHostService; }
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include "Utils/FileDialogUtils.h"
|
#include "Utils/FileDialogUtils.h"
|
||||||
#include "Utils/ProjectFileUtils.h"
|
#include "Utils/ProjectFileUtils.h"
|
||||||
|
|
||||||
|
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cwctype>
|
#include <cwctype>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@@ -331,16 +333,79 @@ inline bool RebuildScriptAssemblies(IEditorContext& context) {
|
|||||||
return rebuilt;
|
return rebuilt;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool CanMigrateSceneAssetReferences(const IEditorContext& context) {
|
inline bool CanManageProjectAssetCache(const IEditorContext& context) {
|
||||||
return IsProjectDocumentEditingAllowed(context) && !context.GetProjectPath().empty();
|
return IsProjectDocumentEditingAllowed(context) && !context.GetProjectPath().empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline IProjectManager::SceneAssetReferenceMigrationReport MigrateSceneAssetReferences(IEditorContext& context) {
|
inline Resources::ResourceManager& GetProjectResourceManager(IEditorContext& context) {
|
||||||
if (!CanMigrateSceneAssetReferences(context)) {
|
auto& resourceManager = Resources::ResourceManager::Get();
|
||||||
return {};
|
resourceManager.Initialize();
|
||||||
|
|
||||||
|
if (!context.GetProjectPath().empty() &&
|
||||||
|
std::string(resourceManager.GetResourceRoot().CStr()) != context.GetProjectPath()) {
|
||||||
|
resourceManager.SetResourceRoot(context.GetProjectPath().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.GetProjectManager().MigrateSceneAssetReferences();
|
return resourceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CanReimportSelectedAsset(IEditorContext& context) {
|
||||||
|
if (!CanManageProjectAssetCache(context)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AssetItemPtr selectedItem = context.GetProjectManager().GetSelectedItem();
|
||||||
|
return selectedItem != nullptr &&
|
||||||
|
!selectedItem->isFolder &&
|
||||||
|
GetProjectResourceManager(context).CanReimportProjectAsset(selectedItem->fullPath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool ReimportSelectedAsset(IEditorContext& context) {
|
||||||
|
if (!CanReimportSelectedAsset(context)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AssetItemPtr selectedItem = context.GetProjectManager().GetSelectedItem();
|
||||||
|
const bool reimported = GetProjectResourceManager(context).ReimportProjectAsset(selectedItem->fullPath.c_str());
|
||||||
|
if (reimported) {
|
||||||
|
context.GetProjectManager().RefreshCurrentFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
return reimported;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CanReimportAllAssets(const IEditorContext& context) {
|
||||||
|
return CanManageProjectAssetCache(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool ReimportAllAssets(IEditorContext& context) {
|
||||||
|
if (!CanReimportAllAssets(context)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool reimported = GetProjectResourceManager(context).RebuildProjectAssetCache();
|
||||||
|
if (reimported) {
|
||||||
|
context.GetProjectManager().RefreshCurrentFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
return reimported;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CanClearLibrary(const IEditorContext& context) {
|
||||||
|
return CanManageProjectAssetCache(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool ClearLibrary(IEditorContext& context) {
|
||||||
|
if (!CanClearLibrary(context)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool cleared = GetProjectResourceManager(context).ClearProjectLibraryCache();
|
||||||
|
if (cleared) {
|
||||||
|
context.GetProjectManager().RefreshCurrentFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleared;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool SwitchProject(IEditorContext& context, const std::string& projectPath) {
|
inline bool SwitchProject(IEditorContext& context, const std::string& projectPath) {
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ namespace Debug {
|
|||||||
EditorConsoleSink* EditorConsoleSink::s_instance = nullptr;
|
EditorConsoleSink* EditorConsoleSink::s_instance = nullptr;
|
||||||
|
|
||||||
EditorConsoleSink* EditorConsoleSink::GetInstance() {
|
EditorConsoleSink* EditorConsoleSink::GetInstance() {
|
||||||
static EditorConsoleSink fallbackInstance;
|
return s_instance;
|
||||||
return s_instance ? s_instance : &fallbackInstance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorConsoleSink::EditorConsoleSink() {
|
EditorConsoleSink::EditorConsoleSink() {
|
||||||
|
|||||||
@@ -11,13 +11,6 @@ namespace Editor {
|
|||||||
|
|
||||||
class IProjectManager {
|
class IProjectManager {
|
||||||
public:
|
public:
|
||||||
struct SceneAssetReferenceMigrationReport {
|
|
||||||
size_t scannedSceneCount = 0;
|
|
||||||
size_t migratedSceneCount = 0;
|
|
||||||
size_t unchangedSceneCount = 0;
|
|
||||||
size_t failedSceneCount = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual ~IProjectManager() = default;
|
virtual ~IProjectManager() = default;
|
||||||
|
|
||||||
virtual const std::vector<AssetItemPtr>& GetCurrentItems() const = 0;
|
virtual const std::vector<AssetItemPtr>& GetCurrentItems() const = 0;
|
||||||
@@ -47,7 +40,6 @@ public:
|
|||||||
virtual bool DeleteItem(const std::string& fullPath) = 0;
|
virtual bool DeleteItem(const std::string& fullPath) = 0;
|
||||||
virtual bool MoveItem(const std::string& sourceFullPath, const std::string& destFolderFullPath) = 0;
|
virtual bool MoveItem(const std::string& sourceFullPath, const std::string& destFolderFullPath) = 0;
|
||||||
virtual bool RenameItem(const std::string& sourceFullPath, const std::string& newName) = 0;
|
virtual bool RenameItem(const std::string& sourceFullPath, const std::string& newName) = 0;
|
||||||
virtual SceneAssetReferenceMigrationReport MigrateSceneAssetReferences() = 0;
|
|
||||||
|
|
||||||
virtual const std::string& GetProjectPath() const = 0;
|
virtual const std::string& GetProjectPath() const = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,30 @@ class Scene;
|
|||||||
|
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
|
struct SceneLoadProgressSnapshot {
|
||||||
|
std::uint64_t revision = 0;
|
||||||
|
bool inProgress = false;
|
||||||
|
bool success = false;
|
||||||
|
bool firstFramePresented = false;
|
||||||
|
bool streamingCompleted = false;
|
||||||
|
bool interactive = false;
|
||||||
|
bool startupScene = false;
|
||||||
|
std::string operation;
|
||||||
|
std::string scenePath;
|
||||||
|
std::string message;
|
||||||
|
std::uint64_t startedAtMs = 0;
|
||||||
|
std::uint64_t structureReadyAtMs = 0;
|
||||||
|
std::uint64_t firstFrameAtMs = 0;
|
||||||
|
std::uint64_t streamingCompletedAtMs = 0;
|
||||||
|
std::uint64_t interactiveAtMs = 0;
|
||||||
|
std::uint32_t currentPendingAsyncLoads = 0;
|
||||||
|
std::uint32_t peakPendingAsyncLoads = 0;
|
||||||
|
|
||||||
|
bool HasValue() const {
|
||||||
|
return revision != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class ISceneManager {
|
class ISceneManager {
|
||||||
public:
|
public:
|
||||||
virtual ~ISceneManager() = default;
|
virtual ~ISceneManager() = default;
|
||||||
@@ -42,6 +66,8 @@ public:
|
|||||||
virtual const std::string& GetCurrentSceneName() const = 0;
|
virtual const std::string& GetCurrentSceneName() const = 0;
|
||||||
virtual ::XCEngine::Components::Scene* GetScene() = 0;
|
virtual ::XCEngine::Components::Scene* GetScene() = 0;
|
||||||
virtual const ::XCEngine::Components::Scene* GetScene() const = 0;
|
virtual const ::XCEngine::Components::Scene* GetScene() const = 0;
|
||||||
|
virtual SceneLoadProgressSnapshot GetSceneLoadProgress() const = 0;
|
||||||
|
virtual void NotifySceneViewportFramePresented(std::uint32_t pendingAsyncLoads) = 0;
|
||||||
virtual SceneSnapshot CaptureSceneSnapshot() const = 0;
|
virtual SceneSnapshot CaptureSceneSnapshot() const = 0;
|
||||||
virtual bool RestoreSceneSnapshot(const SceneSnapshot& snapshot) = 0;
|
virtual bool RestoreSceneSnapshot(const SceneSnapshot& snapshot) = 0;
|
||||||
virtual void CreateDemoScene() = 0;
|
virtual void CreateDemoScene() = 0;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ void EditorLayer::onEvent(void* event) {
|
|||||||
m_workspace.DispatchEvent(event);
|
m_workspace.DispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorLayer::onImGuiRender() {
|
void EditorLayer::onUIRender() {
|
||||||
m_workspace.Render();
|
m_workspace.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public:
|
|||||||
void onDetach() override;
|
void onDetach() override;
|
||||||
void onUpdate(float dt) override;
|
void onUpdate(float dt) override;
|
||||||
void onEvent(void* event) override;
|
void onEvent(void* event) override;
|
||||||
void onImGuiRender() override;
|
void onUIRender() override;
|
||||||
|
|
||||||
void SetContext(std::shared_ptr<IEditorContext> context);
|
void SetContext(std::shared_ptr<IEditorContext> context);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include "UI/DockHostStyle.h"
|
#include "UI/DockHostStyle.h"
|
||||||
#include "UI/DockTabBarChrome.h"
|
#include "UI/DockTabBarChrome.h"
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
|
|
||||||
@@ -70,9 +69,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_layoutDirty) {
|
if (m_layoutDirty) {
|
||||||
if (m_forceRebuild || !HasSavedDockLayout()) {
|
BuildDefaultLayout(dockspaceId, viewport->WorkSize);
|
||||||
BuildDefaultLayout(dockspaceId, viewport->WorkSize);
|
|
||||||
}
|
|
||||||
m_layoutDirty = false;
|
m_layoutDirty = false;
|
||||||
m_forceRebuild = false;
|
m_forceRebuild = false;
|
||||||
}
|
}
|
||||||
@@ -82,31 +79,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool HasSavedDockLayout() {
|
|
||||||
if (ImGui::GetCurrentContext() == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* iniFilename = ImGui::GetIO().IniFilename;
|
|
||||||
if (iniFilename == nullptr || iniFilename[0] == '\0') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ifstream input(iniFilename, std::ios::in);
|
|
||||||
if (!input.is_open()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string line;
|
|
||||||
while (std::getline(input, line)) {
|
|
||||||
if (line == "[Docking][Data]") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuildDefaultLayout(ImGuiID dockspaceId, const ImVec2& dockspaceSize) {
|
void BuildDefaultLayout(ImGuiID dockspaceId, const ImVec2& dockspaceSize) {
|
||||||
ImGui::DockBuilderRemoveNode(dockspaceId);
|
ImGui::DockBuilderRemoveNode(dockspaceId);
|
||||||
ImGui::DockBuilderAddNode(dockspaceId, m_dockspaceFlags | ImGuiDockNodeFlags_DockSpace);
|
ImGui::DockBuilderAddNode(dockspaceId, m_dockspaceFlags | ImGuiDockNodeFlags_DockSpace);
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
#include "ProjectManager.h"
|
#include "ProjectManager.h"
|
||||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
|
||||||
#include <XCEngine/Debug/Logger.h>
|
|
||||||
#include <XCEngine/Scene/Scene.h>
|
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cwctype>
|
#include <cwctype>
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -217,27 +212,6 @@ bool CanPreviewImageAssetExtension(std::wstring_view extension) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSceneAssetFile(const fs::path& path) {
|
|
||||||
if (!fs::is_regular_file(path)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring extension = path.extension().wstring();
|
|
||||||
std::transform(extension.begin(), extension.end(), extension.begin(), ::towlower);
|
|
||||||
return extension == L".xc";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ReadFileText(const fs::path& path) {
|
|
||||||
std::ifstream input(path, std::ios::binary);
|
|
||||||
if (!input.is_open()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream stream;
|
|
||||||
stream << input.rdbuf();
|
|
||||||
return stream.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
const std::vector<AssetItemPtr>& ProjectManager::GetCurrentItems() const {
|
const std::vector<AssetItemPtr>& ProjectManager::GetCurrentItems() const {
|
||||||
@@ -522,89 +496,6 @@ bool ProjectManager::RenameItem(const std::string& sourceFullPath, const std::st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IProjectManager::SceneAssetReferenceMigrationReport ProjectManager::MigrateSceneAssetReferences() {
|
|
||||||
IProjectManager::SceneAssetReferenceMigrationReport report;
|
|
||||||
if (m_projectPath.empty()) {
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fs::path assetsPath = fs::path(Utf8PathToWstring(m_projectPath)) / L"Assets";
|
|
||||||
if (!fs::exists(assetsPath) || !fs::is_directory(assetsPath)) {
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& logger = ::XCEngine::Debug::Logger::Get();
|
|
||||||
::XCEngine::Resources::ResourceManager& resourceManager = ::XCEngine::Resources::ResourceManager::Get();
|
|
||||||
resourceManager.Initialize();
|
|
||||||
|
|
||||||
const std::string previousRoot = resourceManager.GetResourceRoot().CStr();
|
|
||||||
const bool restoreResourceRoot = previousRoot != m_projectPath;
|
|
||||||
if (restoreResourceRoot) {
|
|
||||||
resourceManager.SetResourceRoot(m_projectPath.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (const fs::directory_entry& entry : fs::recursive_directory_iterator(assetsPath)) {
|
|
||||||
if (!IsSceneAssetFile(entry.path())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
++report.scannedSceneCount;
|
|
||||||
const fs::path scenePath = entry.path();
|
|
||||||
const std::string scenePathUtf8 = WstringPathToUtf8(scenePath.wstring());
|
|
||||||
|
|
||||||
try {
|
|
||||||
const std::string before = ReadFileText(scenePath);
|
|
||||||
::XCEngine::Components::Scene scene;
|
|
||||||
{
|
|
||||||
::XCEngine::Resources::ResourceManager::ScopedDeferredSceneLoad deferredLoadScope(resourceManager);
|
|
||||||
scene.Load(scenePathUtf8);
|
|
||||||
}
|
|
||||||
|
|
||||||
scene.Save(scenePathUtf8);
|
|
||||||
const std::string after = ReadFileText(scenePath);
|
|
||||||
if (after != before) {
|
|
||||||
++report.migratedSceneCount;
|
|
||||||
} else {
|
|
||||||
++report.unchangedSceneCount;
|
|
||||||
}
|
|
||||||
} catch (const std::exception& exception) {
|
|
||||||
++report.failedSceneCount;
|
|
||||||
logger.Error(
|
|
||||||
::XCEngine::Debug::LogCategory::FileSystem,
|
|
||||||
("Failed to migrate scene asset references: " + scenePathUtf8 + " - " + exception.what()).c_str());
|
|
||||||
} catch (...) {
|
|
||||||
++report.failedSceneCount;
|
|
||||||
logger.Error(
|
|
||||||
::XCEngine::Debug::LogCategory::FileSystem,
|
|
||||||
("Failed to migrate scene asset references: " + scenePathUtf8 + " - unknown error").c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (const std::exception& exception) {
|
|
||||||
logger.Error(
|
|
||||||
::XCEngine::Debug::LogCategory::FileSystem,
|
|
||||||
("Scene asset reference migration aborted: " + std::string(exception.what())).c_str());
|
|
||||||
} catch (...) {
|
|
||||||
logger.Error(
|
|
||||||
::XCEngine::Debug::LogCategory::FileSystem,
|
|
||||||
"Scene asset reference migration aborted: unknown error");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (restoreResourceRoot) {
|
|
||||||
resourceManager.SetResourceRoot(previousRoot.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info(
|
|
||||||
::XCEngine::Debug::LogCategory::FileSystem,
|
|
||||||
("Scene asset reference migration finished. scanned=" + std::to_string(report.scannedSceneCount) +
|
|
||||||
" migrated=" + std::to_string(report.migratedSceneCount) +
|
|
||||||
" unchanged=" + std::to_string(report.unchangedSceneCount) +
|
|
||||||
" failed=" + std::to_string(report.failedSceneCount)).c_str());
|
|
||||||
|
|
||||||
RefreshCurrentFolder();
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
|
|
||||||
AssetItemPtr ProjectManager::FindCurrentItemByPath(const std::string& fullPath) const {
|
AssetItemPtr ProjectManager::FindCurrentItemByPath(const std::string& fullPath) const {
|
||||||
const int index = FindCurrentItemIndex(fullPath);
|
const int index = FindCurrentItemIndex(fullPath);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ public:
|
|||||||
bool DeleteItem(const std::string& fullPath) override;
|
bool DeleteItem(const std::string& fullPath) override;
|
||||||
bool MoveItem(const std::string& sourceFullPath, const std::string& destFolderFullPath) override;
|
bool MoveItem(const std::string& sourceFullPath, const std::string& destFolderFullPath) override;
|
||||||
bool RenameItem(const std::string& sourceFullPath, const std::string& newName) override;
|
bool RenameItem(const std::string& sourceFullPath, const std::string& newName) override;
|
||||||
SceneAssetReferenceMigrationReport MigrateSceneAssetReferences() override;
|
|
||||||
|
|
||||||
const std::string& GetProjectPath() const override { return m_projectPath; }
|
const std::string& GetProjectPath() const override { return m_projectPath; }
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <XCEngine/Components/CameraComponent.h>
|
#include <XCEngine/Components/CameraComponent.h>
|
||||||
#include <XCEngine/Components/LightComponent.h>
|
#include <XCEngine/Components/LightComponent.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -27,6 +28,48 @@ std::pair<std::string, std::string> SerializeComponent(const ::XCEngine::Compone
|
|||||||
SceneManager::SceneManager(EventBus* eventBus)
|
SceneManager::SceneManager(EventBus* eventBus)
|
||||||
: m_eventBus(eventBus) {}
|
: m_eventBus(eventBus) {}
|
||||||
|
|
||||||
|
std::uint64_t SceneManager::GetCurrentSteadyTimeMs() {
|
||||||
|
return static_cast<std::uint64_t>(
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::BeginSceneLoadProgress(
|
||||||
|
const std::string& operation,
|
||||||
|
const std::string& scenePath,
|
||||||
|
bool startupScene,
|
||||||
|
const std::string& message) {
|
||||||
|
m_sceneLoadProgress = {};
|
||||||
|
m_sceneLoadProgress.revision = m_nextSceneLoadProgressRevision++;
|
||||||
|
m_sceneLoadProgress.inProgress = true;
|
||||||
|
m_sceneLoadProgress.success = false;
|
||||||
|
m_sceneLoadProgress.startupScene = startupScene;
|
||||||
|
m_sceneLoadProgress.operation = operation;
|
||||||
|
m_sceneLoadProgress.scenePath = scenePath;
|
||||||
|
m_sceneLoadProgress.message = message;
|
||||||
|
m_sceneLoadProgress.startedAtMs = GetCurrentSteadyTimeMs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::MarkSceneStructureReady(const std::string& message) {
|
||||||
|
if (!m_sceneLoadProgress.HasValue()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sceneLoadProgress.success = true;
|
||||||
|
m_sceneLoadProgress.inProgress = true;
|
||||||
|
m_sceneLoadProgress.message = message;
|
||||||
|
m_sceneLoadProgress.structureReadyAtMs = GetCurrentSteadyTimeMs();
|
||||||
|
m_sceneLoadProgress.currentPendingAsyncLoads =
|
||||||
|
::XCEngine::Resources::ResourceManager::Get().GetAsyncPendingCount();
|
||||||
|
m_sceneLoadProgress.peakPendingAsyncLoads = (std::max)(
|
||||||
|
m_sceneLoadProgress.peakPendingAsyncLoads,
|
||||||
|
m_sceneLoadProgress.currentPendingAsyncLoads);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::ClearSceneLoadProgress() {
|
||||||
|
m_sceneLoadProgress = {};
|
||||||
|
}
|
||||||
|
|
||||||
void SceneManager::SetSceneDirty(bool dirty) {
|
void SceneManager::SetSceneDirty(bool dirty) {
|
||||||
m_isSceneDirty = dirty;
|
m_isSceneDirty = dirty;
|
||||||
}
|
}
|
||||||
@@ -188,6 +231,7 @@ bool SceneManager::RestoreSceneSnapshot(const SceneSnapshot& snapshot) {
|
|||||||
m_currentScenePath.clear();
|
m_currentScenePath.clear();
|
||||||
m_currentSceneName = "Untitled Scene";
|
m_currentSceneName = "Untitled Scene";
|
||||||
SetSceneDirty(snapshot.dirty);
|
SetSceneDirty(snapshot.dirty);
|
||||||
|
ClearSceneLoadProgress();
|
||||||
|
|
||||||
OnSceneChanged.Invoke();
|
OnSceneChanged.Invoke();
|
||||||
if (m_eventBus) {
|
if (m_eventBus) {
|
||||||
@@ -196,6 +240,11 @@ bool SceneManager::RestoreSceneSnapshot(const SceneSnapshot& snapshot) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BeginSceneLoadProgress(
|
||||||
|
"Restore Scene Snapshot",
|
||||||
|
snapshot.scenePath,
|
||||||
|
false,
|
||||||
|
"Restoring scene snapshot structure...");
|
||||||
auto scene = std::make_unique<::XCEngine::Components::Scene>();
|
auto scene = std::make_unique<::XCEngine::Components::Scene>();
|
||||||
{
|
{
|
||||||
::XCEngine::Resources::ResourceManager::ScopedDeferredSceneLoad deferredLoadScope;
|
::XCEngine::Resources::ResourceManager::ScopedDeferredSceneLoad deferredLoadScope;
|
||||||
@@ -208,6 +257,7 @@ bool SceneManager::RestoreSceneSnapshot(const SceneSnapshot& snapshot) {
|
|||||||
m_currentScenePath = snapshot.scenePath;
|
m_currentScenePath = snapshot.scenePath;
|
||||||
m_currentSceneName = m_scene ? m_scene->GetName() : "Untitled Scene";
|
m_currentSceneName = m_scene ? m_scene->GetName() : "Untitled Scene";
|
||||||
SetSceneDirty(snapshot.dirty);
|
SetSceneDirty(snapshot.dirty);
|
||||||
|
MarkSceneStructureReady("Scene snapshot restored. Waiting for first viewport frame...");
|
||||||
|
|
||||||
OnSceneChanged.Invoke();
|
OnSceneChanged.Invoke();
|
||||||
if (m_eventBus) {
|
if (m_eventBus) {
|
||||||
@@ -238,6 +288,7 @@ void SceneManager::NewScene(const std::string& name) {
|
|||||||
m_currentScenePath.clear();
|
m_currentScenePath.clear();
|
||||||
m_currentSceneName = name;
|
m_currentSceneName = name;
|
||||||
SetSceneDirty(true);
|
SetSceneDirty(true);
|
||||||
|
ClearSceneLoadProgress();
|
||||||
|
|
||||||
OnSceneChanged.Invoke();
|
OnSceneChanged.Invoke();
|
||||||
if (m_eventBus) {
|
if (m_eventBus) {
|
||||||
@@ -252,6 +303,11 @@ bool SceneManager::LoadScene(const std::string& filePath) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BeginSceneLoadProgress(
|
||||||
|
"Load Scene",
|
||||||
|
filePath,
|
||||||
|
false,
|
||||||
|
"Restoring scene structure...");
|
||||||
auto scene = std::make_unique<::XCEngine::Components::Scene>();
|
auto scene = std::make_unique<::XCEngine::Components::Scene>();
|
||||||
{
|
{
|
||||||
::XCEngine::Resources::ResourceManager::ScopedDeferredSceneLoad deferredLoadScope;
|
::XCEngine::Resources::ResourceManager::ScopedDeferredSceneLoad deferredLoadScope;
|
||||||
@@ -264,6 +320,7 @@ bool SceneManager::LoadScene(const std::string& filePath) {
|
|||||||
m_currentScenePath = filePath;
|
m_currentScenePath = filePath;
|
||||||
m_currentSceneName = m_scene ? m_scene->GetName() : "Untitled Scene";
|
m_currentSceneName = m_scene ? m_scene->GetName() : "Untitled Scene";
|
||||||
SetSceneDirty(false);
|
SetSceneDirty(false);
|
||||||
|
MarkSceneStructureReady("Scene structure restored. Waiting for first viewport frame...");
|
||||||
|
|
||||||
OnSceneChanged.Invoke();
|
OnSceneChanged.Invoke();
|
||||||
if (m_eventBus) {
|
if (m_eventBus) {
|
||||||
@@ -310,11 +367,57 @@ bool SceneManager::SaveSceneAs(const std::string& filePath) {
|
|||||||
bool SceneManager::LoadStartupScene(const std::string& projectPath) {
|
bool SceneManager::LoadStartupScene(const std::string& projectPath) {
|
||||||
const std::string defaultScenePath = ResolveDefaultScenePath(projectPath);
|
const std::string defaultScenePath = ResolveDefaultScenePath(projectPath);
|
||||||
if (IsSceneFileUsable(defaultScenePath) && LoadScene(defaultScenePath)) {
|
if (IsSceneFileUsable(defaultScenePath) && LoadScene(defaultScenePath)) {
|
||||||
|
if (m_sceneLoadProgress.HasValue()) {
|
||||||
|
m_sceneLoadProgress.operation = "Load Startup Scene";
|
||||||
|
m_sceneLoadProgress.startupScene = true;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateDemoScene();
|
CreateDemoScene();
|
||||||
return SaveSceneAs(defaultScenePath);
|
const bool saved = SaveSceneAs(defaultScenePath);
|
||||||
|
if (saved) {
|
||||||
|
BeginSceneLoadProgress(
|
||||||
|
"Load Startup Scene",
|
||||||
|
defaultScenePath,
|
||||||
|
true,
|
||||||
|
"Created default startup scene.");
|
||||||
|
MarkSceneStructureReady("Created default startup scene. Waiting for first viewport frame...");
|
||||||
|
}
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::NotifySceneViewportFramePresented(std::uint32_t pendingAsyncLoads) {
|
||||||
|
if (!m_sceneLoadProgress.HasValue() ||
|
||||||
|
!m_sceneLoadProgress.success ||
|
||||||
|
m_sceneLoadProgress.streamingCompleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint64_t nowMs = GetCurrentSteadyTimeMs();
|
||||||
|
m_sceneLoadProgress.currentPendingAsyncLoads = pendingAsyncLoads;
|
||||||
|
m_sceneLoadProgress.peakPendingAsyncLoads = (std::max)(
|
||||||
|
m_sceneLoadProgress.peakPendingAsyncLoads,
|
||||||
|
pendingAsyncLoads);
|
||||||
|
|
||||||
|
if (!m_sceneLoadProgress.firstFramePresented) {
|
||||||
|
m_sceneLoadProgress.firstFramePresented = true;
|
||||||
|
m_sceneLoadProgress.firstFrameAtMs = nowMs;
|
||||||
|
m_sceneLoadProgress.interactive = true;
|
||||||
|
m_sceneLoadProgress.interactiveAtMs = nowMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pendingAsyncLoads == 0) {
|
||||||
|
m_sceneLoadProgress.streamingCompleted = true;
|
||||||
|
m_sceneLoadProgress.streamingCompletedAtMs = nowMs;
|
||||||
|
m_sceneLoadProgress.inProgress = false;
|
||||||
|
m_sceneLoadProgress.message = "Scene ready.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sceneLoadProgress.inProgress = true;
|
||||||
|
m_sceneLoadProgress.message =
|
||||||
|
"Runtime streaming scene assets... (" + std::to_string(pendingAsyncLoads) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::RenameEntity(::XCEngine::Components::GameObject::ID id, const std::string& newName) {
|
void SceneManager::RenameEntity(::XCEngine::Components::GameObject::ID id, const std::string& newName) {
|
||||||
@@ -365,6 +468,7 @@ void SceneManager::CreateDemoScene() {
|
|||||||
m_currentScenePath.clear();
|
m_currentScenePath.clear();
|
||||||
m_currentSceneName = m_scene->GetName();
|
m_currentSceneName = m_scene->GetName();
|
||||||
SetSceneDirty(true);
|
SetSceneDirty(true);
|
||||||
|
ClearSceneLoadProgress();
|
||||||
|
|
||||||
::XCEngine::Components::GameObject* camera = CreateEntity("Main Camera", nullptr);
|
::XCEngine::Components::GameObject* camera = CreateEntity("Main Camera", nullptr);
|
||||||
camera->AddComponent<::XCEngine::Components::CameraComponent>();
|
camera->AddComponent<::XCEngine::Components::CameraComponent>();
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ public:
|
|||||||
const std::string& GetCurrentSceneName() const override { return m_currentSceneName; }
|
const std::string& GetCurrentSceneName() const override { return m_currentSceneName; }
|
||||||
::XCEngine::Components::Scene* GetScene() override { return m_scene.get(); }
|
::XCEngine::Components::Scene* GetScene() override { return m_scene.get(); }
|
||||||
const ::XCEngine::Components::Scene* GetScene() const override { return m_scene.get(); }
|
const ::XCEngine::Components::Scene* GetScene() const override { return m_scene.get(); }
|
||||||
|
SceneLoadProgressSnapshot GetSceneLoadProgress() const override { return m_sceneLoadProgress; }
|
||||||
|
void NotifySceneViewportFramePresented(std::uint32_t pendingAsyncLoads) override;
|
||||||
SceneSnapshot CaptureSceneSnapshot() const override;
|
SceneSnapshot CaptureSceneSnapshot() const override;
|
||||||
bool RestoreSceneSnapshot(const SceneSnapshot& snapshot) override;
|
bool RestoreSceneSnapshot(const SceneSnapshot& snapshot) override;
|
||||||
void CreateDemoScene() override;
|
void CreateDemoScene() override;
|
||||||
@@ -92,6 +94,14 @@ private:
|
|||||||
::XCEngine::Components::GameObject::ID PasteEntityRecursive(const ClipboardData& data, ::XCEngine::Components::GameObject::ID parent);
|
::XCEngine::Components::GameObject::ID PasteEntityRecursive(const ClipboardData& data, ::XCEngine::Components::GameObject::ID parent);
|
||||||
void SetSceneDirty(bool dirty);
|
void SetSceneDirty(bool dirty);
|
||||||
void SyncRootEntities();
|
void SyncRootEntities();
|
||||||
|
static std::uint64_t GetCurrentSteadyTimeMs();
|
||||||
|
void BeginSceneLoadProgress(
|
||||||
|
const std::string& operation,
|
||||||
|
const std::string& scenePath,
|
||||||
|
bool startupScene,
|
||||||
|
const std::string& message);
|
||||||
|
void MarkSceneStructureReady(const std::string& message);
|
||||||
|
void ClearSceneLoadProgress();
|
||||||
static std::string GetScenesDirectory(const std::string& projectPath);
|
static std::string GetScenesDirectory(const std::string& projectPath);
|
||||||
static std::string ResolveDefaultScenePath(const std::string& projectPath);
|
static std::string ResolveDefaultScenePath(const std::string& projectPath);
|
||||||
static bool IsSceneFileUsable(const std::string& filePath);
|
static bool IsSceneFileUsable(const std::string& filePath);
|
||||||
@@ -104,6 +114,8 @@ private:
|
|||||||
std::string m_currentSceneName = "Untitled Scene";
|
std::string m_currentSceneName = "Untitled Scene";
|
||||||
bool m_isSceneDirty = false;
|
bool m_isSceneDirty = false;
|
||||||
bool m_sceneDocumentDirtyTrackingEnabled = true;
|
bool m_sceneDocumentDirtyTrackingEnabled = true;
|
||||||
|
SceneLoadProgressSnapshot m_sceneLoadProgress;
|
||||||
|
std::uint64_t m_nextSceneLoadProgressRevision = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "UI/ImGuiBackendBridge.h"
|
#include "UI/ImGuiBackendBridge.h"
|
||||||
|
|
||||||
#include <XCEngine/Rendering/RenderContext.h>
|
#include <XCEngine/Rendering/RenderContext.h>
|
||||||
|
#include <XCEngine/Rendering/RenderSurface.h>
|
||||||
#include <XCEngine/RHI/RHICommandList.h>
|
#include <XCEngine/RHI/RHICommandList.h>
|
||||||
#include <XCEngine/RHI/RHICommandQueue.h>
|
#include <XCEngine/RHI/RHICommandQueue.h>
|
||||||
#include <XCEngine/RHI/RHIDevice.h>
|
#include <XCEngine/RHI/RHIDevice.h>
|
||||||
@@ -28,6 +29,8 @@ class D3D12WindowRenderer {
|
|||||||
public:
|
public:
|
||||||
static constexpr UINT kSrvDescriptorCount = 64;
|
static constexpr UINT kSrvDescriptorCount = 64;
|
||||||
static constexpr uint32_t kSwapChainBufferCount = 3;
|
static constexpr uint32_t kSwapChainBufferCount = 3;
|
||||||
|
using RenderCallback =
|
||||||
|
std::function<void(const Rendering::RenderContext&, const Rendering::RenderSurface&)>;
|
||||||
|
|
||||||
bool Initialize(HWND hwnd, int width, int height) {
|
bool Initialize(HWND hwnd, int width, int height) {
|
||||||
Shutdown();
|
Shutdown();
|
||||||
@@ -47,6 +50,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
RHI::RHIDeviceDesc deviceDesc = {};
|
RHI::RHIDeviceDesc deviceDesc = {};
|
||||||
|
#ifdef _DEBUG
|
||||||
|
deviceDesc.enableDebugLayer = true;
|
||||||
|
deviceDesc.enableGPUValidation = true;
|
||||||
|
#endif
|
||||||
if (!m_device->Initialize(deviceDesc)) {
|
if (!m_device->Initialize(deviceDesc)) {
|
||||||
Shutdown();
|
Shutdown();
|
||||||
return false;
|
return false;
|
||||||
@@ -175,7 +182,7 @@ public:
|
|||||||
void Render(
|
void Render(
|
||||||
UI::ImGuiBackendBridge& imguiBackend,
|
UI::ImGuiBackendBridge& imguiBackend,
|
||||||
const float clearColor[4],
|
const float clearColor[4],
|
||||||
const std::function<void(const Rendering::RenderContext&)>& beforeUiRender = {}) {
|
const RenderCallback& beforeUiRender = {}) {
|
||||||
auto* d3d12Queue = GetD3D12CommandQueue();
|
auto* d3d12Queue = GetD3D12CommandQueue();
|
||||||
auto* d3d12CommandList = GetD3D12CommandList();
|
auto* d3d12CommandList = GetD3D12CommandList();
|
||||||
if (m_swapChain == nullptr ||
|
if (m_swapChain == nullptr ||
|
||||||
@@ -185,16 +192,15 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (beforeUiRender) {
|
|
||||||
beforeUiRender(GetRenderContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex();
|
const uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||||
if (backBufferIndex >= m_backBufferViews.size() || m_backBufferViews[backBufferIndex] == nullptr) {
|
if (backBufferIndex >= m_backBufferViews.size() ||
|
||||||
|
backBufferIndex >= m_backBufferSurfaces.size() ||
|
||||||
|
m_backBufferViews[backBufferIndex] == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RHI::RHIResourceView* renderTargetView = m_backBufferViews[backBufferIndex];
|
RHI::RHIResourceView* renderTargetView = m_backBufferViews[backBufferIndex];
|
||||||
|
Rendering::RenderSurface& renderSurface = m_backBufferSurfaces[backBufferIndex];
|
||||||
d3d12CommandList->TransitionBarrier(
|
d3d12CommandList->TransitionBarrier(
|
||||||
renderTargetView,
|
renderTargetView,
|
||||||
RHI::ResourceStates::Present,
|
RHI::ResourceStates::Present,
|
||||||
@@ -202,6 +208,16 @@ public:
|
|||||||
d3d12CommandList->SetRenderTargets(1, &renderTargetView, nullptr);
|
d3d12CommandList->SetRenderTargets(1, &renderTargetView, nullptr);
|
||||||
d3d12CommandList->ClearRenderTarget(renderTargetView, clearColor);
|
d3d12CommandList->ClearRenderTarget(renderTargetView, clearColor);
|
||||||
|
|
||||||
|
// Host-owned swapchain surfaces are handed to the callback already bound and ready for rendering.
|
||||||
|
// The callback must leave the color attachment in RenderTarget state so ImGui can render after it.
|
||||||
|
if (beforeUiRender) {
|
||||||
|
beforeUiRender(GetRenderContext(), renderSurface);
|
||||||
|
|
||||||
|
// Viewport rendering can bind offscreen render targets on the shared command list.
|
||||||
|
// Rebind the swapchain backbuffer so the subsequent ImGui draw data lands on the main window.
|
||||||
|
d3d12CommandList->SetRenderTargets(1, &renderTargetView, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvHeap->GetDescriptorHeap() };
|
ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvHeap->GetDescriptorHeap() };
|
||||||
d3d12CommandList->SetDescriptorHeaps(1, descriptorHeaps);
|
d3d12CommandList->SetDescriptorHeaps(1, descriptorHeaps);
|
||||||
imguiBackend.RenderDrawData(d3d12CommandList->GetCommandList());
|
imguiBackend.RenderDrawData(d3d12CommandList->GetCommandList());
|
||||||
@@ -247,6 +263,19 @@ public:
|
|||||||
return m_swapChain;
|
return m_swapChain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Rendering::RenderSurface* GetCurrentRenderSurface() const {
|
||||||
|
if (m_swapChain == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||||
|
if (backBufferIndex >= m_backBufferSurfaces.size()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m_backBufferSurfaces[backBufferIndex];
|
||||||
|
}
|
||||||
|
|
||||||
Rendering::RenderContext GetRenderContext() const {
|
Rendering::RenderContext GetRenderContext() const {
|
||||||
Rendering::RenderContext context = {};
|
Rendering::RenderContext context = {};
|
||||||
context.device = m_device;
|
context.device = m_device;
|
||||||
@@ -287,6 +316,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_backBufferViews.clear();
|
m_backBufferViews.clear();
|
||||||
|
m_backBufferSurfaces.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RecreateBackBufferViews() {
|
bool RecreateBackBufferViews() {
|
||||||
@@ -296,6 +326,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_backBufferViews.resize(kSwapChainBufferCount, nullptr);
|
m_backBufferViews.resize(kSwapChainBufferCount, nullptr);
|
||||||
|
m_backBufferSurfaces.resize(kSwapChainBufferCount);
|
||||||
|
|
||||||
RHI::ResourceViewDesc viewDesc = {};
|
RHI::ResourceViewDesc viewDesc = {};
|
||||||
viewDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
viewDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
||||||
@@ -309,6 +340,15 @@ private:
|
|||||||
ReleaseBackBufferViews();
|
ReleaseBackBufferViews();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rendering::RenderSurface& surface = m_backBufferSurfaces[backBufferIndex];
|
||||||
|
surface = Rendering::RenderSurface(
|
||||||
|
static_cast<uint32_t>(m_width),
|
||||||
|
static_cast<uint32_t>(m_height));
|
||||||
|
surface.SetColorAttachment(m_backBufferViews[backBufferIndex]);
|
||||||
|
surface.SetAutoTransitionEnabled(false);
|
||||||
|
surface.SetColorStateBefore(RHI::ResourceStates::RenderTarget);
|
||||||
|
surface.SetColorStateAfter(RHI::ResourceStates::RenderTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -325,6 +365,7 @@ private:
|
|||||||
RHI::RHIDescriptorPool* m_srvPool = nullptr;
|
RHI::RHIDescriptorPool* m_srvPool = nullptr;
|
||||||
RHI::D3D12DescriptorHeap* m_srvHeap = nullptr;
|
RHI::D3D12DescriptorHeap* m_srvHeap = nullptr;
|
||||||
std::vector<RHI::RHIResourceView*> m_backBufferViews;
|
std::vector<RHI::RHIResourceView*> m_backBufferViews;
|
||||||
|
std::vector<Rendering::RenderSurface> m_backBufferSurfaces;
|
||||||
UINT m_srvDescriptorSize = 0;
|
UINT m_srvDescriptorSize = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <XCEngine/Scene/Scene.h>
|
#include <XCEngine/Scene/Scene.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -60,6 +61,52 @@ Math::Vector3 GetSceneViewportOrientationAxisVector(SceneViewportOrientationAxis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint64_t GetCurrentSceneLoadStatusTimeMs() {
|
||||||
|
return static_cast<std::uint64_t>(
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t ResolveSceneLoadStatusElapsedMs(const SceneLoadProgressSnapshot& status) {
|
||||||
|
if (!status.HasValue() || status.startedAtMs == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.inProgress) {
|
||||||
|
const std::uint64_t nowMs = GetCurrentSceneLoadStatusTimeMs();
|
||||||
|
return nowMs >= status.startedAtMs ? nowMs - status.startedAtMs : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.streamingCompletedAtMs >= status.startedAtMs && status.streamingCompletedAtMs != 0) {
|
||||||
|
return status.streamingCompletedAtMs - status.startedAtMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.firstFrameAtMs >= status.startedAtMs && status.firstFrameAtMs != 0) {
|
||||||
|
return status.firstFrameAtMs - status.startedAtMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.structureReadyAtMs >= status.startedAtMs && status.structureReadyAtMs != 0) {
|
||||||
|
return status.structureReadyAtMs - status.startedAtMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BuildViewportSceneLoadStatusText(const SceneLoadProgressSnapshot& status) {
|
||||||
|
if (!status.HasValue() || !status.inProgress || status.message.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string text = status.message;
|
||||||
|
const std::uint64_t elapsedMs = ResolveSceneLoadStatusElapsedMs(status);
|
||||||
|
if (elapsedMs > 0) {
|
||||||
|
text += " (";
|
||||||
|
text += std::to_string(elapsedMs);
|
||||||
|
text += " ms)";
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class ViewportHostService : public IViewportHostService {
|
class ViewportHostService : public IViewportHostService {
|
||||||
@@ -568,8 +615,10 @@ private:
|
|||||||
|
|
||||||
MarkSceneViewportRenderSuccess(entry.renderTargets, requests[0]);
|
MarkSceneViewportRenderSuccess(entry.renderTargets, requests[0]);
|
||||||
const Core::uint32 pendingAsyncLoads = Resources::ResourceManager::Get().GetAsyncPendingCount();
|
const Core::uint32 pendingAsyncLoads = Resources::ResourceManager::Get().GetAsyncPendingCount();
|
||||||
if (pendingAsyncLoads > 0) {
|
context.GetSceneManager().NotifySceneViewportFramePresented(pendingAsyncLoads);
|
||||||
entry.statusText = "Loading scene assets... (" + std::to_string(pendingAsyncLoads) + ")";
|
if (entry.statusText.empty()) {
|
||||||
|
entry.statusText = BuildViewportSceneLoadStatusText(
|
||||||
|
context.GetSceneManager().GetSceneLoadProgress());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
72
editor/src/XCUIBackend/XCUIDemoRuntime.h
Normal file
72
editor/src/XCUIBackend/XCUIDemoRuntime.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEngine/UI/DrawData.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace UI {
|
||||||
|
struct UIRect;
|
||||||
|
struct UIPoint;
|
||||||
|
} // namespace UI
|
||||||
|
namespace Editor {
|
||||||
|
namespace XCUIBackend {
|
||||||
|
|
||||||
|
struct XCUIDemoInputState {
|
||||||
|
UI::UIRect canvasRect = {};
|
||||||
|
UI::UIPoint pointerPosition = {};
|
||||||
|
bool pointerInside = false;
|
||||||
|
bool pointerPressed = false;
|
||||||
|
bool pointerReleased = false;
|
||||||
|
bool pointerDown = false;
|
||||||
|
bool windowFocused = false;
|
||||||
|
bool shortcutPressed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XCUIDemoFrameStats {
|
||||||
|
bool documentsReady = false;
|
||||||
|
std::string statusMessage = {};
|
||||||
|
std::size_t dependencyCount = 0;
|
||||||
|
std::size_t elementCount = 0;
|
||||||
|
std::size_t dirtyRootCount = 0;
|
||||||
|
std::size_t drawListCount = 0;
|
||||||
|
std::size_t commandCount = 0;
|
||||||
|
std::uint64_t treeGeneration = 0;
|
||||||
|
bool accentEnabled = false;
|
||||||
|
std::string lastCommandId = {};
|
||||||
|
std::string focusedElementId = {};
|
||||||
|
std::string hoveredElementId = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XCUIDemoFrameResult {
|
||||||
|
UI::UIDrawData drawData = {};
|
||||||
|
XCUIDemoFrameStats stats = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class XCUIDemoRuntime {
|
||||||
|
public:
|
||||||
|
XCUIDemoRuntime();
|
||||||
|
~XCUIDemoRuntime();
|
||||||
|
|
||||||
|
XCUIDemoRuntime(XCUIDemoRuntime&& other) noexcept;
|
||||||
|
XCUIDemoRuntime& operator=(XCUIDemoRuntime&& other) noexcept;
|
||||||
|
|
||||||
|
XCUIDemoRuntime(const XCUIDemoRuntime&) = delete;
|
||||||
|
XCUIDemoRuntime& operator=(const XCUIDemoRuntime&) = delete;
|
||||||
|
|
||||||
|
bool ReloadDocuments();
|
||||||
|
|
||||||
|
const XCUIDemoFrameResult& Update(const XCUIDemoInputState& input);
|
||||||
|
const XCUIDemoFrameResult& GetFrameResult() const;
|
||||||
|
|
||||||
|
bool TryGetElementRect(const std::string& elementId, UI::UIRect& outRect) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct RuntimeState;
|
||||||
|
std::unique_ptr<RuntimeState> m_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace XCUIBackend
|
||||||
|
} // namespace Editor
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -430,16 +430,11 @@ const char* SerializeBlendFactor(::XCEngine::Resources::MaterialBlendFactor fact
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplyMaterialStateToResource(
|
void ApplyResolvedMaterialStateToResource(
|
||||||
const InspectorPanel::MaterialAssetState& state,
|
const InspectorPanel::MaterialAssetState& state,
|
||||||
|
const ::XCEngine::Resources::ResourceHandle<::XCEngine::Resources::Shader>& shader,
|
||||||
::XCEngine::Resources::Material& material) {
|
::XCEngine::Resources::Material& material) {
|
||||||
const std::string shaderPath = TrimCopy(BufferToString(state.shaderPath));
|
material.SetShader(shader);
|
||||||
if (shaderPath.empty()) {
|
|
||||||
material.SetShader(::XCEngine::Resources::ResourceHandle<::XCEngine::Resources::Shader>());
|
|
||||||
} else {
|
|
||||||
material.SetShader(::XCEngine::Resources::ResourceManager::Get().Load<::XCEngine::Resources::Shader>(shaderPath.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
material.SetShaderPass(TrimCopy(BufferToString(state.shaderPass)).c_str());
|
material.SetShaderPass(TrimCopy(BufferToString(state.shaderPass)).c_str());
|
||||||
material.SetRenderQueue(state.renderQueue);
|
material.SetRenderQueue(state.renderQueue);
|
||||||
material.SetRenderState(state.renderState);
|
material.SetRenderState(state.renderState);
|
||||||
@@ -454,6 +449,50 @@ void ApplyMaterialStateToResource(
|
|||||||
material.RecalculateMemorySize();
|
material.RecalculateMemorySize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TryGetLoadedShaderHandle(
|
||||||
|
const std::string& shaderPath,
|
||||||
|
::XCEngine::Resources::ResourceHandle<::XCEngine::Resources::Shader>& outShader) {
|
||||||
|
outShader.Reset();
|
||||||
|
if (shaderPath.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
::XCEngine::Resources::IResource* cachedResource =
|
||||||
|
::XCEngine::Resources::ResourceManager::Get().Find(shaderPath.c_str());
|
||||||
|
if (cachedResource == nullptr ||
|
||||||
|
cachedResource->GetType() != ::XCEngine::Resources::ResourceType::Shader ||
|
||||||
|
!cachedResource->IsValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outShader = ::XCEngine::Resources::ResourceHandle<::XCEngine::Resources::Shader>(
|
||||||
|
static_cast<::XCEngine::Resources::Shader*>(cachedResource));
|
||||||
|
return outShader.IsValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BuildMaterialLoadFailureMessage(const ::XCEngine::Resources::LoadResult& result) {
|
||||||
|
if (!result.errorMessage.Empty()) {
|
||||||
|
return std::string("Material file is invalid or unavailable: ") + result.errorMessage.CStr();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Material file is empty or invalid. Showing default values until you save it.";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BuildShaderLoadFailureMessage(
|
||||||
|
const std::string& shaderPath,
|
||||||
|
const ::XCEngine::Resources::LoadResult& result) {
|
||||||
|
std::string message = "Failed to load shader for live material update";
|
||||||
|
if (!shaderPath.empty()) {
|
||||||
|
message += ": ";
|
||||||
|
message += shaderPath;
|
||||||
|
}
|
||||||
|
if (!result.errorMessage.Empty()) {
|
||||||
|
message += " - ";
|
||||||
|
message += result.errorMessage.CStr();
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
std::string BuildMaterialAssetFileText(const InspectorPanel::MaterialAssetState& state) {
|
std::string BuildMaterialAssetFileText(const InspectorPanel::MaterialAssetState& state) {
|
||||||
std::vector<std::string> rootEntries;
|
std::vector<std::string> rootEntries;
|
||||||
|
|
||||||
@@ -596,16 +635,130 @@ void InspectorPanel::SetSubjectMode(SubjectMode mode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InspectorPanel::ClearMaterialAsset() {
|
void InspectorPanel::ClearMaterialAsset() {
|
||||||
|
InvalidateMaterialAssetAsyncState();
|
||||||
m_selectedMaterial.Reset();
|
m_selectedMaterial.Reset();
|
||||||
m_materialAssetState.Reset();
|
m_materialAssetState.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InspectorPanel::InvalidateMaterialAssetAsyncState() {
|
||||||
|
++m_materialAssetLoadRevision;
|
||||||
|
++m_materialShaderLoadRevision;
|
||||||
|
m_materialShaderLoadInFlight = false;
|
||||||
|
m_pendingMaterialShaderPath.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InspectorPanel::PopulateMaterialAssetStateFromResource(::XCEngine::Resources::Material& material) {
|
||||||
|
m_materialAssetState.shaderPath.fill('\0');
|
||||||
|
m_materialAssetState.shaderPass.fill('\0');
|
||||||
|
m_materialAssetState.renderQueue = material.GetRenderQueue();
|
||||||
|
m_materialAssetState.renderState = material.GetRenderState();
|
||||||
|
m_materialAssetState.tags.clear();
|
||||||
|
|
||||||
|
if (::XCEngine::Resources::Shader* shader = material.GetShader()) {
|
||||||
|
CopyToCharBuffer(std::string(shader->GetPath().CStr()), m_materialAssetState.shaderPath);
|
||||||
|
}
|
||||||
|
CopyToCharBuffer(std::string(material.GetShaderPass().CStr()), m_materialAssetState.shaderPass);
|
||||||
|
|
||||||
|
m_materialAssetState.tags.reserve(material.GetTagCount());
|
||||||
|
for (Core::uint32 tagIndex = 0; tagIndex < material.GetTagCount(); ++tagIndex) {
|
||||||
|
MaterialTagEditRow row;
|
||||||
|
CopyToCharBuffer(std::string(material.GetTagName(tagIndex).CStr()), row.name);
|
||||||
|
CopyToCharBuffer(std::string(material.GetTagValue(tagIndex).CStr()), row.value);
|
||||||
|
m_materialAssetState.tags.push_back(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_materialAssetState.loaded = true;
|
||||||
|
m_materialAssetState.dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InspectorPanel::ApplyMaterialAssetStateToSelectedMaterial() {
|
||||||
|
if (!m_selectedMaterial.IsValid() || m_selectedMaterial.Get() == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
::XCEngine::Resources::Material& material = *m_selectedMaterial.Get();
|
||||||
|
const std::string shaderPath = TrimCopy(BufferToString(m_materialAssetState.shaderPath));
|
||||||
|
if (shaderPath.empty()) {
|
||||||
|
++m_materialShaderLoadRevision;
|
||||||
|
m_materialShaderLoadInFlight = false;
|
||||||
|
m_pendingMaterialShaderPath.clear();
|
||||||
|
ApplyResolvedMaterialStateToResource(
|
||||||
|
m_materialAssetState,
|
||||||
|
::XCEngine::Resources::ResourceHandle<::XCEngine::Resources::Shader>(),
|
||||||
|
material);
|
||||||
|
m_materialAssetState.errorMessage.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
::XCEngine::Resources::ResourceHandle<::XCEngine::Resources::Shader> shaderHandle;
|
||||||
|
if (TryGetLoadedShaderHandle(shaderPath, shaderHandle)) {
|
||||||
|
++m_materialShaderLoadRevision;
|
||||||
|
m_materialShaderLoadInFlight = false;
|
||||||
|
m_pendingMaterialShaderPath.clear();
|
||||||
|
ApplyResolvedMaterialStateToResource(m_materialAssetState, shaderHandle, material);
|
||||||
|
m_materialAssetState.errorMessage.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_materialShaderLoadInFlight && m_pendingMaterialShaderPath == shaderPath) {
|
||||||
|
m_materialAssetState.errorMessage =
|
||||||
|
std::string("Loading shader for live material update: ") + shaderPath;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint64_t shaderLoadRevision = ++m_materialShaderLoadRevision;
|
||||||
|
m_materialShaderLoadInFlight = true;
|
||||||
|
m_pendingMaterialShaderPath = shaderPath;
|
||||||
|
m_materialAssetState.errorMessage =
|
||||||
|
std::string("Loading shader for live material update: ") + shaderPath;
|
||||||
|
|
||||||
|
::XCEngine::Resources::ResourceManager::Get().LoadAsync(
|
||||||
|
shaderPath.c_str(),
|
||||||
|
::XCEngine::Resources::ResourceType::Shader,
|
||||||
|
[this, shaderLoadRevision, requestedShaderPath = shaderPath](::XCEngine::Resources::LoadResult result) {
|
||||||
|
if (shaderLoadRevision != m_materialShaderLoadRevision ||
|
||||||
|
m_subjectMode != SubjectMode::MaterialAsset ||
|
||||||
|
TrimCopy(BufferToString(m_materialAssetState.shaderPath)) != requestedShaderPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_materialShaderLoadInFlight = false;
|
||||||
|
m_pendingMaterialShaderPath.clear();
|
||||||
|
|
||||||
|
if (!m_selectedMaterial.IsValid() || m_selectedMaterial.Get() == nullptr) {
|
||||||
|
m_materialAssetState.errorMessage.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result || result.resource == nullptr) {
|
||||||
|
m_materialAssetState.errorMessage =
|
||||||
|
BuildShaderLoadFailureMessage(requestedShaderPath, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
::XCEngine::Resources::ResourceHandle<::XCEngine::Resources::Shader> loadedShader(
|
||||||
|
static_cast<::XCEngine::Resources::Shader*>(result.resource));
|
||||||
|
if (!loadedShader.IsValid()) {
|
||||||
|
m_materialAssetState.errorMessage =
|
||||||
|
BuildShaderLoadFailureMessage(requestedShaderPath, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyResolvedMaterialStateToResource(
|
||||||
|
m_materialAssetState,
|
||||||
|
loadedShader,
|
||||||
|
*m_selectedMaterial.Get());
|
||||||
|
m_materialAssetState.errorMessage.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void InspectorPanel::ReloadMaterialAsset() {
|
void InspectorPanel::ReloadMaterialAsset() {
|
||||||
if (!m_selectedAssetItem || !m_context) {
|
if (!m_selectedAssetItem || !m_context) {
|
||||||
ClearMaterialAsset();
|
ClearMaterialAsset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InvalidateMaterialAssetAsyncState();
|
||||||
m_selectedMaterial.Reset();
|
m_selectedMaterial.Reset();
|
||||||
m_materialAssetState.Reset();
|
m_materialAssetState.Reset();
|
||||||
m_materialAssetState.assetFullPath = m_selectedAssetItem->fullPath;
|
m_materialAssetState.assetFullPath = m_selectedAssetItem->fullPath;
|
||||||
@@ -619,38 +772,41 @@ void InspectorPanel::ReloadMaterialAsset() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_selectedMaterial = ::XCEngine::Resources::ResourceManager::Get().Load<::XCEngine::Resources::Material>(
|
const std::uint64_t loadRevision = m_materialAssetLoadRevision;
|
||||||
m_materialAssetState.assetPath.c_str());
|
const std::string requestedAssetPath = m_materialAssetState.assetPath;
|
||||||
if (!m_selectedMaterial.IsValid()) {
|
m_materialAssetState.errorMessage = "Loading material asset from Library...";
|
||||||
m_materialAssetState.errorMessage = "Material file is empty or invalid. Showing default values until you save it.";
|
|
||||||
m_materialAssetState.loaded = true;
|
|
||||||
m_materialAssetState.dirty = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
::XCEngine::Resources::Material* material = m_selectedMaterial.Get();
|
::XCEngine::Resources::ResourceManager::Get().LoadAsync(
|
||||||
if (material == nullptr) {
|
m_materialAssetState.assetPath.c_str(),
|
||||||
m_materialAssetState.errorMessage = "Material resource is unavailable. Showing default values until you save it.";
|
::XCEngine::Resources::ResourceType::Material,
|
||||||
m_materialAssetState.loaded = true;
|
[this, loadRevision, requestedAssetPath](::XCEngine::Resources::LoadResult result) {
|
||||||
m_materialAssetState.dirty = false;
|
if (loadRevision != m_materialAssetLoadRevision ||
|
||||||
return;
|
m_subjectMode != SubjectMode::MaterialAsset ||
|
||||||
}
|
m_materialAssetState.assetPath != requestedAssetPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (::XCEngine::Resources::Shader* shader = material->GetShader()) {
|
if (!result || result.resource == nullptr) {
|
||||||
CopyToCharBuffer(std::string(shader->GetPath().CStr()), m_materialAssetState.shaderPath);
|
m_selectedMaterial.Reset();
|
||||||
}
|
m_materialAssetState.loaded = true;
|
||||||
CopyToCharBuffer(std::string(material->GetShaderPass().CStr()), m_materialAssetState.shaderPass);
|
m_materialAssetState.dirty = false;
|
||||||
m_materialAssetState.renderQueue = material->GetRenderQueue();
|
m_materialAssetState.errorMessage = BuildMaterialLoadFailureMessage(result);
|
||||||
m_materialAssetState.renderState = material->GetRenderState();
|
return;
|
||||||
m_materialAssetState.tags.reserve(material->GetTagCount());
|
}
|
||||||
for (Core::uint32 tagIndex = 0; tagIndex < material->GetTagCount(); ++tagIndex) {
|
|
||||||
MaterialTagEditRow row;
|
m_selectedMaterial = ::XCEngine::Resources::ResourceHandle<::XCEngine::Resources::Material>(
|
||||||
CopyToCharBuffer(std::string(material->GetTagName(tagIndex).CStr()), row.name);
|
static_cast<::XCEngine::Resources::Material*>(result.resource));
|
||||||
CopyToCharBuffer(std::string(material->GetTagValue(tagIndex).CStr()), row.value);
|
if (!m_selectedMaterial.IsValid() || m_selectedMaterial.Get() == nullptr) {
|
||||||
m_materialAssetState.tags.push_back(row);
|
m_selectedMaterial.Reset();
|
||||||
}
|
m_materialAssetState.loaded = true;
|
||||||
m_materialAssetState.loaded = true;
|
m_materialAssetState.dirty = false;
|
||||||
m_materialAssetState.dirty = false;
|
m_materialAssetState.errorMessage = BuildMaterialLoadFailureMessage(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_materialAssetState.errorMessage.clear();
|
||||||
|
PopulateMaterialAssetStateFromResource(*m_selectedMaterial.Get());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void InspectorPanel::InspectMaterialAsset(const AssetItemPtr& item) {
|
void InspectorPanel::InspectMaterialAsset(const AssetItemPtr& item) {
|
||||||
@@ -811,16 +967,9 @@ bool InspectorPanel::SaveMaterialAsset() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_selectedMaterial.IsValid()) {
|
|
||||||
m_selectedMaterial = ::XCEngine::Resources::ResourceManager::Get().Load<::XCEngine::Resources::Material>(
|
|
||||||
m_materialAssetState.assetPath.c_str());
|
|
||||||
}
|
|
||||||
if (m_selectedMaterial.IsValid()) {
|
|
||||||
ApplyMaterialStateToResource(m_materialAssetState, *m_selectedMaterial.Get());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_materialAssetState.errorMessage.clear();
|
m_materialAssetState.errorMessage.clear();
|
||||||
m_materialAssetState.dirty = false;
|
m_materialAssetState.dirty = false;
|
||||||
|
ApplyMaterialAssetStateToSelectedMaterial();
|
||||||
return true;
|
return true;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
m_materialAssetState.errorMessage = "Saving material asset failed.";
|
m_materialAssetState.errorMessage = "Saving material asset failed.";
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ private:
|
|||||||
void SetSubjectMode(SubjectMode mode);
|
void SetSubjectMode(SubjectMode mode);
|
||||||
void InspectMaterialAsset(const AssetItemPtr& item);
|
void InspectMaterialAsset(const AssetItemPtr& item);
|
||||||
void ClearMaterialAsset();
|
void ClearMaterialAsset();
|
||||||
|
void InvalidateMaterialAssetAsyncState();
|
||||||
|
void PopulateMaterialAssetStateFromResource(::XCEngine::Resources::Material& material);
|
||||||
|
void ApplyMaterialAssetStateToSelectedMaterial();
|
||||||
void RenderMaterialAsset();
|
void RenderMaterialAsset();
|
||||||
void RenderUnsupportedAsset();
|
void RenderUnsupportedAsset();
|
||||||
bool SaveMaterialAsset();
|
bool SaveMaterialAsset();
|
||||||
@@ -92,6 +95,10 @@ private:
|
|||||||
AssetItemPtr m_selectedAssetItem;
|
AssetItemPtr m_selectedAssetItem;
|
||||||
::XCEngine::Resources::ResourceHandle<::XCEngine::Resources::Material> m_selectedMaterial;
|
::XCEngine::Resources::ResourceHandle<::XCEngine::Resources::Material> m_selectedMaterial;
|
||||||
MaterialAssetState m_materialAssetState;
|
MaterialAssetState m_materialAssetState;
|
||||||
|
std::uint64_t m_materialAssetLoadRevision = 0;
|
||||||
|
std::uint64_t m_materialShaderLoadRevision = 0;
|
||||||
|
bool m_materialShaderLoadInFlight = false;
|
||||||
|
std::string m_pendingMaterialShaderPath;
|
||||||
UI::DeferredPopupState m_addComponentPopup;
|
UI::DeferredPopupState m_addComponentPopup;
|
||||||
std::function<void()> m_deferredContextAction;
|
std::function<void()> m_deferredContextAction;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "ProjectPanel.h"
|
#include "ProjectPanel.h"
|
||||||
#include "Core/IEditorContext.h"
|
#include "Core/IEditorContext.h"
|
||||||
#include "Core/IProjectManager.h"
|
#include "Core/IProjectManager.h"
|
||||||
|
#include "Core/ISceneManager.h"
|
||||||
#include "Core/AssetItem.h"
|
#include "Core/AssetItem.h"
|
||||||
#include "Platform/Win32Utf8.h"
|
#include "Platform/Win32Utf8.h"
|
||||||
#include "Utils/ProjectFileUtils.h"
|
#include "Utils/ProjectFileUtils.h"
|
||||||
@@ -60,6 +61,51 @@ ImVec4 ResolveImportStatusColor(const XCEngine::Resources::AssetImportService::I
|
|||||||
: XCEngine::Editor::UI::ConsoleErrorColor();
|
: XCEngine::Editor::UI::ConsoleErrorColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint64_t ResolveSceneLoadElapsedMs(const XCEngine::Editor::SceneLoadProgressSnapshot& status) {
|
||||||
|
if (!status.HasValue() || status.startedAtMs == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.inProgress) {
|
||||||
|
const auto nowMs = static_cast<std::uint64_t>(
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||||
|
return nowMs >= status.startedAtMs ? nowMs - status.startedAtMs : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.streamingCompletedAtMs >= status.startedAtMs && status.streamingCompletedAtMs != 0) {
|
||||||
|
return status.streamingCompletedAtMs - status.startedAtMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.firstFrameAtMs >= status.startedAtMs && status.firstFrameAtMs != 0) {
|
||||||
|
return status.firstFrameAtMs - status.startedAtMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.structureReadyAtMs >= status.startedAtMs && status.structureReadyAtMs != 0) {
|
||||||
|
return status.structureReadyAtMs - status.startedAtMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t ResolveSceneLoadMarkerDeltaMs(std::uint64_t startMs, std::uint64_t endMs) {
|
||||||
|
return endMs >= startMs && startMs != 0 ? endMs - startMs : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec4 ResolveSceneLoadStatusColor(const XCEngine::Editor::SceneLoadProgressSnapshot& status) {
|
||||||
|
if (!status.HasValue()) {
|
||||||
|
return XCEngine::Editor::UI::ConsoleSecondaryTextColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.inProgress) {
|
||||||
|
return XCEngine::Editor::UI::ConsoleWarningColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return status.success
|
||||||
|
? XCEngine::Editor::UI::ConsoleSecondaryTextColor()
|
||||||
|
: XCEngine::Editor::UI::ConsoleErrorColor();
|
||||||
|
}
|
||||||
|
|
||||||
std::string BuildImportStatusText(const XCEngine::Resources::AssetImportService::ImportStatusSnapshot& status) {
|
std::string BuildImportStatusText(const XCEngine::Resources::AssetImportService::ImportStatusSnapshot& status) {
|
||||||
if (!status.HasValue()) {
|
if (!status.HasValue()) {
|
||||||
return "Library: ready";
|
return "Library: ready";
|
||||||
@@ -76,6 +122,32 @@ std::string BuildImportStatusText(const XCEngine::Resources::AssetImportService:
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string BuildSceneLoadStatusText(const XCEngine::Editor::SceneLoadProgressSnapshot& status) {
|
||||||
|
if (!status.HasValue()) {
|
||||||
|
return "Scene: ready";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string text = "Scene: ";
|
||||||
|
if (!status.success) {
|
||||||
|
text += status.message.empty() ? "failed" : status.message;
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.inProgress) {
|
||||||
|
text += status.message.empty() ? "loading..." : status.message;
|
||||||
|
} else {
|
||||||
|
text += "ready";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint64_t elapsedMs = ResolveSceneLoadElapsedMs(status);
|
||||||
|
if (elapsedMs > 0) {
|
||||||
|
text += " (";
|
||||||
|
text += std::to_string(elapsedMs);
|
||||||
|
text += " ms)";
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
std::string BuildImportStatusTooltipText(const XCEngine::Resources::AssetImportService::ImportStatusSnapshot& status) {
|
std::string BuildImportStatusTooltipText(const XCEngine::Resources::AssetImportService::ImportStatusSnapshot& status) {
|
||||||
if (!status.HasValue()) {
|
if (!status.HasValue()) {
|
||||||
return "No import operations have been recorded in this session.";
|
return "No import operations have been recorded in this session.";
|
||||||
@@ -107,6 +179,68 @@ std::string BuildImportStatusTooltipText(const XCEngine::Resources::AssetImportS
|
|||||||
return tooltip;
|
return tooltip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string BuildSceneLoadStatusTooltipText(const XCEngine::Editor::SceneLoadProgressSnapshot& status) {
|
||||||
|
if (!status.HasValue()) {
|
||||||
|
return "No scene load operations have been recorded in this session.";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string tooltip;
|
||||||
|
tooltip += "Operation: ";
|
||||||
|
tooltip += status.operation.empty() ? "Scene Load" : status.operation;
|
||||||
|
if (!status.scenePath.empty()) {
|
||||||
|
tooltip += "\nScene: ";
|
||||||
|
tooltip += status.scenePath;
|
||||||
|
}
|
||||||
|
tooltip += "\nState: ";
|
||||||
|
tooltip += status.inProgress ? "Running" : (status.success ? "Succeeded" : "Failed");
|
||||||
|
|
||||||
|
const std::uint64_t structureMs =
|
||||||
|
ResolveSceneLoadMarkerDeltaMs(status.startedAtMs, status.structureReadyAtMs);
|
||||||
|
if (structureMs > 0) {
|
||||||
|
tooltip += "\nStructure restore: ";
|
||||||
|
tooltip += std::to_string(structureMs);
|
||||||
|
tooltip += " ms";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint64_t firstFrameMs =
|
||||||
|
ResolveSceneLoadMarkerDeltaMs(status.startedAtMs, status.firstFrameAtMs);
|
||||||
|
if (firstFrameMs > 0) {
|
||||||
|
tooltip += "\nFirst viewport frame: ";
|
||||||
|
tooltip += std::to_string(firstFrameMs);
|
||||||
|
tooltip += " ms";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint64_t interactiveMs =
|
||||||
|
ResolveSceneLoadMarkerDeltaMs(status.startedAtMs, status.interactiveAtMs);
|
||||||
|
if (interactiveMs > 0) {
|
||||||
|
tooltip += "\nFirst interactive frame: ";
|
||||||
|
tooltip += std::to_string(interactiveMs);
|
||||||
|
tooltip += " ms";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint64_t streamingMs =
|
||||||
|
ResolveSceneLoadMarkerDeltaMs(status.startedAtMs, status.streamingCompletedAtMs);
|
||||||
|
if (streamingMs > 0) {
|
||||||
|
tooltip += "\nRuntime streaming complete: ";
|
||||||
|
tooltip += std::to_string(streamingMs);
|
||||||
|
tooltip += " ms";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.peakPendingAsyncLoads > 0) {
|
||||||
|
tooltip += "\nPeak async loads: ";
|
||||||
|
tooltip += std::to_string(status.peakPendingAsyncLoads);
|
||||||
|
}
|
||||||
|
if (status.inProgress && status.currentPendingAsyncLoads > 0) {
|
||||||
|
tooltip += "\nPending async loads: ";
|
||||||
|
tooltip += std::to_string(status.currentPendingAsyncLoads);
|
||||||
|
}
|
||||||
|
if (!status.message.empty()) {
|
||||||
|
tooltip += "\nStatus: ";
|
||||||
|
tooltip += status.message;
|
||||||
|
}
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
void QueueDeferredAction(std::function<void()>& pendingAction, Fn&& fn) {
|
void QueueDeferredAction(std::function<void()>& pendingAction, Fn&& fn) {
|
||||||
if (!pendingAction) {
|
if (!pendingAction) {
|
||||||
@@ -457,8 +591,12 @@ void ProjectPanel::Render() {
|
|||||||
|
|
||||||
void ProjectPanel::RenderToolbar() {
|
void ProjectPanel::RenderToolbar() {
|
||||||
const auto importStatus = ::XCEngine::Resources::ResourceManager::Get().GetProjectAssetImportStatus();
|
const auto importStatus = ::XCEngine::Resources::ResourceManager::Get().GetProjectAssetImportStatus();
|
||||||
|
const SceneLoadProgressSnapshot sceneLoadStatus =
|
||||||
|
m_context != nullptr ? m_context->GetSceneManager().GetSceneLoadProgress() : SceneLoadProgressSnapshot{};
|
||||||
const std::string importStatusText = BuildImportStatusText(importStatus);
|
const std::string importStatusText = BuildImportStatusText(importStatus);
|
||||||
const std::string importStatusTooltip = BuildImportStatusTooltipText(importStatus);
|
const std::string importStatusTooltip = BuildImportStatusTooltipText(importStatus);
|
||||||
|
const std::string sceneLoadStatusText = BuildSceneLoadStatusText(sceneLoadStatus);
|
||||||
|
const std::string sceneLoadStatusTooltip = BuildSceneLoadStatusTooltipText(sceneLoadStatus);
|
||||||
|
|
||||||
UI::PanelToolbarScope toolbar(
|
UI::PanelToolbarScope toolbar(
|
||||||
"ProjectToolbar",
|
"ProjectToolbar",
|
||||||
@@ -473,8 +611,9 @@ void ProjectPanel::RenderToolbar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f));
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f));
|
||||||
if (ImGui::BeginTable("##ProjectToolbarLayout", 2, ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_SizingStretchProp)) {
|
if (ImGui::BeginTable("##ProjectToolbarLayout", 3, ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_SizingStretchProp)) {
|
||||||
ImGui::TableSetupColumn("##ImportStatus", ImGuiTableColumnFlags_WidthStretch);
|
ImGui::TableSetupColumn("##ImportStatus", ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
ImGui::TableSetupColumn("##SceneLoadStatus", ImGuiTableColumnFlags_WidthStretch);
|
||||||
ImGui::TableSetupColumn("##Search", ImGuiTableColumnFlags_WidthFixed, 220.0f);
|
ImGui::TableSetupColumn("##Search", ImGuiTableColumnFlags_WidthFixed, 220.0f);
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
@@ -492,6 +631,19 @@ void ProjectPanel::RenderToolbar() {
|
|||||||
ImGui::EndTooltip();
|
ImGui::EndTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ResolveSceneLoadStatusColor(sceneLoadStatus));
|
||||||
|
ImGui::TextUnformatted(sceneLoadStatusText.c_str());
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
if (ImGui::IsItemHovered() && !sceneLoadStatusTooltip.empty()) {
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 36.0f);
|
||||||
|
ImGui::TextUnformatted(sceneLoadStatusTooltip.c_str());
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
UI::ToolbarSearchField("##Search", "Search assets", m_searchBuffer, sizeof(m_searchBuffer));
|
UI::ToolbarSearchField("##Search", "Search assets", m_searchBuffer, sizeof(m_searchBuffer));
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,9 @@ inline void RenderViewportInteractionSurface(
|
|||||||
result.clickedMiddle = ImGui::IsItemClicked(ImGuiMouseButton_Middle);
|
result.clickedMiddle = ImGui::IsItemClicked(ImGuiMouseButton_Middle);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ViewportPanelContentResult RenderViewportPanelContent(IEditorContext& context, EditorViewportKind kind) {
|
inline ViewportPanelContentResult RenderViewportPanelContent(
|
||||||
|
IEditorContext& context,
|
||||||
|
EditorViewportKind kind) {
|
||||||
ViewportPanelContentResult result = {};
|
ViewportPanelContentResult result = {};
|
||||||
UI::CollapsePanelSectionSpacing();
|
UI::CollapsePanelSectionSpacing();
|
||||||
result.availableSize = ImGui::GetContentRegionAvail();
|
result.availableSize = ImGui::GetContentRegionAvail();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public:
|
|||||||
virtual void onDetach() {}
|
virtual void onDetach() {}
|
||||||
virtual void onUpdate(float dt) {}
|
virtual void onUpdate(float dt) {}
|
||||||
virtual void onEvent(void* event) {}
|
virtual void onEvent(void* event) {}
|
||||||
virtual void onImGuiRender() {}
|
virtual void onUIRender() {}
|
||||||
|
|
||||||
const std::string& getName() const { return m_name; }
|
const std::string& getName() const { return m_name; }
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public:
|
|||||||
|
|
||||||
void onUpdate(float dt);
|
void onUpdate(float dt);
|
||||||
void onEvent(void* event);
|
void onEvent(void* event);
|
||||||
void onImGuiRender();
|
void onUIRender();
|
||||||
void onAttach();
|
void onAttach();
|
||||||
void onDetach();
|
void onDetach();
|
||||||
|
|
||||||
@@ -78,9 +78,9 @@ inline void LayerStack::onEvent(void* event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void LayerStack::onImGuiRender() {
|
inline void LayerStack::onUIRender() {
|
||||||
for (auto& layer : m_layers) {
|
for (auto& layer : m_layers) {
|
||||||
layer->onImGuiRender();
|
layer->onUIRender();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user