# Orientation 系统重构方案 (RHS, CCW) ## 1. 引言 本文档概述了项目内部 `Orientation` 系统重构的最终方案。目标是建立一个清晰、一致且技术上合理的坐标系和旋转约定,主要目的是简化与标准数学库(如 `System.Numerics.Matrix4x4`)的集成,并遵守项目规范。 ## 2. 选定的坐标系和旋转约定 经过讨论和评估各种替代方案后,项目将采用以下约定: * **坐标系类型**: 右手坐标系 (RHS)。 * **主轴定义**: * **Y 轴**: 指向**上** (例如,世界坐标中的 `(0,1,0)`)。 * **X 轴**: 指向**右** (例如,世界坐标中的 `(1,0,0)`)。 * **Z 轴**: 指向**外/后** (例如,世界坐标中的 `(0,0,1)`,相对于典型的朝前摄像机视图)。 * **物体前向**: 物体的"前向"将定义为指向世界坐标系或其局部参考系的**负 Z 轴 (-Z)** (例如,未旋转且与世界轴对齐时为 `(0,0,-1)`)。 * **旋转正方向**: 当沿着正轴朝向原点观察时(标准右手定则),为**逆时针 (CCW)**。 * **存储在 `Orientation` 中的欧拉角 (`Yaw`, `Pitch`, `Roll`)**: * 这些角度将直接表示基于 RHS 和 CCW 约定的旋转。 * **正角度增量的影响**: * **正 `Yaw` (绕 Y 轴, CCW)**: 物体的前方 (其 -Z 方向) 向其**左侧**偏转。 * **正 `Pitch` (绕 X 轴, CCW)**: 物体的前方 (其 -Z 方向) **向上**倾斜。 * **正 `Roll` (绕 Z 轴, CCW)**: 物体的顶部 (其 Y 方向) 向其**左侧**倾斜 (左滚)。 * *注意: 用户和开发者需要适应这些标准的泰特-布莱恩角效应,其中定义的偏航、俯仰和滚转的正旋转可能分别对应于向左、向上和向左滚动的运动。像"右转"这样的操作将需要一个负的 Yaw 角。* ## 3. `Orientation` 类 (`Common.cs`) 的核心更改 ### 3.1. `ToVector()` 方法 此方法计算对象在世界空间中的前向向量 (即其局部 -Z 轴经其姿态变换后的向量)。 * 输入: 使用存储的 RHS/CCW `Yaw` 和 `Pitch` 角 (Roll 不改变主前向向量的方向,仅改变其围绕该向量的姿态)。 * 默认前向 (Yaw=0, Pitch=0, Roll=0 时): `(0,0,-1)`。 * 公式 (RHS, Y-Up, -Z Forward, CCW 欧拉角 Yaw, Pitch): * `X_forward = -cos(Pitch) * sin(Yaw)` * `Y_forward = sin(Pitch)` * `Z_forward = -cos(Pitch) * cos(Yaw)` 返回的 `Vector3D(X_forward, Y_forward, Z_forward)` 是表示对象前向的单位向量。 ### 3.2. `FromVector(Vector3D worldForwardVector)` / `LookAt(Vector3D worldTargetDirection)` 方法 此方法计算使对象朝向给定 `worldForwardVector` 所需的 RHS/CCW 欧拉角。 * 输入: `worldForwardVector` (世界空间中的单位向量)。 * 计算出的欧拉角 (RHS/CCW): * `Pitch = Math.Asin(worldForwardVector.Y)` * `Yaw = Math.Atan2(-worldForwardVector.X, -worldForwardVector.Z)` * 这些计算出的 `Yaw` 和 `Pitch` 值直接存储在 `Orientation` 实例中。Roll 通常设置为 0。 ## 4. 与 `System.Numerics.Matrix4x4` 的交互 * 存储在 `Orientation` 中的 `Yaw`、`Pitch` 和 `Roll` 角 (由于是 RHS/CCW) 可以**直接传递**给 `System.Numerics.Matrix4x4.CreateRotationY()`、`CreateRotationX()` 和 `CreateRotationZ()`。 * 由于约定一致,此阶段不需要进行角度符号反转或复杂的左右手性转换。 * `Vector3D.CalculateRotationMatrix()` 中的示例: ```csharp // orientation.Yaw, orientation.Pitch, orientation.Roll 已经是 RHS/CCW Matrix4x4 yawRotation = Matrix4x4.CreateRotationY((float)orientation.Yaw); Matrix4x4 pitchRotation = Matrix4x4.CreateRotationX((float)orientation.Pitch); Matrix4x4 rollRotation = Matrix4x4.CreateRotationZ((float)orientation.Roll); // 按照标准矩阵乘法顺序组合它们,例如:yaw * pitch * roll ``` ## 5. `ObscurationUtils.GetOrientationBasisVectors()` 的更改 此方法应返回对象在世界坐标系中的局部轴 `(RightVector, UpVector, ForwardVector)`。 * `forwardVec_world = orientation.ToVector().Normalize()` (这已经是正确的前向向量)。 * 如果使用基于矩阵的方法: 1. 从 `CalculateRotationMatrix` 获取组合旋转矩阵 `M` (它已经是表示姿态的正确 RHS 矩阵)。 2. `RightVector_world = M.MultiplyVector(Vector3D.UnitX)` (其中 `Vector3D.UnitX` 是 `(1,0,0)`) 3. `UpVector_world = M.MultiplyVector(Vector3D.UnitY)` (其中 `Vector3D.UnitY` 是 `(0,1,0)`) 4. `ForwardVector_world = M.MultiplyVector(-Vector3D.UnitZ)` (其中 `-Vector3D.UnitZ` 是 `(0,0,-1)`,表示局部前向) * 返回这些向量 (归一化)。 * 或者,使用叉积 (假设 `forwardVec_world` 已知且 `Vector3D.UnitY` 是全局向上参考,如果对象有明显的翻滚,则需小心): 1. `RightVector_world = Vector3D.CrossProduct(Vector3D.UnitY, forwardVec_world).Normalize()` 2. `UpVector_world = Vector3D.CrossProduct(forwardVec_world, RightVector_world).Normalize()` * 这种叉积方法更简单,但如果由于翻滚导致对象的局部"向上"与世界"向上"显著不同,则其鲁棒性较差。通常首选矩阵方法以确保准确性。 ## 6. 主要影响和必要的适配 * **审查和修改 `new Orientation(yaw, pitch, roll)` 实例**: 这将是工作量最大的部分。所有使用特定欧拉角值直接实例化 `Orientation` 的现有代码都必须重新审查。需要理解这些角度的原始意图 (基于旧的 X-Forward 系统),并且必须计算新的 RHS/CCW 角度,以便为新的 -Z 前向约定实现相同的世界空间姿态。 * 示例: `new Orientation(0,0,0)` 现在将表示"朝向 -Z 世界轴",而不是"朝向 +X 世界轴"。 * **更新单元测试和集成测试**: 所有断言对象姿态、`ToVector()` 结果或任何依赖于姿态的行为的测试,都需要更新以反映新的 -Z 前向约定和 RHS/CCW 欧拉角效应。 ## 7. 此方案的优势 * **简化与 `System.Numerics.Matrix4x4` 的集成**: 这是主要的好处。数学运算变得更直接,更不容易出错。 * **遵守项目规则**: 完全符合"右手坐标系,Y-Up"规则。 * **与行业标准对齐**: RHS 与 CCW 旋转是许多 3D 图形 API 和物理引擎中的常见标准。 * **核心数学的清晰性**: 减少了在核心变换逻辑中进行左右手性或旋转方向的临时转换的需求。 ## 8. 用户/开发者的适配 * 创建或解释 `Orientation` 欧拉角的用户和开发者必须理解,根据标准的 RHS/CCW 约定,正角度现在会导致向左偏航、向上俯仰和向左翻滚。需要"右转"、"向下俯仰"或"向右翻滚"的操作将涉及负角度值。 ## 9. 潜在的改进点 在 Vector3D.Rotate 方法中,它调用了 ApplyRotationMatrix。根据其上下文(Rotate 一个向量),如果这个向量代表的是一个方向而不是一个点,那么它也应该使用我们新创建的 TransformDirection 方法或者有类似的逻辑来避免平移。不过,这超出了我们当前对 Orientation 和 ObscurationUtils 的直接修改范围,可以作为一个后续的潜在改进点来记录。