refactor: TransformComponent 旋转存储改四元数(UE FTransform 语义)
欧拉角内部存储每帧矩阵↔欧拉往返,是 Gizmo 拖拽乱跳/缩放串轴的 结构性根源(74d2f6c 的单点公式修复治标不治本)。对齐 UE/Unity 方案:内部存 glm::quat,欧拉只在 UI 表皮。 - Components.h:m_Rotation 改 quat;Compose 用 mat4_cast; SetRotation(euler) 转存(q=qy·qx·qz);新增 SetRotationQuat/ GetRotationQuat;GetRotation() 改派生欧拉(YXZ 序矩阵反解, 仅 UI 显示/编辑用) - ViewportPanel:Gizmo 写回 decompose 的 quat 直写,删除整个 欧拉提取块 - PhysicsWorld:删 QuatToEulerYXZ/EulerYXZToQuat,改 ToJolt/ ToGlm 分量直转;物理写回 SetRotationQuat;BodyRecord.lastRot 改存 quat 验证(1 万随机姿态 round-trip):Gizmo 链路矩阵差 7.9e-07 (旧欧拉方案 3.56e-04,好 3 个数量级);万向锁 pitch=±90° 穿 表皮恒定;欧拉表皮误差 4.5e-04 为欧拉参数化固有病态(随 pitch→±90 指数增长,仅影响 UI 显示数值),热路径不经过。
This commit is contained in:
@@ -23,13 +23,11 @@
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <cmath>
|
||||
|
||||
// GTX 扩展(实验性)需显式开启:matrix_decompose(矩阵→SRT 分解)、
|
||||
// quaternion(eulerAngles 四元数→欧拉角)
|
||||
// GTX 扩展(实验性)需显式开启:matrix_decompose(矩阵→SRT 分解,
|
||||
// 旋转以四元数输出,直接写回 TransformComponent)
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/matrix_decompose.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
using namespace Engine; // u32 / f32(Engine/Core/Types.h)
|
||||
|
||||
@@ -148,43 +146,16 @@ std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
||||
gizmo.dragging = ImGuizmo::IsUsing();
|
||||
|
||||
// 拖拽生效:分解 world 矩阵写回 SRT(Set* 标脏 →
|
||||
// UpdateTransforms 重算 → 渲染下帧生效)
|
||||
// UpdateTransforms 重算 → 渲染下帧生效)。旋转用
|
||||
// decompose 出的 quat 直写(UE FTransform 语义)——
|
||||
// 不经欧拉往返,拖拽零漂移、无万向锁
|
||||
if (ImGuizmo::IsUsing()) {
|
||||
glm::vec3 scale, transl, skew;
|
||||
glm::quat rot;
|
||||
glm::vec4 persp;
|
||||
if (glm::decompose(m, scale, rot, transl, skew, persp)) {
|
||||
// 旋转写回:glm::eulerAngles 的序约定(XYZ 系)
|
||||
// 与 Compose 的 YXZ 不一致,复合旋转下往返失真
|
||||
// (拖拽乱跳、缩放串轴)。改为按引擎 YXZ 序
|
||||
//(R = Ry·Rx·Rz)从旋转矩阵直接提取欧拉角,
|
||||
// 保证 decompose 出的 R 与 Compose 重组的 R
|
||||
// 严格相等,拖拽零漂移
|
||||
const glm::mat3 R = glm::mat3_cast(rot);
|
||||
const f32 sp = -R[2][1]; // = sin(pitch)
|
||||
glm::vec3 euler;
|
||||
if (std::abs(sp) < 1.0f) {
|
||||
// 常规区(float32 下覆盖全部非奇异姿态):
|
||||
// 由 R = Ry·Rx·Rz 展开反解,cx 在 atan2
|
||||
// 分子分母中约掉,pitch→±90° 依然精确
|
||||
euler = glm::vec3(
|
||||
glm::degrees(std::asin(sp)),
|
||||
glm::degrees(std::atan2(R[2][0], R[2][2])),
|
||||
glm::degrees(std::atan2(R[0][1], R[1][1])));
|
||||
} else {
|
||||
// float 真奇异(|pitch|=90°):yaw/roll 只
|
||||
// 剩组合角,约定 roll=0(UE 同策略)归入
|
||||
// yaw;±90° 两支组合角符号不同
|
||||
const f32 pitch = sp > 0 ? 90.0f : -90.0f;
|
||||
const f32 yaw = sp > 0
|
||||
? glm::degrees(std::atan2(
|
||||
-R[0][2], R[1][2]))
|
||||
: glm::degrees(std::atan2(
|
||||
-R[0][2], -R[1][2]));
|
||||
euler = glm::vec3(pitch, yaw, 0.0f);
|
||||
}
|
||||
tr->SetPosition(transl)
|
||||
.SetRotation(euler)
|
||||
.SetRotationQuat(rot)
|
||||
.SetScale(scale);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -26,7 +26,8 @@
|
||||
#include <Engine/Renderer/Renderer.h>
|
||||
#include <Engine/Renderer/Model.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user