mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-31 07:42:03 +08:00
SPE-2596 - Camera rotation around screen center, keeping the rotation pivot on the plane z = 0
This commit is contained in:
parent
63ce55166d
commit
015b9f5536
@ -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<float>();
|
||||
Vec3f target = m_target.cast<float>();
|
||||
Vec3f pivot = m_rotation_pivot.cast<float>();
|
||||
float distance = (float)get_distance();
|
||||
float zenit = (float)m_zenit;
|
||||
Vec3f forward = get_dir_forward().cast<float>();
|
||||
@ -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<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBo
|
||||
std::pair<double, double> 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()
|
||||
|
@ -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<double, double> 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<double, double> 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;
|
||||
|
@ -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<int, 4>& 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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user