mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-23 06:33:57 -06:00
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:
parent
1558edf827
commit
345ab82f91
7 changed files with 173 additions and 87 deletions
|
@ -37,6 +37,7 @@ void Camera::set_type(EType type)
|
|||
{
|
||||
if (m_type != type && (type == EType::Ortho || type == EType::Perspective)) {
|
||||
m_type = type;
|
||||
m_prevent_auto_type = true;
|
||||
if (m_update_config_on_type_change_enabled) {
|
||||
wxGetApp().app_config->set_bool("use_perspective_camera", m_type == EType::Perspective);
|
||||
}
|
||||
|
@ -52,6 +53,20 @@ void Camera::select_next_type()
|
|||
set_type((EType)next);
|
||||
}
|
||||
|
||||
void Camera::auto_type(EType preferred_type)
|
||||
{
|
||||
if (!wxGetApp().app_config->get_bool("auto_perspective")) return;
|
||||
if (preferred_type == EType::Perspective) {
|
||||
if (!m_prevent_auto_type) {
|
||||
set_type(preferred_type);
|
||||
m_prevent_auto_type = false;
|
||||
}
|
||||
} else {
|
||||
set_type(preferred_type);
|
||||
m_prevent_auto_type = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::translate(const Vec3d& displacement) {
|
||||
if (!displacement.isApprox(Vec3d::Zero())) {
|
||||
m_view_matrix.translate(-displacement);
|
||||
|
@ -85,24 +100,41 @@ void Camera::set_zoom(double zoom)
|
|||
|
||||
void Camera::select_view(const std::string& direction)
|
||||
{
|
||||
if (direction == "iso")
|
||||
if (direction == "iso") {
|
||||
set_default_orientation();
|
||||
else if (direction == "left")
|
||||
auto_type(EType::Perspective);
|
||||
}
|
||||
else if (direction == "left") {
|
||||
look_at(m_target - m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ());
|
||||
else if (direction == "right")
|
||||
auto_type(EType::Ortho);
|
||||
}
|
||||
else if (direction == "right") {
|
||||
look_at(m_target + m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ());
|
||||
else if (direction == "top")
|
||||
auto_type(EType::Ortho);
|
||||
}
|
||||
else if (direction == "top") {
|
||||
look_at(m_target + m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY());
|
||||
else if (direction == "bottom")
|
||||
auto_type(EType::Ortho);
|
||||
}
|
||||
else if (direction == "bottom") {
|
||||
look_at(m_target - m_distance * Vec3d::UnitZ(), m_target, -Vec3d::UnitY());
|
||||
else if (direction == "front")
|
||||
auto_type(EType::Ortho);
|
||||
}
|
||||
else if (direction == "front") {
|
||||
look_at(m_target - m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
|
||||
else if (direction == "rear")
|
||||
auto_type(EType::Ortho);
|
||||
}
|
||||
else if (direction == "rear") {
|
||||
look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
|
||||
else if (direction == "topfront")
|
||||
auto_type(EType::Ortho);
|
||||
}
|
||||
else if (direction == "topfront") {
|
||||
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
|
||||
auto_type(EType::Perspective);
|
||||
}
|
||||
else if (direction == "plate") {
|
||||
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
|
||||
auto_type(EType::Perspective);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ struct Camera
|
|||
int requires_zoom_to_plate{ REQUIRES_ZOOM_TO_PLATE_IDLE };
|
||||
|
||||
private:
|
||||
bool m_prevent_auto_type = true;
|
||||
EType m_type{ EType::Perspective };
|
||||
bool m_update_config_on_type_change_enabled{ false };
|
||||
Vec3d m_target{ Vec3d::Zero() };
|
||||
|
@ -65,6 +66,7 @@ public:
|
|||
// valid values for type: "false" -> ortho, "true" -> perspective
|
||||
void set_type(const std::string& type) { set_type((type == "true") ? EType::Perspective : EType::Ortho); }
|
||||
void select_next_type();
|
||||
void auto_type(EType preferred_type);
|
||||
|
||||
void enable_update_config_on_type_change(bool enable) { m_update_config_on_type_change_enabled = enable; }
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2676,10 +2676,19 @@ void MainFrame::init_menubar_as_editor()
|
|||
wxGetApp().app_config->set_bool("use_perspective_camera", false);
|
||||
wxGetApp().update_ui_from_settings();
|
||||
}, nullptr);
|
||||
if (wxGetApp().app_config->get("use_perspective_camera").compare("true") == 0)
|
||||
viewMenu->Check(wxID_CAMERA_PERSPECTIVE + camera_id_base, true);
|
||||
else
|
||||
viewMenu->Check(wxID_CAMERA_ORTHOGONAL + camera_id_base, true);
|
||||
this->Bind(wxEVT_UPDATE_UI, [viewMenu, camera_id_base](wxUpdateUIEvent& evt) {
|
||||
if (wxGetApp().app_config->get("use_perspective_camera").compare("true") == 0)
|
||||
viewMenu->Check(wxID_CAMERA_PERSPECTIVE + camera_id_base, true);
|
||||
else
|
||||
viewMenu->Check(wxID_CAMERA_ORTHOGONAL + camera_id_base, true);
|
||||
}, wxID_ANY);
|
||||
append_menu_check_item(viewMenu, wxID_ANY, _L("Auto Perspective"), _L("Automatically switch between orthographic and perspective when changing from top/bottom/side views"),
|
||||
[this](wxCommandEvent&) {
|
||||
wxGetApp().app_config->set_bool("auto_perspective", !wxGetApp().app_config->get_bool("auto_perspective"));
|
||||
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
|
||||
},
|
||||
this, [this]() { return m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview; },
|
||||
[this]() { return wxGetApp().app_config->get_bool("auto_perspective"); }, this);
|
||||
|
||||
viewMenu->AppendSeparator();
|
||||
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &G-code Window") + "\tC", _L("Show g-code window in Preview scene"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue