From 74d2f6cdaa5e13a052248c318d1ae4e65af42f7c Mon Sep 17 00:00:00 2001 From: atdunbg Date: Sat, 3 Oct 2026 00:27:16 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Gizmo=20=E6=97=8B=E8=BD=AC/=E7=BC=A9?= =?UTF-8?q?=E6=94=BE=E5=86=99=E5=9B=9E=E4=B9=B1=E8=B7=B3=E2=80=94=E2=80=94?= =?UTF-8?q?eulerAngles=20=E5=BA=8F=E7=BA=A6=E5=AE=9A=E4=B8=8E=20Compose=20?= =?UTF-8?q?YXZ=20=E5=A4=B1=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glm::eulerAngles 假定 XYZ 系约定,与 TransformComponent::Compose 的 YXZ 旋转序不一致:复合旋转下(如 Dynamic 实体被物理写入非零姿态) decompose 出的角度按错误序重组 → 写回的 R ≠ 拖拽时的 R → 下一帧 Manipulate 拿到歪矩阵 → 旋转乱跳、缩放串轴。 改为按引擎 YXZ 序(R = Ry·Rx·Rz)从旋转矩阵直接提取欧拉角: - 常规区:atan2 分子分母 cx 约掉,pitch 任意角精确(含 ±90° 邻域) - float 真奇异(|pitch|=90°):组合角归 yaw、roll=0(UE 同策略), ±90° 两支 atan2 符号不同 验证(round-trip 1e4 样本,矩阵 Frobenius 差判据): - 欧拉链 2.67e-04、全 SRT 链 3.56e-04(float32 极限) - 万向锁邻域专项覆盖 --- App/editor/ViewportPanel.cpp | 37 ++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/App/editor/ViewportPanel.cpp b/App/editor/ViewportPanel.cpp index 78a29a1..e754fce 100644 --- a/App/editor/ViewportPanel.cpp +++ b/App/editor/ViewportPanel.cpp @@ -23,6 +23,7 @@ #include #include +#include // GTX 扩展(实验性)需显式开启:matrix_decompose(矩阵→SRT 分解)、 // quaternion(eulerAngles 四元数→欧拉角) @@ -147,17 +148,41 @@ std::optional ViewportPanel::Draw(const EditorContext &ctx, gizmo.dragging = ImGuizmo::IsUsing(); // 拖拽生效:分解 world 矩阵写回 SRT(Set* 标脏 → - // UpdateTransforms 重算 → 渲染下帧生效)。 - // eulerAngles 与 Compose 的 YXZ 旋转序在复合旋转下 - // 可能微差——每帧从 World 重读不累积;表现不对再换 - // ImGuizmo::DecomposeMatrixToComponents(TODO) + // UpdateTransforms 重算 → 渲染下帧生效) if (ImGuizmo::IsUsing()) { glm::vec3 scale, transl, skew; glm::quat rot; glm::vec4 persp; if (glm::decompose(m, scale, rot, transl, skew, persp)) { - const glm::vec3 euler = - degrees(eulerAngles(rot)); + // 旋转写回: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) .SetScale(scale);