editor: revert self-drawn scale gizmo, clip degenerate axes instead
User feedback: the self-drawn scale handles clash visually with the ImGuizmo translate/rotate rods (different size/style, clearly not one widget). Keep the library widget for all three ops for visual consistency and prevent mis-drags by masking degenerate axes via SetAxisMask: - translate: axis ~ parallel to camera-to-object line (sin < 0.25) - scale: axis ~ perpendicular to camera-to-object line (|dot| < 0.1, intersection numerator identically zero -> grab fails/values jump) OR axis ~ parallel to view direction (|dot| > 0.98, zero screen displacement -> value never changes) - rotate: screen-space math, no degeneracy, no mask Masked axes are neither drawn nor activatable; a slight view change restores them. Center uniform-scale square (MT_SCALE_XYZ, pure screen space) stays available in any view.
This commit is contained in:
+65
-307
@@ -25,8 +25,6 @@
|
|||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cmath>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
// GTX 扩展(实验性)需显式开启:matrix_decompose(矩阵→SRT 分解,
|
// GTX 扩展(实验性)需显式开启:matrix_decompose(矩阵→SRT 分解,
|
||||||
// 旋转以四元数输出,直接写回 TransformComponent)
|
// 旋转以四元数输出,直接写回 TransformComponent)
|
||||||
@@ -48,19 +46,13 @@ namespace
|
|||||||
// 只藏真正病态的轴,不动可用轴
|
// 只藏真正病态的轴,不动可用轴
|
||||||
constexpr float kMoveMinSin = 0.25f;
|
constexpr float kMoveMinSin = 0.25f;
|
||||||
|
|
||||||
// ---- 自绘缩放 gizmo 常量(SCALE op 整体自绘,不走 ImGuizmo;
|
// 缩放退化轴裁剪(防误拖动)阈值。库内缩放 hover/拖拽依赖
|
||||||
// 库内缩放数学在正对视角结构性退化,submodule 禁改)----
|
// "法线=轴"平面求交,两种病态几何:
|
||||||
constexpr float kScaleAxisLenFrac = 0.11f; // 杆长 = 视口高比例
|
// - 轴⊥"相机→物体"连线(正对视角无横偏):交点分子 numer =
|
||||||
//(库 gizmoSize 视觉近似)
|
// -dist·dot(camToObj, 轴) ≡ 0 → 杆抓不到、数值随机猛跳
|
||||||
constexpr float kScaleDegenerateCos = 0.98f; // 轴与视线 |cos|>此值 =
|
// - 轴∥视线(轴指向相机):屏幕零位移 → 拖了数值不变
|
||||||
// 指向相机(≈11.5°,与
|
constexpr float kScaleMinDot = 0.1f; // ≈84.3°
|
||||||
// 库藏杆角一致)
|
constexpr float kScaleMaxFwdDot = 0.98f; // ≈11.5°,与库藏杆角一致
|
||||||
constexpr float kScaleAxisHitPx = 6.0f; // 杆线命中半径 px
|
|
||||||
constexpr float kScaleGrabHalfPx = 6.0f; // 端点方块半边 px
|
|
||||||
constexpr float kScaleCenterHalfPx = 9.0f; // 中心等比方块半边 px
|
|
||||||
constexpr float kScaleDragPerPx = 0.01f; // 灵敏度 1/px(库中心
|
|
||||||
// 块同款)
|
|
||||||
constexpr float kScaleMinScale = 0.01f; // scale 下限(防 0/负)
|
|
||||||
|
|
||||||
// Vulkan proj → ImGuizmo 期望的 OpenGL 形式(Y-up、Z∈[-1,1]):
|
// Vulkan proj → ImGuizmo 期望的 OpenGL 形式(Y-up、Z∈[-1,1]):
|
||||||
// Y 行翻回(抵消 Vulkan clip Y 向下翻转);z 行 = 2*z - w 即
|
// Y 行翻回(抵消 Vulkan clip Y 向下翻转);z 行 = 2*z - w 即
|
||||||
@@ -74,232 +66,6 @@ namespace
|
|||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImGuizmo directionColorRGB 同款轴色(X 红 / Y 黄 / Z 青),
|
|
||||||
// hot = 悬停/拖拽中 → 向白提亮
|
|
||||||
ImU32 ScaleAxisColor(const int i, const bool hot)
|
|
||||||
{
|
|
||||||
static constexpr float kCol[3][3] = {
|
|
||||||
{0.666f, 0.000f, 0.000f},
|
|
||||||
{0.533f, 0.533f, 0.000f},
|
|
||||||
{0.000f, 0.666f, 0.666f}};
|
|
||||||
const auto ch = [&](const float c) {
|
|
||||||
const float v = hot ? c + (1.0f - c) * 0.35f : c;
|
|
||||||
return static_cast<ImU32>(v * 255.0f + 0.5f);
|
|
||||||
};
|
|
||||||
return IM_COL32(ch(kCol[i][0]), ch(kCol[i][1]), ch(kCol[i][2]), 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 自绘缩放 gizmo(SCALE op 专用,替代 ImGuizmo 缩放路径)----
|
|
||||||
// 为什么自绘:库内缩放的 hover(GetScaleType) 与拖拽(HandleScale
|
|
||||||
// ratio 除法)依赖"法线=轴"的射线-平面求交,正对视角(轴⊥相机→
|
|
||||||
// 物体连线)时求交分子 numer = -dist·dot(camToObj, 轴) ≡ 0 →
|
|
||||||
// 杆 hover 不到、数值随机猛变;轴∥视线时屏幕零位移,任何投影
|
|
||||||
// 式交互都退化。库层不可改(submodule 红线),AxisMask 只能"藏"
|
|
||||||
// 不能"补"(用户要求缩放真正可用)→ 整体自绘,纯屏幕空间数学:
|
|
||||||
// - 三轴杆(线+端点方块)+ 中心等比方块,杆长恒定屏幕尺寸
|
|
||||||
// - 普通轴:鼠标位移在轴屏幕方向上的投影 → scale 增量(0.01/px,
|
|
||||||
// 与库中心块同灵敏度)
|
|
||||||
// - 轴指向相机(|cos(axis, 视线)|>0.98):径向映射——拖离杆心 =
|
|
||||||
// 放大、拖近 = 缩小(沿视线零屏幕位移的几何必然,UE 同款处理:
|
|
||||||
// 该轴退化为径向可拖而非不可用)
|
|
||||||
// - 中心方块:等比缩放(库 MT_SCALE_XYZ 同款 x 位移数学)
|
|
||||||
// - Ctrl 吸附 = 结果值按 kSnapScale 量化
|
|
||||||
void ScaleGizmoSelf(ImDrawList* dl, const ImVec2& org,
|
|
||||||
const ImVec2& vsize, const glm::mat4& view,
|
|
||||||
const glm::mat4& projGL, const glm::mat4& world,
|
|
||||||
ECS::TransformComponent* tr, GizmoState& gizmo)
|
|
||||||
{
|
|
||||||
const ImGuiIO& io = ImGui::GetIO();
|
|
||||||
|
|
||||||
// 世界 → 面板屏幕坐标(GL clip y 向上,ImGui y 向下 → 翻转;
|
|
||||||
// 与 ProjVulkanToGL 后的 projGL 配套)
|
|
||||||
const glm::mat4 vp = projGL * view;
|
|
||||||
const auto toScreen = [&vp, &org, &vsize](const glm::vec3& w,
|
|
||||||
ImVec2& out) {
|
|
||||||
const glm::vec4 c = vp * glm::vec4(w, 1.0f);
|
|
||||||
if (c.w <= 1e-5f) return false; // 相机后
|
|
||||||
out = ImVec2(org.x + (c.x / c.w * 0.5f + 0.5f) * vsize.x,
|
|
||||||
org.y + (0.5f - c.y / c.w * 0.5f) * vsize.y);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const glm::vec3 posW(world[3]);
|
|
||||||
const glm::vec3 viewFwd = -glm::normalize(
|
|
||||||
glm::vec3(view[0][2], view[1][2], view[2][2]));
|
|
||||||
ImVec2 cScr;
|
|
||||||
if (!toScreen(posW, cScr)) {
|
|
||||||
// 杆心在相机后:收拖拽防卡死,本帧不画不响应
|
|
||||||
gizmo.scaleDragActive = false;
|
|
||||||
gizmo.scaleDragAxis = -1;
|
|
||||||
gizmo.scaleHover = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float axisLen = kScaleAxisLenFrac * vsize.y;
|
|
||||||
const glm::mat3 rotM(world);
|
|
||||||
|
|
||||||
// 每轴屏幕几何:轴方向 / 端点 / 是否指向相机(退化)
|
|
||||||
struct AxisVis {
|
|
||||||
bool degenerate = false;
|
|
||||||
ImVec2 tip{0.0f, 0.0f}; // 非退化 = 杆端点;退化 = 杆心
|
|
||||||
ImVec2 dir{0.0f, 0.0f}; // 非退化 = 杆方向(单位 px 向量)
|
|
||||||
} vis[3];
|
|
||||||
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));
|
|
||||||
vis[i].degenerate =
|
|
||||||
glm::abs(glm::dot(viewFwd, axis)) > kScaleDegenerateCos;
|
|
||||||
if (!vis[i].degenerate) {
|
|
||||||
ImVec2 u;
|
|
||||||
if (toScreen(posW + axis, u)) {
|
|
||||||
const float dx = u.x - cScr.x, dy = u.y - cScr.y;
|
|
||||||
const float len = std::sqrt(dx * dx + dy * dy);
|
|
||||||
if (len > 1e-4f) {
|
|
||||||
vis[i].dir = ImVec2(dx / len, dy / len);
|
|
||||||
vis[i].tip = ImVec2(cScr.x + vis[i].dir.x * axisLen,
|
|
||||||
cScr.y + vis[i].dir.y * axisLen);
|
|
||||||
} else {
|
|
||||||
vis[i].degenerate = true; // 投影意外退化兜底
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vis[i].degenerate = true; // 端点在相机后兜底
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (vis[i].degenerate) vis[i].tip = cScr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 命中检测(优先级:退化轴小方块 > 轴杆 > 中心等比)----
|
|
||||||
const float mx = io.MousePos.x, my = io.MousePos.y;
|
|
||||||
const auto inSquare = [mx, my](const ImVec2& c, const float half) {
|
|
||||||
return mx >= c.x - half && mx <= c.x + half &&
|
|
||||||
my >= c.y - half && my <= c.y + half;
|
|
||||||
};
|
|
||||||
const auto distToSeg = [mx, my](const ImVec2& a, const ImVec2& b) {
|
|
||||||
const float abx = b.x - a.x, aby = b.y - a.y;
|
|
||||||
const float t = std::clamp(
|
|
||||||
((mx - a.x) * abx + (my - a.y) * aby) /
|
|
||||||
(abx * abx + aby * aby), 0.0f, 1.0f);
|
|
||||||
const float dx = mx - (a.x + abx * t),
|
|
||||||
dy = my - (a.y + aby * t);
|
|
||||||
return std::sqrt(dx * dx + dy * dy);
|
|
||||||
};
|
|
||||||
|
|
||||||
int hover = -1; // 0/1/2 轴;-2 中心等比;-1 无
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
if (vis[i].degenerate
|
|
||||||
? inSquare(cScr, kScaleGrabHalfPx)
|
|
||||||
: (inSquare(vis[i].tip, kScaleGrabHalfPx + 1.0f) ||
|
|
||||||
distToSeg(cScr, vis[i].tip) <= kScaleAxisHitPx)) {
|
|
||||||
hover = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hover == -1 && inSquare(cScr, kScaleCenterHalfPx)) hover = -2;
|
|
||||||
|
|
||||||
// ---- 交互:启动 / 更新 / 结束 ----
|
|
||||||
gizmo.scaleHover = hover != -1 || gizmo.scaleDragActive;
|
|
||||||
if (ImGui::IsWindowHovered() && !gizmo.scaleDragActive &&
|
|
||||||
hover != -1 && ImGui::IsMouseClicked(0)) {
|
|
||||||
gizmo.scaleDragActive = true;
|
|
||||||
gizmo.scaleDragAxis = hover;
|
|
||||||
gizmo.scaleStartMouse[0] = mx;
|
|
||||||
gizmo.scaleStartMouse[1] = my;
|
|
||||||
const glm::vec3 s = tr->GetScale();
|
|
||||||
gizmo.scaleStartScale[0] = s.x;
|
|
||||||
gizmo.scaleStartScale[1] = s.y;
|
|
||||||
gizmo.scaleStartScale[2] = s.z;
|
|
||||||
const float ddx = mx - cScr.x, ddy = my - cScr.y;
|
|
||||||
gizmo.scaleStartDist = std::sqrt(ddx * ddx + ddy * ddy);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gizmo.scaleDragActive) {
|
|
||||||
if (!io.MouseDown[0]) {
|
|
||||||
gizmo.scaleDragActive = false;
|
|
||||||
gizmo.scaleDragAxis = -1;
|
|
||||||
} else {
|
|
||||||
const int a = gizmo.scaleDragAxis;
|
|
||||||
float d = 0.0f; // 缩放倍率增量(乘法语义,等手感于库)
|
|
||||||
if (a == -2) {
|
|
||||||
// 中心等比:x 位移(库 MT_SCALE_XYZ 同款数学)
|
|
||||||
d = (mx - gizmo.scaleStartMouse[0]) * kScaleDragPerPx;
|
|
||||||
} else if (vis[a].degenerate) {
|
|
||||||
// 轴指向相机:径向——拖离杆心放大、拖近缩小
|
|
||||||
const float ddx = mx - cScr.x, ddy = my - cScr.y;
|
|
||||||
d = (std::sqrt(ddx * ddx + ddy * ddy) -
|
|
||||||
gizmo.scaleStartDist) * kScaleDragPerPx;
|
|
||||||
} else {
|
|
||||||
// 鼠标位移在轴屏幕方向上的投影
|
|
||||||
d = ((mx - gizmo.scaleStartMouse[0]) * vis[a].dir.x +
|
|
||||||
(my - gizmo.scaleStartMouse[1]) * vis[a].dir.y) *
|
|
||||||
kScaleDragPerPx;
|
|
||||||
}
|
|
||||||
const float k = 1.0f + d;
|
|
||||||
glm::vec3 s(gizmo.scaleStartScale[0], gizmo.scaleStartScale[1],
|
|
||||||
gizmo.scaleStartScale[2]);
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
if (a == -2 || i == a) {
|
|
||||||
s[i] = glm::max(kScaleMinScale, s[i] * k);
|
|
||||||
if (io.KeyCtrl) { // 吸附 = 结果按步长量化
|
|
||||||
s[i] = glm::max(kScaleMinScale,
|
|
||||||
std::round(s[i] / kSnapScale) *
|
|
||||||
kSnapScale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tr->SetScale(s); // 标脏 → UpdateTransforms 下帧生效
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 绘制:三轴杆 → 中心等比方块 → 退化轴小方块(压顶)----
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
if (vis[i].degenerate) continue;
|
|
||||||
const bool hot =
|
|
||||||
(gizmo.scaleDragActive && gizmo.scaleDragAxis == i) ||
|
|
||||||
(!gizmo.scaleDragActive && hover == i);
|
|
||||||
const ImU32 col = ScaleAxisColor(i, hot);
|
|
||||||
dl->AddLine(cScr, vis[i].tip, col, 2.0f);
|
|
||||||
dl->AddRectFilled(
|
|
||||||
ImVec2(vis[i].tip.x - kScaleGrabHalfPx,
|
|
||||||
vis[i].tip.y - kScaleGrabHalfPx),
|
|
||||||
ImVec2(vis[i].tip.x + kScaleGrabHalfPx,
|
|
||||||
vis[i].tip.y + kScaleGrabHalfPx),
|
|
||||||
col, 2.0f);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const bool hot =
|
|
||||||
(gizmo.scaleDragActive && gizmo.scaleDragAxis == -2) ||
|
|
||||||
(!gizmo.scaleDragActive && hover == -2);
|
|
||||||
dl->AddRectFilled(
|
|
||||||
ImVec2(cScr.x - kScaleCenterHalfPx,
|
|
||||||
cScr.y - kScaleCenterHalfPx),
|
|
||||||
ImVec2(cScr.x + kScaleCenterHalfPx,
|
|
||||||
cScr.y + kScaleCenterHalfPx),
|
|
||||||
hot ? IM_COL32(255, 255, 255, 255)
|
|
||||||
: IM_COL32(230, 230, 230, 210),
|
|
||||||
3.0f);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
if (!vis[i].degenerate) continue;
|
|
||||||
const bool hot =
|
|
||||||
(gizmo.scaleDragActive && gizmo.scaleDragAxis == i) ||
|
|
||||||
(!gizmo.scaleDragActive && hover == i);
|
|
||||||
const ImU32 col = ScaleAxisColor(i, hot);
|
|
||||||
dl->AddRectFilled(
|
|
||||||
ImVec2(cScr.x - kScaleGrabHalfPx,
|
|
||||||
cScr.y - kScaleGrabHalfPx),
|
|
||||||
ImVec2(cScr.x + kScaleGrabHalfPx,
|
|
||||||
cScr.y + kScaleGrabHalfPx),
|
|
||||||
col, 2.0f);
|
|
||||||
dl->AddRect(
|
|
||||||
ImVec2(cScr.x - kScaleGrabHalfPx,
|
|
||||||
cScr.y - kScaleGrabHalfPx),
|
|
||||||
ImVec2(cScr.x + kScaleGrabHalfPx,
|
|
||||||
cScr.y + kScaleGrabHalfPx),
|
|
||||||
IM_COL32(20, 20, 20, 200), 2.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
||||||
@@ -379,12 +145,6 @@ std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
|||||||
selected != entt::null
|
selected != entt::null
|
||||||
? reg.try_get<ECS::TransformComponent>(selected)
|
? reg.try_get<ECS::TransformComponent>(selected)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
// 自绘缩放拖拽中选中丢失(清选/实体销毁):复位,防止
|
|
||||||
// dragging 永久置位锁死鼠标
|
|
||||||
if (gizmo.scaleDragActive && tr == nullptr) {
|
|
||||||
gizmo.scaleDragActive = false;
|
|
||||||
gizmo.scaleDragAxis = -1;
|
|
||||||
}
|
|
||||||
if (tr != nullptr) {
|
if (tr != nullptr) {
|
||||||
// 相机矩阵:primary 相机 view + Vulkan proj(转 GL 形式),
|
// 相机矩阵:primary 相机 view + Vulkan proj(转 GL 形式),
|
||||||
// 参数与渲染侧一致(aspect = 渲染分辨率、near/far 0.1/100)
|
// 参数与渲染侧一致(aspect = 渲染分辨率、near/far 0.1/100)
|
||||||
@@ -410,84 +170,83 @@ std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
|||||||
gizmo.localSpace ? ImGuizmo::LOCAL : ImGuizmo::WORLD;
|
gizmo.localSpace ? ImGuizmo::LOCAL : ImGuizmo::WORLD;
|
||||||
const glm::mat3 rotM(m);
|
const glm::mat3 rotM(m);
|
||||||
|
|
||||||
// 退化轴隐藏(仅平移):拖拽灵敏度 = sinθ(轴与相机
|
// 退化轴裁剪(防误拖动):被裁轴不可见、不可激活,
|
||||||
// 连线夹角),θ→0 时鼠标位移在轴上投影→0("数值不
|
// 转个视角即可用。三操作病态几何不同,按 op 分派:
|
||||||
// 变")。旋转 = 屏幕空间角度数学无退化;缩放整体
|
// - 平移:拖拽灵敏度 = sinθ(轴与相机连线夹角),θ→0
|
||||||
// 自绘(见下),两者不 mask
|
// 时鼠标位移在轴上投影→0("数值不变")
|
||||||
|
// - 缩放:库内 hover/拖拽依赖"法线=轴"平面求交——
|
||||||
|
// 轴⊥相机连线(正对无横偏)时交点分子 numer≡0
|
||||||
|
// (抓不到/数值乱跳);轴∥视线时屏幕零位移(数值
|
||||||
|
// 不变)。两判据并集裁剪
|
||||||
|
// - 旋转:屏幕空间角度数学,无退化,不裁
|
||||||
const bool isTranslate = (gizmo.op
|
const bool isTranslate = (gizmo.op
|
||||||
& int(ImGuizmo::TRANSLATE)) == int(ImGuizmo::TRANSLATE);
|
& int(ImGuizmo::TRANSLATE)) == int(ImGuizmo::TRANSLATE);
|
||||||
const bool isScale = (gizmo.op
|
const bool isScale = (gizmo.op
|
||||||
& int(ImGuizmo::SCALE)) == int(ImGuizmo::SCALE);
|
& int(ImGuizmo::SCALE)) == int(ImGuizmo::SCALE);
|
||||||
if (!isScale) {
|
if (isTranslate || isScale) {
|
||||||
// 拖拽中切 op(R→W/E):打断自绘缩放拖拽
|
|
||||||
gizmo.scaleDragActive = false;
|
|
||||||
gizmo.scaleDragAxis = -1;
|
|
||||||
bool maskAxis[3] = {false, false, false};
|
bool maskAxis[3] = {false, false, false};
|
||||||
const glm::vec3 camPos(glm::inverse(view)[3]);
|
const glm::vec3 camPos(glm::inverse(view)[3]);
|
||||||
glm::vec3 camToObj = glm::vec3(m[3]) - camPos;
|
glm::vec3 camToObj = glm::vec3(m[3]) - camPos;
|
||||||
const float camDist = glm::length(camToObj);
|
const float camDist = glm::length(camToObj);
|
||||||
if (isTranslate && camDist > 1e-4f) {
|
if (camDist > 1e-4f) {
|
||||||
camToObj /= camDist;
|
camToObj /= camDist;
|
||||||
|
const glm::vec3 viewFwd = -glm::normalize(
|
||||||
|
glm::vec3(view[0][2], view[1][2],
|
||||||
|
view[2][2]));
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
const glm::vec3 axis = gizmo.localSpace
|
const glm::vec3 axis = gizmo.localSpace
|
||||||
? glm::normalize(rotM[i])
|
? glm::normalize(rotM[i])
|
||||||
: glm::vec3(float(i == 0), float(i == 1),
|
: glm::vec3(float(i == 0), float(i == 1),
|
||||||
float(i == 2));
|
float(i == 2));
|
||||||
maskAxis[i] =
|
maskAxis[i] = isScale
|
||||||
glm::length(glm::cross(camToObj, axis))
|
? glm::abs(glm::dot(camToObj, axis))
|
||||||
< kMoveMinSin;
|
< kScaleMinDot ||
|
||||||
|
glm::abs(glm::dot(viewFwd, axis))
|
||||||
|
> kScaleMaxFwdDot
|
||||||
|
: glm::length(glm::cross(camToObj, axis))
|
||||||
|
< kMoveMinSin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGuizmo::SetAxisMask(maskAxis[0], maskAxis[1],
|
ImGuizmo::SetAxisMask(maskAxis[0], maskAxis[1],
|
||||||
maskAxis[2]);
|
maskAxis[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isScale) {
|
// Ctrl 吸附(UE Grid Snap 语义)。库的约定
|
||||||
// ---- 缩放 = 自绘 gizmo(不走 ImGuizmo)。
|
//(docs/documentation.md):平移 = 世界单位 float[3]
|
||||||
// 库内缩放 hover/拖拽依赖"法线=轴"平面求交,
|
// 逐轴;旋转 = snap[0] 按度(库内自行 DEG2RAD,传弧
|
||||||
// 正对视角(轴⊥相机连线)时分子 numer≡0:杆
|
// 度会被解释成极小步进);缩放 = 只取 snap[0]
|
||||||
// hover 不到、数值随机猛变;轴∥视线无屏幕位
|
float snap[3] = {0.0f, 0.0f, 0.0f};
|
||||||
// 移——AxisMask 只能藏不能补 → 整体自绘,屏
|
const bool snapping = ImGui::GetIO().KeyCtrl;
|
||||||
// 幕空间数学任意视角良构 ----
|
if (snapping) {
|
||||||
ScaleGizmoSelf(ImGui::GetWindowDrawList(), mn, size,
|
if (gizmo.op == ImGuizmo::TRANSLATE) {
|
||||||
view, projGL, m, tr, gizmo);
|
snap[0] = snap[1] = snap[2] = kSnapTranslate;
|
||||||
} else {
|
} else if (gizmo.op == ImGuizmo::ROTATE) {
|
||||||
// Ctrl 吸附(UE Grid Snap 语义)。库的约定
|
snap[0] = kSnapRotateDeg; // 度,非弧度
|
||||||
//(docs/documentation.md):平移 = 世界单位
|
} else if (gizmo.op == ImGuizmo::SCALE) {
|
||||||
// float[3] 逐轴;旋转 = snap[0] 按度(库内自行
|
snap[0] = kSnapScale;
|
||||||
// DEG2RAD,传弧度会被解释成极小步进)
|
}
|
||||||
float snap[3] = {0.0f, 0.0f, 0.0f};
|
}
|
||||||
const bool snapping = ImGui::GetIO().KeyCtrl;
|
|
||||||
if (snapping) {
|
ImGuizmo::Manipulate(&view[0][0], &projGL[0][0],
|
||||||
if (gizmo.op == ImGuizmo::TRANSLATE) {
|
static_cast<ImGuizmo::OPERATION>(gizmo.op),
|
||||||
snap[0] = snap[1] = snap[2] = kSnapTranslate;
|
mode, &m[0][0], nullptr,
|
||||||
} else if (gizmo.op == ImGuizmo::ROTATE) {
|
snapping ? snap : nullptr);
|
||||||
snap[0] = kSnapRotateDeg; // 度,非弧度
|
gizmo.dragging = ImGuizmo::IsUsing();
|
||||||
}
|
|
||||||
}
|
// 拖拽生效:分解 world 矩阵写回 SRT(Set* 标脏 →
|
||||||
|
// UpdateTransforms 重算 → 渲染下帧生效)。旋转用
|
||||||
ImGuizmo::Manipulate(&view[0][0], &projGL[0][0],
|
// decompose 出的 quat 直写(UE FTransform 语义)——
|
||||||
static_cast<ImGuizmo::OPERATION>(gizmo.op),
|
// 不经欧拉往返,拖拽零漂移、无万向锁
|
||||||
mode, &m[0][0], nullptr,
|
if (ImGuizmo::IsUsing()) {
|
||||||
snapping ? snap : nullptr);
|
glm::vec3 scale, transl, skew;
|
||||||
|
glm::quat rot;
|
||||||
// 拖拽生效:分解 world 矩阵写回 SRT(Set* 标脏 →
|
glm::vec4 persp;
|
||||||
// UpdateTransforms 重算 → 渲染下帧生效)。旋转用
|
if (glm::decompose(m, scale, rot, transl, skew, persp)) {
|
||||||
// decompose 出的 quat 直写(UE FTransform 语义)——
|
tr->SetPosition(transl)
|
||||||
// 不经欧拉往返,拖拽零漂移、无万向锁
|
.SetRotationQuat(rot)
|
||||||
if (ImGuizmo::IsUsing()) {
|
.SetScale(scale);
|
||||||
glm::vec3 scale, transl, skew;
|
|
||||||
glm::quat rot;
|
|
||||||
glm::vec4 persp;
|
|
||||||
if (glm::decompose(m, scale, rot, transl, skew, persp)) {
|
|
||||||
tr->SetPosition(transl)
|
|
||||||
.SetRotationQuat(rot)
|
|
||||||
.SetScale(scale);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gizmo.dragging = ImGuizmo::IsUsing() ||
|
|
||||||
gizmo.scaleDragActive;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,11 +279,10 @@ std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 点击拾取:单击 Image 且未命中 UI 遮挡(IsItemHovered 已含)。
|
// 点击拾取:单击 Image 且未命中 UI 遮挡(IsItemHovered 已含)。
|
||||||
// 抓杆时不出 pick(IsOver 悬停杆/IsUsing 拖拽中/自绘缩放
|
// 抓杆时不出 pick(IsOver 悬停杆/IsUsing 拖拽中)——否则按在
|
||||||
// 手柄悬停或拖拽中)——否则按在操纵杆上的点击会穿透发拾取
|
// 操纵杆上的点击会穿透发拾取请求
|
||||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) &&
|
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) &&
|
||||||
!ImGuizmo::IsOver() && !ImGuizmo::IsUsing() &&
|
!ImGuizmo::IsOver() && !ImGuizmo::IsUsing()) {
|
||||||
!gizmo.scaleHover) {
|
|
||||||
RequestPickAt(ctx, size);
|
RequestPickAt(ctx, size);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -35,21 +35,7 @@ struct GizmoState {
|
|||||||
int op = 7; // ImGuizmo::TRANSLATE(TRANSLATE_X|Y|Z)
|
int op = 7; // ImGuizmo::TRANSLATE(TRANSLATE_X|Y|Z)
|
||||||
bool localSpace = true; // 操纵空间:true=模型轴(旋转/缩放语义),
|
bool localSpace = true; // 操纵空间:true=模型轴(旋转/缩放语义),
|
||||||
// T 键切 world
|
// T 键切 world
|
||||||
bool dragging = false; // 拖拽中(IsUsing 或自绘缩放拖拽)→ 鼠标归
|
bool dragging = false; // 拖拽中(IsUsing)→ 鼠标归 Gizmo 不给游戏侧
|
||||||
// Gizmo 不给游戏侧(Editor::BlockingMouse)
|
|
||||||
|
|
||||||
// ---- 自绘缩放 gizmo(op==SCALE 整体不走 ImGuizmo)----
|
|
||||||
// 库内缩放的 hover/拖拽依赖"法线=轴"平面求交:正对视角(轴⊥
|
|
||||||
// 相机连线)时求交分子恒 0 → 杆抓不到、数值随机猛变;轴∥视线
|
|
||||||
// 时又无屏幕位移。submodule 禁改,AxisMask 只能"藏"不能"补"
|
|
||||||
// → 调用层整体自绘三轴杆 + 中心等比方块(纯屏幕空间数学,任意
|
|
||||||
// 视角良构),实现见 ViewportPanel.cpp ScaleGizmoSelf
|
|
||||||
bool scaleDragActive = false; // 自绘缩放拖拽中
|
|
||||||
int scaleDragAxis = -1; // 拖拽目标:0/1/2 轴;-2 = 中心等比
|
|
||||||
bool scaleHover = false; // 本帧悬停/拖拽自绘手柄 → 抑制 pick
|
|
||||||
float scaleStartMouse[2] = {0.0f, 0.0f}; // 拖拽起始鼠标(屏幕系)
|
|
||||||
float scaleStartDist = 0.0f; // 起始鼠标到杆心距离(径向映射用)
|
|
||||||
float scaleStartScale[3] = {1.0f, 1.0f, 1.0f}; // 拖拽起始 scale
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ViewportPanel {
|
class ViewportPanel {
|
||||||
|
|||||||
Reference in New Issue
Block a user