From 21b33d7d7f98b0c72e1580fcaad4cbeb226f7712 Mon Sep 17 00:00:00 2001 From: atdunbg Date: Sat, 3 Oct 2026 12:11:16 +0800 Subject: [PATCH] =?UTF-8?q?fix(editor):=20=E7=BC=A9=E6=94=BE=E8=BD=B4?= =?UTF-8?q?=E7=97=85=E6=80=81=E5=88=A4=E6=8D=AE=E4=B8=8E=E5=B9=B3=E7=A7=BB?= =?UTF-8?q?=E4=BA=92=E8=A1=A5=EF=BC=8C=E6=8C=89=20op=20=E5=88=86=E6=B4=BE?= =?UTF-8?q?=20AxisMask?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 缩放独有老问题根因(源码实证):缩放 hover(GetScaleType)与拖拽 (HandleScale ratio 分母)依赖"法线=轴"平面求交,交点距离分子 numer = -dist·dot(camToObj, 轴)。正对视角相机与物体无横偏时 numer≡0 → len=0/垃圾 → 杆 hover 不到、ratio 分子分母乱跳(数值随机猛变)。 平移 hover 是纯屏幕距离、平面法线经正交化自适应;旋转是屏幕角数学 ——故唯独缩放有此病。SCALE op 下判据换 |dot(camToObj,轴)|<0.1, ROTATE 不 mask,TRANSLATE 保持 sinθ<0.25。中心块等比缩放 (MT_SCALE_XYZ,屏幕空间数学)不受 mask 恒可用。 --- App/editor/ViewportPanel.cpp | 51 ++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/App/editor/ViewportPanel.cpp b/App/editor/ViewportPanel.cpp index dfb5587..c210f5d 100644 --- a/App/editor/ViewportPanel.cpp +++ b/App/editor/ViewportPanel.cpp @@ -40,11 +40,17 @@ namespace constexpr float kSnapRotateDeg = 15.0f; // 度 constexpr float kSnapScale = 0.1f; // 缩放步进 - // 单轴拖拽灵敏度下限:sinθ(轴与"相机→物体"连线夹角)。低于该值 + // 平移拖拽灵敏度下限:sinθ(轴与"相机→物体"连线夹角)。低于该值 // 时拖拽投影系数 <0.25 且激活平面与鼠标射线近共面——视觉上 // "看得见、抓了没反应"。≈14.5°,略宽于库默认可见角(≈11.5°), // 只藏真正病态的轴,不动可用轴 - constexpr float kAxisMinSin = 0.25f; + constexpr float kMoveMinSin = 0.25f; + + // 缩放求交稳定性下限:|dot(camToObj, 轴)|。缩放的 hover 与拖拽 + // 依赖"法线=轴"平面求交,交点距离分子 numer = -dist·该 dot—— + // 轴⊥连线(正对视角相机无横偏)时 numer≈0,len→0/垃圾:杆 + // hover 不到、ratio 分子分母乱跳(数值随机猛变)。≈84.3° + constexpr float kScaleMinDot = 0.1f; // Vulkan proj → ImGuizmo 期望的 OpenGL 形式(Y-up、Z∈[-1,1]): // Y 行翻回(抵消 Vulkan clip Y 向下翻转);z 行 = 2*z - w 即 @@ -161,13 +167,16 @@ std::optional ViewportPanel::Draw(const EditorContext &ctx, const ImGuizmo::MODE mode = gizmo.localSpace ? ImGuizmo::LOCAL : ImGuizmo::WORLD; - // 退化轴隐藏(UE 语义:可激活性决定可见性)。库的单轴 - // 激活平面包含"轴 + 相机连线",鼠标射线∥连线时求交 - // denom→0 返回 -1 被当交点距离(L2347-2349),拖拽 - // delta 在轴上的投影系数 = sinθ 也同时趋 0——表现为 - // "杆看得见、抓了没反应/数值不变"。按灵敏度把病态轴 - // SetAxisMask 隐藏(库内按轴关绘制+激活,单轴 mask - // 时保留含该轴的平面手柄兜底);相机一移动自动恢复 + // 退化轴隐藏(UE 语义:可激活性决定可见性)。三种 + // 操作病态几何不同,按 op 分派判据: + // - 平移:拖拽灵敏度 = sinθ(轴与相机连线夹角),θ→0 + // 时鼠标位移在轴上投影→0("数值不变") + // - 缩放:hover(GetScaleType)与拖拽(HandleScale 的 + // ratio 分母)依赖"法线=轴"平面求交,分子 numer = + // -dist·dot(camToObj, 轴)——正对视角相机无横偏时 + // numer≡0,len→0/垃圾:杆 hover 不到、数值随机 + // 猛变(缩放独有的老问题,平移/旋转不走此路径) + // - 旋转:屏幕空间角度数学,无退化,不 mask const glm::vec3 camPos(glm::inverse(view)[3]); glm::vec3 camToObj = glm::vec3(m[3]) - camPos; const float camDist = glm::length(camToObj); @@ -175,14 +184,22 @@ std::optional ViewportPanel::Draw(const EditorContext &ctx, bool maskAxis[3] = {false, false, false}; if (camDist > 1e-4f) { camToObj /= camDist; - for (int i = 0; i < 3; ++i) { - const glm::vec3 axis = gizmo.localSpace - ? glm::normalize(rotM[i]) - : glm::vec3(float(i == 0), float(i == 1), - float(i == 2)); - maskAxis[i] = - glm::length(glm::cross(camToObj, axis)) - < kAxisMinSin; + const bool isTranslate = (gizmo.op + & int(ImGuizmo::TRANSLATE)) == int(ImGuizmo::TRANSLATE); + const bool isScale = (gizmo.op + & int(ImGuizmo::SCALE)) == int(ImGuizmo::SCALE); + if (isTranslate || isScale) { + for (int i = 0; i < 3; ++i) { + const glm::vec3 axis = gizmo.localSpace + ? glm::normalize(rotM[i]) + : glm::vec3(float(i == 0), float(i == 1), + float(i == 2)); + maskAxis[i] = isScale + ? glm::abs(glm::dot(camToObj, axis)) + < kScaleMinDot + : glm::length(glm::cross(camToObj, axis)) + < kMoveMinSin; + } } } ImGuizmo::SetAxisMask(maskAxis[0], maskAxis[1],