# Unity式 SceneView Gizmo 系统完整审查与正式化重构方案 日期:`2026-04-03` 相关旧文档: - `docs/plan/SceneViewport_Overlay_Gizmo_Rework_Plan.md` - `docs/plan/SceneViewport_Overlay_Gizmo_Rework_Checkpoint_2026-04-02.md` - `docs/plan/Unity SRP API参考文档.md` 本文档以当前代码真实状态为准,覆盖: - `editor` 中 SceneView gizmo / grid / outline / overlay 的现状审查 - `engine` 与 `editor` 的职责边界重划 - 一套更接近 Unity SceneView / Gizmos / Handles 思路的正式化重构方案 - 可分阶段落地、可测试、可验收的迁移计划 ## 0. 执行结论 当前 SceneView 可视化链路已经完成了第一轮“从 ImGui 临时绘制向正式渲染 pass 迁移”的关键基础设施建设,但还没有真正收口。 当前最重要的结论有四条: 1. `engine` 里现有的通用渲染扩展点是对的,应该保留。 - `RenderPass` - `RenderPassSequence` - `CameraRenderRequest::preScenePasses` - `CameraRenderRequest::postScenePasses` - `CameraRenderRequest::overlayPasses` 2. `editor` 里现有的 `SceneViewportEditorOverlayData` 和 `SceneViewportEditorOverlayPass` 方向是对的,应该继续扩展,而不是回退到 ImGui world draw。 3. 目前真正的架构问题,不是“某个 gizmo 画得还不够像 Unity”,而是 **SceneView 的 editor 语义仍然泄漏进了 engine 的 builtin postprocess**。 - `infinite grid` - `selection outline` - `outline debug mask` - 与之绑定的 shader / resource 注册 4. 最终正确边界应当是: - `engine/runtime` 负责“能画什么、怎样插 pass、怎样跑后端” - `editor/SceneView` 负责“为什么要画、何时画、画哪些 gizmo、哪些对象参与、交互规则是什么” 换句话说,**Unity-like 的正确做法不是把所有 gizmo 都塞进 runtime engine,而是 runtime 提供绘制能力,editor 拥有 SceneView gizmo 的语义和调度权。** --- ## 1. 当前实现完整审查 ## 1.1 已经正式化、应保留的部分 ### A. CameraRenderer 的可扩展 pass 时序已经成立 当前 `CameraRenderer` 的执行顺序已经具备正规的扩展缝: ```text preScenePasses -> scene geometry -> object id -> postScenePasses -> builtin postprocess -> overlayPasses ``` 这条缝本身是正确的,说明现在的 renderer 已经不是一个只能硬编码单条流水线的结构,后续 SceneView 的正式化工作不需要推翻它。 ### B. Editor world overlay 已经有 canonical frame data `SceneViewportEditorOverlayData.h` 已经提供了一份比较正确的中间表示: - `worldLines` - `worldSprites` - `screenTriangles` - `handleRecords` 这意味着当前系统已经不必再依赖“某个 gizmo 组件自己画、自己 hit test、自己决定屏幕图元”的散乱做法,而是可以朝一份单帧 canonical overlay 数据前进。 ### C. Editor overlay 已经具备独立 GPU pass `SceneViewportEditorOverlayPass` 已经能够在 renderer pass 中绘制: - world line - sprite billboard - screen-space triangle 这一步非常关键。它说明 camera/light icon、camera frustum、light shape、transform gizmo 等内容已经不需要再走 ImGui world draw 这条临时路径。 ### D. Scene object picking 已经走 object-id GPU 路线 当前对象选中本身已经不是纯 CPU 射线猜测,而是建立在 object id buffer 之上的正式读回流程。这个方向是对的,后续无需推翻。 结论: **当前系统最有价值的资产不是若干单独功能,而是已经出现了一套可继续扩张的“SceneView render extension seam + canonical overlay data + editor overlay pass”。** --- ## 1.2 已经进入正式化,但还没有彻底收口的部分 ### A. Camera / Light icon、Camera frustum、Directional Light gizmo 这部分现在已经通过 `SceneViewportOverlayBuilder -> SceneViewportEditorOverlayPass` 进入 GPU overlay 链路,已经不再是纯 ImGui hack。 这是正确方向,但当前还存在几个明显问题: - `SceneViewportOverlayBuilder` 仍然是一个偏“堆逻辑”的单体 builder - gizmo provider 还没有独立注册体系 - 灯光 gizmo 的覆盖范围还不完整 - `PointLight` / `SpotLight` 还没有形成完整正式方案 ### B. Transform gizmo 的绘制链路已经正规化,但来源还偏临时 当前 transform gizmo 的 solver、状态机、屏幕几何、handle records 已经大量进入 canonical overlay 路线,这是好的。 但当前仍然有两个收口问题: - `SceneViewPanel` 仍然参与了 transform gizmo 输入组装与 transient overlay 提交 - `ViewportHostService` 仍然通过 `SetSceneViewTransientTransformGizmoOverlayData(...)` 注入一份“临时 overlay” 这说明 transform gizmo 还没有真正变成一套由 SceneView gizmo system 自己统一调度的 provider,而是“正规链路 + 临时桥接层”并存。 ### C. Orientation gizmo 仍然走 ImGui / HUD 路线 这本身不算错误,因为 orientation gizmo 本质上就是 HUD,而不是 world overlay。 真正的问题在于: - 当前 HUD overlay 与 world overlay 的系统边界还没有被正式命名 - `SceneViewportOverlayRenderer.cpp` 还承担着一个“历史遗留收容层”的角色 结论: **不是所有 gizmo 都必须迁入 GPU world pass。SceneView 右上角 orientation gizmo 属于 HUD,可以继续保留在 editor UI 层,但应该被正式归类为 HUD overlay,而不是继续以“杂项 overlay”存在。** --- ## 1.3 当前最核心的架构问题:SceneView 语义泄漏进了 engine ### A. grid / outline 不应该继续作为 engine 的 builtin scene semantics 存在 当前 `CameraRenderRequest::BuiltinPostProcessRequest` 直接携带: - `InfiniteGridPassData` - `selectedObjectIds` - `ObjectIdOutlineStyle` 这在工程上能跑,但从架构边界看是不对的。原因很简单: - SceneView infinite grid 是编辑器语义,不是 runtime camera 的通用语义 - Scene selection outline 是编辑器语义,不是 runtime scene 的通用语义 - `debugSelectionMask` 更是纯 editor 调试语义 如果这些字段挂在 engine 的公共 camera request 上,等价于让 runtime camera API 默认理解 SceneView 的编辑器概念。 这与 Unity 风格不一致。 ### B. 具体 editor 效果被注册成 engine builtin shader / builtin resource 当前 engine builtin resource 中已经出现: - `builtin://shaders/object-id-outline` - `builtin://shaders/infinite-grid` 这意味着: - runtime 资源表里已经掺入 editor SceneView 专用效果 - 后续 player/package 很容易继续被这些 editor-only 资源污染 - engine API 会逐步被 SceneView 需求反向牵着走 ### C. SceneView 的 post scene policy 目前仍然依赖 engine 内建的固定流程 现在 `SceneView` 的 grid / outline 虽然是从 editor 发起,但执行上还是通过 engine builtin postprocess builder 被硬编码规划: - `SceneInfiniteGrid` - `SceneSelectionOutline` - `SceneSelectionMaskDebug` 这相当于 SceneView 的一部分渲染语义并不真正属于 editor,而是“借用 engine builtin 实现”。 短期可用,长期会越来越难维护。 结论: **当前系统最大的结构性缺陷,不是 gizmo 样式,而是 editor 语义与 runtime 语义没有彻底分层。** --- ## 1.4 仍然偏临时的实现点 ### A. icon 资源加载仍然是 ad hoc 文件路径解析 `SceneViewportEditorOverlayPass` 当前直接解析: - `editor/resources/Icons/camera_gizmo.png` - `editor/resources/Icons/main_light_gizmo.png` 问题在于: - 没有 editor 内建资源注册表 - 没有生命周期与缓存抽象 - 没有 icon atlas / versioning / fallback 策略 - 这条链仍然像“功能先跑起来”的工程写法 ### B. gizmo provider 没有正式注册点 当前 camera/light gizmo 是 `SceneViewportOverlayBuilder` 直接扫描 scene 后构建。 这在功能上可行,但有明显扩展隐患: - 新增 gizmo 类型时会继续堆进单个 builder - provider 的职责无法单测 - 组件与 gizmo 的映射关系不清晰 - 后续 collider / reflection probe / audio source / particle / custom component gizmo 很难优雅接入 ### C. 视觉图元与交互图元虽然已经接近统一,但规范还不完整 现在 `handleRecords` 已经是一个很好的方向,但还缺少更明确的约束: - 哪些图元负责可见渲染 - 哪些记录负责 hit test - 同一 handle 是否允许多份可视 primitive - 优先级、遮挡、深度模式、拾取扩张半径等是否有统一规则 当前这些规则大多“隐含在代码里”,还没有真正上升为系统规范。 --- ## 1.5 测试体系现状 当前测试并不差,但仍然没有覆盖到最关键的“正式化边界”。 已有较好覆盖: - grid 数学与相机投影相关测试 - render flow utils 测试 - object id picker 测试 - move / rotate / scale gizmo solver 测试 当前缺口: - `SceneViewportOverlayBuilder` 的 contract 测试不足 - `SceneViewportEditorOverlayPass` 的资源契约与 primitive 分发测试不足 - camera / light gizmo provider 行为测试不足 - SceneView pass 顺序与装配测试不足 - “editor-only 资源不污染 runtime” 的构建层测试缺失 结论: **当前最大测试缺口不是数学公式,而是架构边界、provider 契约、pass 组合关系。** --- ## 2. Unity 风格的目标架构 ## 2.1 总体原则 参考 Unity 的思路,应当把 SceneView 可视化拆成三层: ### Layer A:Runtime Render Capability(属于 `engine`) 负责“能画什么”: - scene geometry 渲染 - object id 渲染 - render pass 扩展点 - 通用 full-screen / line / sprite / mesh / debug primitive 渲染能力 - 后端抽象、资源绑定、shader 编译与执行 ### Layer B:SceneView Gizmo Orchestration(属于 `editor`) 负责“为什么画、何时画、画哪些”: - SceneView grid - selection outline - camera / light / helper gizmo - transform gizmo - Scene icon - gizmo subset 调度 - 交互 handle 组织与命中规则 ### Layer C:HUD Overlay(属于 `editor UI`) 负责固定在面板屏幕空间的内容: - orientation gizmo - toolbar - 状态提示 - 调试开关 - 2D 操作提示与 hover 标签 这三层之间的关系应该是: ```text engine 提供绘制能力 editor SceneView 决定 world gizmo / editor pass 计划 editor UI 决定 HUD 展示 ``` --- ## 2.2 Unity-like 的关键不是“都放 engine”,而是“runtime 与 editor 的语义边界清楚” Unity 的本质做法并不是让 player runtime 永远背着 SceneView gizmo 语义。 更接近本项目的正确映射应当是: - `engine` 对应 Unity 中底层 render context / renderer capability - `editor` 的 SceneView 系统对应 Unity 中 SceneView 对 gizmos / wire overlay / handles 的调度 - `HUD` 对应 SceneView 窗口自己的 UI overlay 因此本项目的最终方案应当遵循: 1. `engine` 暴露“generic render extension seam” 2. `editor` 组装 SceneView 专属 render plan 3. SceneView 的 gizmo/grid/outline 作为 editor-only feature 实现 4. runtime/player 构建不再默认携带 SceneView 语义资产 --- ## 2.3 正式目标流水线 推荐的 SceneView 渲染时序如下: ```text PreScenePasses -> Scene Geometry -> ObjectId Pass -> SceneView Editor PostScene Passes - Grid - Selection Outline - Future: bounds / selection mask visualize / editor-only debug fullscreen pass -> SceneView World Overlay Pass - Scene icons - Camera frustum - Light gizmos - Transform gizmos - Future: collider / audio / volume / probe gizmos -> SceneView HUD Overlay - Orientation gizmo - Toolbar - Labels / hints / debug text ``` 这里面最关键的一点是: - `grid` 和 `selection outline` 仍然属于 world-space / scene-view editor pass - 但它们不再属于 engine 的 builtin camera semantics - 它们应该变成 editor 自己构建的 post-scene pass chain --- ## 2.4 Gizmo 系统内部应再拆三类数据 最终系统不应只有“画什么”,还要清楚地区分三种数据: ### A. Visual Primitive Data 纯视觉数据: - line - billboard sprite - mesh / wire mesh - screen triangle ### B. Handle Interaction Data 纯交互数据: - handle id - entity id - pick shape - priority - depth / sort rule - hit thickness / pick expansion ### C. Gizmo Provider Output 语义层输出: - Camera gizmo provider 输出 camera icon + frustum + camera specific handles - Light gizmo provider 输出 light icon + light shape - Transform gizmo provider 输出 axis / plane / ring / center handles 这样做的好处是: - 视觉可以演进 - 交互可以演进 - provider 可以独立测试 - 不会再把 SceneViewPanel 变成绘制器 + 命中器 + 业务调度器的混合体 --- ## 3. 最终职责边界 ## 3.1 `engine` 应保留的内容 以下内容应明确保留在 `engine`: - `RenderPass` / `RenderPassSequence` - `CameraRenderer` 的 pass 执行骨架 - scene geometry 渲染 - object id pass - render target / state transition / descriptor / backend shader compilation - 如果未来可复用,则保留“无 editor 语义”的底层 helper - full-screen quad/blit helper - line primitive renderer helper - sprite billboard renderer helper - debug mesh renderer helper 核心要求: **这些能力必须是 generic 的,不能带 SceneView / Gizmo / Editor 的具体业务语义。** --- ## 3.2 `editor` 应拥有的内容 以下内容应明确归 `editor`: - SceneView grid - selection outline 及其颜色、宽度、debug mask 策略 - camera / light scene icon - camera frustum - directional / point / spot light gizmo - transform gizmo - orientation gizmo - 组件到 gizmo provider 的注册关系 - editor-only gizmo shader / icon / material / texture 资源 换句话说: **凡是只有 SceneView 才关心的“显示语义”,最终都必须回收到 `editor`。** --- ## 3.3 可以下沉到 `engine` 的只有“无语义 helper”,不是 SceneView 语义本身 例如: - 如果 full-screen outline 的底层执行流程是通用的,可以提炼成 helper - 如果 world line / billboard / wire mesh 的 GPU 提交代码是通用的,可以提炼成 helper 但是不能继续暴露成这样的 engine 公共语义: - `SceneInfiniteGrid` - `SceneSelectionOutline` - `debugSelectionMask` 原则非常简单: **实现可以共享,语义不能泄漏。** --- ## 4. 正式重构方案 ## 4.1 Phase 1:先把 SceneView pass 规划权完全收回 editor ### 目标 让 `editor` 自己决定 SceneView 需要哪些 pass,而不是继续把 grid / outline 塞进 `engine::BuiltinPostProcessRequest`。 ### 方案 新增 editor 侧的 SceneView render planning 层,例如: - `SceneViewportRenderPlan` - `SceneViewportPassChainBuilder` 由它负责: - 生成 SceneView 的 `postScenePasses` - 生成 SceneView 的 `overlayPasses` - 规划 object id 是否需要 - 规划 grid / outline / gizmo 的具体顺序 ### 结果 `CameraRenderRequest` 仍然保留: - `preScenePasses` - `postScenePasses` - `overlayPasses` 但 `BuiltinPostProcessRequest` 不再继续承载 SceneView 专属语义。 ### 验收标准 - `SceneView` 渲染路径不再依赖 `BuiltinPostProcessRequest` 中的 grid / outline 字段 - SceneView pass 顺序完全由 editor 侧 builder 决定 - engine 只负责执行 pass,不再理解 SceneView 概念 --- ## 4.2 Phase 2:把 Grid 与 Selection Outline 从 engine builtin 中迁出 ### 目标 完成这次重构中最关键的一刀:**切断 SceneView editor 语义对 engine builtin postprocess 的依赖。** ### 方案 在 `editor/src/Viewport/Passes` 新建 editor-owned passes: - `SceneViewportGridPass` - `SceneViewportSelectionOutlinePass` 如果当前实现代码中有通用部分,可以这样拆: - `engine` - 保留无语义的 GPU helper - `editor` - 维护 `SceneViewportGridPassData` - 维护 `SceneViewportSelectionOutlineSettings` - 维护 SceneView 下的调用时机与参数策略 ### 需要同步迁移的内容 - `InfiniteGridPassData` 从 engine 公共 camera request 中移除 - `ObjectIdOutlineStyle` 不再作为 engine camera request 的 editor 语义字段暴露 - `builtin://shaders/infinite-grid` - `builtin://shaders/object-id-outline` 上述资源应迁移为 editor-owned builtin 资源,或至少从注册语义上变为 editor 专属。 ### 验收标准 - runtime build 不再默认依赖 SceneView grid / outline shader - `CameraRenderRequest` 不再携带 SceneView grid / outline 语义 - SceneView grid / outline 仍能正常工作,并维持现有视觉效果 --- ## 4.3 Phase 3:建立正式的 Gizmo Provider Registry ### 目标 把当前“一个大 builder 扫全场”的模式,升级为“SceneView 系统调度多个 provider”。 ### 建议接口 ```text ISceneViewportGizmoProvider Gather(const SceneViewportGizmoBuildContext&, SceneViewportOverlayFrameData&) ``` ### 建议首批 provider - `SceneCameraGizmoProvider` - `SceneLightGizmoProvider` - `SceneTransformHandleProvider` - `SceneSelectionHelperProvider` 后续可继续扩展: - collider gizmo provider - audio source gizmo provider - reflection probe gizmo provider - custom editor component gizmo provider ### 为什么必须这么做 因为 Unity-like 的 SceneView 不是“一个巨大 builder 拼所有形状”,而是“editor 根据对象类型和选择状态,调度多种 gizmo provider / handle provider / wire provider”。 ### 验收标准 - `SceneViewportOverlayBuilder` 从单体 builder 退化为 orchestration layer - 新增 gizmo 类型时,不需要继续往一个 cpp 文件里堆逻辑 - provider 可以独立单测 --- ## 4.4 Phase 4:把 World Overlay 与 HUD Overlay 正式分家 ### 目标 让系统命名和责任与实际表现一致。 ### 最终划分 #### World Overlay 使用 renderer pass: - scene icons - frustum - light shapes - transform gizmos - future helper volumes #### HUD Overlay 使用 editor UI / ImGui: - orientation gizmo - toolbar - screen label - 操作提示 ### 注意 这一步不是要求 orientation gizmo 也上 GPU。 正确目标是: - world-space 的内容不再走 ImGui - HUD 的内容明确归 UI ### 验收标准 - `SceneViewportOverlayRenderer.cpp` 不再承担历史兼容杂项职责 - 所有 world-anchored overlay 都从统一 world overlay frame 进入 - HUD overlay 有独立职责命名 --- ## 4.5 Phase 5:建立 editor gizmo 资源体系 ### 目标 消除当前 ad hoc 图标与 shader 资源加载方式。 ### 方案 新增 editor 资源注册层,例如: - `EditorBuiltinResourceRegistry` - `EditorGizmoResourceRegistry` 负责: - icon texture 注册 - gizmo shader 注册 - gizmo material / pipeline preset 注册 - future atlas / hot reload / fallback ### 具体要求 - 不再在 pass 内部直接写文件路径解析作为长期方案 - camera / light / future icons 统一纳入 editor builtin 资源 - gizmo pass 通过资源 key 查询,而不是硬编码本地 png 路径 ### 验收标准 - gizmo 资源加载路径统一 - 资源生命周期与缓存不再散落在单个 pass 里 - future 新增 icon / shader 不需要继续复制粘贴路径解析代码 --- ## 4.6 Phase 6:测试体系重构 ### 目标 让测试覆盖系统边界,而不只是覆盖若干数学细节。 ### 建议补齐的测试类别 #### A. Provider contract tests 例如: - camera provider 在 camera enabled 时输出 icon + frustum - disabled camera 不输出 gizmo - directional / point / spot light 分别输出正确图元组合 #### B. Overlay builder orchestration tests 例如: - 多 provider 输出能正确合并到单帧 overlay data - selected / unselected subset 行为正确 - provider 顺序不影响 hit test 规则 #### C. Pass assembly tests 例如: - SceneView render plan 能生成正确的 `postScenePasses` - SceneView world overlay pass 顺序正确 - object id / outline / gizmo 的依赖关系正确 #### D. Editor resource contract tests 例如: - icon key 能解析到合法资源 - 缺失资源时能给出可诊断 fallback #### E. Packaging / build boundary tests 例如: - player runtime target 不依赖 editor gizmo shader / icon 资源 - editor target 才链接或注册 SceneView gizmo 资源 ### 验收标准 - 新增 gizmo 类型时,至少有 provider contract test - 修改 SceneView pass 顺序时,至少有 render plan test 可以兜底 - editor/runtime 边界错误能通过构建或测试尽早暴露 --- ## 5. 关于 Picking 与 Handle 命中的最终建议 ## 5.1 Scene object picking 当前对象选择已经基于 object-id buffer,这条路线应保留。 原因: - 这是正规的 GPU picking - 它与场景真实渲染结果一致性更好 - 更适合 editor 里对 mesh / model / future submesh 的对象级选中 ## 5.2 Gizmo handle picking 当前 gizmo handle 的命中是基于 canonical `handleRecords` 的 CPU hit test。 这不是临时方案,反而是合理的正式方案之一。 原因: - gizmo handle 本身就是高度人工设计的 screen-space / mixed-space 交互图元 - CPU 命中更容易做 priority、命中扩张、前后景优先级、hover 宽容度 - 没必要为了“全 GPU 化”把 handles 也做成 object-id pass 最终建议: - Scene object picking:继续 GPU object-id - Gizmo handle picking:继续 CPU hit test,但必须建立在统一 canonical handle 数据之上 这与 Unity 的使用体验和工程组织都更接近。 --- ## 6. 对当前代码的具体重构建议 ## 6.1 立即保留、不要动错的部分 - 保留 `CameraRenderer` 的 pass 时序骨架 - 保留 `overlayPasses` - 保留 `SceneViewportEditorOverlayData` - 保留 `SceneViewportEditorOverlayPass` - 保留 object-id picking 这些是已经验证正确的基础设施,不应推翻重做。 ## 6.2 应优先迁出的部分 - `BuiltinPostProcessRequest` 中的 SceneView 语义字段 - `BuiltinInfiniteGridPass` - `BuiltinObjectIdOutlinePass` - SceneView 专属 shader 的 engine builtin 注册 ## 6.3 应逐步拆分的部分 - `SceneViewportOverlayBuilder` - `ViewportHostService` 中 SceneView overlay / transient gizmo 的编排逻辑 - `SceneViewPanel` 中仍然残留的 gizmo orchestration 代码 --- ## 7. 推荐落地顺序 为避免大爆炸式重写,建议严格按下面顺序推进: 1. 先做 Phase 1 - editor 拿回 SceneView pass 规划权 - 不要先拆 gizmo provider 2. 再做 Phase 2 - grid / outline 脱离 engine builtin - 先保证边界正确 3. 再做 Phase 3 - provider registry - camera / light / transform 分 provider 4. 再做 Phase 4 - 正式命名 HUD 与 world overlay 的分层 5. 然后做 Phase 5 - 资源体系 6. 最后做 Phase 6 - 测试与 packaging 收口 核心原则: **不要先推翻正在工作的 overlay pass;应当先纠正语义归属,再做 provider 化和资源化。** --- ## 8. 非目标 本轮重构不应顺手扩展成以下主题: - render graph 改造 - gameplay runtime debug draw 系统 - 把所有 ImGui HUD 都迁到 GPU - 把 gizmo handle picking 也改成 GPU object-id - 重写整个 editor UI 框架 这些都不是本轮的主目标。 本轮的唯一主目标是: **把 SceneView gizmo / grid / outline 的系统边界、调度方式、资源归属、测试契约一次性做正规。** --- ## 9. 完成态判定标准 当以下条件全部满足时,可以认为本轮正式化重构收口: 1. `engine` 不再公开 SceneView grid / outline 的具体 camera 语义 2. `editor` 自己构建 SceneView post-scene passes 与 world overlay passes 3. camera / light / transform gizmo 由正式 provider 输出 4. orientation gizmo 被正式归类为 HUD overlay 5. gizmo 图标和 shader 拥有 editor 侧资源注册体系 6. player runtime 构建不再依赖 SceneView gizmo 资源 7. 测试覆盖 provider、render plan、resource boundary 三类关键契约 达到这一步后,当前 SceneView gizmo 系统才算真正进入“可持续扩展”的阶段。 --- ## 10. 本文档对应的直接行动建议 下一阶段的实施建议如下: 1. 先开一个“SceneView pass ownership 回收”阶段 - 目标只做 Phase 1 + Phase 2 - 不在同一阶段内继续大规模重写 gizmo 样式 2. 该阶段完成后,再开“Gizmo Provider Registry”阶段 - 先把 camera / light provider 正式拆出 - transform gizmo provider 最后并入 3. 再开“Editor Gizmo Resources + Tests 收口”阶段 - 把图标、shader、测试边界一次性清干净 这会比“看到哪改哪、边做边塞进现有 builder”稳定得多。 --- ## 11. 进度更新 2026-04-03 本轮已完成“SceneView pass ownership 回收”的最后一个收口步骤: 1. `engine` 不再注册 SceneView 专属的 `object-id-outline` / `infinite-grid` builtin shader 2. `BuiltinInfiniteGridPass` 与 `BuiltinObjectIdOutlinePass` 改为由调用方注入 shader 路径 3. SceneView grid / outline shader 已迁移到 `editor/resources/shaders/scene-viewport` 4. `editor` 通过自己的 shader 路径入口组装这两个 pass 5. 已补齐 editor 侧 shader 路径与加载测试,并回归验证 runtime builtin shader 测试 对应结论: - Phase 1:已完成 - Phase 2:已完成并收口 - Phase 3 及后续阶段:仍待继续推进 --- ## 12. 下一阶段执行计划 2026-04-03 基于当前真实代码状态,下一阶段不应继续直接堆叠新 gizmo 功能,而应优先完成 `Phase 3: Gizmo Provider Registry`。当前最主要的结构性问题有三处: 1. `SceneViewportOverlayBuilder` 仍是大单体 - camera gizmo - light gizmo - scene icon / frustum / helper 生成逻辑仍堆在同一处 2. `SceneViewPanel` 仍参与 transform gizmo 的 transient overlay 注入 - 仍通过 `SetSceneViewTransientTransformGizmoOverlayData(...)` 把临时 frame data 注入宿主 - 这说明 transform gizmo 还没有真正并入正式 provider 体系 3. `OrientationGizmo` 仍属于 HUD/ImGui 历史链路 - 现在功能上可用 - 但尚未在架构层被正式命名为 HUD overlay 因此,下一阶段推荐拆成如下 4 个可执行小阶段。 ### 12.1 Phase 3A:Overlay Provider Registry 落地 目标: - 把“谁负责生成 SceneView world overlay”从 `SceneViewportOverlayBuilder` 中拆出 - 让 `builder` 退化成聚合器,而不是所有 gizmo 规则的实现者 本阶段应新增: - `ISceneViewportOverlayProvider` - `SceneViewportOverlayProviderRegistry` - `SceneViewportOverlayBuildContext` 本阶段应先拆出的 provider: - `SceneViewportCameraOverlayProvider` - `SceneViewportLightOverlayProvider` 本阶段不应做: - 不修改 gizmo 样式 - 不动 transform gizmo 交互算法 - 不改 orientation gizmo 完成标志: - `SceneViewportOverlayBuilder` 不再直接内嵌 camera/light 具体生成逻辑 - provider 可以按顺序注册并聚合输出 `SceneViewportOverlayFrameData` - SceneView 现有 camera/light 图标、frustum、directional light helper 视觉结果保持不变 建议测试: - provider registry 装配顺序测试 - camera provider contract 测试 - light provider contract 测试 - 现有 overlay render flow 测试继续通过 ### 12.2 Phase 3B:Transform Gizmo 并入正式 Provider 体系 目标: - 去掉当前的 transient gizmo overlay 桥接层 - 让 move / rotate / scale gizmo 和其他 SceneView overlay 一样,成为正式 provider 输出 本阶段应处理: - `SceneViewPanel` 中 gizmo frame 组织逻辑 - `ViewportHostService` 中的 transient overlay 存储字段 - `SceneViewportTransformGizmoFrameBuilder` 建议方向: - 引入 `SceneViewportTransformGizmoOverlayProvider` - provider 输入来自统一的 gizmo state / selection state / camera state - 输出仍然是统一的 `worldLines / screenTriangles / handleRecords` 本阶段完成后,应删除或废弃: - `SetSceneViewTransientTransformGizmoOverlayData(...)` - `m_sceneViewTransientTransformGizmoOverlay` - `m_sceneViewTransientTransformGizmoInputs` 完成标志: - `SceneViewPanel` 不再向宿主注入临时 gizmo overlay 数据 - `ViewportHostService` 只消费正式 overlay provider 输出 - move / rotate / scale gizmo 交互行为不回退 建议测试: - move/rotate/scale gizmo provider 输出测试 - overlay handle hit test 回归测试 - SceneView render plan 下 gizmo 仍能正常显示与拾取 ### 12.3 Phase 4:HUD Overlay 与 World Overlay 正式分层 目标: - 把当前“能工作但还没正式命名”的 HUD 链路正规化 - 明确区分 `world overlay` 与 `HUD overlay` 本阶段应做: - 保留 `SceneViewportEditorOverlayPass` 作为 world overlay GPU pass - 将 `SceneViewportOverlayRenderer` 明确归类为 HUD overlay renderer - 将 `OrientationGizmo` 明确纳入 HUD overlay 体系 本阶段之后的职责边界应为: - `world overlay` - scene icon - camera frustum - light helper - transform gizmo - `HUD overlay` - orientation gizmo - Scene toolbar - 后续 label / hint / debug text 完成标志: - world/HUD 两条链路命名清楚 - orientation gizmo 不再被视为历史遗留杂项 - 后续 HUD 元素可持续接入,而不再向 world overlay 污染 建议测试: - HUD overlay 独立渲染入口测试 - SceneView panel 中 HUD/world 顺序与命中关系测试 ### 12.4 Phase 5:Editor Gizmo 资源体系继续收口 目标: - 继 shader 之后,把 gizmo 相关 editor 资源定位逻辑继续集中 本阶段建议新增: - `SceneViewportResourcePaths` 或等价 editor resource locator 本阶段应纳入统一入口的内容: - gizmo shader 路径 - camera/light icon 路径 - 后续如果引入 mesh/wire helper 资源,也走同一入口 本阶段不要求: - 不必立刻做 atlas - 不必引入复杂资源注册中心 完成标志: - panel/pass 内不再到处手写 `exeDir/../../resources/...` - editor 资源定位逻辑集中,便于后续 packaging 与迁移 建议测试: - 资源路径解析测试 - working directory 改变后的 editor 资源加载测试 ### 12.5 Phase 6:测试与打包边界收口 目标: - 把当前已经成型的边界真正测住 需要补齐的关键测试类别: 1. provider contract - provider 输入什么 - 必须输出什么 - 禁止输出什么 2. render assembly contract - SceneView render plan 如何装配 post-scene / overlay / HUD 3. resource boundary - editor-only shader / icon 不污染 runtime builtin - player/runtime 构建不依赖 SceneView 专属资源 4. interaction regression - gizmo handle hit test - scene icon picking - HUD 与 world overlay 的点击优先级 完成标志: - 当前 plan 第 9 节中的 7 条完成态判定标准全部可被验证 ### 12.6 推荐执行顺序 严格建议按下面顺序推进,不要跳阶段: 1. Phase 3A 2. Phase 3B 3. Phase 4 4. Phase 5 5. Phase 6 原因: - 先做 provider 化,才能消除 `builder` 与 `SceneViewPanel` 的持续膨胀 - 先做 transform gizmo provider 化,后面 HUD/world 分层才不会再被临时桥接逻辑牵制 - 资源与测试应在结构稳定后统一收口,而不是提前做成半成品 ### 12.7 下一次会话的直接开工点 下一次正式实施时,优先执行 `Phase 3A`,并以如下拆分作为第一批提交目标: 1. 新建 `ISceneViewportOverlayProvider` 2. 新建 `SceneViewportOverlayProviderRegistry` 3. 抽离 `SceneViewportCameraOverlayProvider` 4. 抽离 `SceneViewportLightOverlayProvider` 5. 让 `SceneViewportOverlayBuilder` 改为只负责: - 构建上下文 - 调用 provider registry - 合并 `SceneViewportOverlayFrameData` 这一步完成后,当前 SceneView gizmo 系统才算真正进入“可持续扩展”的第二阶段。 --- ## 13. 进度更新 2026-04-03 Phase 3A 已完成 本次已完成 `Phase 3A: Overlay Provider Registry 落地`,且已经通过代码、测试与 editor 编译验证。 已落地内容: 1. 新增 `SceneViewportOverlayBuildContext` 2. 新增 `ISceneViewportOverlayProvider` 3. 新增 `SceneViewportOverlayProviderRegistry` 4. 新增默认 provider: - `SceneViewportCameraOverlayProvider` - `SceneViewportLightOverlayProvider` 5. `SceneViewportOverlayBuilder` 已从大单体逻辑改为 provider 聚合器 6. `ViewportHostService` 已改为持有 `SceneViewportOverlayBuilder` 实例,而不是依赖静态构建入口 本阶段行为保持不变的部分: - `BuildSceneViewEditorOverlayContentSignature(...)` 未改 - `SceneViewPanel` 的 transient transform gizmo 注入链路未改 - orientation gizmo / HUD 分层未改 - grid / outline pass 未改 新增验证: 1. `SceneViewportOverlayProviderRegistryTest.AppendsProvidersInRegistrationOrder` 2. `SceneViewportOverlayProviderRegistryTest.CameraProviderBuildsSceneIconAndSelectedFrustum` 3. `SceneViewportOverlayProviderRegistryTest.LightProviderBuildsSceneIconAndSelectedDirectionalHelper` 4. `SceneViewportOverlayProviderRegistryTest.OverlayBuilderUsesDefaultRegistryToAggregateCameraAndLightProviders` 已执行验证: - `cmake --build build --config Debug --target editor_tests -- /p:BuildProjectReferences=false` - `build/tests/Editor/Debug/editor_tests.exe --gtest_filter=SceneViewportOverlayProviderRegistryTest.*:ViewportRenderFlowUtilsTest.*` - `cmake --build build --config Debug --target XCEditor` 阶段结论: - Phase 3A 已完成并可作为后续 `Phase 3B: Transform Gizmo 并入正式 Provider 体系` 的稳定起点 - 当前 `camera/light overlay` 的职责边界已经从单体 builder 中抽离,但 `transform gizmo` 仍然保留在 transient 注入链路,需要下一阶段继续收口