diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 16b2fda0ea..302c628797 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -69,6 +69,8 @@ #define ENABLE_SHOW_TOOLPATHS_COG (1 && ENABLE_2_5_0_ALPHA1) // Enable recalculating toolpaths when switching to/from volumetric rate visualization #define ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC (1 && ENABLE_2_5_0_ALPHA1) +// Enable editing volumes transformation in world coordinates and instances in local coordinates +#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1) // Enable modified camera control using mouse #define ENABLE_NEW_CAMERA_MOVEMENTS (1 && ENABLE_2_5_0_ALPHA1) // Enable modified rectangle selection diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 6ab87150ba..1cc35be7f5 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -444,8 +444,13 @@ void ObjectManipulation::Show(const bool show) if (show) { // Show the "World Coordinates" / "Local Coordintes" Combo in Advanced / Expert mode only. - bool show_world_local_combo = wxGetApp().plater()->canvas3D()->get_selection().is_single_full_instance() && wxGetApp().get_mode() != comSimple; - m_word_local_combo->Show(show_world_local_combo); +#if ENABLE_WORLD_COORDINATE + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + bool show_world_local_combo = wxGetApp().get_mode() != comSimple && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()); +#else + bool show_world_local_combo = wxGetApp().plater()->canvas3D()->get_selection().is_single_full_instance() && wxGetApp().get_mode() != comSimple; +#endif // ENABLE_WORLD_COORDINATE + m_word_local_combo->Show(show_world_local_combo); m_empty_str->Show(!show_world_local_combo); } } @@ -529,7 +534,9 @@ void ObjectManipulation::update_settings_value(const Selection& selection) if (selection.is_single_full_instance()) { // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if !ENABLE_WORLD_COORDINATE m_new_position = volume->get_instance_offset(); +#endif // !ENABLE_WORLD_COORDINATE // Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible. if (m_world_coordinates && ! m_uniform_scale && @@ -540,14 +547,20 @@ void ObjectManipulation::update_settings_value(const Selection& selection) } if (m_world_coordinates) { - m_new_rotate_label_string = L("Rotate"); +#if ENABLE_WORLD_COORDINATE + m_new_position = volume->get_instance_offset(); +#endif // ENABLE_WORLD_COORDINATE + m_new_rotate_label_string = L("Rotate"); m_new_rotation = Vec3d::Zero(); m_new_size = selection.get_scaled_instance_bounding_box().size(); - m_new_scale = m_new_size.cwiseProduct(selection.get_unscaled_instance_bounding_box().size().cwiseInverse()) * 100.; + m_new_scale = m_new_size.cwiseProduct(selection.get_unscaled_instance_bounding_box().size().cwiseInverse()) * 100.0; } else { - m_new_rotation = volume->get_instance_rotation() * (180. / M_PI); - m_new_size = volume->get_instance_scaling_factor().cwiseProduct(wxGetApp().model().objects[volume->object_idx()]->raw_mesh_bounding_box().size()); +#if ENABLE_WORLD_COORDINATE + m_new_position = Vec3d::Zero(); +#endif // ENABLE_WORLD_COORDINATE + m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI); + m_new_size = volume->get_instance_scaling_factor().cwiseProduct(wxGetApp().model().objects[volume->object_idx()]->raw_mesh_bounding_box().size()); m_new_scale = volume->get_instance_scaling_factor() * 100.; } @@ -566,10 +579,29 @@ void ObjectManipulation::update_settings_value(const Selection& selection) else if (selection.is_single_modifier() || selection.is_single_volume()) { // the selection contains a single volume const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); +#if ENABLE_WORLD_COORDINATE + if (m_world_coordinates) { + const Geometry::Transformation trafo(volume->world_matrix()); + + const Vec3d& offset = trafo.get_offset(); + const Vec3d& rotation = trafo.get_rotation(); + const Vec3d& scaling_factor = trafo.get_scaling_factor(); + const Vec3d& m = trafo.get_mirror(); + + m_new_position = offset; + m_new_rotation = rotation * (180.0 / M_PI); + m_new_scale = scaling_factor * 100.0; + m_new_size = volume->bounding_box().size().cwiseProduct(scaling_factor); + } + else { +#endif // ENABLE_WORLD_COORDINATE m_new_position = volume->get_volume_offset(); m_new_rotation = volume->get_volume_rotation() * (180. / M_PI); m_new_scale = volume->get_volume_scaling_factor() * 100.; - m_new_size = volume->get_instance_scaling_factor().cwiseProduct(volume->get_volume_scaling_factor().cwiseProduct(volume->bounding_box().size())); + m_new_size = volume->get_instance_scaling_factor().cwiseProduct(volume->get_volume_scaling_factor().cwiseProduct(volume->bounding_box().size())); +#if ENABLE_WORLD_COORDINATE + } +#endif // ENABLE_WORLD_COORDINATE m_new_enabled = true; } else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot)) { @@ -815,7 +847,11 @@ void ObjectManipulation::change_position_value(int axis, double value) auto canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); selection.setup_cache(); +#if ENABLE_WORLD_COORDINATE + selection.translate(position - m_cache.position, !m_world_coordinates); +#else selection.translate(position - m_cache.position, selection.requires_local_axes()); +#endif // ENABLE_WORLD_COORDINATE canvas->do_move(L("Set Position")); m_cache.position = position; @@ -997,6 +1033,17 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value) m_uniform_scale = new_value; } +#if ENABLE_WORLD_COORDINATE +void ObjectManipulation::set_world_coordinates(const bool world_coordinates) +{ + m_world_coordinates = world_coordinates; + this->UpdateAndShow(true); + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + canvas->set_as_dirty(); + canvas->request_extra_frame(); +} +#endif // ENABLE_WORLD_COORDINATE + void ObjectManipulation::msw_rescale() { const int em = wxGetApp().em_unit(); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index a15c72fb8e..6d0383038e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -183,7 +183,11 @@ public: void set_uniform_scaling(const bool uniform_scale); bool get_uniform_scaling() const { return m_uniform_scale; } // Does the object manipulation panel work in World or Local coordinates? +#if ENABLE_WORLD_COORDINATE + void set_world_coordinates(const bool world_coordinates); +#else void set_world_coordinates(const bool world_coordinates) { m_world_coordinates = world_coordinates; this->UpdateAndShow(true); } +#endif // ENABLE_WORLD_COORDINATE bool get_world_coordinates() const { return m_world_coordinates; } void reset_cache() { m_cache.reset(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 9dfb8bc9e0..22d777d2c7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -2,6 +2,9 @@ #include "GLGizmoMove.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" +#if ENABLE_WORLD_COORDINATE +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#endif // ENABLE_WORLD_COORDINATE #if ENABLE_GL_SHADERS_ATTRIBUTES #include "slic3r/GUI/Plater.hpp" #endif // ENABLE_GL_SHADERS_ATTRIBUTES @@ -22,15 +25,15 @@ GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filenam std::string GLGizmoMove3D::get_tooltip() const { const Selection& selection = m_parent.get_selection(); - bool show_position = selection.is_single_full_instance(); + const bool show_position = selection.is_single_full_instance(); const Vec3d& position = selection.get_bounding_box().center(); if (m_hover_id == 0 || m_grabbers[0].dragging) - return "X: " + format(show_position ? position(0) : m_displacement(0), 2); + return "X: " + format(show_position ? position.x() : m_displacement.x(), 2); else if (m_hover_id == 1 || m_grabbers[1].dragging) - return "Y: " + format(show_position ? position(1) : m_displacement(1), 2); + return "Y: " + format(show_position ? position.y() : m_displacement.y(), 2); else if (m_hover_id == 2 || m_grabbers[2].dragging) - return "Z: " + format(show_position ? position(2) : m_displacement(2), 2); + return "Z: " + format(show_position ? position.z() : m_displacement.z(), 2); else return ""; } @@ -80,10 +83,18 @@ void GLGizmoMove3D::on_start_dragging() m_displacement = Vec3d::Zero(); const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); +#if ENABLE_WORLD_COORDINATE + const Vec3d center = box.center(); + m_starting_drag_position = center + m_grabbers[m_hover_id].center; + m_starting_box_center = center; + m_starting_box_bottom_center = center; + m_starting_box_bottom_center.z() = box.min.z(); +#else m_starting_drag_position = m_grabbers[m_hover_id].center; m_starting_box_center = box.center(); m_starting_box_bottom_center = box.center(); - m_starting_box_bottom_center(2) = box.min(2); + m_starting_box_bottom_center.z() = box.min.z(); +#endif // ENABLE_WORLD_COORDINATE } void GLGizmoMove3D::on_stop_dragging() @@ -119,7 +130,25 @@ void GLGizmoMove3D::on_render() const BoundingBoxf3& box = selection.get_bounding_box(); const Vec3d& center = box.center(); +#if ENABLE_WORLD_COORDINATE + glsafe(::glPushMatrix()); + transform_to_local(selection); + const Vec3d zero = Vec3d::Zero(); + const Vec3d half_box_size = 0.5 * box.size(); + + // x axis + m_grabbers[0].center = { half_box_size.x() + Offset, 0.0, 0.0 }; + m_grabbers[0].color = AXES_COLOR[0]; + + // y axis + m_grabbers[1].center = { 0.0, half_box_size.y() + Offset, 0.0 }; + m_grabbers[1].color = AXES_COLOR[1]; + + // z axis + m_grabbers[2].center = { 0.0, 0.0, half_box_size.z() + Offset }; + m_grabbers[2].color = AXES_COLOR[2]; +#else // x axis m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() }; m_grabbers[0].color = AXES_COLOR[0]; @@ -131,6 +160,7 @@ void GLGizmoMove3D::on_render() // z axis m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; m_grabbers[2].color = AXES_COLOR[2]; +#endif // ENABLE_WORLD_COORDINATE glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); @@ -183,7 +213,11 @@ void GLGizmoMove3D::on_render() if (m_grabbers[i].enabled) { glsafe(::glColor4fv(AXES_COLOR[i].data())); ::glBegin(GL_LINES); +#if ENABLE_WORLD_COORDINATE + ::glVertex3dv(zero.data()); +#else ::glVertex3dv(center.data()); +#endif // ENABLE_WORLD_COORDINATE ::glVertex3dv(m_grabbers[i].center.data()); glsafe(::glEnd()); } @@ -225,7 +259,11 @@ void GLGizmoMove3D::on_render() #else glsafe(::glColor4fv(AXES_COLOR[m_hover_id].data())); ::glBegin(GL_LINES); +#if ENABLE_WORLD_COORDINATE + ::glVertex3dv(zero.data()); +#else ::glVertex3dv(center.data()); +#endif // ENABLE_WORLD_COORDINATE ::glVertex3dv(m_grabbers[m_hover_id].center.data()); glsafe(::glEnd()); @@ -243,36 +281,50 @@ void GLGizmoMove3D::on_render() render_grabber_extension((Axis)m_hover_id, box, false); #endif // !ENABLE_GIZMO_GRABBER_REFACTOR } + +#if ENABLE_WORLD_COORDINATE + glsafe(::glPopMatrix()); +#endif // ENABLE_WORLD_COORDINATE } void GLGizmoMove3D::on_render_for_picking() { glsafe(::glDisable(GL_DEPTH_TEST)); +#if ENABLE_WORLD_COORDINATE + const Selection& selection = m_parent.get_selection(); + const BoundingBoxf3& box = selection.get_bounding_box(); + glsafe(::glPushMatrix()); + transform_to_local(selection); +#else const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); +#endif // ENABLE_WORLD_COORDINATE render_grabbers_for_picking(box); #if !ENABLE_GIZMO_GRABBER_REFACTOR render_grabber_extension(X, box, true); render_grabber_extension(Y, box, true); render_grabber_extension(Z, box, true); #endif // !ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_WORLD_COORDINATE + glsafe(::glPopMatrix()); +#endif // ENABLE_WORLD_COORDINATE } double GLGizmoMove3D::calc_projection(const UpdateData& data) const { double projection = 0.0; - Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; - double len_starting_vec = starting_vec.norm(); + const Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; + const double len_starting_vec = starting_vec.norm(); if (len_starting_vec != 0.0) { Vec3d mouse_dir = data.mouse_ray.unit_vector(); // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form // in our case plane normal and ray direction are the same (orthogonal view) // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + const Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; // vector from the starting position to the found intersection - Vec3d inters_vec = inters - m_starting_drag_position; + const Vec3d inters_vec = inters - m_starting_drag_position; // finds projection of the vector along the staring direction projection = inters_vec.dot(starting_vec.normalized()); @@ -345,5 +397,18 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box } #endif // !ENABLE_GIZMO_GRABBER_REFACTOR +#if ENABLE_WORLD_COORDINATE +void GLGizmoMove3D::transform_to_local(const Selection& selection) const +{ + const Vec3d center = selection.get_bounding_box().center(); + glsafe(::glTranslated(center.x(), center.y(), center.z())); + + if (!wxGetApp().obj_manipul()->get_world_coordinates()) { + const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true); + glsafe(::glMultMatrixd(orient_matrix.data())); + } +} +#endif // ENABLE_WORLD_COORDINATE + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 6a618c3e4d..db9d3ac749 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -7,6 +7,10 @@ namespace Slic3r { namespace GUI { +#if ENABLE_WORLD_COORDINATE + class Selection; +#endif // ENABLE_WORLD_COORDINATE + class GLGizmoMove3D : public GLGizmoBase { static const double Offset; @@ -51,17 +55,23 @@ public: void data_changed() override; protected: - bool on_init() override; - std::string on_get_name() const override; - bool on_is_activable() const override; - void on_start_dragging() override; - void on_stop_dragging() override; - void on_dragging(const UpdateData& data) override; - void on_render() override; - void on_render_for_picking() override; + virtual bool on_init() override; + virtual std::string on_get_name() const override; + virtual bool on_is_activable() const override; + virtual void on_start_dragging() override; + virtual void on_stop_dragging() override; +#if !ENABLE_WORLD_COORDINATE + virtual void on_start_dragging() override; +#endif // !ENABLE_WORLD_COORDINATE + virtual void on_dragging(const UpdateData& data) override; + virtual void on_render() override; + virtual void on_render_for_picking() override; private: double calc_projection(const UpdateData& data) const; +#if ENABLE_WORLD_COORDINATE + void transform_to_local(const Selection& selection) const; +#endif // ENABLE_WORLD_COORDINATE #if !ENABLE_GIZMO_GRABBER_REFACTOR void render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking); #endif // !ENABLE_GIZMO_GRABBER_REFACTOR diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index e7c4e1763f..ae261fc7a8 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -699,13 +699,31 @@ void Selection::translate(const Vec3d& displacement, bool local) if (local) v.set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); else { +#if ENABLE_WORLD_COORDINATE + const VolumeCache& volume_data = m_cache.volumes_data[i]; + const Vec3d local_displacement = (volume_data.get_instance_rotation_matrix() * volume_data.get_instance_scale_matrix() * volume_data.get_instance_mirror_matrix()).inverse() * displacement; + v.set_volume_offset(volume_data.get_volume_position() + local_displacement); +#else const Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; v.set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); +#endif // ENABLE_WORLD_COORDINATE } } else if (m_mode == Instance) { +#if ENABLE_WORLD_COORDINATE + if (is_from_fully_selected_instance(i)) { + if (local) { + const VolumeCache& volume_data = m_cache.volumes_data[i]; + const Vec3d world_displacement = (volume_data.get_instance_rotation_matrix() * volume_data.get_instance_scale_matrix() * volume_data.get_instance_mirror_matrix()) * displacement; + v.set_instance_offset(volume_data.get_instance_position() + world_displacement); + } + else + v.set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); + } +#else if (is_from_fully_selected_instance(i)) v.set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); +#endif // ENABLE_WORLD_COORDINATE else { const Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; v.set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);