diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 341bfd4322..00fac429da 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -24,8 +24,7 @@ double Camera::FrustrumMinZRange = 50.0; double Camera::FrustrumMinNearZ = 100.0; double Camera::FrustrumZMargin = 10.0; double Camera::MaxFovDeg = 60.0; -double Camera::SingleBedSceneBoxScaleFactor = 1.5; -double Camera::MultipleBedSceneBoxScaleFactor = 4.0; +double Camera::SceneBoxScaleFactor = 2.5; std::string Camera::get_type_as_string() const { @@ -67,10 +66,20 @@ void Camera::set_target(const Vec3d& target) Transform3d inv_view_matrix = m_view_matrix.inverse(); inv_view_matrix.translation() = m_target - m_distance * get_dir_forward(); m_view_matrix = inv_view_matrix.inverse(); - m_rotation_pivot = m_target; } } +BoundingBoxf3 Camera::get_target_validation_box() const +{ + const Vec3d center = m_scene_box.center(); + Vec3d size = m_scene_box.size(); + size.x() *= m_scene_box_scale_factor; + size.y() *= m_scene_box_scale_factor; + size.z() *= 1.5; + const Vec3d half_size = 0.5 * size; + return BoundingBoxf3(center - half_size, center + half_size); +} + void Camera::set_zoom(double zoom) { // Don't allow to zoom too far outside the scene. @@ -267,6 +276,8 @@ void Camera::apply_projection(double left, double right, double bottom, double t void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor) { + m_distance = DefaultDistance; + // Calculate the zoom factor needed to adjust the view around the given box. const double zoom = calc_zoom_to_bounding_box_factor(box, margin_factor); if (zoom > 0.0) { @@ -278,6 +289,8 @@ void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor) void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor) { + m_distance = DefaultDistance; + Vec3d center; const double zoom = calc_zoom_to_volumes_factor(volumes, center, margin_factor); if (zoom > 0.0) { @@ -301,7 +314,6 @@ void Camera::debug_render() const Vec3f position = get_position().cast(); Vec3f target = m_target.cast(); - Vec3f pivot = m_rotation_pivot.cast(); float distance = (float)get_distance(); float zenit = (float)m_zenit; Vec3f forward = get_dir_forward().cast(); @@ -319,7 +331,6 @@ void Camera::debug_render() const ImGui::Separator(); ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); - ImGui::InputFloat3("Pivot", pivot.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat("Distance", &distance, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::Separator(); ImGui::InputFloat("Zenit", &zenit, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); @@ -356,31 +367,11 @@ void Camera::rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad, b } } - const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_rotation_pivot; + const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target; const auto rot_z = Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ()); m_view_rotation *= rot_z * Eigen::AngleAxisd(delta_zenit_rad, rot_z.inverse() * get_dir_right()); m_view_rotation.normalize(); - m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-m_rotation_pivot) + translation, m_view_rotation, Vec3d(1., 1., 1.)); - - // adjust target from position - const Vec3d pivot = m_rotation_pivot; - if (!m_target.isApprox(pivot)) { - const Vec3d position = get_position(); - const Vec3d forward = get_dir_forward(); - const Vec3d new_target = position + m_distance * forward; - if (!is_target_valid()) { - const float forward_dot_unitZ = forward.dot(Vec3d::UnitZ()); - if (std::abs(forward_dot_unitZ) > EPSILON) { - const float dist_to_xy = -position.dot(Vec3d::UnitZ()) / forward_dot_unitZ; - set_target(position + dist_to_xy * forward); - } - else - set_target(new_target); - } - else - set_target(new_target); - set_rotation_pivot(pivot); - } + m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.)); } // Virtual trackball, rotate around an axis, where the eucledian norm of the axis gives the rotation angle in radians. @@ -402,8 +393,6 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo std::pair ret; auto& [near_z, far_z] = ret; - set_distance(DefaultDistance); - // box in eye space const BoundingBoxf3 eye_box = box.transformed(m_view_matrix); near_z = -eye_box.max.z(); @@ -591,21 +580,6 @@ void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up update_zenit(); } -bool Camera::is_target_valid() const -{ - if (m_target.isApprox(m_rotation_pivot)) - return true; - - const Vec3d box_size = m_scene_box.size(); - BoundingBoxf3 test_box = m_scene_box; - const Vec3d scene_box_center = m_scene_box.center(); - test_box.translate(-scene_box_center); - test_box.scale(m_scene_box_scale_factor); - test_box.translate(scene_box_center); - test_box.offset(EPSILON); - return test_box.contains(get_position() + m_distance * get_dir_forward()); -} - void Camera::set_default_orientation() { m_zenit = 45.0f; @@ -620,15 +594,10 @@ void Camera::set_default_orientation() Vec3d Camera::validate_target(const Vec3d& target) const { - BoundingBoxf3 test_box = m_scene_box; - test_box.translate(-m_scene_box.center()); - // We may let this factor be customizable - test_box.scale(m_scene_box_scale_factor); - test_box.translate(m_scene_box.center()); - - return { std::clamp(target(0), test_box.min.x(), test_box.max.x()), - std::clamp(target(1), test_box.min.y(), test_box.max.y()), - std::clamp(target(2), test_box.min.z(), test_box.max.z())}; + const BoundingBoxf3 test_box = get_target_validation_box(); + return { std::clamp(target.x(), test_box.min.x(), test_box.max.x()), + std::clamp(target.y(), test_box.min.y(), test_box.max.y()), + std::clamp(target.z(), test_box.min.z(), test_box.max.z())}; } void Camera::update_zenit() diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 2729474d60..af3b243999 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -17,8 +17,7 @@ struct Camera static const double DefaultDistance; static const double DefaultZoomToBoxMarginFactor; static const double DefaultZoomToVolumesMarginFactor; - static double SingleBedSceneBoxScaleFactor; - static double MultipleBedSceneBoxScaleFactor; + static double SceneBoxScaleFactor; static double FrustrumMinZRange; static double FrustrumMinNearZ; static double FrustrumZMargin; @@ -38,7 +37,6 @@ private: EType m_type{ EType::Perspective }; bool m_update_config_on_type_change_enabled{ false }; Vec3d m_target{ Vec3d::Zero() }; - Vec3d m_rotation_pivot{ Vec3d::Zero() }; float m_zenit{ 45.0f }; double m_zoom{ 1.0 }; // Distance between camera position and camera target measured along the camera Z axis @@ -51,7 +49,7 @@ private: Eigen::Quaterniond m_view_rotation{ 1.0, 0.0, 0.0, 0.0 }; Transform3d m_projection_matrix{ Transform3d::Identity() }; std::pair m_frustrum_zs; - double m_scene_box_scale_factor{ SingleBedSceneBoxScaleFactor }; + double m_scene_box_scale_factor{ SceneBoxScaleFactor }; BoundingBoxf3 m_scene_box; @@ -70,11 +68,7 @@ public: const Vec3d& get_target() const { return m_target; } void set_target(const Vec3d& target); - void set_scene_box_scale_factor(float factor) { m_scene_box_scale_factor = factor; } - double get_scene_box_scale_factor() const { return m_scene_box_scale_factor; } - - const Vec3d& get_rotation_pivot() const { return m_rotation_pivot; } - void set_rotation_pivot(const Vec3d& pivot) { m_rotation_pivot = pivot; } + BoundingBoxf3 get_target_validation_box() const; double get_distance() const { return (get_position() - m_target).norm(); } double get_gui_scale() const { return m_gui_scale; } @@ -151,7 +145,7 @@ public: double max_zoom() const { return 250.0; } double min_zoom() const { return 0.7 * calc_zoom_to_bounding_box_factor(m_scene_box); } - bool is_target_valid() const; + void set_distance(double distance); private: // returns tight values for nearZ and farZ plane around the given bounding box @@ -159,7 +153,6 @@ private: std::pair calc_tight_frustrum_zs_around(const BoundingBoxf3& box); double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor) const; double calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& center, double margin_factor = DefaultZoomToVolumesMarginFactor) const; - void set_distance(double distance); void set_default_orientation(); Vec3d validate_target(const Vec3d& target) const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9533319ca2..51d56fa996 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1869,8 +1869,6 @@ void GLCanvas3D::render() // and the viewport was set incorrectly, leading to tripping glAsserts further down // the road (in apply_projection). That's why the minimum size is forced to 10. Camera& camera = wxGetApp().plater()->get_camera(); - camera.set_scene_box_scale_factor((s_multiple_beds.get_number_of_beds() > 1) ? - Camera::MultipleBedSceneBoxScaleFactor : Camera::SingleBedSceneBoxScaleFactor); camera.set_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height())); camera.apply_viewport(); @@ -1886,7 +1884,6 @@ void GLCanvas3D::render() if (m_last_active_bed_id != curr_active_bed_id) { const Vec3d bed_offset = s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()); const Vec2d bed_center = m_bed.build_volume().bed_center() + Vec2d(bed_offset.x(), bed_offset.y()); - camera.set_rotation_pivot({ bed_center.x(), bed_center.y(), 0.0f }); m_last_active_bed_id = curr_active_bed_id; } @@ -1961,7 +1958,6 @@ void GLCanvas3D::render() #if ENABLE_SHOW_CAMERA_TARGET _render_camera_target(); - _render_camera_pivot(); _render_camera_target_validation_box(); #endif // ENABLE_SHOW_CAMERA_TARGET @@ -3858,8 +3854,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_mouse.is_start_position_2D_defined()) { // get point in model space at Z = 0 const float z = 0.0f; - const Vec3d cur_pos = _mouse_to_3d(pos, &z); - const Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z); + const Vec3d cur_pos = _mouse_to_3d(pos, &z, true); + const Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z, true); if (!wxGetApp().app_config->get_bool("use_free_camera")) // 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(), @@ -3881,6 +3877,25 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (evt.LeftUp() && m_sequential_print_clearance.is_dragging()) m_sequential_print_clearance.stop_dragging(); + if (evt.RightUp() && m_mouse.is_start_position_2D_defined()) { + // forces camera target to be on the plane z = 0 + Camera& camera = wxGetApp().plater()->get_camera(); + if (std::abs(camera.get_dir_forward().dot(Vec3d::UnitZ())) > EPSILON) { + const Vec3d old_pos = camera.get_position(); + const double old_distance = camera.get_distance(); + const Vec3d old_target = camera.get_target(); + const Linef3 ray(old_pos, old_target); + const Vec3d new_target = ray.intersect_plane(0.0); + const BoundingBoxf3 validation_box = camera.get_target_validation_box(); + if (validation_box.contains(new_target)) { + const double new_distance = (new_target - old_pos).norm(); + camera.set_target(new_target); + camera.set_distance(new_distance); + if (camera.get_type() == Camera::EType::Perspective) + camera.set_zoom(camera.get_zoom() * old_distance / new_distance); + } + } + } if (m_layers_editing.state != LayersEditing::Unknown) { m_layers_editing.state = LayersEditing::Unknown; @@ -6627,84 +6642,10 @@ void GLCanvas3D::_render_camera_target() } } -void GLCanvas3D::_render_camera_pivot() -{ - static const float half_length = 10.0f; - - glsafe(::glDisable(GL_DEPTH_TEST)); -#if !SLIC3R_OPENGL_ES - if (!OpenGLManager::get_gl_info().is_core_profile()) - glsafe(::glLineWidth(2.0f)); -#endif // !SLIC3R_OPENGL_ES - - m_camera_pivot.target = wxGetApp().plater()->get_camera().get_rotation_pivot(); - - for (int i = 0; i < 3; ++i) { - if (!m_camera_pivot.axis[i].is_initialized()) { - m_camera_pivot.axis[i].reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = (i == X) ? ColorRGBA::X() : (i == Y) ? ColorRGBA::Y() : ColorRGBA::Z(); - init_data.reserve_vertices(2); - init_data.reserve_indices(2); - - // vertices - if (i == X) { - init_data.add_vertex(Vec3f(-half_length, 0.0f, 0.0f)); - init_data.add_vertex(Vec3f(+half_length, 0.0f, 0.0f)); - } - else if (i == Y) { - init_data.add_vertex(Vec3f(0.0f, -half_length, 0.0f)); - init_data.add_vertex(Vec3f(0.0f, +half_length, 0.0f)); - } - else { - init_data.add_vertex(Vec3f(0.0f, 0.0f, -half_length)); - init_data.add_vertex(Vec3f(0.0f, 0.0f, +half_length)); - } - - // indices - init_data.add_line(0, 1); - - m_camera_pivot.axis[i].init_from(std::move(init_data)); - } - } - -#if SLIC3R_OPENGL_ES - GLShaderProgram* shader = wxGetApp().get_shader("dashed_lines"); -#else - GLShaderProgram* shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); -#endif // SLIC3R_OPENGL_ES - if (shader != nullptr) { - shader->start_using(); - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(m_camera_pivot.target)); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#if !SLIC3R_OPENGL_ES - if (OpenGLManager::get_gl_info().is_core_profile()) { -#endif // !SLIC3R_OPENGL_ES - const std::array& viewport = camera.get_viewport(); - shader->set_uniform("viewport_size", Vec2d(double(viewport[2]), double(viewport[3]))); - shader->set_uniform("width", 0.5f); - shader->set_uniform("gap_size", 0.0f); -#if !SLIC3R_OPENGL_ES - } -#endif // !SLIC3R_OPENGL_ES - for (int i = 0; i < 3; ++i) { - m_camera_pivot.axis[i].render(); - } - shader->stop_using(); - } -} - void GLCanvas3D::_render_camera_target_validation_box() { const BoundingBoxf3& curr_box = m_target_validation_box.get_bounding_box(); - BoundingBoxf3 camera_box = wxGetApp().plater()->get_camera().get_scene_box(); - Vec3d camera_box_center = camera_box.center(); - camera_box.translate(-camera_box_center); - camera_box.scale(wxGetApp().plater()->get_camera().get_scene_box_scale_factor()); - camera_box.translate(camera_box_center); + const BoundingBoxf3 camera_box = wxGetApp().plater()->get_camera().get_target_validation_box(); if (!m_target_validation_box.is_initialized() || !is_approx(camera_box.min, curr_box.min) || !is_approx(camera_box.max, curr_box.max)) { m_target_validation_box.reset(); @@ -7043,7 +6984,7 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) _start_timer(); } -Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, const float* z) +Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, const float* z, bool use_ortho) { if (m_canvas == nullptr) return Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); @@ -7055,11 +6996,13 @@ Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, const float* z) else { Camera& camera = wxGetApp().plater()->get_camera(); const Camera::EType type = camera.get_type(); - camera.set_type(Camera::EType::Ortho); + if (use_ortho) + camera.set_type(Camera::EType::Ortho); const Vec4i viewport(camera.get_viewport().data()); Vec3d out; igl::unproject(Vec3d(mouse_pos.x(), viewport[3] - mouse_pos.y(), *z), camera.get_view_matrix().matrix(), camera.get_projection_matrix().matrix(), viewport, out); - camera.set_type(type); + if (use_ortho) + camera.set_type(type); return out; } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index b97d6d7bd5..037637878d 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -692,7 +692,6 @@ private: }; CameraTarget m_camera_target; - CameraTarget m_camera_pivot; GLModel m_target_validation_box; #endif // ENABLE_SHOW_CAMERA_TARGET GLModel m_background; @@ -1085,7 +1084,6 @@ private: void _render_view_toolbar() const; #if ENABLE_SHOW_CAMERA_TARGET void _render_camera_target(); - void _render_camera_pivot(); void _render_camera_target_validation_box(); #endif // ENABLE_SHOW_CAMERA_TARGET void _render_sla_slices(); @@ -1106,7 +1104,7 @@ private: // Convert the screen space coordinate to an object space coordinate. // If the Z screen space coordinate is not provided, a depth buffer value is substituted. - Vec3d _mouse_to_3d(const Point& mouse_pos, const float* z = nullptr); + Vec3d _mouse_to_3d(const Point& mouse_pos, const float* z = nullptr, bool use_ortho = false); // Convert the screen space coordinate to world coordinate on the bed. Vec3d _mouse_to_bed_3d(const Point& mouse_pos);