16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-17 22:39:17 +02:00

ImGuizmo System OpenGL

This commit is contained in:
2026-06-17 01:19:59 +02:00
parent 3bed2212d7
commit 5a39ed5193
10 changed files with 3206 additions and 99 deletions

View File

@@ -222,6 +222,7 @@ set(SOURCES
"imgui/imgui_draw.cpp"
"imgui/imgui_widgets.cpp"
"imgui/imgui_impl_glfw.cpp"
"imgui/ImGuizmo.cpp"
"stb/stb_image.c"
@@ -444,7 +445,7 @@ if(NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0")
if (USE_PCH)
target_precompile_headers(${PROJECT_NAME} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/stdafx.h>")
set_source_files_properties("/glad/src/glad.c" "ref/dds-ktx/src/dds-ktx.c" "stb/stb_image.c"
"imgui/imgui.cpp" "imgui/imgui_demo.cpp" "imgui/imgui_draw.cpp" "imgui/imgui_widgets.cpp" "imgui/imgui_impl_glfw.cpp"
"imgui/imgui.cpp" "imgui/imgui_demo.cpp" "imgui/imgui_draw.cpp" "imgui/imgui_widgets.cpp" "imgui/imgui_impl_glfw.cpp" "imgui/ImGuizmo.cpp"
PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE)
endif()
endif()

View File

@@ -25,7 +25,10 @@ http://mozilla.org/MPL/2.0/.
#include "imgui/imgui.h"
#include "imgui/ImGuizmo.h"
#include "utilities/Logs.h"
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <cmath>
#include <functional>
#include <vector>
@@ -106,13 +109,13 @@ void editor_mode::start_focus(scene::basic_node *node, double duration)
m_focus_duration = duration;
m_focus_start_pos = Camera.Pos;
m_focus_start_lookat = Camera.LookAt;
m_focus_start_angle = Camera.LookAt;
m_focus_target_lookat = node->location();
m_focus_target_pos = node->location();
glm::dvec3 dir = m_focus_start_pos - m_focus_start_lookat;
glm::dvec3 dir = m_focus_target_pos - m_focus_start_pos;
double dist = glm::length(dir);
m_focus_target_pos = m_focus_target_lookat + glm::dvec3(10.0, 3.0, 10.0);
m_focus_target_angle = m_focus_target_pos + glm::dvec3(10.0, 3.0, 10.0);
}
void editor_mode::handle_brush_mouse_hold(int Action, int Button)
@@ -212,10 +215,12 @@ void editor_mode::push_snapshot(scene::basic_node *node, EditorSnapshot::Action
if (auto *model = dynamic_cast<TAnimModel *>(node))
{
snap.rotation = model->Angles();
snap.scale = model->Scale();
}
else
{
snap.rotation = glm::vec3(0.0f);
snap.scale = glm::vec3(1.0f);
}
if (Action == EditorSnapshot::Action::Delete || Action == EditorSnapshot::Action::Add)
@@ -301,7 +306,10 @@ void editor_mode::undo_last()
current.node_ptr = target;
current.position = target->location();
if (auto *model = dynamic_cast<TAnimModel *>(target))
{
current.rotation = model->Angles();
current.scale = model->Scale();
}
else
current.rotation = glm::vec3(0.0f);
g_redo.push_back(std::move(current));
@@ -328,6 +336,7 @@ void editor_mode::undo_last()
glm::vec3 cur = model->Angles();
glm::vec3 delta = snap.rotation - cur;
m_editor.rotate(target, delta, 0);
model->Scale(snap.scale);
}
m_node = target;
@@ -379,7 +388,10 @@ void editor_mode::redo_last()
{
hist.position = target->location();
if (auto *model = dynamic_cast<TAnimModel *>(target))
{
hist.rotation = model->Angles();
hist.scale = model->Scale();
}
hist.uuid = snap.uuid;
}
m_history.push_back(std::move(hist));
@@ -391,6 +403,7 @@ void editor_mode::redo_last()
{
created->location(snap.position);
created->Angles(snap.rotation);
created->Scale(snap.scale);
m_node = created;
m_node->uuid = snap.uuid;
ui()->set_node(m_node);
@@ -410,6 +423,7 @@ void editor_mode::redo_last()
glm::vec3 cur = model->Angles();
glm::vec3 delta = snap.rotation - cur;
m_editor.rotate(target, delta, 0);
model->Scale(snap.scale);
}
m_node = target;
@@ -467,6 +481,9 @@ bool editor_mode::update()
simulation::is_ready = true;
// --- ImGuizmo: in-viewport transform gizmo for the selected node ---
render_gizmo();
// --- ImGui: Editor Settings & History windows ---
if(m_settings_open)
render_settings();
@@ -494,9 +511,144 @@ void editor_mode::render_settings()
EditorSettings.save();
}
ImGui::Separator();
ImGui::Checkbox("Transform gizmo (ImGuizmo)", &m_gizmo_enabled);
ImGui::End();
}
void editor_mode::render_gizmo()
{
if (!m_gizmo_enabled)
{
m_gizmo_using = false;
return;
}
// compact control window: lets the user pick the transform mode without keyboard shortcuts
ImGui::Begin("Gizmo", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing);
int op = static_cast<int>(m_gizmo_op);
ImGui::RadioButton("Translate (Q)", &op, static_cast<int>(gizmo_operation::translate));
ImGui::SameLine();
ImGui::RadioButton("Rotate (W)", &op, static_cast<int>(gizmo_operation::rotate));
ImGui::SameLine();
ImGui::RadioButton("Scale (E)", &op, static_cast<int>(gizmo_operation::scale));
m_gizmo_op = static_cast<gizmo_operation>(op);
if (m_gizmo_op != gizmo_operation::scale) // ImGuizmo always scales in local space
ImGui::Checkbox("Local space (R)", &m_gizmo_local);
if (m_gizmo_op == gizmo_operation::translate)
{
ImGui::SetNextItemWidth(120.0f);
ImGui::InputFloat("Snap (hold Ctrl)", &m_gizmo_snap);
if (m_gizmo_snap < 0.0f)
m_gizmo_snap = 0.0f;
}
if (!m_node)
ImGui::TextDisabled("No node selected");
ImGui::End();
if (!m_node)
{
m_gizmo_using = false;
return;
}
ImGuizmo::BeginFrame();
ImGuizmo::SetOrthographic(false);
ImGuiIO const &io = ImGui::GetIO();
ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y);
// the view matrix comes from the most recent color pass and is camera-relative
// (rotation only), so the gizmo is positioned relative to the camera as well.
glm::mat4 const view = GfxRenderer->Camera_View_Matrix();
glm::dvec3 const camerapos = GfxRenderer->Camera_Position();
// the engine's own projection bakes in reverse-Z (and screen orientation), which ImGuizmo
// doesn't expect; rebuild a clean, standard perspective that matches the rendered view.
// for the main viewport the engine uses a symmetric frustum with this exact fov/aspect.
float const fovy = glm::radians(Global.FieldOfView / Global.ZoomFactor);
float const aspect = (io.DisplaySize.y > 0.0f) ? (io.DisplaySize.x / io.DisplaySize.y) : 1.0f;
glm::mat4 const projection = glm::perspective(fovy, aspect, 0.1f, 10000.0f);
// rotation/scale are only meaningful for instanced models; other node types translate only
TAnimModel *model = dynamic_cast<TAnimModel *>(m_node);
glm::vec3 const relativepos = glm::vec3(m_node->location() - camerapos);
glm::vec3 const angles = model ? model->Angles() : glm::vec3(0.0f);
glm::vec3 const scalevec = model ? model->Scale() : glm::vec3(1.0f);
// build the gizmo model matrix from the node's current translation + rotation + scale
float const translation[3] = {relativepos.x, relativepos.y, relativepos.z};
float const rotation[3] = {angles.x, angles.y, angles.z};
float const scale[3] = {scalevec.x, scalevec.y, scalevec.z};
glm::mat4 matrix(1.0f);
ImGuizmo::RecomposeMatrixFromComponents(translation, rotation, scale, glm::value_ptr(matrix));
// map the editor's transform mode onto ImGuizmo; fall back to translate for non-models
ImGuizmo::OPERATION operation = ImGuizmo::TRANSLATE;
EditorSnapshot::Action action = EditorSnapshot::Action::Move;
if (model && m_gizmo_op == gizmo_operation::rotate)
{
operation = ImGuizmo::ROTATE;
action = EditorSnapshot::Action::Rotate;
}
else if (model && m_gizmo_op == gizmo_operation::scale)
{
operation = ImGuizmo::SCALE;
action = EditorSnapshot::Action::Scale;
}
ImGuizmo::MODE const mode = m_gizmo_local ? ImGuizmo::LOCAL : ImGuizmo::WORLD;
// optional snapping while Ctrl is held (metres / degrees / scale factor depending on mode)
glm::vec3 snapvalue(0.0f);
if (operation == ImGuizmo::TRANSLATE)
snapvalue = glm::vec3(m_gizmo_snap);
else if (operation == ImGuizmo::ROTATE)
snapvalue = glm::vec3(5.0f);
else
snapvalue = glm::vec3(0.1f);
float const *snap = (Global.ctrlState && snapvalue.x > 0.0f) ? glm::value_ptr(snapvalue) : nullptr;
ImGuizmo::Manipulate(glm::value_ptr(view), glm::value_ptr(projection),
operation, mode, glm::value_ptr(matrix), nullptr, snap);
if (ImGuizmo::IsUsing())
{
// record a single undo snapshot at the start of the drag
if (!m_gizmo_using)
{
push_snapshot(m_node, action);
m_gizmo_using = true;
}
float newtranslation[3], newrotation[3], newscale[3];
ImGuizmo::DecomposeMatrixToComponents(glm::value_ptr(matrix), newtranslation, newrotation, newscale);
if (operation == ImGuizmo::ROTATE && model)
{
// apply the rotation delta relative to the model's current orientation
glm::vec3 const newangles(newrotation[0], newrotation[1], newrotation[2]);
m_editor.rotate(model, newangles - model->Angles(), 0.0f);
}
else if (operation == ImGuizmo::SCALE && model)
{
model->Scale(glm::vec3(newscale[0], newscale[1], newscale[2]));
}
else
{
glm::dvec3 const newworldpos = camerapos + glm::dvec3(newtranslation[0], newtranslation[1], newtranslation[2]);
// pass Snaptoground == true so the gizmo's Y component is applied (free 3D move)
m_editor.translate(m_node, newworldpos, true);
}
}
else
{
m_gizmo_using = false;
}
}
void editor_mode::update_camera(double const Deltatime)
{
// account for keyboard-driven motion
@@ -510,7 +662,7 @@ void editor_mode::update_camera(double const Deltatime)
// smoothstep easing
double s = t * t * (3.0 - 2.0 * t);
Camera.Pos = glm::mix(m_focus_start_pos, m_focus_target_pos, s);
Camera.LookAt = glm::mix(m_focus_start_lookat, m_focus_target_lookat, s);
Camera.LookAt = glm::mix(m_focus_start_angle, m_focus_target_angle, s);
if (t >= 1.0)
m_focus_active = false;
}
@@ -561,6 +713,11 @@ void editor_mode::exit()
g_redo.clear();
m_history.clear();
// drop selection so a stale/dangling node pointer isn't used on the next editor session
m_node = nullptr;
m_gizmo_using = false;
ui()->set_node(nullptr);
Application.set_cursor((Global.ControlPicking ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED));
if (!Global.ControlPicking)
@@ -582,6 +739,25 @@ void editor_mode::on_key(int const Key, int const Scancode, int const Action, in
if (!anyModifier && m_userinterface->on_key(Key, Action))
return;
// gizmo transform shortcuts (Q/W/E/R) — only when the camera isn't being flown (RMB up).
// handled before the camera keyboard step because Q/W/E are also the fly-mode movement keys,
// which would otherwise consume them.
if (!anyModifier && is_press(Action)
&& m_input.mouse.button(GLFW_MOUSE_BUTTON_RIGHT) != GLFW_PRESS)
{
bool handled = true;
switch (Key)
{
case GLFW_KEY_Q: m_gizmo_op = gizmo_operation::translate; break;
case GLFW_KEY_W: m_gizmo_op = gizmo_operation::rotate; break;
case GLFW_KEY_E: m_gizmo_op = gizmo_operation::scale; break;
case GLFW_KEY_R: m_gizmo_local = !m_gizmo_local; break;
default: handled = false; break;
}
if (handled)
return;
}
// then internal input handling
if (m_input.keyboard.key(Key, Action))
return;
@@ -669,57 +845,9 @@ void editor_mode::on_key(int const Key, int const Scancode, int const Action, in
void editor_mode::on_cursor_pos(double const Horizontal, double const Vertical)
{
dvec2 const mousemove = dvec2{Horizontal, Vertical} - m_input.mouse.position();
// object transforms are handled by the gizmo now; here we only forward the cursor to the
// mouse input, which rotates the camera while the right mouse button is held (panning mode)
m_input.mouse.position(Horizontal, Vertical);
if (m_input.mouse.button(GLFW_MOUSE_BUTTON_LEFT) == GLFW_RELEASE)
return;
if (!m_node)
return;
if (m_takesnapshot)
{
// record appropriate action type depending on current input mode
if (mode_rotationX() || mode_rotationY() || mode_rotationZ())
push_snapshot(m_node, EditorSnapshot::Action::Rotate);
else
push_snapshot(m_node, EditorSnapshot::Action::Move);
m_takesnapshot = false;
}
if (mode_translation())
{
if (mode_translation_vertical())
{
float const translation = static_cast<float>(mousemove.y * -0.01);
m_editor.translate(m_node, translation);
}
else
{
auto mouseOffset = clamp_mouse_offset_to_max(GfxRenderer->Mouse_Position());
auto const mouseworldposition = Camera.Pos + mouseOffset;
m_editor.translate(m_node, mouseworldposition, mode_snap());
}
}
else if (mode_rotationY())
{
vec3 const rotation{0.0f, static_cast<float>(mousemove.x) * 0.25f, 0.0f};
float const quantization = (mode_snap() ? 5.0f : 0.0f);
m_editor.rotate(m_node, rotation, quantization);
}
else if (mode_rotationZ())
{
vec3 const rotation{0.0f, 0.0f, static_cast<float>(mousemove.x) * 0.25f};
float const quantization = (mode_snap() ? 5.0f : 0.0f);
m_editor.rotate(m_node, rotation, quantization);
}
else if (mode_rotationX())
{
vec3 const rotation{static_cast<float>(mousemove.y) * 0.25f, 0.0f, 0.0f};
float const quantization = (mode_snap() ? 5.0f : 0.0f);
m_editor.rotate(m_node, rotation, quantization);
}
}
void editor_mode::on_mouse_button(int const Button, int const Action, int const Mods)
@@ -811,6 +939,11 @@ void editor_mode::on_mouse_button(int const Button, int const Action, int const
m_dragging = false;
}
}
else if (Button == GLFW_MOUSE_BUTTON_RIGHT)
{
// game-engine style look: hide & grab the cursor while flying, restore it on release
Application.set_cursor(is_press(Action) ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
}
m_input.mouse.button(Button, Action);
}
@@ -852,7 +985,8 @@ void editor_mode::render_change_history(){
(s.action == EditorSnapshot::Action::Add) ? "ADD" :
(s.action == EditorSnapshot::Action::Delete) ? "DEL" :
(s.action == EditorSnapshot::Action::Move) ? "MOV" :
(s.action == EditorSnapshot::Action::Rotate) ? "ROT" : "OTH",
(s.action == EditorSnapshot::Action::Rotate) ? "ROT" :
(s.action == EditorSnapshot::Action::Scale) ? "SCA" : "OTH",
s.node_name.empty() ? "(noname)" : s.node_name.c_str(),
s.position.x, s.position.y, s.position.z);
@@ -889,7 +1023,20 @@ void editor_mode::render_change_history(){
void editor_mode::on_event_poll()
{
m_input.poll();
// game-engine style camera: WSAD/EQ only fly the camera while the right mouse button is held.
// when it's released the keyboard is free for gizmo shortcuts, and we flush a zero-movement
// command once so the camera doesn't keep coasting on the last velocity it was given.
bool const flying = (m_input.mouse.button(GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
if (flying)
{
m_input.poll();
}
else if (m_camera_flying)
{
m_camera_relay.post(user_command::movehorizontal, 0.0, 0.0, GLFW_PRESS, 0);
m_camera_relay.post(user_command::movevertical, 0.0, 0.0, GLFW_PRESS, 0);
}
m_camera_flying = flying;
}
bool editor_mode::is_command_processor() const
@@ -897,36 +1044,6 @@ bool editor_mode::is_command_processor() const
return false;
}
bool editor_mode::mode_translation() const
{
return (false == Global.altState);
}
bool editor_mode::mode_translation_vertical() const
{
return (true == Global.shiftState);
}
bool editor_mode::mode_rotationY() const
{
return ((true == Global.altState) && (false == Global.ctrlState) && (false == Global.shiftState));
}
bool editor_mode::mode_rotationX() const
{
return ((true == Global.altState) && (true == Global.ctrlState) && (false == Global.shiftState));
}
bool editor_mode::mode_rotationZ() const
{
return ((true == Global.altState) && (true == Global.ctrlState) && (true == Global.shiftState));
}
bool editor_mode::mode_snap() const
{
return ((false == Global.altState) && (true == Global.ctrlState) && (false == Global.shiftState));
}
bool editor_mode::focus_active()
{
return m_focus_active;

View File

@@ -75,7 +75,7 @@ class editor_mode : public application_mode
struct EditorSnapshot
{
enum class Action { Move, Rotate, Add, Delete, Other };
enum class Action { Move, Rotate, Scale, Add, Delete, Other };
Action action{Action::Other};
std::string node_name; // node identifier (basic_node::name())
@@ -84,6 +84,7 @@ class editor_mode : public application_mode
std::string serialized; // full text for recreate (used for Add/Delete)
glm::dvec3 position{0.0, 0.0, 0.0};
glm::vec3 rotation{0.0f, 0.0f, 0.0f};
glm::vec3 scale{1.0f, 1.0f, 1.0f};
UID uuid; // node UUID for reference, used as fallback lookup for deleted/recreated nodes
};
@@ -93,12 +94,6 @@ class editor_mode : public application_mode
std::vector<EditorSnapshot> g_redo;
// methods
void update_camera(double const Deltatime);
bool mode_translation() const;
bool mode_translation_vertical() const;
bool mode_rotationY() const;
bool mode_rotationX() const;
bool mode_rotationZ() const;
bool mode_snap() const;
editor_ui *ui() const;
void redo_last();
@@ -113,14 +108,14 @@ class editor_mode : public application_mode
static bool m_focus_active;
glm::dvec3 m_focus_start_pos{0.0,0.0,0.0};
glm::dvec3 m_focus_target_pos{0.0,0.0,0.0};
glm::dvec3 m_focus_start_lookat{0.0,0.0,0.0};
glm::dvec3 m_focus_target_lookat{0.0,0.0,0.0};
glm::vec3 m_focus_start_angle{0.0f}; // camera pitch/yaw/roll at focus start
glm::vec3 m_focus_target_angle{0.0f}; // camera pitch/yaw/roll facing the focused object
double m_focus_time{0.0};
double m_focus_duration{0.6};
double fTime50Hz{0.0}; // bufor czasu dla komunikacji z PoKeys
scene::basic_editor m_editor;
scene::basic_node *m_node; // currently selected scene node
scene::basic_node *m_node{nullptr}; // currently selected scene node
bool m_takesnapshot{true}; // helper, hints whether snapshot of selected node(s) should be taken before modification
bool m_dragging = false;
glm::dvec3 oldPos;
@@ -129,6 +124,10 @@ class editor_mode : public application_mode
static bool m_change_history;
static bool m_settings_open;
// camera fly-mode (right mouse button held); used to flush motion when it's released
command_relay m_camera_relay;
bool m_camera_flying{false};
// UI/history settings
int m_max_history_size{200};
int m_selected_history_idx{-1};
@@ -147,4 +146,13 @@ class editor_mode : public application_mode
void nullify_history_pointers(scene::basic_node *node);
void render_change_history();
void render_settings();
// ImGuizmo-based transform gizmo for the selected node
enum class gizmo_operation { translate, rotate, scale };
void render_gizmo();
bool m_gizmo_enabled{true}; // master switch for the in-viewport gizmo
bool m_gizmo_using{false}; // tracks an ongoing drag, so a single undo snapshot is taken per drag
bool m_gizmo_local{false}; // manipulate in the object's local space instead of world space
gizmo_operation m_gizmo_op{gizmo_operation::translate}; // current transform mode (translate/rotate/scale)
float m_gizmo_snap{1.0f}; // translation snap step (metres) applied while Ctrl is held
};

2735
imgui/ImGuizmo.cpp Normal file

File diff suppressed because it is too large Load Diff

213
imgui/ImGuizmo.h Normal file
View File

@@ -0,0 +1,213 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.83
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -------------------------------------------------------------------------------------------
// History :
// 2019/11/03 View gizmo
// 2016/09/11 Behind camera culling. Scaling Delta matrix not multiplied by source matrix scales. local/world rotation and translation fixed. Display message is incorrect (X: ... Y:...) in local mode.
// 2016/09/09 Hatched negative axis. Snapping. Documentation update.
// 2016/09/04 Axis switch and translation plan autohiding. Scale transform stability improved
// 2016/09/01 Mogwai changed to Manipulate. Draw debug cube. Fixed inverted scale. Mixing scale and translation/rotation gives bad results.
// 2016/08/31 First version
//
// -------------------------------------------------------------------------------------------
// Future (no order):
//
// - Multi view
// - display rotation/translation/scale infos in local/world space and not only local
// - finish local/world matrix application
// - OPERATION as bitmask
//
// -------------------------------------------------------------------------------------------
// Example
#if 0
void EditTransform(const Camera& camera, matrix_t& matrix)
{
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
if (ImGui::IsKeyPressed(90))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
if (ImGui::IsKeyPressed(69))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed(82)) // r Key
mCurrentGizmoOperation = ImGuizmo::SCALE;
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
ImGui::SameLine();
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
ImGui::SameLine();
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
mCurrentGizmoOperation = ImGuizmo::SCALE;
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale);
ImGui::InputFloat3("Tr", matrixTranslation, 3);
ImGui::InputFloat3("Rt", matrixRotation, 3);
ImGui::InputFloat3("Sc", matrixScale, 3);
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16);
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
{
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
mCurrentGizmoMode = ImGuizmo::LOCAL;
ImGui::SameLine();
if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
mCurrentGizmoMode = ImGuizmo::WORLD;
}
static bool useSnap(false);
if (ImGui::IsKeyPressed(83))
useSnap = !useSnap;
ImGui::Checkbox("", &useSnap);
ImGui::SameLine();
vec_t snap;
switch (mCurrentGizmoOperation)
{
case ImGuizmo::TRANSLATE:
snap = config.mSnapTranslation;
ImGui::InputFloat3("Snap", &snap.x);
break;
case ImGuizmo::ROTATE:
snap = config.mSnapRotation;
ImGui::InputFloat("Angle Snap", &snap.x);
break;
case ImGuizmo::SCALE:
snap = config.mSnapScale;
ImGui::InputFloat("Scale Snap", &snap.x);
break;
}
ImGuiIO& io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL);
}
#endif
#pragma once
#ifdef USE_IMGUI_API
#include "imconfig.h"
#endif
#ifndef IMGUI_API
#define IMGUI_API
#endif
namespace ImGuizmo
{
// call inside your own window and before Manipulate() in order to draw gizmo to that window.
// Or pass a specific ImDrawList to draw to (e.g. ImGui::GetForegroundDrawList()).
IMGUI_API void SetDrawlist(ImDrawList* drawlist = nullptr);
// call BeginFrame right after ImGui_XXXX_NewFrame();
IMGUI_API void BeginFrame();
// this is necessary because when imguizmo is compiled into a dll, and imgui into another
// globals are not shared between them.
// More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam
// expose method to set imgui context
IMGUI_API void SetImGuiContext(ImGuiContext* ctx);
// return true if mouse cursor is over any gizmo control (axis, plan or screen component)
IMGUI_API bool IsOver();
// return true if mouse IsOver or if the gizmo is in moving state
IMGUI_API bool IsUsing();
// enable/disable the gizmo. Stay in the state until next call to Enable.
// gizmo is rendered with gray half transparent color when disabled
IMGUI_API void Enable(bool enable);
// helper functions for manualy editing translation/rotation/scale with an input float
// translation, rotation and scale float points to 3 floats each
// Angles are in degrees (more suitable for human editing)
// example:
// float matrixTranslation[3], matrixRotation[3], matrixScale[3];
// ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale);
// ImGui::InputFloat3("Tr", matrixTranslation, 3);
// ImGui::InputFloat3("Rt", matrixRotation, 3);
// ImGui::InputFloat3("Sc", matrixScale, 3);
// ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
//
// These functions have some numerical stability issues for now. Use with caution.
IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale);
IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix);
IMGUI_API void SetRect(float x, float y, float width, float height);
// default is false
IMGUI_API void SetOrthographic(bool isOrthographic);
// Render a cube with face color corresponding to face normal. Usefull for debug/tests
IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount);
IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize);
// call it when you want a gizmo
// Needs view and projection matrices.
// matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional
// translation is applied in world space
enum OPERATION
{
TRANSLATE_X = (1u << 0),
TRANSLATE_Y = (1u << 1),
TRANSLATE_Z = (1u << 2),
ROTATE_X = (1u << 3),
ROTATE_Y = (1u << 4),
ROTATE_Z = (1u << 5),
ROTATE_SCREEN = (1u << 6),
SCALE_X = (1u << 7),
SCALE_Y = (1u << 8),
SCALE_Z = (1u << 9),
BOUNDS = (1u << 10),
TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z,
ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN,
SCALE = SCALE_X | SCALE_Y | SCALE_Z
};
inline OPERATION operator|(OPERATION lhs, OPERATION rhs)
{
return static_cast<OPERATION>(static_cast<int>(lhs) | static_cast<int>(rhs));
}
enum MODE
{
LOCAL,
WORLD
};
IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL);
//
// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
// other software are using the same mechanics. But just in case, you are now warned!
//
IMGUI_API void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
IMGUI_API void SetID(int id);
// return true if the cursor is over the operation's gizmo
IMGUI_API bool IsOver(OPERATION op);
IMGUI_API void SetGizmoSizeClipSpace(float value);
// Allow axis to flip
// When true (default), the guizmo axis flip for better visibility
// When false, they always stay along the positive world/local axis
IMGUI_API void AllowAxisFlip(bool value);
}

View File

@@ -25,6 +25,14 @@ editormouse_input::position( double Horizontal, double Vertical ) {
m_cursorposition.y = Vertical;
return;
}
if( true == m_pickmodepanning_resync ) {
// panning just started (cursor may have been grabbed/hidden); reset the reference so the
// first frame doesn't produce a large, jarring camera jump
m_cursorposition.x = Horizontal;
m_cursorposition.y = Vertical;
m_pickmodepanning_resync = false;
return;
}
glm::dvec2 cursorposition { Horizontal, Vertical };
auto const viewoffset = cursorposition - m_cursorposition;
m_relay.post(
@@ -47,7 +55,12 @@ editormouse_input::button( int const Button, int const Action ) {
// right button controls panning
if( Button == GLFW_MOUSE_BUTTON_RIGHT ) {
m_pickmodepanning = ( Action == GLFW_PRESS );
bool const panning = ( Action == GLFW_PRESS );
// when panning starts, request a one-frame resync so toggling the cursor grab doesn't jerk the view
if( panning && ( false == m_pickmodepanning ) ) {
m_pickmodepanning_resync = true;
}
m_pickmodepanning = panning;
}
}

View File

@@ -37,6 +37,7 @@ private:
// members
command_relay m_relay;
bool m_pickmodepanning { false }; // indicates mouse is in view panning mode
bool m_pickmodepanning_resync { false }; // skip the first delta after panning starts (avoids a jump when the cursor is grabbed/hidden)
glm::dvec2 m_cursorposition { 0.0 }; // stored last cursor position, used for panning
std::array<int, GLFW_MOUSE_BUTTON_LAST> m_buttons { GLFW_RELEASE };
};

View File

@@ -105,6 +105,12 @@ class opengl33_renderer : public gfx_renderer {
Pick_Node() const override { return m_picksceneryitem; }
glm::dvec3
Mouse_Position() const override { return m_worldmousecoordinates; }
glm::mat4
Camera_View_Matrix() const override { return glm::mat4( glm::mat3( m_colorpass.pass_camera.modelview() ) ); }
glm::mat4
Camera_Projection_Matrix() const override { return m_colorpass.pass_camera.projection(); }
glm::dvec3
Camera_Position() const override { return m_colorpass.pass_camera.position(); }
// maintenance methods
void
Update( double const Deltatime ) override;

View File

@@ -105,6 +105,12 @@ public:
Pick_Node() const override { return m_picksceneryitem; }
glm::dvec3
Mouse_Position() const override { return m_worldmousecoordinates; }
glm::mat4
Camera_View_Matrix() const override { return glm::mat4( glm::mat3( m_colorpass.camera.modelview() ) ); }
glm::mat4
Camera_Projection_Matrix() const override { return m_colorpass.camera.projection(); }
glm::dvec3
Camera_Position() const override { return m_colorpass.camera.position(); }
// maintenance methods
void
Update( double const Deltatime ) override;

View File

@@ -73,6 +73,13 @@ public:
virtual auto Pick_Node() const -> scene::basic_node const * = 0;
virtual auto Mouse_Position() const -> glm::dvec3 = 0;
// editor helpers: matrices/position of the most recent color pass camera.
// the view matrix is camera-relative (rotation only, camera at origin), matching the
// camera-relative rendering used by the engine; build object matrices relative to Camera_Position().
// default implementations return identity so backends without a usable camera still compile.
virtual auto Camera_View_Matrix() const -> glm::mat4 { return glm::mat4( 1.f ); }
virtual auto Camera_Projection_Matrix() const -> glm::mat4 { return glm::mat4( 1.f ); }
virtual auto Camera_Position() const -> glm::dvec3 { return glm::dvec3( 0.0 ); }
// maintenance methods
virtual void Update( double const Deltatime ) = 0;
virtual void Update_Pick_Control() = 0;