fix(editor): 缩放轴病态判据与平移互补,按 op 分派 AxisMask
缩放独有老问题根因(源码实证):缩放 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 恒可用。
This commit is contained in:
@@ -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<entt::entity> 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<entt::entity> 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],
|
||||
|
||||
Reference in New Issue
Block a user