docs: add scene viewport camera and picking docs
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# SceneViewportCameraController::ApplyInput
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `method`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportCameraController.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
void ApplyInput(const SceneViewportCameraInputState& input);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
消化一帧 Scene View 导航输入,并更新控制器的焦点、距离、朝向、飞行速度和动画状态。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
### 前置条件与动画中断
|
||||
|
||||
- `input.viewportHeight <= 0` 时直接返回。
|
||||
- 只要这一帧存在任意手动输入:
|
||||
- look
|
||||
- orbit
|
||||
- pan
|
||||
- zoom
|
||||
- flySpeedDelta
|
||||
- moveForward / moveRight / moveUp
|
||||
|
||||
就会先取消正在进行的朝向 snap 动画。
|
||||
|
||||
### look 输入
|
||||
|
||||
- 使用较低灵敏度更新 `yaw / pitch`。
|
||||
- look 的语义是“相机原地转向”,因此旋转后会调用 `UpdateFocalPointFromPosition()`:
|
||||
- 保持当前位置不动
|
||||
- 重新推导焦点位置
|
||||
|
||||
### orbit 输入
|
||||
|
||||
- 使用独立于 look 的 orbit 灵敏度更新 `yaw / pitch`。
|
||||
- orbit 的语义是“围绕当前焦点旋转”,因此旋转后会调用 `UpdatePositionFromFocalPoint()`:
|
||||
- 保持焦点不动
|
||||
- 重新推导相机位置
|
||||
|
||||
### pan 输入
|
||||
|
||||
- 根据当前 `viewportHeight` 与 `distance` 计算每像素对应的世界单位。
|
||||
- 使用当前相机 `right / up` 向量把 2D 鼠标位移映射到世界空间 delta。
|
||||
- `focalPoint` 和 `position` 会一起平移,因此观察偏移保持不变。
|
||||
|
||||
### fly speed 输入
|
||||
|
||||
- `flySpeedDelta` 用指数因子 `1.20 ^ delta` 调整飞行速度。
|
||||
- 飞行速度最终会被钳制在 `[0.1, 500.0]`。
|
||||
|
||||
### fly movement 输入
|
||||
|
||||
- 当 `deltaTime > 0` 且存在方向输入时,会按 `forward + right + world up` 混合生成移动方向。
|
||||
- `fastMove = true` 时,当前实现使用 `4x` 速度倍率。
|
||||
- `position` 和 `focalPoint` 会一起沿该方向平移,因此相机与焦点相对偏移不变。
|
||||
|
||||
### zoom 输入
|
||||
|
||||
- 使用 `0.85 ^ zoomDelta` 调整 `distance`。
|
||||
- `distance` 会被钳制在 `[0.5, 500.0]`。
|
||||
- 缩放后通过 `UpdatePositionFromFocalPoint()` 重算相机位置。
|
||||
- 当前 zoom 不会修改 `flySpeed`。
|
||||
|
||||
### 朝向 snap 动画推进
|
||||
|
||||
- 当 `m_snapAnimating = true` 且 `deltaTime > 0` 时,会推进朝向插值。
|
||||
- 当前动画时长固定约 `0.22s`。
|
||||
- 插值曲线使用三次缓出。
|
||||
- yaw 使用 `LerpAngleDegrees(...)` 处理跨 `180/-180` 的角度插值。
|
||||
- 动画完成后会自动把 `m_snapAnimating` 设回 `false`。
|
||||
|
||||
## 与测试的对应关系
|
||||
|
||||
- `LookInputRotatesCameraInPlaceAndKeepsDistance`
|
||||
- `OrbitInputRotatesAroundFocalPointAndKeepsDistance`
|
||||
- `PanAndZoomUpdateCameraStateConsistently`
|
||||
- `FlyInputMovesCameraAndFocalPointTogether`
|
||||
- `ZoomDoesNotChangeFlySpeed`
|
||||
- `FlySpeedDeltaAdjustsMovementSpeedIndependentlyFromZoom`
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportCameraController](SceneViewportCameraController.md)
|
||||
- [SceneViewportCameraInputState](SceneViewportCameraInputState.md)
|
||||
- [ApplyTo](ApplyTo.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
# SceneViewportCameraController::ApplyTo
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `method`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportCameraController.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
void ApplyTo(Components::TransformComponent& transform) const;
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
把控制器内部维护的 Scene View 相机状态写回一个实际的 `TransformComponent`。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 会先把 `transform.position` 设置为 `GetPosition()`。
|
||||
- 然后用当前 `yaw / pitch` 生成欧拉角四元数并写回旋转。
|
||||
- 当前 roll 固定为 `0`。
|
||||
- 旋转写回使用:
|
||||
- `pitch` 取负后转弧度
|
||||
- `yaw` 保持正向后转弧度
|
||||
|
||||
## 与测试的对应关系
|
||||
|
||||
- `ApplyToMatchesComputedPositionAndForward` 验证了写回后的位置与 forward 和控制器状态一致。
|
||||
- `ApplyToLooksAtControllerFocalPointInViewSpace` 验证了焦点点位在相机视空间内落在正前方 `distance` 位置。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportCameraController](SceneViewportCameraController.md)
|
||||
- [Reset / Focus](Reset-And-Focus.md)
|
||||
- [ApplyInput](ApplyInput.md)
|
||||
@@ -0,0 +1,47 @@
|
||||
# SceneViewportCameraController::Reset / Focus
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `methods`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportCameraController.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
void Reset();
|
||||
void Focus(const Math::Vector3& point);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
重置 Scene View 相机控制状态,或把当前观察焦点切到指定世界点。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
### `Reset()`
|
||||
|
||||
- 把内部控制状态恢复到编辑器默认值:
|
||||
- `focalPoint = Vector3::Zero()`
|
||||
- `distance = 6.0f`
|
||||
- `flySpeed = 5.0f`
|
||||
- `yaw = -35`
|
||||
- `pitch = -20`
|
||||
- 取消任何正在进行的朝向 snap 动画。
|
||||
- 最后调用 `UpdatePositionFromFocalPoint()`,用当前 forward 和 distance 重新计算相机位置。
|
||||
|
||||
### `Focus(...)`
|
||||
|
||||
- 直接把 `m_focalPoint` 设置为目标点。
|
||||
- 保持当前 `distance`、`yaw`、`pitch` 不变。
|
||||
- 然后重新计算相机位置。
|
||||
|
||||
## 与测试的对应关系
|
||||
|
||||
- `FocusMovesPivotWithoutChangingDistance` 验证了 `Focus(...)` 会移动焦点但不改变距离。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportCameraController](SceneViewportCameraController.md)
|
||||
- [SnapToForward / AnimateToForward](SnapToForward-And-AnimateToForward.md)
|
||||
- [ApplyTo](ApplyTo.md)
|
||||
@@ -0,0 +1,84 @@
|
||||
# SceneViewportCameraController
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `header-only class + input struct`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportCameraController.h`
|
||||
|
||||
**描述**: Scene View 隐藏编辑器相机控制器,负责焦点点位、观察方向、距离、飞行速度和朝向对齐动画。
|
||||
|
||||
## 概述
|
||||
|
||||
`SceneViewportCameraController` 不是通用相机组件,而是专门为 Editor Scene View 服务的一层控制器。它内部维护:
|
||||
|
||||
- `focalPoint`
|
||||
- `distance`
|
||||
- `yaw / pitch`
|
||||
- `flySpeed`
|
||||
- orientation snap 动画状态
|
||||
|
||||
然后通过 `ApplyTo(...)` 把这组状态写回一个 `TransformComponent`。
|
||||
|
||||
这种设计和商业引擎编辑器很像:编辑器视角先维护一份独立控制状态,再把结果同步给真正参与渲染的隐藏相机对象。
|
||||
|
||||
## 公开成员
|
||||
|
||||
| 成员 | 说明 |
|
||||
|------|------|
|
||||
| [SceneViewportCameraInputState](SceneViewportCameraInputState.md) | 控制器输入快照,覆盖 look、orbit、pan、zoom、fly movement 和 fly speed 调整。 |
|
||||
| [Reset / Focus](Reset-And-Focus.md) | 恢复默认控制状态,或把焦点切到指定世界点。 |
|
||||
| [SnapToForward / AnimateToForward](SnapToForward-And-AnimateToForward.md) | 立即或渐进地对齐到目标朝向。 |
|
||||
| [ApplyInput](ApplyInput.md) | 消化本帧输入,更新内部状态。 |
|
||||
| [ApplyTo](ApplyTo.md) | 将当前状态写回相机变换。 |
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
按 `SceneViewportCameraController.h` 的实现:
|
||||
|
||||
- look 输入会原地转向,并重新计算焦点,使“当前位置不动、视线改变”。
|
||||
- orbit 输入会围绕焦点转动,并重新计算相机位置。
|
||||
- pan 输入会根据视口高度与当前距离换算世界单位位移。
|
||||
- zoom 只改变 `distance`,不会改变 `flySpeed`。
|
||||
- fly movement 会同时平移相机位置和焦点点位,因此观察偏移保持不变。
|
||||
- orientation snap 使用约 `0.22s` 的三次缓出动画。
|
||||
|
||||
## 与测试的对应关系
|
||||
|
||||
`tests/Editor/test_scene_viewport_camera_controller.cpp` 当前验证了:
|
||||
|
||||
- `ApplyTo(...)` 后相机位置与 forward 一致。
|
||||
- `Focus(...)` 不改变距离。
|
||||
- look / orbit / pan / zoom / fly 的状态演化符合预期。
|
||||
- zoom 不会偷偷影响飞行速度。
|
||||
- `flySpeedDelta` 会独立改变飞行移动速度。
|
||||
|
||||
这使它成为当前 `Viewport` 模块里覆盖相对完整的一个基础类型。
|
||||
|
||||
## 设计说明
|
||||
|
||||
为什么不是直接在 `CameraComponent` 上堆一堆 Scene View 状态?
|
||||
|
||||
- 编辑器相机控制状态属于编辑器,不属于场景序列化对象。
|
||||
- `CameraComponent` 更适合表达“被场景保存的真实相机”,而不是“编辑器当前观察姿态”。
|
||||
- 通过控制器与 `ApplyTo(...)` 分层,后续要替换输入映射、增加插值或保存 Editor 视角历史都更容易。
|
||||
|
||||
## 生命周期与线程语义
|
||||
|
||||
- 这是一个纯内存控制器对象,没有独立资源生命周期。
|
||||
- 当前按值持有在 [ViewportHostService](../ViewportHostService/ViewportHostService.md) 内部。
|
||||
- 应视为主线程 / UI 线程更新对象。
|
||||
- 当前公开工作流可参考 [Reset / Focus](Reset-And-Focus.md)、[SnapToForward / AnimateToForward](SnapToForward-And-AnimateToForward.md)、[ApplyInput](ApplyInput.md) 与 [ApplyTo](ApplyTo.md)。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 当前俯仰角被钳制到 `[-89, 89]`,不支持翻转。
|
||||
- `ComputeWorldUnitsPerPixel(...)` 内部使用固定 `60` 度 FOV 估算 pan 比例,和任意 FOV 相机并不完全等价。
|
||||
- 控制器本身支持 orbit 输入,但当前 `SceneViewPanel` 主要使用的是 look、pan、fly 和 focus-selection。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ViewportHostService](../ViewportHostService/ViewportHostService.md)
|
||||
- [IViewportHostService](../IViewportHostService/IViewportHostService.md)
|
||||
- [SceneViewPanel](../../panels/SceneViewPanel/SceneViewPanel.md)
|
||||
- `tests/Editor/test_scene_viewport_camera_controller.cpp`
|
||||
@@ -0,0 +1,35 @@
|
||||
# SceneViewportCameraInputState
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportCameraController.h`
|
||||
|
||||
## 作用
|
||||
|
||||
表示 `SceneViewportCameraController` 在单帧内消费的输入快照。
|
||||
|
||||
## 字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `lookDeltaX / lookDeltaY` | 原地观察模式的鼠标增量。 |
|
||||
| `orbitDeltaX / orbitDeltaY` | 围绕当前焦点旋转的鼠标增量。 |
|
||||
| `panDeltaX / panDeltaY` | 视平面平移的鼠标增量。 |
|
||||
| `zoomDelta` | 缩放滚轮增量。 |
|
||||
| `flySpeedDelta` | 飞行速度调节增量。 |
|
||||
| `deltaTime` | 本帧时间步长,供飞行移动与朝向动画使用。 |
|
||||
| `moveForward / moveRight / moveUp` | 飞行移动的三个方向分量。 |
|
||||
| `viewportHeight` | 当前视口高度,用于把 pan 像素位移换算成世界单位。 |
|
||||
| `fastMove` | 是否启用快速飞行移动。 |
|
||||
|
||||
## 设计含义
|
||||
|
||||
- 这是“已经被 `SceneViewPanel` 解释过意图”的输入快照。
|
||||
- 控制器不直接监听按键,而是只关心 look / orbit / pan / fly / zoom 这些抽象动作。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportCameraController](SceneViewportCameraController.md)
|
||||
- [ApplyInput](ApplyInput.md)
|
||||
@@ -0,0 +1,49 @@
|
||||
# SceneViewportCameraController::SnapToForward / AnimateToForward
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `methods`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportCameraController.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
void SnapToForward(const Math::Vector3& forward);
|
||||
void AnimateToForward(const Math::Vector3& forward);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
把 Scene View 相机朝向立即或渐进地对齐到目标 forward 方向。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
### 共同前置条件
|
||||
|
||||
- 目标 `forward` 的长度平方小于等于 `Math::EPSILON` 时,两者都会直接返回。
|
||||
- 两者都会先通过 `ComputeOrientationAngles(forward)` 把方向向量换算成 `yaw / pitch`。
|
||||
|
||||
### `SnapToForward(...)`
|
||||
|
||||
- 直接把 `m_yawDegrees`、`m_pitchDegrees` 设置为目标朝向。
|
||||
- 取消正在进行的 snap 动画。
|
||||
- 立即根据当前焦点与距离重算位置。
|
||||
|
||||
### `AnimateToForward(...)`
|
||||
|
||||
- 记录当前 yaw / pitch 作为动画起点。
|
||||
- 记录目标 yaw / pitch 作为动画终点。
|
||||
- 把 `m_snapElapsed` 归零并设置 `m_snapAnimating = true`。
|
||||
- 真正的插值推进发生在后续 [ApplyInput](ApplyInput.md) 中,依赖 `input.deltaTime`。
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- 当前朝向动画只插值 yaw / pitch,不插值焦点或距离。
|
||||
- `pitch` 会被限制在 `[-89, 89]`,不支持翻转过顶。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportCameraController](SceneViewportCameraController.md)
|
||||
- [Reset / Focus](Reset-And-Focus.md)
|
||||
- [ApplyInput](ApplyInput.md)
|
||||
@@ -0,0 +1,46 @@
|
||||
# BuildSceneViewportRay
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `function`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportPicker.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
bool BuildSceneViewportRay(
|
||||
const SceneViewportOverlayData& overlay,
|
||||
const Math::Vector2& viewportSize,
|
||||
const Math::Vector2& viewportPosition,
|
||||
Math::Ray& outRay);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
根据 Scene View 相机 overlay 数据和视口内坐标构造一条世界空间射线。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 若 `overlay.valid` 为假,或视口尺寸/位置非法,则返回 `false`。
|
||||
- 会把 `viewportPosition` 归一化成 NDC 坐标,然后结合:
|
||||
- `cameraForward`
|
||||
- `cameraRight`
|
||||
- `cameraUp`
|
||||
- `verticalFovDegrees`
|
||||
计算世界射线方向。
|
||||
- 若最终方向长度退化到 `EPSILON` 以内,也会返回 `false`。
|
||||
- 成功时输出:
|
||||
- `origin = overlay.cameraPosition`
|
||||
- `direction = 归一化后的世界方向`
|
||||
|
||||
## 当前使用位置
|
||||
|
||||
- `SceneViewportMoveGizmo.cpp`
|
||||
- `SceneViewportRotateGizmo.cpp`
|
||||
- [PickSceneViewportEntity](PickSceneViewportEntity.md)
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportPicker](SceneViewportPicker.md)
|
||||
- [PickSceneViewportEntity](PickSceneViewportEntity.md)
|
||||
@@ -0,0 +1,55 @@
|
||||
# PickSceneViewportEntity
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `function`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportPicker.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
SceneViewportPickResult PickSceneViewportEntity(const SceneViewportPickRequest& request);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
使用 CPU 射线与场景网格做求交,返回最近命中的实体与世界坐标。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
### 1. 构造世界射线
|
||||
|
||||
- 若 `request.scene == nullptr`,直接返回未命中结果。
|
||||
- 否则先调用 [BuildSceneViewportRay](BuildSceneViewportRay.md);如果射线构造失败,也直接返回未命中。
|
||||
|
||||
### 2. 递归遍历场景
|
||||
|
||||
- 从 `scene->GetRootGameObjects()` 出发递归遍历整棵层级树。
|
||||
- 仅当对象:
|
||||
- `IsActiveInHierarchy() == true`
|
||||
- 具有 `MeshFilterComponent`
|
||||
- 具有 `MeshRendererComponent`
|
||||
- 且二者都启用
|
||||
时才进入网格求交。
|
||||
|
||||
### 3. 局部空间求交
|
||||
|
||||
- 会把世界射线变换到对象局部空间。
|
||||
- 先对 mesh bounds 做一次包围盒测试。
|
||||
- 之后按 mesh 形态分别遍历:
|
||||
- indexed sections
|
||||
- non-indexed sections
|
||||
- 求交成功后把局部命中点重新变回世界坐标,并用平方距离更新最近命中结果。
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- 当前只基于渲染网格三角形命中,不考虑更粗的 editor proxy 或 collider-only 选择。
|
||||
- Scene View 的主点击选择路径当前仍是 object-id picking,不是这个 CPU picker。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportPicker](SceneViewportPicker.md)
|
||||
- [SceneViewportPickRequest](SceneViewportPickRequest.md)
|
||||
- [SceneViewportPickResult](SceneViewportPickResult.md)
|
||||
- [ViewportObjectIdPicker](../ViewportObjectIdPicker/ViewportObjectIdPicker.md)
|
||||
@@ -0,0 +1,41 @@
|
||||
# SceneViewportPickRequest
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportPicker.h`
|
||||
|
||||
## 定义
|
||||
|
||||
```cpp
|
||||
struct SceneViewportPickRequest {
|
||||
const Components::Scene* scene = nullptr;
|
||||
SceneViewportOverlayData overlay = {};
|
||||
Math::Vector2 viewportSize = Math::Vector2::Zero();
|
||||
Math::Vector2 viewportPosition = Math::Vector2::Zero();
|
||||
};
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
封装一次 Scene View CPU 射线拾取所需的全部输入。
|
||||
|
||||
## 字段说明
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `scene` | 被遍历的场景指针。 |
|
||||
| `overlay` | 当前 Scene View 相机姿态、FOV 和有效性数据。 |
|
||||
| `viewportSize` | 视口像素尺寸。 |
|
||||
| `viewportPosition` | 视口内鼠标位置或点击位置。 |
|
||||
|
||||
## 当前使用方式
|
||||
|
||||
- [PickSceneViewportEntity](PickSceneViewportEntity.md) 会读取该结构体,先用其中的 `overlay + viewportSize + viewportPosition` 构造世界射线。
|
||||
- 若 `scene == nullptr`,pick 会直接返回未命中结果。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportPicker](SceneViewportPicker.md)
|
||||
- [PickSceneViewportEntity](PickSceneViewportEntity.md)
|
||||
@@ -0,0 +1,41 @@
|
||||
# SceneViewportPickResult
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportPicker.h`
|
||||
|
||||
## 定义
|
||||
|
||||
```cpp
|
||||
struct SceneViewportPickResult {
|
||||
uint64_t entityId = 0;
|
||||
Math::Vector3 worldPosition = Math::Vector3::Zero();
|
||||
float distanceSq = Math::FLOAT_MAX;
|
||||
bool hit = false;
|
||||
};
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
表示一次 Scene View CPU pick 的最近命中结果。
|
||||
|
||||
## 字段说明
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `entityId` | 最近命中的实体 ID;未命中时为 `0`。 |
|
||||
| `worldPosition` | 命中的世界坐标。 |
|
||||
| `distanceSq` | 命中点到射线原点的平方距离,用于比较最近命中。 |
|
||||
| `hit` | 是否存在有效命中。 |
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- `PickSceneViewportEntity(...)` 会在遍历中不断比较 `distanceSq`,只保留最近命中。
|
||||
- 若没有命中任何三角形,则保持默认值返回。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [SceneViewportPicker](SceneViewportPicker.md)
|
||||
- [PickSceneViewportEntity](PickSceneViewportEntity.md)
|
||||
@@ -0,0 +1,84 @@
|
||||
# SceneViewportPicker
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `structs + free functions`
|
||||
|
||||
**源文件**: `editor/src/Viewport/SceneViewportPicker.h`
|
||||
|
||||
**描述**: 提供 Scene View 的 CPU 射线拾取辅助函数,可从视口坐标生成世界射线并对场景网格执行三角形求交。
|
||||
|
||||
## 概述
|
||||
|
||||
`SceneViewportPicker` 代表的是传统编辑器里另一条常见的选取路径: CPU ray cast。
|
||||
|
||||
当前它能做两件事:
|
||||
|
||||
- [BuildSceneViewportRay](BuildSceneViewportRay.md): 由相机姿态、FOV、视口尺寸和点击位置生成一条世界射线。
|
||||
- [PickSceneViewportEntity](PickSceneViewportEntity.md): 遍历场景根对象,对 `MeshFilterComponent` 持有的网格做三角形求交,返回最近命中对象。
|
||||
|
||||
## 公开类型
|
||||
|
||||
| 类型 | 说明 |
|
||||
|------|------|
|
||||
| [SceneViewportPickRequest](SceneViewportPickRequest.md) | 一次 CPU pick 所需的 scene、overlay 和视口坐标输入。 |
|
||||
| [SceneViewportPickResult](SceneViewportPickResult.md) | pick 输出,包含命中实体、世界命中点和距离。 |
|
||||
|
||||
## 公开函数
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| [BuildSceneViewportRay](BuildSceneViewportRay.md) | 从 Scene View overlay 相机参数构造世界射线。 |
|
||||
| [PickSceneViewportEntity](PickSceneViewportEntity.md) | 对场景网格做 CPU 射线求交并返回最近命中。 |
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
按 `SceneViewportPicker.cpp` 的实现:
|
||||
|
||||
- 会先把世界射线变换到对象局部空间。
|
||||
- 会先过一次本地包围盒相交,再进入三角形测试。
|
||||
- 同时支持 indexed mesh 和 non-indexed mesh 的 section 遍历。
|
||||
- 返回值里会带最近命中的 `entityId`、命中世界位置和 `distanceSq`。
|
||||
|
||||
## 当前在编辑器里的使用状态
|
||||
|
||||
这是很关键的一点:
|
||||
|
||||
- 当前 `SceneViewPanel` 左键选取主路径不是它。
|
||||
- 当前主路径是 [ViewportHostService](../ViewportHostService/ViewportHostService.md) 里的 object-id 读回 `PickSceneViewEntity(...)`。
|
||||
- 但 [BuildSceneViewportRay](BuildSceneViewportRay.md) 不是“完全闲置”的 helper;`SceneViewportMoveGizmo` 和 `SceneViewportRotateGizmo` 当前仍直接复用它来建立拖拽/命中射线。
|
||||
|
||||
也就是说,这个 picker 目前更像一个保留的独立 helper,而不是 Scene View 点击选择的主实现。
|
||||
|
||||
## 设计说明
|
||||
|
||||
保留 CPU picker 仍然有价值,因为它适合:
|
||||
|
||||
- 没有 object-id 缓冲时的回退方案。
|
||||
- 做更精细的编辑器工具命中测试。
|
||||
- 离线几何查询或非渲染式拾取。
|
||||
|
||||
但从当前产品方向看,Scene View 已经明显在向“渲染结果驱动的 object-id picking”收拢。
|
||||
|
||||
## 与测试的对应关系
|
||||
|
||||
`tests/Editor/test_scene_viewport_picker.cpp` 当前验证了:
|
||||
|
||||
- `BuildSceneViewportRay(...)` 会让视口中心点穿过相机 forward。
|
||||
- `PickSceneViewportEntity(...)` 会返回最近命中的对象,而不是更远的命中。
|
||||
- 旋转和缩放后的网格仍能通过局部空间求交正确选中。
|
||||
- 空白区域点击会返回未命中结果。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- `PickSceneViewportEntity(...)` 当前不是 Scene View 左键选取的主路径。
|
||||
- 纯 CPU 三角形遍历在大场景下不如 object-id 路径稳定。
|
||||
- 当前只检查带 `MeshFilterComponent + MeshRendererComponent` 且二者都启用的对象。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Viewport](../Viewport.md)
|
||||
- [ViewportObjectIdPicker](../ViewportObjectIdPicker/ViewportObjectIdPicker.md)
|
||||
- [SceneViewPanel](../../panels/SceneViewPanel/SceneViewPanel.md)
|
||||
- [SceneViewportMoveGizmo](../SceneViewportMoveGizmo/SceneViewportMoveGizmo.md)
|
||||
- [SceneViewportRotateGizmo](../SceneViewportRotateGizmo/SceneViewportRotateGizmo.md)
|
||||
@@ -6,78 +6,55 @@
|
||||
|
||||
**源目录**: `editor/src/Viewport/`
|
||||
|
||||
**描述**: 编辑器视口基础设施模块,负责 Scene / Game 视口请求、隐藏编辑器相机、对象选择、overlay 构建与 gizmo 交互底层。
|
||||
**描述**: 编辑器视口基础设施模块,负责 Scene / Game 视口请求、Scene View 相机与 picking、overlay 数据装配,以及 transform gizmo 的命中与渲染辅助链路。
|
||||
|
||||
## 概述
|
||||
## 概览
|
||||
|
||||
`Viewport` 是当前 Editor 里最接近“场景视图内核”的一层。`SceneViewPanel` 和 `GameViewPanel` 都不直接管理渲染目标、场景相机请求或对象 ID 读回,而是通过这层完成:
|
||||
`Viewport` 是当前 Editor 里最接近“Scene / Game 视口运行时”的一层。
|
||||
`SceneViewPanel` 和 `GameViewPanel` 本身主要做 UI 编排;真正的视口宿主、overlay 合成和 gizmo 辅助逻辑都在这里。
|
||||
|
||||
- 视口纹理请求与尺寸管理。
|
||||
- Scene View 专用编辑器相机控制。
|
||||
- Scene View 的对象 ID picking。
|
||||
- 场景图标、相机视锥、方向光辅助线等 overlay 数据构建。
|
||||
- Move / Rotate / Scale gizmo 的命中、拖拽和绘制数据生成。
|
||||
- Scene View GPU pass 注入。
|
||||
## 当前职责拆分
|
||||
|
||||
从设计上看,这层相当于“Editor 视口宿主服务 + 交互 helper”的组合,而不是单一类。
|
||||
- [IViewportHostService](IViewportHostService/IViewportHostService.md)
|
||||
统一抽象 Scene / Game 视口宿主服务。
|
||||
- [ViewportHostService](ViewportHostService/ViewportHostService.md)
|
||||
当前宿主实现,持有 editor camera、render target 与 Scene View overlay 缓存。
|
||||
- [SceneViewportCameraController](SceneViewportCameraController/SceneViewportCameraController.md)
|
||||
维护 Scene View 相机的焦点、距离、朝向和飞行速度,并把结果写回隐藏相机变换。
|
||||
- [SceneViewportPicker](SceneViewportPicker/SceneViewportPicker.md)
|
||||
提供 CPU 射线选取 helper,当前更多用于辅助 gizmo 命中和保留的几何拾取路径。
|
||||
- [ViewportObjectIdPicker](ViewportObjectIdPicker/ViewportObjectIdPicker.md)
|
||||
封装 Scene View object-id 读回请求构建、像素坐标映射与颜色解码,是当前主点击选取链路的底层 helper。
|
||||
- [SceneViewportHudOverlay](SceneViewportHudOverlay/SceneViewportHudOverlay.md)
|
||||
负责前端 HUD / orientation gizmo 的构造、绘制和命中。
|
||||
- [SceneViewportInteractionResolver](SceneViewportInteractionResolver/SceneViewportInteractionResolver.md)
|
||||
把 HUD 命中与 overlay handle 命中合并成统一交互结果。
|
||||
- [SceneViewportInteractionActions](SceneViewportInteractionActions/SceneViewportInteractionActions.md)
|
||||
把交互结果折叠成 hover / click action,并分发 selection / orientation 对齐。
|
||||
- [SceneViewportOverlayHandleBuilder](SceneViewportOverlayHandleBuilder/SceneViewportOverlayHandleBuilder.md)
|
||||
把 gizmo overlay state 转成 `screenTriangles` 与 `handleRecords`。
|
||||
- [SceneViewportTransformGizmoFrameBuilder](SceneViewportTransformGizmoFrameBuilder/SceneViewportTransformGizmoFrameBuilder.md)
|
||||
组装每帧 move / rotate / scale gizmo context。
|
||||
- [SceneViewportTransformGizmoCoordinator](SceneViewportTransformGizmoCoordinator/SceneViewportTransformGizmoCoordinator.md)
|
||||
在 `SceneViewPanel` 与宿主服务之间桥接 gizmo overlay 提交和 begin / update / end 生命周期命令。
|
||||
|
||||
## 设计要点
|
||||
## 与面板层的关系
|
||||
|
||||
- `SceneViewPanel` 只负责 UI、快捷键和交互编排,真正的视口资源管理放在 [ViewportHostService](ViewportHostService/ViewportHostService.md)。
|
||||
- Scene View 不直接复用场景内相机,而是维护一台隐藏的编辑器相机,这和 Unity Scene View 的思路一致,能把编辑视角与游戏相机解耦。
|
||||
- 当前 Scene View 的可视化链路已经拆成三段:
|
||||
- editor-owned post-scene pass: [SceneViewportGridPass](Passes/SceneViewportGridPass/SceneViewportGridPass.md) 与 [SceneViewportSelectionOutlinePass](Passes/SceneViewportSelectionOutlinePass/SceneViewportSelectionOutlinePass.md)
|
||||
- GPU 世界 overlay pass: [SceneViewportEditorOverlayPass](Passes/SceneViewportEditorOverlayPass/SceneViewportEditorOverlayPass.md)
|
||||
- ImGui 前端 HUD / 交互 overlay: [SceneViewportOverlayRenderer](SceneViewportOverlayRenderer/SceneViewportOverlayRenderer.md)
|
||||
- 宿主服务内部还把 Scene View overlay 再拆成:
|
||||
- 可缓存的基础 editor overlay
|
||||
- hit test 用的 interaction overlay
|
||||
- 渲染阶段追加的 transient gizmo overlay
|
||||
- gizmo 的“可视几何构建”和“鼠标命中”当前已经拆成 [SceneViewportOverlayHandleBuilder](SceneViewportOverlayHandleBuilder/SceneViewportOverlayHandleBuilder.md) 与 [SceneViewportOverlayHitTester](SceneViewportOverlayHitTester/SceneViewportOverlayHitTester.md) 两层。
|
||||
- 当前 `SceneViewPanel` 的点击选取主路径是对象 ID 读回,不是 CPU mesh 射线拾取;后者目前保留在 [SceneViewportPicker](SceneViewportPicker/SceneViewportPicker.md) 里作为独立 helper。
|
||||
- [SceneViewPanel](../panels/SceneViewPanel/SceneViewPanel.md)
|
||||
是当前 `Viewport` 模块的主调用方。
|
||||
- [GameViewPanel](../panels/GameViewPanel/GameViewPanel.md)
|
||||
也会复用 `RequestViewport(...)` 这一层,但不会进入 Scene gizmo / picking 主链路。
|
||||
|
||||
## 当前实现边界
|
||||
## 当前边界
|
||||
|
||||
- `Viewport` 是 `editor/src` 下的 Editor 私有模块,不是 `engine/include/XCEngine/**` 那种 Runtime 公共 API。
|
||||
- 当前 Scene View 的 GPU pass 只在 `D3D12` 后端上真正可渲染。
|
||||
- `SceneViewportCameraController` 具备 orbit 输入能力,但当前 `SceneViewPanel` 主要驱动的是 look / pan / fly / focus selection。
|
||||
|
||||
## 目录结构
|
||||
|
||||
| 页面 | 说明 |
|
||||
|------|------|
|
||||
| [IViewportHostService](IViewportHostService/IViewportHostService.md) | Scene / Game 视口宿主服务抽象,以及共享输入/输出数据结构。 |
|
||||
| [SceneViewportCameraController](SceneViewportCameraController/SceneViewportCameraController.md) | 隐藏编辑器相机的轨道/飞行控制器。 |
|
||||
| [SceneViewportEditorOverlayData](SceneViewportEditorOverlayData/SceneViewportEditorOverlayData.md) | 世界 overlay 基元与帧缓存数据。 |
|
||||
| [SceneViewportMath](SceneViewportMath/SceneViewportMath.md) | 视口投影、拖拽平面、屏幕方向等数学 helper。 |
|
||||
| [SceneViewportMoveGizmo](SceneViewportMoveGizmo/SceneViewportMoveGizmo.md) | 位移 gizmo。 |
|
||||
| [SceneViewportOrientationGizmo](SceneViewportOrientationGizmo/SceneViewportOrientationGizmo.md) | 右上角朝向立方体与点击对齐 helper。 |
|
||||
| [SceneViewportOverlayHandleBuilder](SceneViewportOverlayHandleBuilder/SceneViewportOverlayHandleBuilder.md) | 把 gizmo draw data 转成屏幕三角形和 handle 记录。 |
|
||||
| [SceneViewportOverlayHitTester](SceneViewportOverlayHitTester/SceneViewportOverlayHitTester.md) | 基于 handle 记录做鼠标命中选择。 |
|
||||
| [SceneViewportOverlayBuilder](SceneViewportOverlayBuilder/SceneViewportOverlayBuilder.md) | 构建 Scene View 世界 overlay 帧数据。 |
|
||||
| [SceneViewportOverlayRenderer](SceneViewportOverlayRenderer/SceneViewportOverlayRenderer.md) | 在 ImGui draw list 上绘制 gizmo 与前端 overlay。 |
|
||||
| [SceneViewportRenderPlan](SceneViewportRenderPlan/SceneViewportRenderPlan.md) | Scene View 渲染前的 post-scene passes、overlay pass 与 clear-color override 计划对象。 |
|
||||
| [SceneViewportShaderPaths](SceneViewportShaderPaths/SceneViewportShaderPaths.md) | Scene View 专用 shader 资源路径 helper。 |
|
||||
| [SceneViewportPicker](SceneViewportPicker/SceneViewportPicker.md) | CPU 射线拾取 helper。 |
|
||||
| [SceneViewportRotateGizmo](SceneViewportRotateGizmo/SceneViewportRotateGizmo.md) | 旋转 gizmo。 |
|
||||
| [SceneViewportScaleGizmo](SceneViewportScaleGizmo/SceneViewportScaleGizmo.md) | 缩放 gizmo。 |
|
||||
| [SceneViewportTransformGizmoFrameBuilder](SceneViewportTransformGizmoFrameBuilder/SceneViewportTransformGizmoFrameBuilder.md) | 组装每帧 transform gizmo 选择状态、pivot 和三类 gizmo context。 |
|
||||
| [ViewportHostRenderFlowUtils](ViewportHostRenderFlowUtils/ViewportHostRenderFlowUtils.md) | Scene / Game 视口渲染流程辅助函数。 |
|
||||
| [ViewportHostRenderTargets](ViewportHostRenderTargets/ViewportHostRenderTargets.md) | 视口颜色 / 深度 / object-id 目标创建与销毁。 |
|
||||
| [ViewportHostService](ViewportHostService/ViewportHostService.md) | `IViewportHostService` 的当前实现。 |
|
||||
| [ViewportHostSurfaceUtils](ViewportHostSurfaceUtils/ViewportHostSurfaceUtils.md) | render surface、重用判断和像素坐标工具。 |
|
||||
| [ViewportObjectIdPicker](ViewportObjectIdPicker/ViewportObjectIdPicker.md) | object-id 读回与颜色解码 helper。 |
|
||||
| [Passes](Passes/Passes.md) | Scene View editor-owned post-scene / overlay pass 子目录。 |
|
||||
|
||||
## 与上层面板的关系
|
||||
|
||||
- [SceneViewPanel](../panels/SceneViewPanel/SceneViewPanel.md) 是当前 `Viewport` 模块最主要的调用方。
|
||||
- `SceneViewPanel` 通过 [ViewportPanelContent](../panels/ViewportPanelContent/ViewportPanelContent.md) 统一请求纹理并建立交互表面。
|
||||
- `GameViewPanel` 也会复用 `IViewportHostService::RequestViewport(...)` 这条链路,但不会进入 Scene gizmo / picking 流程。
|
||||
- 这里是 editor 内部模块,不是 runtime public API。
|
||||
- transform gizmo 的“形式化状态 -> overlay frame data -> hit-test / render”目前已经拆成多层 helper,不应再按旧口径理解为某个单一 renderer 直接完成全部工作。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Editor](../Editor.md)
|
||||
- [SceneViewPanel](../panels/SceneViewPanel/SceneViewPanel.md)
|
||||
- [SceneViewportCameraController](SceneViewportCameraController/SceneViewportCameraController.md)
|
||||
- [SceneViewportPicker](SceneViewportPicker/SceneViewportPicker.md)
|
||||
- [ViewportObjectIdPicker](ViewportObjectIdPicker/ViewportObjectIdPicker.md)
|
||||
- [SceneView Interaction And Gizmo Model](../../../_guides/Editor/SceneView-Interaction-And-Gizmo-Model.md)
|
||||
- [ViewportPanelContent](../panels/ViewportPanelContent/ViewportPanelContent.md)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# BuildViewportObjectIdReadbackRequest
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `function`
|
||||
|
||||
**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
bool BuildViewportObjectIdReadbackRequest(
|
||||
const ViewportObjectIdPickContext& context,
|
||||
ViewportObjectIdReadbackRequest& outRequest);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
把视口鼠标位置转换成一次可执行的像素读回请求。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 先把 `outRequest` 重置为空初始态。
|
||||
- 若 [CanPickViewportObjectId](CanPickViewportObjectId.md) 返回 `false`,则直接失败返回。
|
||||
- 否则会把:
|
||||
- `commandQueue`
|
||||
- `texture`
|
||||
- `textureState`
|
||||
从 `context` 原样拷入。
|
||||
- `pixelX` 与 `pixelY` 则通过 [ClampViewportPixelCoordinate](../ViewportHostSurfaceUtils/ViewportHostSurfaceUtils.md) 从浮点鼠标坐标映射成纹理像素坐标。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ViewportObjectIdPicker](ViewportObjectIdPicker.md)
|
||||
- [CanPickViewportObjectId](CanPickViewportObjectId.md)
|
||||
- [ViewportObjectIdReadbackRequest](ViewportObjectIdReadbackRequest.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
# CanPickViewportObjectId
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `function`
|
||||
|
||||
**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
bool CanPickViewportObjectId(const ViewportObjectIdPickContext& context);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
判断当前上下文是否满足 object-id 像素读回的前置条件。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 当前要求同时满足:
|
||||
- `commandQueue != nullptr`
|
||||
- `texture != nullptr`
|
||||
- `textureWidth > 0`
|
||||
- `textureHeight > 0`
|
||||
- `hasValidFrame == true`
|
||||
- `viewportSize.x > 1`
|
||||
- `viewportSize.y > 1`
|
||||
- 鼠标坐标位于视口矩形内
|
||||
- 只要任一条件不满足,就返回 `false`。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ViewportObjectIdPicker](ViewportObjectIdPicker.md)
|
||||
- [ViewportObjectIdPickContext](ViewportObjectIdPickContext.md)
|
||||
- [BuildViewportObjectIdReadbackRequest](BuildViewportObjectIdReadbackRequest.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
# PickViewportObjectIdEntity
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `function-template`
|
||||
|
||||
**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
template <typename ReadPixelFn>
|
||||
ViewportObjectIdPickResult PickViewportObjectIdEntity(
|
||||
const ViewportObjectIdPickContext& context,
|
||||
ReadPixelFn&& readPixel);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
执行一次 object-id 读回,并把 `RGBA8` 样本解码为实体 ID。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 先构造空的 `ViewportObjectIdPickResult result`。
|
||||
- 若 [BuildViewportObjectIdReadbackRequest](BuildViewportObjectIdReadbackRequest.md) 失败,则直接返回 `Unavailable` 结果。
|
||||
- 否则会调用外部提供的 `readPixel(request, rgba)`。
|
||||
- 若读回失败,则把状态设为 `ReadbackFailed`。
|
||||
- 若读回成功,则:
|
||||
- 状态设为 `Success`
|
||||
- 通过 `Rendering::DecodeObjectIdFromColor(...)` 解码 `rgba`
|
||||
- 把结果写入 `entityId`
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ViewportObjectIdPicker](ViewportObjectIdPicker.md)
|
||||
- [BuildViewportObjectIdReadbackRequest](BuildViewportObjectIdReadbackRequest.md)
|
||||
- [TryPickViewportObjectIdEntity](TryPickViewportObjectIdEntity.md)
|
||||
@@ -0,0 +1,35 @@
|
||||
# TryPickViewportObjectIdEntity
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `function-template`
|
||||
|
||||
**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h`
|
||||
|
||||
## 签名
|
||||
|
||||
```cpp
|
||||
template <typename ReadPixelFn>
|
||||
bool TryPickViewportObjectIdEntity(
|
||||
const ViewportObjectIdPickContext& context,
|
||||
ReadPixelFn&& readPixel,
|
||||
uint64_t& outEntityId);
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
以简化的 `bool + outEntityId` 形式执行 object-id picking。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- 内部直接调用 [PickViewportObjectIdEntity](PickViewportObjectIdEntity.md)。
|
||||
- 无论成功还是失败,都会先把 `outEntityId` 写成结果中的 `entityId`。
|
||||
- 最终返回值来自 `result.HasResolvedSample()`:
|
||||
- `true` 表示读回和解码成功
|
||||
- `false` 表示不可用或读回失败
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ViewportObjectIdPicker](ViewportObjectIdPicker.md)
|
||||
- [PickViewportObjectIdEntity](PickViewportObjectIdEntity.md)
|
||||
- [ViewportObjectIdPickResult](ViewportObjectIdPickResult.md)
|
||||
@@ -0,0 +1,45 @@
|
||||
# ViewportObjectIdPickContext
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h`
|
||||
|
||||
## 定义
|
||||
|
||||
```cpp
|
||||
struct ViewportObjectIdPickContext {
|
||||
RHI::RHICommandQueue* commandQueue = nullptr;
|
||||
RHI::RHITexture* texture = nullptr;
|
||||
RHI::ResourceStates textureState = RHI::ResourceStates::Common;
|
||||
uint32_t textureWidth = 0;
|
||||
uint32_t textureHeight = 0;
|
||||
bool hasValidFrame = false;
|
||||
ImVec2 viewportSize = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 viewportMousePosition = ImVec2(0.0f, 0.0f);
|
||||
};
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
封装一次 object-id picking 所需的全部输入上下文。
|
||||
|
||||
## 字段说明
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `commandQueue` | 执行像素读回的命令队列。 |
|
||||
| `texture` | object-id 纹理。 |
|
||||
| `textureState` | 当前纹理资源状态。 |
|
||||
| `textureWidth` | 纹理宽度。 |
|
||||
| `textureHeight` | 纹理高度。 |
|
||||
| `hasValidFrame` | 当前帧是否已生成可读 object-id 内容。 |
|
||||
| `viewportSize` | 视口尺寸。 |
|
||||
| `viewportMousePosition` | 视口内鼠标位置。 |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ViewportObjectIdPicker](ViewportObjectIdPicker.md)
|
||||
- [CanPickViewportObjectId](CanPickViewportObjectId.md)
|
||||
- [BuildViewportObjectIdReadbackRequest](BuildViewportObjectIdReadbackRequest.md)
|
||||
@@ -0,0 +1,42 @@
|
||||
# ViewportObjectIdPickResult
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h`
|
||||
|
||||
## 定义
|
||||
|
||||
```cpp
|
||||
struct ViewportObjectIdPickResult {
|
||||
ViewportObjectIdPickStatus status = ViewportObjectIdPickStatus::Unavailable;
|
||||
uint64_t entityId = 0;
|
||||
|
||||
bool HasResolvedSample() const {
|
||||
return status == ViewportObjectIdPickStatus::Success;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
表示一次 object-id picking 的最终结果。
|
||||
|
||||
## 字段说明
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `status` | 本次 pick 的状态。 |
|
||||
| `entityId` | 解析出的实体 ID;不可用或失败时通常为 `0`。 |
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- `HasResolvedSample()` 只在 `status == Success` 时返回 `true`。
|
||||
- 这意味着它表达的是“像素是否成功读回并完成解码”,而不是“entityId 是否非零”。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ViewportObjectIdPicker](ViewportObjectIdPicker.md)
|
||||
- [ViewportObjectIdPickStatus](ViewportObjectIdPickStatus.md)
|
||||
- [PickViewportObjectIdEntity](PickViewportObjectIdEntity.md)
|
||||
@@ -0,0 +1,34 @@
|
||||
# ViewportObjectIdPickStatus
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `enum class`
|
||||
|
||||
**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h`
|
||||
|
||||
## 定义
|
||||
|
||||
```cpp
|
||||
enum class ViewportObjectIdPickStatus : uint8_t {
|
||||
Unavailable = 0,
|
||||
Success,
|
||||
ReadbackFailed
|
||||
};
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
表示一次 object-id picking 的结果状态。
|
||||
|
||||
## 枚举值
|
||||
|
||||
| 值 | 说明 |
|
||||
|------|------|
|
||||
| `Unavailable` | 当前上下文不满足 pick 前置条件,未发起读回。 |
|
||||
| `Success` | 已成功读回像素并解析出 object-id。 |
|
||||
| `ReadbackFailed` | 前置条件满足,但实际像素读回失败。 |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ViewportObjectIdPicker](ViewportObjectIdPicker.md)
|
||||
- [ViewportObjectIdPickResult](ViewportObjectIdPickResult.md)
|
||||
@@ -0,0 +1,88 @@
|
||||
# ViewportObjectIdPicker
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `structs + enum + header-helper`
|
||||
|
||||
**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h`
|
||||
|
||||
**描述**: 封装 Scene View object-id 纹理的像素读回请求构建、状态判断与颜色解码。
|
||||
|
||||
## 概述
|
||||
|
||||
`ViewportObjectIdPicker.h` 把 Scene View 点击选取里最容易出错的一段逻辑独立了出来:
|
||||
|
||||
- 当前点击位置是否合法。
|
||||
- 当前帧是否真的有可读的 object-id 结果。
|
||||
- 鼠标位置如何映射到纹理像素坐标。
|
||||
- 读回的 `RGBA8` 如何还原成实体 ID。
|
||||
|
||||
这样 [ViewportHostService](../ViewportHostService/ViewportHostService.md) 就只需要提供“怎么读像素”的函数,而不用自己重复处理输入合法性和解码细节。
|
||||
|
||||
## 公开类型
|
||||
|
||||
| 成员 | 说明 |
|
||||
|------|------|
|
||||
| [ViewportObjectIdPickContext](ViewportObjectIdPickContext.md) | 一次 pick 所需的 command queue、纹理、状态、尺寸和鼠标位置。 |
|
||||
| [ViewportObjectIdReadbackRequest](ViewportObjectIdReadbackRequest.md) | 已解析出的真实读回请求。 |
|
||||
| [ViewportObjectIdPickStatus](ViewportObjectIdPickStatus.md) | `Unavailable`、`Success`、`ReadbackFailed` 三种状态。 |
|
||||
| [ViewportObjectIdPickResult](ViewportObjectIdPickResult.md) | 最终 pick 结果,包含状态和实体 ID。 |
|
||||
|
||||
## 公开函数
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| [CanPickViewportObjectId](CanPickViewportObjectId.md) | 判断当前上下文是否满足 object-id 读回前置条件。 |
|
||||
| [BuildViewportObjectIdReadbackRequest](BuildViewportObjectIdReadbackRequest.md) | 把视口鼠标位置转换成真实像素读回请求。 |
|
||||
| [PickViewportObjectIdEntity](PickViewportObjectIdEntity.md) | 执行一次对象 ID 读回并解码实体 ID。 |
|
||||
| [TryPickViewportObjectIdEntity](TryPickViewportObjectIdEntity.md) | 以 `bool + outEntityId` 形式暴露简化 pick 结果。 |
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
- [CanPickViewportObjectId](CanPickViewportObjectId.md) 会要求:
|
||||
- 有有效 command queue
|
||||
- 有有效纹理
|
||||
- 纹理尺寸非零
|
||||
- 当前帧 `hasValidFrame = true`
|
||||
- 视口和鼠标坐标都在合法范围内
|
||||
- [BuildViewportObjectIdReadbackRequest](BuildViewportObjectIdReadbackRequest.md) 会用 [ClampViewportPixelCoordinate](../ViewportHostSurfaceUtils/ViewportHostSurfaceUtils.md) 把浮点坐标稳定映射到像素坐标。
|
||||
- [PickViewportObjectIdEntity](PickViewportObjectIdEntity.md) 通过 `Rendering::DecodeObjectIdFromColor(...)` 把 `RGBA8` 样本还原为对象 ID。
|
||||
|
||||
## 设计说明
|
||||
|
||||
对象 ID picking 是商业引擎编辑器里非常常见的方案。相比 CPU mesh 射线拾取,它的优点是:
|
||||
|
||||
- 点击结果与最终渲染结果更一致。
|
||||
- 不需要在 UI 点击时重新遍历大量网格三角形。
|
||||
- 更容易和 outline / selection mask 共用同一套 object-id 数据链路。
|
||||
|
||||
代价是它依赖有效渲染帧和 GPU 读回。
|
||||
|
||||
## 与测试的对应关系
|
||||
|
||||
`tests/Editor/test_viewport_object_id_picker.cpp` 当前验证了:
|
||||
|
||||
- 非法上下文会被 [CanPickViewportObjectId](CanPickViewportObjectId.md) 拒绝。
|
||||
- [BuildViewportObjectIdReadbackRequest](BuildViewportObjectIdReadbackRequest.md) 会把边界鼠标坐标钳制到真实像素范围。
|
||||
- [TryPickViewportObjectIdEntity](TryPickViewportObjectIdEntity.md) 会把 `RGBA8` 正确解码成对象 ID。
|
||||
- 零对象 ID 仍会被视为一次成功解析的样本。
|
||||
- 读回失败时会返回 `ReadbackFailed`,并把简化接口结果降级为失败。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 当前状态只区分“不可用”和“读回失败”,没有更细粒度失败原因。
|
||||
- 当前依赖 `R8G8B8A8` 颜色编码对象 ID。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ViewportObjectIdPickContext](ViewportObjectIdPickContext.md)
|
||||
- [ViewportObjectIdReadbackRequest](ViewportObjectIdReadbackRequest.md)
|
||||
- [ViewportObjectIdPickStatus](ViewportObjectIdPickStatus.md)
|
||||
- [ViewportObjectIdPickResult](ViewportObjectIdPickResult.md)
|
||||
- [CanPickViewportObjectId](CanPickViewportObjectId.md)
|
||||
- [BuildViewportObjectIdReadbackRequest](BuildViewportObjectIdReadbackRequest.md)
|
||||
- [PickViewportObjectIdEntity](PickViewportObjectIdEntity.md)
|
||||
- [TryPickViewportObjectIdEntity](TryPickViewportObjectIdEntity.md)
|
||||
- [ViewportHostService](../ViewportHostService/ViewportHostService.md)
|
||||
- [ViewportHostSurfaceUtils](../ViewportHostSurfaceUtils/ViewportHostSurfaceUtils.md)
|
||||
- [SceneViewPanel](../../panels/SceneViewPanel/SceneViewPanel.md)
|
||||
@@ -0,0 +1,39 @@
|
||||
# ViewportObjectIdReadbackRequest
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `struct`
|
||||
|
||||
**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h`
|
||||
|
||||
## 定义
|
||||
|
||||
```cpp
|
||||
struct ViewportObjectIdReadbackRequest {
|
||||
RHI::RHICommandQueue* commandQueue = nullptr;
|
||||
RHI::RHITexture* texture = nullptr;
|
||||
RHI::ResourceStates textureState = RHI::ResourceStates::Common;
|
||||
uint32_t pixelX = 0;
|
||||
uint32_t pixelY = 0;
|
||||
};
|
||||
```
|
||||
|
||||
## 作用
|
||||
|
||||
表示一次已经解析完成、可直接交给 GPU 读回层执行的像素采样请求。
|
||||
|
||||
## 字段说明
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `commandQueue` | 目标命令队列。 |
|
||||
| `texture` | 待读回的 object-id 纹理。 |
|
||||
| `textureState` | 读回前纹理所处资源状态。 |
|
||||
| `pixelX` | 要采样的像素 X 坐标。 |
|
||||
| `pixelY` | 要采样的像素 Y 坐标。 |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ViewportObjectIdPicker](ViewportObjectIdPicker.md)
|
||||
- [BuildViewportObjectIdReadbackRequest](BuildViewportObjectIdReadbackRequest.md)
|
||||
- [PickViewportObjectIdEntity](PickViewportObjectIdEntity.md)
|
||||
Reference in New Issue
Block a user