From b7523db30dd194254d0729632e67ed4b4a3670ed Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sat, 4 Apr 2026 01:17:54 +0800 Subject: [PATCH] docs: add scene viewport camera and picking docs --- .../ApplyInput.md | 93 +++++++++++++++++ .../SceneViewportCameraController/ApplyTo.md | 37 +++++++ .../Reset-And-Focus.md | 47 +++++++++ .../SceneViewportCameraController.md | 84 ++++++++++++++++ .../SceneViewportCameraInputState.md | 35 +++++++ .../SnapToForward-And-AnimateToForward.md | 49 +++++++++ .../BuildSceneViewportRay.md | 46 +++++++++ .../PickSceneViewportEntity.md | 55 +++++++++++ .../SceneViewportPickRequest.md | 41 ++++++++ .../SceneViewportPickResult.md | 41 ++++++++ .../SceneViewportPicker.md | 84 ++++++++++++++++ docs/api/XCEngine/Editor/Viewport/Viewport.md | 99 +++++++------------ .../BuildViewportObjectIdReadbackRequest.md | 36 +++++++ .../CanPickViewportObjectId.md | 36 +++++++ .../PickViewportObjectIdEntity.md | 37 +++++++ .../TryPickViewportObjectIdEntity.md | 35 +++++++ .../ViewportObjectIdPickContext.md | 45 +++++++++ .../ViewportObjectIdPickResult.md | 42 ++++++++ .../ViewportObjectIdPickStatus.md | 34 +++++++ .../ViewportObjectIdPicker.md | 88 +++++++++++++++++ .../ViewportObjectIdReadbackRequest.md | 39 ++++++++ 21 files changed, 1042 insertions(+), 61 deletions(-) create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/ApplyInput.md create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/ApplyTo.md create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/Reset-And-Focus.md create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SceneViewportCameraController.md create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SceneViewportCameraInputState.md create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SnapToForward-And-AnimateToForward.md create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/BuildSceneViewportRay.md create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/PickSceneViewportEntity.md create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPickRequest.md create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPickResult.md create mode 100644 docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPicker.md create mode 100644 docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/BuildViewportObjectIdReadbackRequest.md create mode 100644 docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/CanPickViewportObjectId.md create mode 100644 docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/PickViewportObjectIdEntity.md create mode 100644 docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/TryPickViewportObjectIdEntity.md create mode 100644 docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickContext.md create mode 100644 docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickResult.md create mode 100644 docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickStatus.md create mode 100644 docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPicker.md create mode 100644 docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdReadbackRequest.md diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/ApplyInput.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/ApplyInput.md new file mode 100644 index 00000000..e3368eda --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/ApplyInput.md @@ -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) diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/ApplyTo.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/ApplyTo.md new file mode 100644 index 00000000..722aa811 --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/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) diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/Reset-And-Focus.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/Reset-And-Focus.md new file mode 100644 index 00000000..2944eb5e --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/Reset-And-Focus.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) diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SceneViewportCameraController.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SceneViewportCameraController.md new file mode 100644 index 00000000..cf1acaef --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SceneViewportCameraController.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` diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SceneViewportCameraInputState.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SceneViewportCameraInputState.md new file mode 100644 index 00000000..6c3dae80 --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SceneViewportCameraInputState.md @@ -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) diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SnapToForward-And-AnimateToForward.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SnapToForward-And-AnimateToForward.md new file mode 100644 index 00000000..06346da4 --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportCameraController/SnapToForward-And-AnimateToForward.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) diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/BuildSceneViewportRay.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/BuildSceneViewportRay.md new file mode 100644 index 00000000..57eaed05 --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/BuildSceneViewportRay.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) diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/PickSceneViewportEntity.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/PickSceneViewportEntity.md new file mode 100644 index 00000000..2972b627 --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/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) diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPickRequest.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPickRequest.md new file mode 100644 index 00000000..902dcce2 --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPickRequest.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) diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPickResult.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPickResult.md new file mode 100644 index 00000000..2d1bf64f --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPickResult.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) diff --git a/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPicker.md b/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPicker.md new file mode 100644 index 00000000..0842c8bb --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/SceneViewportPicker/SceneViewportPicker.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) diff --git a/docs/api/XCEngine/Editor/Viewport/Viewport.md b/docs/api/XCEngine/Editor/Viewport/Viewport.md index 9557c978..ee5c84e6 100644 --- a/docs/api/XCEngine/Editor/Viewport/Viewport.md +++ b/docs/api/XCEngine/Editor/Viewport/Viewport.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) diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/BuildViewportObjectIdReadbackRequest.md b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/BuildViewportObjectIdReadbackRequest.md new file mode 100644 index 00000000..1fdefc02 --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/BuildViewportObjectIdReadbackRequest.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) diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/CanPickViewportObjectId.md b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/CanPickViewportObjectId.md new file mode 100644 index 00000000..de75edf7 --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/CanPickViewportObjectId.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) diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/PickViewportObjectIdEntity.md b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/PickViewportObjectIdEntity.md new file mode 100644 index 00000000..0aae6873 --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/PickViewportObjectIdEntity.md @@ -0,0 +1,37 @@ +# PickViewportObjectIdEntity + +**命名空间**: `XCEngine::Editor` + +**类型**: `function-template` + +**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h` + +## 签名 + +```cpp +template +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) diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/TryPickViewportObjectIdEntity.md b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/TryPickViewportObjectIdEntity.md new file mode 100644 index 00000000..8d77fbf5 --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/TryPickViewportObjectIdEntity.md @@ -0,0 +1,35 @@ +# TryPickViewportObjectIdEntity + +**命名空间**: `XCEngine::Editor` + +**类型**: `function-template` + +**源文件**: `editor/src/Viewport/ViewportObjectIdPicker.h` + +## 签名 + +```cpp +template +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) diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickContext.md b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickContext.md new file mode 100644 index 00000000..df8aa8df --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickContext.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) diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickResult.md b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickResult.md new file mode 100644 index 00000000..376f1afa --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickResult.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) diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickStatus.md b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickStatus.md new file mode 100644 index 00000000..22ce794e --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPickStatus.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) diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPicker.md b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPicker.md new file mode 100644 index 00000000..226d8f2c --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdPicker.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) diff --git a/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdReadbackRequest.md b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdReadbackRequest.md new file mode 100644 index 00000000..420d430b --- /dev/null +++ b/docs/api/XCEngine/Editor/Viewport/ViewportObjectIdPicker/ViewportObjectIdReadbackRequest.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)