167 lines
4.9 KiB
C++
167 lines
4.9 KiB
C++
#include <destrum/Util/TransformGizmo.h>
|
|
|
|
#include <destrum/Graphics/Camera.h>
|
|
#include <destrum/ObjectModel/GameObject.h>
|
|
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
#include <glm/gtx/matrix_decompose.hpp>
|
|
|
|
#include <imgui.h>
|
|
#include <ImGuizmo.h>
|
|
|
|
namespace {
|
|
ImGuizmo::OPERATION toImGuizmo(GizmoOperation operation)
|
|
{
|
|
switch (operation) {
|
|
case GizmoOperation::Rotate:
|
|
return ImGuizmo::ROTATE;
|
|
case GizmoOperation::Scale:
|
|
return ImGuizmo::SCALE;
|
|
case GizmoOperation::Translate:
|
|
break;
|
|
}
|
|
return ImGuizmo::TRANSLATE;
|
|
}
|
|
}
|
|
|
|
void TransformGizmo::HandleShortcuts()
|
|
{
|
|
if (ImGui::GetIO().WantTextInput || ImGuizmo::IsUsing()) {
|
|
return;
|
|
}
|
|
|
|
if (ImGui::IsKeyPressed(ImGuiKey_T, false)) {
|
|
operation = GizmoOperation::Translate;
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGuiKey_R, false)) {
|
|
operation = GizmoOperation::Rotate;
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGuiKey_Y, false)) {
|
|
operation = GizmoOperation::Scale;
|
|
}
|
|
if (ImGui::IsKeyPressed(ImGuiKey_L, false)) {
|
|
space = space == GizmoSpace::World ? GizmoSpace::Local : GizmoSpace::World;
|
|
}
|
|
}
|
|
|
|
void TransformGizmo::DrawToolbar()
|
|
{
|
|
if (!ImGui::Begin("Gizmo", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
const auto operationButton = [this](const char* label, GizmoOperation buttonOperation) {
|
|
if (ImGui::RadioButton(label, operation == buttonOperation)) {
|
|
operation = buttonOperation;
|
|
}
|
|
};
|
|
operationButton("Move (T)", GizmoOperation::Translate);
|
|
ImGui::SameLine();
|
|
operationButton("Rotate (R)", GizmoOperation::Rotate);
|
|
ImGui::SameLine();
|
|
operationButton("Scale (Y)", GizmoOperation::Scale);
|
|
|
|
if (operation != GizmoOperation::Scale) {
|
|
bool local = space == GizmoSpace::Local;
|
|
if (ImGui::Checkbox("Local (L)", &local)) {
|
|
space = local ? GizmoSpace::Local : GizmoSpace::World;
|
|
}
|
|
ImGui::SameLine();
|
|
}
|
|
|
|
ImGui::Checkbox("Snap", &snap);
|
|
if (snap) {
|
|
ImGui::SetNextItemWidth(80.0f);
|
|
switch (operation) {
|
|
case GizmoOperation::Translate:
|
|
ImGui::DragFloat("Step", &translationSnap, 0.05f, 0.01f, 10.0f, "%.2f m");
|
|
break;
|
|
case GizmoOperation::Rotate:
|
|
ImGui::DragFloat("Step", &rotationSnapDegrees, 1.0f, 1.0f, 90.0f, "%.0f deg");
|
|
break;
|
|
case GizmoOperation::Scale:
|
|
ImGui::DragFloat("Step", &scaleSnap, 0.01f, 0.01f, 1.0f, "%.2f");
|
|
break;
|
|
}
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
bool TransformGizmo::Manipulate(GameObject& object, const Camera& camera)
|
|
{
|
|
ImGuizmo::BeginFrame();
|
|
|
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
|
ImGuizmo::SetOrthographic(false);
|
|
ImGuizmo::SetRect(viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y);
|
|
|
|
const glm::mat4 view = camera.GetViewMatrix();
|
|
glm::mat4 projection = camera.GetProjectionMatrix();
|
|
// ImGuizmo expects OpenGL's y up clip space, the engine flips y for Vulkan
|
|
projection[1][1] *= -1.0f;
|
|
|
|
Transform& transform = object.GetTransform();
|
|
glm::mat4 worldMatrix = transform.GetWorldMatrix();
|
|
|
|
const float snapStep = operation == GizmoOperation::Translate ? translationSnap
|
|
: operation == GizmoOperation::Rotate ? rotationSnapDegrees
|
|
: scaleSnap;
|
|
const float snapValues[3] = {snapStep, snapStep, snapStep};
|
|
const ImGuizmo::MODE mode =
|
|
operation == GizmoOperation::Scale || space == GizmoSpace::Local ? ImGuizmo::LOCAL : ImGuizmo::WORLD;
|
|
|
|
const bool changed = ImGuizmo::Manipulate(
|
|
glm::value_ptr(view),
|
|
glm::value_ptr(projection),
|
|
toImGuizmo(operation),
|
|
mode,
|
|
glm::value_ptr(worldMatrix),
|
|
nullptr,
|
|
snap ? snapValues : nullptr);
|
|
|
|
// Stops the click on a handle from reaching the game, e.g. grabbing the mouse for first person
|
|
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
|
|
ImGui::SetNextFrameWantCaptureMouse(true);
|
|
}
|
|
|
|
if (!changed) {
|
|
return false;
|
|
}
|
|
|
|
glm::vec3 position;
|
|
glm::quat rotation;
|
|
glm::vec3 scale;
|
|
glm::vec3 skew;
|
|
glm::vec4 perspective;
|
|
if (!glm::decompose(worldMatrix, scale, rotation, position, skew, perspective)) {
|
|
return false;
|
|
}
|
|
|
|
switch (operation) {
|
|
case GizmoOperation::Translate:
|
|
transform.SetWorldPosition(position);
|
|
break;
|
|
case GizmoOperation::Rotate:
|
|
transform.SetWorldRotation(rotation);
|
|
break;
|
|
case GizmoOperation::Scale:
|
|
transform.SetWorldScale(scale);
|
|
break;
|
|
}
|
|
|
|
object.RefreshPhysicsInHierarchy();
|
|
return true;
|
|
}
|
|
|
|
bool TransformGizmo::IsUsing() const
|
|
{
|
|
return ImGuizmo::IsUsing();
|
|
}
|
|
|
|
bool TransformGizmo::IsOver() const
|
|
{
|
|
return ImGuizmo::IsOver();
|
|
}
|