docs: sync editor viewport host docs
This commit is contained in:
@@ -6,23 +6,246 @@
|
||||
|
||||
**源文件**: `editor/src/panels/SceneViewPanel.h`
|
||||
|
||||
**描述**: 场景视图面板,占位承载 Scene 视图窗口并标记动作路由焦点。
|
||||
**描述**: 编辑器 Scene View 面板,负责场景视口 UI、工具栏与工具叠层、对象选择、相机导航,以及 transform gizmo 的交互编排与 overlay 接线。
|
||||
|
||||
## 概述
|
||||
|
||||
当前 `SceneViewPanel` 还很轻。
|
||||
`SceneViewPanel` 是当前 Editor 里最像商业级场景视图壳层的面板之一。按 `SceneViewPanel.cpp` 的真实实现,它把以下链路编排到同一帧里:
|
||||
|
||||
它目前主要做两件事:
|
||||
- 顶部 `Pivot / Center` 与 `Global / Local` 切换。
|
||||
- 左侧 `ViewMove / Move / Rotate / Scale / Transform` 工具条。
|
||||
- Scene 视口纹理申请与交互表面建立。
|
||||
- scene icon、orientation gizmo、transform gizmo 与空白区域 picking 的命中仲裁。
|
||||
- look / pan / fly / focus-selection 输入回传。
|
||||
- transform gizmo 的绘制、命中、拖拽和撤销事务。
|
||||
|
||||
- 以 `"Scene"` 为名字打开一个面板窗口
|
||||
- 在该窗口激活时通知 `Actions` 层观察焦点路由
|
||||
`SceneViewPanel` 自己不持有 render target,也不直接做 object-id 读回或 `SceneRenderer` 调用;这些都通过 [IViewportHostService](../../Viewport/IViewportHostService/IViewportHostService.md) 完成。它的职责更准确地说是交互编排层。
|
||||
|
||||
## 当前实现边界
|
||||
## 生命周期与公开入口
|
||||
|
||||
- 当前没有真正的编辑器视口渲染、相机操控或 gizmo 逻辑。
|
||||
- 它现在更像后续 Scene View 功能的面板占位入口。
|
||||
- [Constructor](Constructor.md)
|
||||
创建标题固定为 `"Scene"` 的面板实例,并保留 Scene View 运行期私有状态。
|
||||
- [Render](Render.md)
|
||||
执行完整的 Scene View 工具栏、命中仲裁、导航输入、gizmo 拖拽与 overlay 提交流程。
|
||||
|
||||
## 当前帧执行链路
|
||||
|
||||
`Render()` 当前不是简单的 ImGui 绘制,而是按下面的顺序组织 Scene View:
|
||||
|
||||
1. 绘制顶部 Scene toolbar。
|
||||
2. 调用 [ViewportPanelContent](../ViewportPanelContent/ViewportPanelContent.md) 请求 `Scene` 视口纹理并建立交互表面。
|
||||
3. 绘制左侧工具条,并处理 `Q/W/E/R` 快捷键与工具切换。
|
||||
4. 若视口可交互,先读取宿主服务的 `SceneViewportOverlayData`,刷新 move / rotate / scale gizmo 上下文。
|
||||
5. 通过 `BuildSceneViewportTransformGizmoHandleBuildInputs(...)` 和 `BuildSceneViewportTransformGizmoOverlayState(...)` 组装当前帧 gizmo state,并先调用 `SetSceneViewTransformGizmoOverlayState(...)` 写回宿主服务。
|
||||
6. 调用 `GetSceneViewEditorOverlayFrameData(...)` 取回一份已经合成好 scene icon、相机/灯光辅助几何和 gizmo handle 的 frame data。
|
||||
7. 对这份 frame data 执行 [SceneViewportOverlayHitTester](../../Viewport/SceneViewportOverlayHitTester/SceneViewportOverlayHitTester.md) 命中测试,并再和 orientation gizmo 命中做统一仲裁。
|
||||
8. 处理点击、拖拽和导航,然后把 `SceneViewportInput` 回传给宿主服务。
|
||||
9. 视口图像存在时,再次刷新 gizmo,使其对齐本帧更新后的相机与输入状态,并再次调用 `SetSceneViewTransformGizmoOverlayState(...)`,让后续 render path 消费最新 gizmo state。
|
||||
10. 最后绘制前端 HUD / orientation overlay。
|
||||
|
||||
这里有个实现细节很重要:gizmo 当前会在一帧里刷新两次。
|
||||
|
||||
- 第一次用于交互命中。
|
||||
- 第二次用于最终 Scene View render path。
|
||||
|
||||
这样做的目的,是让导航输入和 gizmo 命中使用同一套几何语义,同时保证最终画出来的 overlay 和本帧相机状态保持一致。
|
||||
|
||||
## 顶部工具栏语义
|
||||
|
||||
当前顶部 Scene toolbar 只有两个切换,但它们都会直接改变 gizmo 的数学语义。
|
||||
|
||||
### Pivot / Center
|
||||
|
||||
`m_pivotMode` 当前有两种值:
|
||||
|
||||
- `Pivot`
|
||||
- `Center`
|
||||
|
||||
它会改变 `BuildSceneViewportSelectionGizmoState(...)` 的 pivot 计算:
|
||||
|
||||
- `Pivot`
|
||||
- 使用 primary selected object 的 transform 世界位置。
|
||||
- `Center`
|
||||
- 对所有选中对象求中心点平均值。
|
||||
|
||||
这里的“中心点”当前按源码有两级回退:
|
||||
|
||||
- 若对象有有效 `MeshFilterComponent + Mesh`,使用 `mesh->GetBounds().center` 经过对象 transform 变换后的世界位置。
|
||||
- 否则回退到对象 transform 世界位置。
|
||||
|
||||
因此 `Center` 更接近视觉中心,而 `Pivot` 更接近主对象原点。
|
||||
|
||||
### Global / Local
|
||||
|
||||
`m_transformSpaceMode` 当前有两种值:
|
||||
|
||||
- `Global`
|
||||
- `Local`
|
||||
|
||||
它直接影响 gizmo context 中的 `axisOrientation`:
|
||||
|
||||
- `Global`
|
||||
- 使用单位旋转,gizmo 轴始终对齐世界轴。
|
||||
- `Local`
|
||||
- 使用 primary selected object 的稳定世界旋转。
|
||||
|
||||
这里的稳定世界旋转不是直接取单一字段,而是沿父链把 local rotation 连乘得到,再归一化,因此它反映的是当前层级下的真实世界朝向。
|
||||
|
||||
## primary object 规则
|
||||
|
||||
gizmo 使用的 primary object 规则是:
|
||||
|
||||
- 优先取 `SelectionManager::GetSelectedEntity()`。
|
||||
- 再读取 `GetSelectedEntities()` 构造 `selectedObjects`。
|
||||
- 若 primary entity 为空,但多选列表非空,则回退到 `selectedObjects.back()`。
|
||||
- 若只有 primary,没有多选列表,则把 primary 补回 `selectedObjects`。
|
||||
|
||||
这意味着多选时仍然会有一个主对象,它决定:
|
||||
|
||||
- `Pivot` 模式的 pivot 原点。
|
||||
- `Local` 模式的轴向旋转来源。
|
||||
|
||||
## 命中仲裁与点击顺序
|
||||
|
||||
当前 Scene View 不是“点到什么就直接处理什么”,而是先把不同来源的命中结果统一抽象成 `SceneViewportInteractionCandidate`,再比较优先级、距离和深度。
|
||||
|
||||
### 命中前置条件
|
||||
|
||||
只有在以下条件同时满足时,面板才会解析视口交互:
|
||||
|
||||
- 视口纹理存在且尺寸有效。
|
||||
- 鼠标位于 viewport 内容区。
|
||||
- 当前不是 `ViewMove` 工具。
|
||||
- 当前不在 look / pan 拖拽中。
|
||||
- 左侧工具条没有悬停。
|
||||
- 没有 gizmo 正在活动拖拽。
|
||||
|
||||
### 当前优先级
|
||||
|
||||
按当前源码中的 priority 常量,交互优先级从高到低是:
|
||||
|
||||
- scale uniform: `333`
|
||||
- scale axis cap: `332`
|
||||
- scale axis line: `331`
|
||||
- move axis: `322`
|
||||
- move plane: `321`
|
||||
- rotate axis: `311`
|
||||
- orientation gizmo: `200`
|
||||
- scene icon: `100`
|
||||
- 空白区域 object-id picking: 兜底路径
|
||||
|
||||
这带来几个当前行为:
|
||||
|
||||
- transform gizmo 总会压过 orientation gizmo 和 scene icon。
|
||||
- orientation gizmo 会压过 scene icon。
|
||||
- 只有没有 overlay handle 命中时,才会退回到普通 object-id picking。
|
||||
|
||||
### 点击结果
|
||||
|
||||
左键点击当前有四种分支:
|
||||
|
||||
- 命中 transform gizmo
|
||||
- 调用对应 gizmo 的 `TryBeginDrag(...)`
|
||||
- 命中 orientation gizmo
|
||||
- `AlignSceneViewToOrientationAxis(...)`
|
||||
- 命中 scene icon
|
||||
- 直接 `SetSelectedEntity(entityId)`
|
||||
- 都没命中
|
||||
- 调用 `PickSceneViewEntity(...)` 走 object-id picking,没选中则清空选择
|
||||
|
||||
scene icon 这一支不依赖 object-id 纹理,而是直接消费 overlay frame data 里的 handle record,因此图标点击优先走 overlay 选择,不和场景几何 picking 混在一起。
|
||||
|
||||
## gizmo 更新、拖拽与撤销
|
||||
|
||||
`SceneViewPanel` 自己不实现 move / rotate / scale 数学,但负责决定哪一套 gizmo 现在拥有输入权。
|
||||
|
||||
当前面板会做这些事情:
|
||||
|
||||
- 根据 selection、pivot mode、transform space 和鼠标位置构造 gizmo context。
|
||||
- 当某一套 gizmo 处于活动状态时,把其他 gizmo 的 `mousePosition` 置成 `(-1, -1)`,防止 hover 状态串扰。
|
||||
- 工具切换、视口失效、selection 变更到其他对象时,主动 `CancelDrag(...)`。
|
||||
- 鼠标按住左键时持续 `UpdateDrag(...)`,释放时 `EndDrag(...)`。
|
||||
|
||||
撤销事务本身仍由 gizmo 类发起,但面板负责保证事务只被当前活动 gizmo 使用。
|
||||
|
||||
## 相机导航与 Focus Selection
|
||||
|
||||
导航模型当前和成熟 Scene View 很接近:
|
||||
|
||||
- 右键按住:look
|
||||
- 右键 look 期间:`WASDQE` 飞行移动
|
||||
- `Shift`:快速移动
|
||||
- 中键拖拽:pan
|
||||
- `ViewMove` 工具下左键拖拽:pan
|
||||
- 滚轮
|
||||
- 普通悬停时:缩放
|
||||
- 右键 look 时:调整飞行速度
|
||||
- `F`:发送 `focusSelectionRequested`
|
||||
|
||||
真正的 focus 行为由 [ViewportHostService](../../Viewport/ViewportHostService/ViewportHostService.md) 执行:
|
||||
|
||||
- 有 primary selection 时聚焦该对象的 transform position。
|
||||
- 否则聚焦所有根对象位置的平均值。
|
||||
- 场景为空时聚焦原点。
|
||||
|
||||
当前 [SceneViewportCameraController](../../Viewport/SceneViewportCameraController/SceneViewportCameraController.md) 虽然支持 orbit 输入,但 `SceneViewPanel` 仍把 `orbiting` 固定为 `false`,所以完整 orbit 手势还没有在面板层接线。
|
||||
|
||||
## overlay 接线模型
|
||||
|
||||
`SceneViewPanel` 当前和宿主服务之间的 overlay 接线可以概括成两步:
|
||||
|
||||
### 命中前
|
||||
|
||||
- 面板先构造当前帧 `SceneViewportTransformGizmoOverlayState`
|
||||
- 再调用 `SetSceneViewTransformGizmoOverlayState(...)`
|
||||
- 然后通过 `GetSceneViewEditorOverlayFrameData(...)` 拿到合成 frame data 做 hit-test
|
||||
|
||||
### 渲染前
|
||||
|
||||
- 导航和拖拽处理结束后,面板再次刷新 gizmo state
|
||||
- 再次调用 `SetSceneViewTransformGizmoOverlayState(...)`
|
||||
- 后续 `ViewportHostService::RenderRequestedViewports(...)` 会因为 state 重新变脏而重建 overlay frame data,并把它交给 [SceneViewportEditorOverlayPass](../../Viewport/Passes/SceneViewportEditorOverlayPass/SceneViewportEditorOverlayPass.md)
|
||||
|
||||
这意味着当前 transform gizmo overlay 是“面板生成、宿主缓存并渲染、逐帧失效”的状态输入,而不是面板自己持有的一份 GPU pass 数据。
|
||||
|
||||
与之对应,`DrawSceneViewportHudOverlay(...)` 现在只补一层 ImGui HUD / orientation gizmo,不再承担 transform gizmo 主绘制。
|
||||
|
||||
## 设计说明
|
||||
|
||||
`SceneViewPanel` 当前的结构体现出一个比较成熟的编辑器设计取向:
|
||||
|
||||
- 面板负责交互编排和 UI 状态。
|
||||
- 宿主服务负责视口资源、渲染与 picking。
|
||||
- gizmo 类负责具体变换数学。
|
||||
- overlay 构建与渲染进一步拆成数据层、命中层、GPU pass 层和 ImGui 层。
|
||||
|
||||
这种分层的好处是 Scene View 能持续长大,而不会迅速坍缩成一个几千行、什么都管的大面板类。
|
||||
|
||||
## 生命周期与线程语义
|
||||
|
||||
- 该面板依赖有效 `IEditorContext` 和 `IViewportHostService`。
|
||||
- 所有交互与绘制都应视为 Editor 主线程 / ImGui 帧内行为。
|
||||
- `m_moveGizmo`、`m_rotateGizmo`、`m_scaleGizmo`、导航拖拽标记、工具模式、pivot mode 和 transform space mode 都属于面板实例私有状态。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 选择主路径仍依赖有效 object-id 帧;只有 scene icon 点击走 overlay 直选。
|
||||
- orbit 手势能力仍未从控制器完整接到面板。
|
||||
- `Transform` 模式下的 scale 只暴露 `uniformOnly` 行为。
|
||||
- scale gizmo 当前仍偏单对象语义,多对象缩放没有和 move / rotate 一样展开。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Constructor](Constructor.md)
|
||||
- [Render](Render.md)
|
||||
- [panels](../panels.md)
|
||||
- [Actions](../../Actions/Actions.md)
|
||||
- [Viewport](../../Viewport/Viewport.md)
|
||||
- [ViewportPanelContent](../ViewportPanelContent/ViewportPanelContent.md)
|
||||
- [IViewportHostService](../../Viewport/IViewportHostService/IViewportHostService.md)
|
||||
- [ViewportHostService](../../Viewport/ViewportHostService/ViewportHostService.md)
|
||||
- [SceneViewportOverlayHandleBuilder](../../Viewport/SceneViewportOverlayHandleBuilder/SceneViewportOverlayHandleBuilder.md)
|
||||
- [SceneViewportOverlayHitTester](../../Viewport/SceneViewportOverlayHitTester/SceneViewportOverlayHitTester.md)
|
||||
- [SceneViewportMoveGizmo](../../Viewport/SceneViewportMoveGizmo/SceneViewportMoveGizmo.md)
|
||||
- [SceneViewportRotateGizmo](../../Viewport/SceneViewportRotateGizmo/SceneViewportRotateGizmo.md)
|
||||
- [SceneViewportScaleGizmo](../../Viewport/SceneViewportScaleGizmo/SceneViewportScaleGizmo.md)
|
||||
- [SceneView Interaction And Gizmo Model](../../../../_guides/Editor/SceneView-Interaction-And-Gizmo-Model.md)
|
||||
|
||||
Reference in New Issue
Block a user