353 lines
11 KiB
C++
Executable File
353 lines
11 KiB
C++
Executable File
#include "ViewportGizmo.hpp"
|
|
#include "noggit/ModelInstance.h"
|
|
#include "noggit/WMOInstance.h"
|
|
#include "noggit/ActionManager.hpp"
|
|
#include "noggit/Action.hpp"
|
|
#include "external/glm/glm.hpp"
|
|
#include <external/glm/gtx/matrix_decompose.hpp>
|
|
#include <external/glm/gtc/type_ptr.hpp>
|
|
#include <external/glm/gtc/quaternion.hpp>
|
|
#include <external/glm/gtx/string_cast.hpp>
|
|
#include <noggit/MapView.h>
|
|
#include <noggit/application/NoggitApplication.hpp>
|
|
|
|
#include <limits>
|
|
|
|
|
|
using namespace Noggit::Ui::Tools::ViewportGizmo;
|
|
|
|
ViewportGizmo::ViewportGizmo(Noggit::Ui::Tools::ViewportGizmo::GizmoContext gizmo_context, World* world)
|
|
: _gizmo_context(gizmo_context)
|
|
, _world(world)
|
|
{}
|
|
|
|
void ViewportGizmo::handleTransformGizmo(MapView* map_view
|
|
, const std::vector<selection_type>& selection
|
|
, glm::mat4x4 const& model_view
|
|
, glm::mat4x4 const& projection)
|
|
{
|
|
|
|
if (!isUsing())
|
|
{
|
|
_last_pivot_scale = 1.f;
|
|
}
|
|
|
|
GizmoInternalMode gizmo_selection_type;
|
|
|
|
auto model_view_trs = model_view;
|
|
auto projection_trs = projection;
|
|
|
|
int n_selected = static_cast<int>(selection.size());
|
|
|
|
if (!n_selected || (n_selected == 1 && selection[0].index() != eEntry_Object))
|
|
return;
|
|
|
|
if (n_selected == 1)
|
|
{
|
|
gizmo_selection_type = std::get<selected_object_type>(selection[0])->which() == eMODEL ? GizmoInternalMode::MODEL : GizmoInternalMode::WMO;
|
|
}
|
|
else
|
|
{
|
|
gizmo_selection_type = GizmoInternalMode::MULTISELECTION;
|
|
}
|
|
|
|
SceneObject* obj_instance;
|
|
|
|
ImGuizmo::SetID(_gizmo_context);
|
|
|
|
ImGuizmo::SetDrawlist();
|
|
|
|
ImGuizmo::SetOrthographic(false);
|
|
ImGuizmo::SetScaleGizmoAxisLock(true);
|
|
ImGuizmo::BeginFrame();
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
|
|
|
|
glm::mat4x4 delta_matrix = glm::mat4x4(1.0f);
|
|
glm::mat4x4 object_matrix = glm::mat4x4(1.0f);
|
|
glm::mat4x4 pivot_matrix = glm::translate(glm::mat4x4(1.f),
|
|
{_multiselection_pivot.x,
|
|
_multiselection_pivot.y,
|
|
_multiselection_pivot.z });
|
|
float last_pivot_scale = 1.f;
|
|
|
|
switch (gizmo_selection_type)
|
|
{
|
|
case MODEL:
|
|
case WMO:
|
|
{
|
|
obj_instance = std::get<selected_object_type>(selection[0]);
|
|
obj_instance->ensureExtents();
|
|
object_matrix = obj_instance->transformMatrix();
|
|
ImGuizmo::Manipulate(glm::value_ptr(model_view_trs), glm::value_ptr(projection_trs), _gizmo_operation, _gizmo_mode, glm::value_ptr(object_matrix), glm::value_ptr(delta_matrix), nullptr);
|
|
break;
|
|
}
|
|
case MULTISELECTION:
|
|
{
|
|
if (isUsing())
|
|
_last_pivot_scale = ImGuizmo::GetOperationScaleLast();
|
|
|
|
ImGuizmo::Manipulate(glm::value_ptr(model_view_trs), glm::value_ptr(projection_trs), _gizmo_operation, _gizmo_mode, glm::value_ptr(pivot_matrix), glm::value_ptr(delta_matrix), nullptr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isUsing())
|
|
{
|
|
return;
|
|
}
|
|
|
|
glm::mat4 glm_transform_mat = delta_matrix;
|
|
|
|
glm::vec3 new_scale;
|
|
glm::quat new_orientation;
|
|
glm::vec3 new_translation;
|
|
glm::vec3 new_skew_;
|
|
glm::vec4 new_perspective_;
|
|
|
|
glm::decompose(glm_transform_mat,
|
|
new_scale,
|
|
new_orientation,
|
|
new_translation,
|
|
new_skew_,
|
|
new_perspective_
|
|
);
|
|
|
|
new_orientation = glm::conjugate(new_orientation);
|
|
|
|
// if nothing was changed, just return early
|
|
switch (_gizmo_operation)
|
|
{
|
|
case ImGuizmo::TRANSLATE:
|
|
{
|
|
if (new_translation.x == 0.0f && new_translation.y == 0.0f && new_translation.z == 0.0f)
|
|
return;
|
|
break;
|
|
}
|
|
case ImGuizmo::ROTATE:
|
|
{
|
|
|
|
if (new_orientation.x == -0.0f && new_orientation.y == -0.0f && new_orientation.z == -0.0f)
|
|
return;
|
|
break;
|
|
}
|
|
case ImGuizmo::SCALE:
|
|
{
|
|
if (new_scale.x == 1.0f && new_scale.y == 1.0f && new_scale.z == 1.0f)
|
|
return;
|
|
break;
|
|
}
|
|
case ImGuizmo::BOUNDS:
|
|
{
|
|
throw std::logic_error("Bounds are not supported by this gizmo.");
|
|
}
|
|
}
|
|
|
|
// Clamp scale to allowed values
|
|
new_scale.x = std::clamp(new_scale.x, SceneObject::min_scale(), SceneObject::max_scale());
|
|
new_scale.y = std::clamp(new_scale.y, SceneObject::min_scale(), SceneObject::max_scale());
|
|
new_scale.z = std::clamp(new_scale.z, SceneObject::min_scale(), SceneObject::max_scale());
|
|
|
|
NOGGIT_ACTION_MGR->beginAction(map_view, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
|
|
Noggit::ActionModalityControllers::eLMB);
|
|
|
|
bool modern_features = Noggit::Application::NoggitApplication::instance()->getConfiguration()->modern_features;
|
|
|
|
if (gizmo_selection_type == MULTISELECTION)
|
|
{
|
|
|
|
for (auto& selected : selection)
|
|
{
|
|
|
|
if (selected.index() != eEntry_Object)
|
|
continue;
|
|
|
|
obj_instance = std::get<selected_object_type>(selected);
|
|
NOGGIT_CUR_ACTION->registerObjectTransformed(obj_instance);
|
|
|
|
obj_instance->ensureExtents();
|
|
object_matrix = obj_instance->transformMatrix();
|
|
|
|
glm::vec3& pos = obj_instance->pos;
|
|
math::degrees::vec3& rotation = obj_instance->dir;
|
|
float& scale = obj_instance->scale;
|
|
|
|
// If modern features are disabled, we don't want to scale WMOs
|
|
if (obj_instance->which() == eWMO && !modern_features && _gizmo_operation == ImGuizmo::SCALE)
|
|
{
|
|
scale = 1.0f;
|
|
continue;
|
|
}
|
|
|
|
if (_world)
|
|
_world->updateTilesEntry(selected, model_update::remove);
|
|
|
|
switch (_gizmo_operation)
|
|
{
|
|
case ImGuizmo::TRANSLATE:
|
|
{
|
|
pos += glm::vec3(new_translation.x, new_translation.y, new_translation.z);
|
|
break;
|
|
}
|
|
case ImGuizmo::ROTATE:
|
|
{
|
|
auto rot_euler = glm::degrees(glm::eulerAngles(new_orientation).operator*=(-1.f));
|
|
auto rot_euler_pivot = glm::eulerAngles(new_orientation);
|
|
|
|
if (!_use_multiselection_pivot)
|
|
{
|
|
rotation += glm::vec3(math::degrees(rot_euler.x)._, math::degrees(rot_euler.y)._, math::degrees(rot_euler.z)._);
|
|
}
|
|
else
|
|
{
|
|
//LogDebug << rot_euler.x << " " << rot_euler.y << " " << rot_euler.z << std::endl;
|
|
|
|
/*float alpha = math::degrees(rot_euler.x)._;
|
|
float beta = math::degrees(rot_euler.y)._;
|
|
float gamma = math::degrees(rot_euler.z)._;
|
|
|
|
glm::mat3 rotation_matrix = glm::mat3(
|
|
cos(beta)*cos(gamma), -cos(beta)*sin(gamma), sin(beta),
|
|
cos(alpha)*sin(gamma) + cos(gamma)*sin(alpha)*sin(beta), cos(alpha)*cos(gamma) - sin(alpha)*sin(beta)*sin(gamma), -cos(beta)*sin(alpha),
|
|
sin(alpha)*sin(gamma) - cos(alpha)*cos(gamma)*sin(beta), cos(gamma)*sin(alpha) + cos(alpha)*sin(beta)*sin(gamma), cos(alpha)*cos(beta)
|
|
);
|
|
|
|
rotation.x += atan(-rotation_matrix[1].z / rotation_matrix[2].z);
|
|
rotation.z += atan(-rotation_matrix[0].y / rotation_matrix[0].x);*/
|
|
|
|
float beta = math::degrees(rot_euler.y)._;
|
|
rotation.y += atan(sin(beta) / (sqrt(1 - pow(sin(beta), 2))));
|
|
|
|
// building model matrix
|
|
glm::mat4 model_transform = object_matrix;
|
|
|
|
// only translation of pivot
|
|
glm::mat4 transformed_pivot = pivot_matrix;
|
|
|
|
// model matrix relative to translated pivot
|
|
glm::mat4 model_transform_rel = glm::inverse(transformed_pivot) * model_transform;
|
|
|
|
// rotate multiselection in same direction as user want
|
|
glm::mat4 gizmo_rotation = glm::mat4_cast(glm::quat(new_orientation.w, glm::eulerAngles(new_orientation).operator*=(-1.f)));
|
|
|
|
glm::mat4 _transformed_pivot_rot = transformed_pivot * gizmo_rotation;
|
|
|
|
// apply transform to model matrix
|
|
glm::mat4 result_matrix = _transformed_pivot_rot * model_transform_rel;
|
|
|
|
glm::vec3 rot_result_scale;
|
|
glm::quat rot_result_orientation;
|
|
glm::vec3 rot_result_translation;
|
|
glm::vec3 rot_result_skew_;
|
|
glm::vec4 rot_result_perspective_;
|
|
|
|
glm::decompose(result_matrix,
|
|
rot_result_scale,
|
|
rot_result_orientation,
|
|
rot_result_translation,
|
|
rot_result_skew_,
|
|
rot_result_perspective_
|
|
);
|
|
|
|
rot_result_orientation = glm::conjugate(rot_result_orientation);
|
|
|
|
auto rot_result_orientation_euler = glm::degrees(glm::eulerAngles(rot_result_orientation));
|
|
|
|
pos = {rot_result_translation.x, rot_result_translation.y, rot_result_translation.z};
|
|
//rotation = {rot_result_orientation_euler.x, rot_result_orientation_euler.y, rot_result_orientation_euler.z};
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ImGuizmo::SCALE:
|
|
{
|
|
scale = std::max(0.001f, scale * (new_scale.x / _last_pivot_scale));
|
|
break;
|
|
}
|
|
case ImGuizmo::BOUNDS:
|
|
{
|
|
throw std::logic_error("Bounds are not supported by this gizmo.");
|
|
break;
|
|
}
|
|
}
|
|
obj_instance->recalcExtents();
|
|
|
|
if (_world)
|
|
_world->updateTilesEntry(selected, model_update::add);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto& selected : selection)
|
|
{
|
|
if (selected.index() != eEntry_Object)
|
|
continue;
|
|
|
|
obj_instance = std::get<selected_object_type>(selected);
|
|
NOGGIT_CUR_ACTION->registerObjectTransformed(obj_instance);
|
|
|
|
obj_instance->ensureExtents();
|
|
object_matrix = obj_instance->transformMatrix();
|
|
|
|
glm::vec3& pos = obj_instance->pos;
|
|
math::degrees::vec3& rotation = obj_instance->dir;
|
|
float& scale = obj_instance->scale;
|
|
|
|
// If modern features are disabled, we don't want to scale WMOs
|
|
if (obj_instance->which() == eWMO && !modern_features && _gizmo_operation == ImGuizmo::SCALE)
|
|
{
|
|
scale = 1.0f;
|
|
continue;
|
|
}
|
|
|
|
if (_world)
|
|
_world->updateTilesEntry(selected, model_update::remove);
|
|
|
|
switch (_gizmo_operation)
|
|
{
|
|
|
|
case ImGuizmo::TRANSLATE:
|
|
{
|
|
pos += glm::vec3(new_translation.x, new_translation.y, new_translation.z);
|
|
break;
|
|
}
|
|
case ImGuizmo::ROTATE:
|
|
{
|
|
auto rot_euler = glm::eulerAngles(new_orientation).operator*=(-1.f) * 57.2957795f;
|
|
rotation += glm::vec3(math::degrees(rot_euler.x)._, math::degrees(rot_euler.y)._, math::degrees(rot_euler.z)._);
|
|
break;
|
|
}
|
|
case ImGuizmo::SCALE:
|
|
{
|
|
scale = std::max(0.001f, new_scale.x);
|
|
break;
|
|
}
|
|
case ImGuizmo::BOUNDS:
|
|
{
|
|
throw std::logic_error("Bounds are not supported by this gizmo.");
|
|
}
|
|
}
|
|
|
|
obj_instance->normalizeDirection();
|
|
|
|
obj_instance->recalcExtents();
|
|
|
|
|
|
if (map_view)
|
|
{
|
|
emit map_view->rotationChanged();
|
|
}
|
|
|
|
if (_world)
|
|
_world->updateTilesEntry(selected, model_update::add);
|
|
}
|
|
}
|
|
if (_world)
|
|
_world->update_selected_model_groups();
|
|
|
|
_world->update_selection_pivot();
|
|
}
|
|
|