31 KiB
Unity式 SceneView Gizmo 系统完整审查与正式化重构方案
日期:2026-04-03
相关旧文档:
docs/plan/SceneViewport_Overlay_Gizmo_Rework_Plan.mddocs/plan/SceneViewport_Overlay_Gizmo_Rework_Checkpoint_2026-04-02.mddocs/plan/Unity SRP API参考文档.md
本文档以当前代码真实状态为准,覆盖:
editor中 SceneView gizmo / grid / outline / overlay 的现状审查engine与editor的职责边界重划- 一套更接近 Unity SceneView / Gizmos / Handles 思路的正式化重构方案
- 可分阶段落地、可测试、可验收的迁移计划
0. 执行结论
当前 SceneView 可视化链路已经完成了第一轮“从 ImGui 临时绘制向正式渲染 pass 迁移”的关键基础设施建设,但还没有真正收口。
当前最重要的结论有四条:
-
engine里现有的通用渲染扩展点是对的,应该保留。RenderPassRenderPassSequenceCameraRenderRequest::preScenePassesCameraRenderRequest::postScenePassesCameraRenderRequest::overlayPasses
-
editor里现有的SceneViewportEditorOverlayData和SceneViewportEditorOverlayPass方向是对的,应该继续扩展,而不是回退到 ImGui world draw。 -
目前真正的架构问题,不是“某个 gizmo 画得还不够像 Unity”,而是 SceneView 的 editor 语义仍然泄漏进了 engine 的 builtin postprocess。
infinite gridselection outlineoutline debug mask- 与之绑定的 shader / resource 注册
-
最终正确边界应当是:
engine/runtime负责“能画什么、怎样插 pass、怎样跑后端”editor/SceneView负责“为什么要画、何时画、画哪些 gizmo、哪些对象参与、交互规则是什么”
换句话说,Unity-like 的正确做法不是把所有 gizmo 都塞进 runtime engine,而是 runtime 提供绘制能力,editor 拥有 SceneView gizmo 的语义和调度权。
1. 当前实现完整审查
1.1 已经正式化、应保留的部分
A. CameraRenderer 的可扩展 pass 时序已经成立
当前 CameraRenderer 的执行顺序已经具备正规的扩展缝:
preScenePasses
-> scene geometry
-> object id
-> postScenePasses
-> builtin postprocess
-> overlayPasses
这条缝本身是正确的,说明现在的 renderer 已经不是一个只能硬编码单条流水线的结构,后续 SceneView 的正式化工作不需要推翻它。
B. Editor world overlay 已经有 canonical frame data
SceneViewportEditorOverlayData.h 已经提供了一份比较正确的中间表示:
worldLinesworldSpritesscreenTriangleshandleRecords
这意味着当前系统已经不必再依赖“某个 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 直接携带:
InfiniteGridPassDataselectedObjectIdsObjectIdOutlineStyle
这在工程上能跑,但从架构边界看是不对的。原因很简单:
- 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-outlinebuiltin://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 被硬编码规划:
SceneInfiniteGridSceneSelectionOutlineSceneSelectionMaskDebug
这相当于 SceneView 的一部分渲染语义并不真正属于 editor,而是“借用 engine builtin 实现”。
短期可用,长期会越来越难维护。
结论: 当前系统最大的结构性缺陷,不是 gizmo 样式,而是 editor 语义与 runtime 语义没有彻底分层。
1.4 仍然偏临时的实现点
A. icon 资源加载仍然是 ad hoc 文件路径解析
SceneViewportEditorOverlayPass 当前直接解析:
editor/resources/Icons/camera_gizmo.pngeditor/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 标签
这三层之间的关系应该是:
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 capabilityeditor的 SceneView 系统对应 Unity 中 SceneView 对 gizmos / wire overlay / handles 的调度HUD对应 SceneView 窗口自己的 UI overlay
因此本项目的最终方案应当遵循:
engine暴露“generic render extension seam”editor组装 SceneView 专属 render plan- SceneView 的 gizmo/grid/outline 作为 editor-only feature 实现
- runtime/player 构建不再默认携带 SceneView 语义资产
2.3 正式目标流水线
推荐的 SceneView 渲染时序如下:
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/RenderPassSequenceCameraRenderer的 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 公共语义:
SceneInfiniteGridSceneSelectionOutlinedebugSelectionMask
原则非常简单:
实现可以共享,语义不能泄漏。
4. 正式重构方案
4.1 Phase 1:先把 SceneView pass 规划权完全收回 editor
目标
让 editor 自己决定 SceneView 需要哪些 pass,而不是继续把 grid / outline 塞进 engine::BuiltinPostProcessRequest。
方案
新增 editor 侧的 SceneView render planning 层,例如:
SceneViewportRenderPlanSceneViewportPassChainBuilder
由它负责:
- 生成 SceneView 的
postScenePasses - 生成 SceneView 的
overlayPasses - 规划 object id 是否需要
- 规划 grid / outline / gizmo 的具体顺序
结果
CameraRenderRequest 仍然保留:
preScenePassespostScenePassesoverlayPasses
但 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:
SceneViewportGridPassSceneViewportSelectionOutlinePass
如果当前实现代码中有通用部分,可以这样拆:
engine- 保留无语义的 GPU helper
editor- 维护
SceneViewportGridPassData - 维护
SceneViewportSelectionOutlineSettings - 维护 SceneView 下的调用时机与参数策略
- 维护
需要同步迁移的内容
InfiniteGridPassData从 engine 公共 camera request 中移除ObjectIdOutlineStyle不再作为 engine camera request 的 editor 语义字段暴露builtin://shaders/infinite-gridbuiltin://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”。
建议接口
ISceneViewportGizmoProvider
Gather(const SceneViewportGizmoBuildContext&, SceneViewportOverlayFrameData&)
建议首批 provider
SceneCameraGizmoProviderSceneLightGizmoProviderSceneTransformHandleProviderSceneSelectionHelperProvider
后续可继续扩展:
- 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 资源注册层,例如:
EditorBuiltinResourceRegistryEditorGizmoResourceRegistry
负责:
- 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 语义字段BuiltinInfiniteGridPassBuiltinObjectIdOutlinePass- SceneView 专属 shader 的 engine builtin 注册
6.3 应逐步拆分的部分
SceneViewportOverlayBuilderViewportHostService中 SceneView overlay / transient gizmo 的编排逻辑SceneViewPanel中仍然残留的 gizmo orchestration 代码
7. 推荐落地顺序
为避免大爆炸式重写,建议严格按下面顺序推进:
-
先做 Phase 1
- editor 拿回 SceneView pass 规划权
- 不要先拆 gizmo provider
-
再做 Phase 2
- grid / outline 脱离 engine builtin
- 先保证边界正确
-
再做 Phase 3
- provider registry
- camera / light / transform 分 provider
-
再做 Phase 4
- 正式命名 HUD 与 world overlay 的分层
-
然后做 Phase 5
- 资源体系
-
最后做 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. 完成态判定标准
当以下条件全部满足时,可以认为本轮正式化重构收口:
engine不再公开 SceneView grid / outline 的具体 camera 语义editor自己构建 SceneView post-scene passes 与 world overlay passes- camera / light / transform gizmo 由正式 provider 输出
- orientation gizmo 被正式归类为 HUD overlay
- gizmo 图标和 shader 拥有 editor 侧资源注册体系
- player runtime 构建不再依赖 SceneView gizmo 资源
- 测试覆盖 provider、render plan、resource boundary 三类关键契约
达到这一步后,当前 SceneView gizmo 系统才算真正进入“可持续扩展”的阶段。
10. 本文档对应的直接行动建议
下一阶段的实施建议如下:
-
先开一个“SceneView pass ownership 回收”阶段
- 目标只做 Phase 1 + Phase 2
- 不在同一阶段内继续大规模重写 gizmo 样式
-
该阶段完成后,再开“Gizmo Provider Registry”阶段
- 先把 camera / light provider 正式拆出
- transform gizmo provider 最后并入
-
再开“Editor Gizmo Resources + Tests 收口”阶段
- 把图标、shader、测试边界一次性清干净
这会比“看到哪改哪、边做边塞进现有 builder”稳定得多。
11. 进度更新 2026-04-03
本轮已完成“SceneView pass ownership 回收”的最后一个收口步骤:
engine不再注册 SceneView 专属的object-id-outline/infinite-gridbuiltin shaderBuiltinInfiniteGridPass与BuiltinObjectIdOutlinePass改为由调用方注入 shader 路径- SceneView grid / outline shader 已迁移到
editor/resources/shaders/scene-viewport editor通过自己的 shader 路径入口组装这两个 pass- 已补齐 editor 侧 shader 路径与加载测试,并回归验证 runtime builtin shader 测试
对应结论:
- Phase 1:已完成
- Phase 2:已完成并收口
- Phase 3 及后续阶段:仍待继续推进
12. 下一阶段执行计划 2026-04-03
基于当前真实代码状态,下一阶段不应继续直接堆叠新 gizmo 功能,而应优先完成 Phase 3: Gizmo Provider Registry。当前最主要的结构性问题有三处:
-
SceneViewportOverlayBuilder仍是大单体- camera gizmo
- light gizmo
- scene icon / frustum / helper 生成逻辑仍堆在同一处
-
SceneViewPanel仍参与 transform gizmo 的 transient overlay 注入- 仍通过
SetSceneViewTransientTransformGizmoOverlayData(...)把临时 frame data 注入宿主 - 这说明 transform gizmo 还没有真正并入正式 provider 体系
- 仍通过
-
OrientationGizmo仍属于 HUD/ImGui 历史链路- 现在功能上可用
- 但尚未在架构层被正式命名为 HUD overlay
因此,下一阶段推荐拆成如下 4 个可执行小阶段。
12.1 Phase 3A:Overlay Provider Registry 落地
目标:
- 把“谁负责生成 SceneView world overlay”从
SceneViewportOverlayBuilder中拆出 - 让
builder退化成聚合器,而不是所有 gizmo 规则的实现者
本阶段应新增:
ISceneViewportOverlayProviderSceneViewportOverlayProviderRegistrySceneViewportOverlayBuildContext
本阶段应先拆出的 provider:
SceneViewportCameraOverlayProviderSceneViewportLightOverlayProvider
本阶段不应做:
- 不修改 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_sceneViewTransientTransformGizmoOverlaym_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:测试与打包边界收口
目标:
- 把当前已经成型的边界真正测住
需要补齐的关键测试类别:
-
provider contract
- provider 输入什么
- 必须输出什么
- 禁止输出什么
-
render assembly contract
- SceneView render plan 如何装配 post-scene / overlay / HUD
-
resource boundary
- editor-only shader / icon 不污染 runtime builtin
- player/runtime 构建不依赖 SceneView 专属资源
-
interaction regression
- gizmo handle hit test
- scene icon picking
- HUD 与 world overlay 的点击优先级
完成标志:
- 当前 plan 第 9 节中的 7 条完成态判定标准全部可被验证
12.6 推荐执行顺序
严格建议按下面顺序推进,不要跳阶段:
- Phase 3A
- Phase 3B
- Phase 4
- Phase 5
- Phase 6
原因:
- 先做 provider 化,才能消除
builder与SceneViewPanel的持续膨胀 - 先做 transform gizmo provider 化,后面 HUD/world 分层才不会再被临时桥接逻辑牵制
- 资源与测试应在结构稳定后统一收口,而不是提前做成半成品
12.7 下一次会话的直接开工点
下一次正式实施时,优先执行 Phase 3A,并以如下拆分作为第一批提交目标:
- 新建
ISceneViewportOverlayProvider - 新建
SceneViewportOverlayProviderRegistry - 抽离
SceneViewportCameraOverlayProvider - 抽离
SceneViewportLightOverlayProvider - 让
SceneViewportOverlayBuilder改为只负责:- 构建上下文
- 调用 provider registry
- 合并
SceneViewportOverlayFrameData
这一步完成后,当前 SceneView gizmo 系统才算真正进入“可持续扩展”的第二阶段。