feat: Gizmo local/world 空间切换 + Ctrl 吸附 + Inspector 轴色手输行
- GizmoState.localSpace(默认 local,模型轴旋转直观),T 键切换 Manipulate 的 LOCAL/WORLD 模式;无父层级两模式 world 矩阵同源, decompose 写回链路不变 - Ctrl 按住吸附(UE Grid Snap 语义):平移 0.25m / 旋转 15° / 缩放 0.1x;ImGuizmo 约定旋转 snap 单位为弧度 - 视口左上角 drawlist 叠加状态提示(Space/Snap,不占布局行) - Inspector Transform 三行升级 UE 风轴色输入行(对齐 Gizmo 轴色): 拖拽粗调 + Ctrl+Click 键入精确值
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
// GTX 扩展(实验性)需显式开启:matrix_decompose(矩阵→SRT 分解,
|
||||
// 旋转以四元数输出,直接写回 TransformComponent)
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
@@ -33,6 +35,11 @@ using namespace Engine; // u32 / f32(Engine/Core/Types.h)
|
||||
|
||||
namespace
|
||||
{
|
||||
// Ctrl 吸附步长(UE Grid Snap 语义:按住 Ctrl 拖拽才吸附)
|
||||
constexpr float kSnapTranslate = 0.25f; // 米/格
|
||||
constexpr float kSnapRotateDeg = 15.0f; // 度
|
||||
constexpr float kSnapScale = 0.1f; // 缩放步进
|
||||
|
||||
// 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
|
||||
@@ -105,13 +112,15 @@ std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
||||
ImGui::Image((ImTextureID) (intptr_t) sceneTex, size);
|
||||
|
||||
// ---- W/E/R 切换 Gizmo 模式(UE 语义:W 平移 / E 旋转 /
|
||||
// R 缩放)。仅 Viewport 持焦时生效——输入框打字(WantTextInput)
|
||||
// 由 BlockingKeyboard 拦游戏侧,但 ImGui 自身仍收键,故必须
|
||||
// 限定持焦面板,避免在 Inspector 拖数值时误切 ----
|
||||
// R 缩放),T 切换 local/world 操纵空间。仅 Viewport 持焦时
|
||||
// 生效——输入框打字(WantTextInput)由 BlockingKeyboard 拦
|
||||
// 游戏侧,但 ImGui 自身仍收键,故必须限定持焦面板,避免在
|
||||
// Inspector 拖数值时误切 ----
|
||||
if (out.focused && !ImGui::GetIO().WantTextInput) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_W)) gizmo.op = ImGuizmo::TRANSLATE;
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_E)) gizmo.op = ImGuizmo::ROTATE;
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_R)) gizmo.op = ImGuizmo::SCALE;
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_T)) gizmo.localSpace = !gizmo.localSpace;
|
||||
}
|
||||
|
||||
// ---- Gizmo 操纵杆(ImGui drawlist 叠加,无 GPU pass)----
|
||||
@@ -138,11 +147,30 @@ std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
||||
ImGuizmo::SetRect(mn.x, mn.y, size.x, size.y);
|
||||
|
||||
glm::mat4 m = tr->World();
|
||||
// 注:操作空间为 WORLD(UE 默认);snap / local-world
|
||||
// 切换留 TODO
|
||||
// 操作空间:local(模型轴,旋转/缩放随物体朝向直观)/
|
||||
// world(T 键切换)。无父层级时两种模式 world 矩阵同源,
|
||||
// decompose 写回链路不变
|
||||
const ImGuizmo::MODE mode =
|
||||
gizmo.localSpace ? ImGuizmo::LOCAL : ImGuizmo::WORLD;
|
||||
|
||||
// Ctrl 按住 → 吸附(UE Grid Snap 语义)。ImGuizmo 约定:
|
||||
// 平移 snap = 世界单位(逐轴)、旋转 = 弧度、缩放 = 步进
|
||||
float snap[3] = {0.0f, 0.0f, 0.0f};
|
||||
const bool snapping = ImGui::GetIO().KeyCtrl;
|
||||
if (snapping) {
|
||||
if (gizmo.op == ImGuizmo::TRANSLATE) {
|
||||
snap[0] = snap[1] = snap[2] = kSnapTranslate;
|
||||
} else if (gizmo.op == ImGuizmo::ROTATE) {
|
||||
snap[0] = glm::radians(kSnapRotateDeg);
|
||||
} else if (gizmo.op == ImGuizmo::SCALE) {
|
||||
snap[0] = snap[1] = snap[2] = kSnapScale;
|
||||
}
|
||||
}
|
||||
|
||||
ImGuizmo::Manipulate(&view[0][0], &projGL[0][0],
|
||||
static_cast<ImGuizmo::OPERATION>(gizmo.op),
|
||||
ImGuizmo::WORLD, &m[0][0]);
|
||||
mode, &m[0][0], nullptr,
|
||||
snapping ? snap : nullptr);
|
||||
gizmo.dragging = ImGuizmo::IsUsing();
|
||||
|
||||
// 拖拽生效:分解 world 矩阵写回 SRT(Set* 标脏 →
|
||||
@@ -162,6 +190,17 @@ std::optional<entt::entity> ViewportPanel::Draw(const EditorContext &ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Gizmo 状态提示(左上角 drawlist 叠加,原因同下:不占布局行)
|
||||
{
|
||||
char tip[64];
|
||||
std::snprintf(tip, sizeof(tip), "Space: %s [T] Snap: Ctrl",
|
||||
gizmo.localSpace ? "Local" : "World");
|
||||
const ImVec2 imgMin = ImGui::GetItemRectMin();
|
||||
ImGui::GetWindowDrawList()->AddText(
|
||||
ImVec2(imgMin.x + 8.0f, imgMin.y + 8.0f),
|
||||
IM_COL32(255, 255, 255, 150), tip);
|
||||
}
|
||||
|
||||
// Dynamic 刚体提示:物理每帧把姿态写回 Transform,拖拽即刻
|
||||
// 被覆盖(Static/Kinematic 编辑器权威,可直接拖)。
|
||||
// 用 drawlist 叠加而非 ImGui::TextDisabled:后者在 Image 下方
|
||||
|
||||
Reference in New Issue
Block a user