142 lines
16 KiB
Markdown
142 lines
16 KiB
Markdown
# XCEditor Agent Guide
|
||
|
||
本文面向以后在 `editor/` 下工作的 coding agent / 开发者。若本文与代码、`CMakeLists.txt` 或测试目标冲突,以代码为准,并在同一次改动中更新本文。
|
||
|
||
## 长期目标
|
||
|
||
- `editor/` 是当前 XCUI 编辑器主线,不是旧 `mvs/editor` 或 `new_editor` 入口。长期方向是把可复用 UI shell 与产品编辑器装配继续分离清楚。
|
||
- `XCUIEditor` 保持为平台无关、后端无关的静态库。公共头在 `include/XCEditor/**`,实现主要在 `src/**`;不要放入 Win32、D3D12、DXGI、产品面板状态或工程 runtime 事实。
|
||
- `XCEditorCore` 承担产品编辑器核心。代码主要在 `app/Core`、`app/Composition`、`app/Features`、`app/Services`、`app/Windowing`,并通过 `XCEditorCoreRendering` 对象库接入 `app/Rendering`;它不直接拥有 Win32 消息循环。
|
||
- `XCEditor` 是可选 Win32 + D3D12 应用壳。代码在 `app/Bootstrap`、`app/Host/Win32`、`app/Host/D3D12`,负责窗口、DPI、消息分发、swapchain、UI 纹理和截图。
|
||
- 产品装配应以 `app/Core/Product/EditorProductManifest.*` 为单一事实源。正式 panel 集、action route、runtime owner、viewport renderer owner 先在 manifest 中声明,再派生 shell / menu / command / runtime / viewport 注册。
|
||
- UI widget / shell / workspace 代码优先保持 model/state/request/frame/result 风格。新增行为要能被 `tests/UI/Editor/unit` 以纯状态方式测试。
|
||
- scene/project 的用户操作应通过 runtime 或 command route 进入,不要在 draw/append 阶段直接改 scene 或文件系统。scene feature / panel 层不得直接 `SetPosition()`、`SetRotation()`、`SetLocalScale()`、`CreateComponent()` 或类似写入 engine object graph;交互预览也必须回到 `EditorSceneRuntime`,再由 backend/adapter 执行。
|
||
- engine 全局 runtime 访问权统一收口到 `app/Core/Engine/EditorEngineServices.h` 合约和 `app/Services/Engine/EngineEditorServices.*` 生产实现。`SceneManager::Get()`、`ResourceManager::Get()`、`RenderObjectIdRegistry::Get()` 这类入口只能在该生产 adapter 内出现。
|
||
- scene 事实所有权走 `app/Core/Scene/EditorSceneBackend.h` 合约。`EditorSceneRuntime` 只消费显式 backend;`EngineEditorSceneBackend` 必须吃显式注入的 engine manager / resource manager,不要在 scene runtime、panel、composition 或 viewport pass 中重新触碰 engine 全局单例。读模型优先经由 runtime/backend 快照派生,例如 hierarchy 使用 `EditorSceneHierarchySnapshot`,不要让面板从 `Scene*` 现场重建业务模型。
|
||
- scene 渲染私有逻辑不要塞进 panel 或 shell。新增渲染能力时先判断它属于 engine `Rendering/RHI`、editor viewport pass bundle,还是 UI overlay。
|
||
- 运行时路径以 `app/Core/Environment/EditorRuntimePaths.h` 为显式契约。workspace、executable、resource、project、capture 根路径由 bootstrap 一次性解析并向下传递;不要在下游重新从 repo root、当前工作目录或文档文件推导运行环境。
|
||
- 资源路径、图标、shader、截图输出都应走明确服务或 host 接口。不要硬编码从当前工作目录猜路径;手动验证截图不得写回 source tree。
|
||
|
||
## 当前情况
|
||
|
||
- `editor/AGENTS.md` 只是协作说明,不是运行时定位标记。`Application::ResolveRuntimePaths()` 当前用稳定工程标记(根 `CMakeLists.txt`、`editor/resources`、`project`)定位开发 workspace,并填充 `EditorRuntimePaths`。
|
||
- 顶层 `CMakeLists.txt` 默认启用 `XCENGINE_BUILD_XCUI_EDITOR_CORE` 和 `XCENGINE_BUILD_XCUI_EDITOR_APP`。构建 core/app 时必须启用 `XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT`。
|
||
- `XCEditor` 目标输出名是 `XCEngine.exe`,Debug 产物位于 `build/editor/Debug/XCEngine.exe`。
|
||
- 当前应用没有命令行项目选择流程;默认项目根由 `EditorRuntimePaths.projectRoot` 提供,开发 workspace 下通常是 `<workspaceRoot>/project`。不要沿用旧文档里的 `--project <path>` 说法。
|
||
- 当前面板 ID 为 `hierarchy`、`scene`、`game`、`inspector`、`console`、`project`,由 `EditorProductManifest.*` 声明并驱动注册。
|
||
- 当前 `game` panel 是 viewport shell,但 renderer owner 是 placeholder,不是正式 Game runtime。它会显示 `Game view runtime is not implemented`,不要假设 Game view 已完整实现。
|
||
- 当前 command 事实:`file.exit` 已绑定退出;`edit.*` 通过 active route 分发到 hierarchy/project/scene/inspector;`assets.*` 通过 project route 分发;多数 `file.*`、`run.*`、`scripts.*`、`help.about` 仍只是菜单/命令 surface,未拥有完整 host owner。
|
||
- `EditorContext` 是产品级状态聚合点:`EditorSession`、project runtime、scene runtime、selection、command focus、utility window request、shortcut manager 和 host command bridge 都在这里串接;初始化只接收 `EditorRuntimePaths`,不再自行拼接 repo/project/resource 路径。
|
||
- `UIEditorWorkspaceController` 管单窗口 workspace model/session;`EditorWindowSystem` 管多窗口 workspace set。跨窗口 detach、close、update 必须经过 synchronization plan。
|
||
- `EditorWorkspacePanelRuntimeSet` 托管产品面板生命周期。新增 panel 时先改 `EditorProductManifest.*`,再按需要调整 `BuildEditorWorkspaceModel()` 的默认布局和测试;不要再手工在 registry / menu / runtime set / viewport host 多处分别补定义。
|
||
- `EditorSelectionService` 是 hierarchy/project/inspector/scene viewport 之间的选择同步核心。不要在单个 panel 内维护另一套长期选择真相。
|
||
- `EditorProjectRuntime` 包装 `ProjectBrowserModel`,负责 project tree/grid、选择、文件操作、scene asset open request。文件系统改动后要刷新并 revalidate selection。
|
||
- `ProjectPanel` 只消费由 `EditorContext` / `EditorPanelServices` 提供的 `EditorProjectRuntime`,不再拥有或初始化自己的 project runtime。测试也应显式创建 `EditorProjectRuntime` 并通过 `SetProjectRuntime()` 注入,不要把 `projectRoot` 传给 panel。
|
||
- `EditorEngineServices` 是 editor 接入 engine 全局 runtime 的唯一生产 adapter。`Application` 创建它并向 `EditorContext`、window shell runtime、viewport runtime 传递;下游只消费显式服务。
|
||
- `EditorSceneBackend` 是 scene document/backend 的显式边界;`EngineEditorSceneBackend` 由 `EditorEngineServices` 创建,并通过显式 `SceneManager&` / `ResourceManager&` 接入真实 engine runtime。`EditorSceneRuntime` 负责 startup scene 编排、editor scene camera、hierarchy selection、component list、transform edit history 和 scene tool state,但不直接访问 engine 单例。
|
||
- scene object graph 写入必须停在 `EditorSceneBackend` 生产 adapter 内。`EditorSceneRuntime` 可以编排 undo/redo、selection stamp、inspector revision 和 tool state,但组件移除、component mutation、Transform inspector 写入、gizmo 预览/撤销写入都应通过 backend command 执行,不要在 runtime / feature / panel 层直接 `RemoveComponent()`、`SetPosition()`、`SetRotation()`、`SetScale()`、`SetLocal*()`。
|
||
- `EditorSceneRuntime::BuildHierarchySnapshot()` 是 hierarchy 面板的 scene 读入口;`HierarchyModel` 从该快照构建 UI tree。不要重新把 hierarchy 绑定回 `Scene*`。
|
||
- transform gizmo support 只负责几何计算和 hit/drag 状态;预览写入通过 `SceneGizmoUndoBridge` 调回 `EditorSceneRuntime::ApplyTransformTool*Preview()`。不要在 `SceneViewportTransformGizmoSupport.*` 里直接写 `GameObject` transform。
|
||
|
||
当前目录地图:
|
||
|
||
- `include/XCEditor/Foundation`:命令注册/分发、快捷键、主题、文本测量接口、runtime trace。
|
||
- `include/XCEditor/Fields`:bool、number、text、enum、asset、object、color、vector、property grid 等编辑字段的绘制模型与交互。
|
||
- `include/XCEditor/Collections`:list/tree/tab/scroll/inline rename/drag-drop 等集合控件。
|
||
- `include/XCEditor/Docking`、`include/XCEditor/Workspace`:dock host、workspace tree、layout persistence、splitter correction、panel detach/transfer。
|
||
- `include/XCEditor/Shell`:menu/toolbar/status/workspace 的 shell 组合与交互入口。
|
||
- `include/XCEditor/Viewport`:viewport slot/shell/input bridge。这里是通用 UI viewport 容器,不是 scene renderer。
|
||
- `include/XCEditor/Windowing`:多窗口 workspace 状态、同步计划和 presentation policy。
|
||
- `src/**`:对应公共头的实现。保持偏纯函数/状态机风格。
|
||
- `app/Core`:产品级 contracts、session、command focus、selection、panel services、engine services、scene/project/viewport/windowing 接口。
|
||
- `app/Core/Scene`:scene backend 合约和 editor scene runtime 可消费的公共 scene 标识工具。
|
||
- `app/Core/Environment`:运行时路径契约。bootstrap 负责解析 `EditorRuntimePaths`,core/composition/features/rendering 只消费显式路径。
|
||
- `app/Core/Product`:产品 manifest。这里定义正式 panel 集、route 归属、runtime owner 和 viewport renderer owner。
|
||
- `app/Composition`:装配编辑器 shell。`EditorContext` 拥有 session、project runtime、scene runtime、selection、command bridge;`EditorShellRuntime` 驱动 shell interaction、hosted panels 和 viewport runtime。
|
||
- `app/Features`:产品面板与场景视图工具。
|
||
- `app/Services`:产品 runtime 的生产服务实现。engine 全局 runtime 接入集中在 `app/Services/Engine/EngineEditorServices.*`;scene backend 由该 adapter 生产;project runtime 集中在 `app/Services/Project`。
|
||
- `app/Rendering`:编辑器 viewport、icon、object-id picking、grid/outline/helper pass 相关服务。渲染执行仍走 engine `Rendering + RHI`。
|
||
- `app/Host`:宿主接口实现。Win32/D3D12 细节只能待在这里或 rendering host 实现里。
|
||
- `app/Windowing`:窗口实例、内容控制器、生命周期协调器、workspace 多窗口同步、截图和 frame orchestration。
|
||
- `resources/Icons`、`resources/shaders/scene-viewport`:内置图标和 scene viewport shader。资源路径由 app/rendering 层解析。
|
||
|
||
当前启动和帧流程:
|
||
|
||
```text
|
||
wWinMain
|
||
-> RunXCEditor
|
||
-> Application::Initialize / Run
|
||
-> Application::ResolveRuntimePaths
|
||
-> EditorContext::Initialize
|
||
-> BuildEditorApplicationShellAsset
|
||
-> EditorWindowSystem::BootstrapPrimaryWindow
|
||
-> EditorWindowManager::CreateWorkspaceWindow
|
||
-> EditorWindowRuntimeController
|
||
-> EditorWorkspaceWindowContentController
|
||
-> EditorShellRuntime::Update / Append / RenderRequestedViewports
|
||
```
|
||
|
||
- `Application::Run()` pump Win32 message,更新 `ResourceManager::UpdateAsyncLoads()`,然后让 `EditorWindowManager::RenderAllWindows()` 驱动所有窗口。
|
||
- `EditorWindowRuntimeController::BeginFrame()` 从 D3D12 runtime 取 `RenderContext`,content controller 先更新 shell 与 hosted panels,再 present UI draw data。
|
||
- `EditorShellInteractionEngine::Update()` 先 `BeginFrame()` 清空 viewport 请求,再运行 `UpdateUIEditorShellInteraction()`,最后把每个 viewport shell 的尺寸提交给 `EditorViewportRuntimeServices::RequestViewport()`。
|
||
- `EditorShellRuntime::RenderRequestedViewports()` 在 UI shell 更新后调用 viewport runtime,把 scene viewport 渲染进离屏纹理,再由 shell frame 展示。
|
||
|
||
当前 Scene Viewport 分层:
|
||
|
||
- `src/Viewport` 和 `include/XCEditor/Viewport`:viewport slot/shell/input bridge,只处理 UI 容器和输入桥。
|
||
- `app/Features/Scene/SceneViewportController.*`:场景视图产品交互,处理 Q/W/E/R 工具切换、F 聚焦、鼠标导航、scene icon picking、transform gizmo。
|
||
- `app/Core/Scene/EditorSceneBackend.h`:scene backend contract、startup scene result 和 game object item id helpers。
|
||
- `app/Services/Scene/EditorSceneRuntime.*`:scene selection、editor camera、transform undo/redo、component mutation 和 scene render request 的事实来源;它必须通过注入的 `EditorSceneBackend` 访问真实 scene。
|
||
- `app/Services/Engine/EngineEditorServices.*`:当前唯一允许直接接入 engine `SceneManager` / `ResourceManager` / `RenderObjectIdRegistry` 的生产 adapter。
|
||
- `app/Rendering/Viewport/ViewportHostService.*`:离屏 viewport 资源管理和 renderer 注册。
|
||
- `app/Rendering/Viewport/SceneViewportRenderService.*`:调用 engine `SceneRenderer`,插入 grid、selection outline、selected helpers、object-id 等 editor pass。
|
||
- object-id picking 依赖有效 object-id surface 和 frame serial,修改时要覆盖 `test_viewport_object_id_picker.cpp` 及相关 viewport render plan 测试。
|
||
|
||
当前构建和验证入口:
|
||
|
||
```powershell
|
||
cmake --build build --config Debug --target XCUIEditor
|
||
cmake --build build --config Debug --target XCEditorCore
|
||
cmake --build build --config Debug --target XCEditor
|
||
cmake --build build --config Debug --target editor_ui_tests
|
||
cmake --build build --config Debug --target editor_app_core_tests
|
||
cmake --build build --config Debug --target editor_app_feature_tests
|
||
cmake --build build --config Debug --target editor_windowing_phase1_tests
|
||
cmake --build build --config Debug --target editor_ui_smoke_targets
|
||
cmake --build build --config Debug --target editor_ui_manual_validation_scenarios
|
||
```
|
||
|
||
```powershell
|
||
ctest --test-dir build -C Debug -R "editor|xceditor" --output-on-failure
|
||
```
|
||
|
||
- `XCUIEditor` widget/shell/workspace 改动:跑 `editor_ui_tests`、`editor_windowing_phase1_tests`。
|
||
- `app/Core`、project/scene/session/command 改动:跑 `editor_app_core_tests`。
|
||
- scene viewport、project panel、window input routing 改动:跑 `editor_app_feature_tests`。
|
||
- Win32/D3D12 host 或启动流程改动:跑 `editor_ui_smoke_targets`,必要时跑 `xceditor_smoke`。
|
||
- 手动 UI 场景:跑 `editor_ui_manual_validation_scenarios`。
|
||
|
||
有用环境变量:
|
||
|
||
- `XCUIEDITOR_SMOKE_TEST=1`:启用应用自退出 smoke 模式。
|
||
- `XCUIEDITOR_SMOKE_TEST_FRAME_LIMIT=<n>`:smoke 模式帧数上限。
|
||
- `XCUIEDITOR_SMOKE_TEST_DURATION_SECONDS=<n>`:smoke 模式持续时间。
|
||
- `XCUI_AUTO_CAPTURE_ON_STARTUP=1`:启动后自动截图。
|
||
|
||
## 过去执行
|
||
|
||
- 已把 editor 产品装配的 panel 声明收敛到 `app/Core/Product/EditorProductManifest.*`,并让 panel registry、View > Panels 菜单、panel 激活命令、action route、workspace runtime set 和 viewport renderer 注册从 manifest 派生。
|
||
- 已把 `game` panel 明确标成 placeholder viewport,而不是隐式共享 scene renderer 或假装 Game runtime 已完成。
|
||
- 已新增 manifest validation 测试,确保产品 manifest 能声明 panel runtime owner 和 viewport renderer owner,并覆盖 `game` placeholder 的预期状态。
|
||
- 已更新根 `AGENT.md` 和本文件,去掉旧 `--project` 说法,记录当前 XCUI editor、`XCEditorCore` 分层和 product manifest 规则。
|
||
- 已移除 `ProjectPanel` 自持 `EditorProjectRuntime` 的 fallback 路径;project runtime 事实源现在只来自 `EditorContext`,面板测试改为显式 runtime 注入。
|
||
- 已把 scene 的 engine 全局访问收敛到 `EngineEditorSceneBackend`,让 `EditorSceneRuntime` 通过 `EditorSceneBackend` 合约初始化、打开 scene 和执行 hierarchy mutation;新增 backend contract 单测覆盖无 backend 失败和 fake backend 注入。
|
||
- 已新增 `EditorEngineServices` 边界,把 `SceneManager::Get()`、`ResourceManager::Get()`、`RenderObjectIdRegistry::Get()` 从 `Application`、`EditorContext` 和 viewport shader/object-id 路径收口到 `app/Services/Engine/EngineEditorServices.*`;`EditorContext` 现在通过显式 engine service 创建 scene backend,并会在 scene runtime 初始化失败时返回失败。
|
||
- 已把 hierarchy 面板的 scene 读取改为 `EditorSceneHierarchySnapshot` 快照路径,`HierarchyModel` 不再由 panel 直接拿 `Scene*` 构建;已把 inspector 添加组件动作收口到 `EditorSceneBackend::AddComponent()`,让 `ComponentFactoryRegistry::Get()` 留在 engine adapter 内。
|
||
- 已把 scene transform gizmo 的拖拽预览写入从 feature support 层移到 `EditorSceneRuntime`,support 层不再直接 `SetPosition()` / `SetRotation()` / `SetLocalScale()`。
|
||
- 已把 `EditorSceneRuntime` 内的组件移除、Transform inspector 写入、component mutation、transform snapshot restore 和 gizmo preview 写入继续收口到 `EditorSceneBackend` command;真实 engine object graph 写入现在由 `EngineEditorSceneBackend` 执行,runtime 只负责状态编排和 revision/undo 维护。
|
||
- 本次改动验证过:
|
||
- `cmake --build build --config Debug --target XCEditor`
|
||
- `cmake --build build --config Debug --target editor_app_core_tests`
|
||
- `cmake --build build --config Debug --target editor_app_feature_tests`
|