QoL: Add auto perspective (#8312)

* ImGuizmo: Comment out unused code

* 3DNav: Avoid gimbal lock by using polar coordinates

* 3DNav: Make sure top and bottom are oriented correctly

* Add auto perspective

* Add options

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
Noisyfox 2025-03-09 14:04:46 +08:00 committed by GitHub
parent 1558edf827
commit 345ab82f91
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 173 additions and 87 deletions

View file

@ -3934,6 +3934,7 @@ void GLCanvas3D::on_gesture(wxGestureEvent &evt)
camera.rotate_on_sphere_with_target(-rotate, 0, rotate_limit, plate->get_bounding_box().center());
else
camera.rotate_on_sphere(-rotate, 0, rotate_limit);
camera.auto_type(Camera::EType::Perspective);
}
m_dirty = true;
}
@ -4297,10 +4298,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// Orca: Sphere rotation for painting view
// if dragging over blank area with left button, rotate
if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) {
Camera& camera = wxGetApp().plater()->get_camera();
const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.);
if (this->m_canvas_type == ECanvasType::CanvasAssembleView || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports ||
m_gizmos.get_current_type() == GLGizmosManager::Seam || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) {
Camera& camera = wxGetApp().plater()->get_camera();
Vec3d rotate_target = Vec3d::Zero();
if (!m_selection.is_empty())
rotate_target = m_selection.get_bounding_box().center();
@ -4311,14 +4312,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else {
if (wxGetApp().app_config->get_bool("use_free_camera"))
// Virtual track ball (similar to the 3DConnexion mouse).
wxGetApp().plater()->get_camera().rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.));
camera.rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.));
else {
// Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation.
// It is cheaper to call this function right away instead of testing wxGetApp().plater()->get_mouse3d_controller().connected(),
// which checks an atomics (flushes CPU caches).
// See GH issue #3816.
Camera& camera = wxGetApp().plater()->get_camera();
bool rotate_limit = current_printer_technology() != ptSLA;
camera.recover_from_free_camera();
@ -4352,6 +4351,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
}
camera.auto_type(Camera::EType::Perspective);
m_dirty = true;
}
@ -5687,7 +5687,6 @@ bool GLCanvas3D::_render_arrange_menu(float left, float right, float bottom, flo
return settings_changed;
}
static float identityMatrix[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
static const float cameraProjection[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
void GLCanvas3D::_render_3d_navigator()
@ -5697,7 +5696,6 @@ void GLCanvas3D::_render_3d_navigator()
}
ImGuizmo::BeginFrame();
ImGuizmo::AllowAxisFlip(false);
auto& style = ImGuizmo::GetStyle();
style.Colors[ImGuizmo::COLOR::DIRECTION_X] = ImGuiWrapper::to_ImVec4(ColorRGBA::Y());
@ -5725,7 +5723,6 @@ void GLCanvas3D::_render_3d_navigator()
const float viewManipulateLeft = 0;
const float viewManipulateTop = io.DisplaySize.y;
const float camDistance = 8.f;
ImGuizmo::SetID(0);
Camera& camera = wxGetApp().plater()->get_camera();
Transform3d m = Transform3d::Identity();
@ -5741,11 +5738,11 @@ void GLCanvas3D::_render_3d_navigator()
}
const float size = 128 * sc;
const bool dirty = ImGuizmo::ViewManipulate(cameraView, cameraProjection, ImGuizmo::OPERATION::ROTATE, ImGuizmo::MODE::WORLD,
identityMatrix, camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size),
ImVec2(size, size), 0x00101010);
const auto result = ImGuizmo::ViewManipulate(cameraView, cameraProjection, ImGuizmo::OPERATION::ROTATE, ImGuizmo::MODE::WORLD, nullptr,
camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size), ImVec2(size, size),
0x00101010);
if (dirty) {
if (result.changed) {
for (unsigned int c = 0; c < 4; ++c) {
for (unsigned int r = 0; r < 4; ++r) {
m(r, c) = cameraView[c * 4 + r];
@ -5755,6 +5752,25 @@ void GLCanvas3D::_render_3d_navigator()
m = m * (coord_mapping_transform.inverse());
camera.set_rotation(m);
if (result.dragging) {
// Switch back to perspective view when normal dragging
camera.auto_type(Camera::EType::Perspective);
} else if (result.clicked_box >= 0) {
switch (result.clicked_box) {
case 4: // back
case 10: // top
case 12: // right
case 14: // left
case 16: // bottom
case 22: // front
// Automatically switch to orthographic view when click the center face of the navigator
camera.auto_type(Camera::EType::Ortho); break;
default:
// Otherwise switch back to perspective view
camera.auto_type(Camera::EType::Perspective); break;
}
}
request_extra_frame();
}
}