feat(editor): Gizmo 退化轴自动隐藏(调用层 SetAxisMask,UE 语义)

正对视角 + 物体竖直偏移时,单轴拖拽灵敏度 sinθ(轴与相机连线夹角)→0:
拖拽投影系数趋 0("数值不变"),激活平面与鼠标射线共面(IntersectRayPlane
denom→0 返回 -1 被当交点距离)。按灵敏度 <0.25(θ<14.5°)将病态轴
SetAxisMask 隐藏(库公开 API,按轴关绘制+激活,保留含该轴的平面手柄
与中心块兜底),相机移动后自动恢复——可激活性决定可见性,零库改动。
This commit is contained in:
2026-10-03 12:03:07 +08:00
parent 1d360369c9
commit a130850276
+34 -3
View File
@@ -40,6 +40,12 @@ namespace
constexpr float kSnapRotateDeg = 15.0f; // 度
constexpr float kSnapScale = 0.1f; // 缩放步进
// 单轴拖拽灵敏度下限:sinθ(轴与"相机→物体"连线夹角)。低于该值
// 时拖拽投影系数 <0.25 且激活平面与鼠标射线近共面——视觉上
// "看得见、抓了没反应"。≈14.5°,略宽于库默认可见角(≈11.5°),
// 只藏真正病态的轴,不动可用轴
constexpr float kAxisMinSin = 0.25f;
// Vulkan proj → ImGuizmo 期望的 OpenGL 形式(Y-up、Z∈[-1,1]):
// Y 行翻回(抵消 Vulkan clip Y 向下翻转);z 行 = 2*z - w 即
// 深度 [0,1] → [-1,1]。glm 列主序 m[col][row],逐列改 row1/row2
@@ -146,9 +152,7 @@ std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
const ImVec2 mn = ImGui::GetItemRectMin();
ImGuizmo::SetRect(mn.x, mn.y, size.x, size.y);
// 不调 SetPlaneLimit/SetAxisLimit——全默认参数,与
// Horizon 同款(官方库出货行为:轴仅在几乎正对视线
// 时隐藏,任何自行抬高阈值都会过早藏轴)
// 不调 SetPlaneLimit/SetAxisLimit——可见度保持库默认
glm::mat4 m = tr->World();
// 操作空间:local(模型轴,旋转/缩放随物体朝向直观)/
@@ -157,6 +161,33 @@ 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
// 时保留含该轴的平面手柄兜底);相机一移动自动恢复
const glm::vec3 camPos(glm::inverse(view)[3]);
glm::vec3 camToObj = glm::vec3(m[3]) - camPos;
const float camDist = glm::length(camToObj);
const glm::mat3 rotM(m);
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;
}
}
ImGuizmo::SetAxisMask(maskAxis[0], maskAxis[1],
maskAxis[2]);
// Ctrl 按住 → 吸附(UE Grid Snap 语义)。库的约定
//(docs/documentation.md):平移 = 世界单位 float[3]
// 逐轴;旋转 = snap[0] 按度(库内自行 DEG2RAD,传弧度