From 086f11df98c20d133baac7367ffda2988eb0cd7d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 2 Apr 2019 13:47:49 +0200 Subject: [PATCH] Handling of left hand oriented coordinate systems: is_left_handed() method on transformations and volumes rendering of GLVolumes in left handed coordinate systems by glFrontFace(GL_CW); SLA slicing on left hand oriented instances by flipping the mesh for SLAPrintObject in X. rendering of the SLA cutting plane in left handed systems resetting the SLA clipping planes on 3D preview invalidation --- src/libslic3r/Geometry.hpp | 1 + src/libslic3r/Model.hpp | 1 + src/libslic3r/SLAPrint.cpp | 7 ++- src/libslic3r/SLAPrint.hpp | 7 ++- src/slic3r/GUI/3DScene.cpp | 23 ++++++++++ src/slic3r/GUI/3DScene.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 81 +++++++++++++--------------------- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/GUI_Preview.cpp | 13 ++++-- src/slic3r/GUI/GUI_Preview.hpp | 5 ++- src/slic3r/GUI/Plater.cpp | 2 +- 11 files changed, 80 insertions(+), 62 deletions(-) diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 380245b5fc..d556f664c8 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -246,6 +246,7 @@ public: const Vec3d& get_mirror() const { return m_mirror; } double get_mirror(Axis axis) const { return m_mirror(axis); } + bool is_left_handed() const { return m_mirror.x() * m_mirror.y() * m_mirror.z() < 0.; } void set_mirror(const Vec3d& mirror); void set_mirror(Axis axis, double mirror); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 8a48f8ee96..3d476cbb07 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -394,6 +394,7 @@ public: const Vec3d& get_mirror() const { return m_transformation.get_mirror(); } double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); } + bool is_left_handed() const { return m_transformation.is_left_handed(); } void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); } void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index c7bacaa31b..04778f5842 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -93,7 +93,10 @@ static Transform3d sla_trafo(const ModelObject &model_object) offset(0) = 0.; offset(1) = 0.; rotation(2) = 0.; - return Geometry::assemble_transform(offset, rotation, model_instance.get_scaling_factor(), model_instance.get_mirror()); + Transform3d trafo = Geometry::assemble_transform(offset, rotation, model_instance.get_scaling_factor(), model_instance.get_mirror()); + if (model_instance.is_left_handed()) + trafo = Eigen::Scaling(Vec3d(-1., 1., 1.)) * trafo; + return trafo; } // List of instances, where the ModelInstance transformation is a composite of sla_trafo and the transformation defined by SLAPrintObject::Instance. @@ -399,7 +402,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf // FIXME: this invalidates the transformed mesh in SLAPrintObject // which is expensive to calculate (especially the raw_mesh() call) - print_object->set_trafo(sla_trafo(model_object)); + print_object->set_trafo(sla_trafo(model_object), model_object.instances.front()->is_left_handed()); print_object->set_instances(new_instances); print_object->config_apply(config, true); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 9cf826097e..272252a2af 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -51,6 +51,7 @@ public: const SLAPrintObjectConfig& config() const { return m_config; } const Transform3d& trafo() const { return m_trafo; } + bool is_left_handed() const { return m_left_handed; } struct Instance { Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {} @@ -241,8 +242,8 @@ protected: void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } - void set_trafo(const Transform3d& trafo) { - m_transformed_rmesh.invalidate([this, &trafo](){ m_trafo = trafo; }); + void set_trafo(const Transform3d& trafo, bool left_handed) { + m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; }); } void set_instances(const std::vector &instances) { m_instances = instances; } @@ -262,6 +263,8 @@ private: // Translation in Z + Rotation by Y and Z + Scaling / Mirroring. Transform3d m_trafo = Transform3d::Identity(); + // m_trafo is left handed -> 3x3 affine transformation has negative determinant. + bool m_left_handed = false; std::vector m_instances; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 58011730b6..7ba61bdb69 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -333,6 +333,13 @@ Transform3d GLVolume::world_matrix() const return m; } +bool GLVolume::is_left_handed() const +{ + const Vec3d &m1 = m_instance_transformation.get_mirror(); + const Vec3d &m2 = m_volume_transformation.get_mirror(); + return m1.x() * m1.y() * m1.z() * m2.x() * m2.y() * m2.z() < 0.; +} + const BoundingBoxf3& GLVolume::transformed_bounding_box() const { assert(bounding_box.defined || bounding_box.min(0) >= bounding_box.max(0) || bounding_box.min(1) >= bounding_box.max(1) || bounding_box.min(2) >= bounding_box.max(2)); @@ -401,6 +408,8 @@ void GLVolume::render() const if (!is_active) return; + if (this->is_left_handed()) + glFrontFace(GL_CW); glsafe(::glCullFace(GL_BACK)); glsafe(::glPushMatrix()); @@ -410,6 +419,8 @@ void GLVolume::render() const else this->indexed_vertex_array.render(); glsafe(::glPopMatrix()); + if (this->is_left_handed()) + glFrontFace(GL_CCW); } void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const @@ -420,6 +431,9 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id) return; + if (this->is_left_handed()) + glFrontFace(GL_CW); + GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first)); GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first)); if (n_triangles + n_quads == 0) @@ -481,6 +495,9 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c } glsafe(::glPopMatrix()); + + if (this->is_left_handed()) + glFrontFace(GL_CCW); } void GLVolume::render_legacy() const @@ -489,6 +506,9 @@ void GLVolume::render_legacy() const if (!is_active) return; + if (this->is_left_handed()) + glFrontFace(GL_CW); + GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first)); GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first)); if (n_triangles + n_quads == 0) @@ -520,6 +540,9 @@ void GLVolume::render_legacy() const glsafe(::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, indexed_vertex_array.quad_indices.data() + qverts_range.first)); glsafe(::glPopMatrix()); + + if (this->is_left_handed()) + glFrontFace(GL_CCW); } std::vector GLVolumeCollection::load_object( diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index e421997e5d..5cc301a393 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -388,6 +388,7 @@ public: int instance_idx() const { return this->composite_id.instance_id; } Transform3d world_matrix() const; + bool is_left_handed() const; const BoundingBoxf3& transformed_bounding_box() const; const BoundingBoxf3& transformed_convex_hull_bounding_box() const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 31c3717ff9..862ce74b7c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5010,24 +5010,12 @@ void GLCanvas3D::_render_sla_slices() const Pointf3s &top_obj_triangles = it_caps_top->second.object; Pointf3s &top_sup_triangles = it_caps_top->second.supports; - const std::vector& instances = obj->instances(); - struct InstanceTransform - { - Vec3d offset; - float rotation; - }; - - std::vector instance_transforms; - for (const SLAPrintObject::Instance& inst : instances) - { - instance_transforms.push_back({ to_3d(unscale(inst.shift), 0.), Geometry::rad2deg(inst.rotation) }); - } - if ((bottom_obj_triangles.empty() || bottom_sup_triangles.empty() || top_obj_triangles.empty() || top_sup_triangles.empty()) && obj->is_step_done(slaposSliceSupports) && !obj->get_slice_index().empty()) { double layer_height = print->default_object_config().layer_height.value; double initial_layer_height = print->material_config().initial_layer_height.value; + bool left_handed = obj->is_left_handed(); coord_t key_zero = obj->get_slice_index().front().print_level(); // Slice at the center of the slab starting at clip_min_z will be rendered for the lower plane. @@ -5046,10 +5034,10 @@ void GLCanvas3D::_render_sla_slices() const const ExPolygons& sup_bottom = slice_low.get_slice(soSupport); // calculate model bottom cap if (bottom_obj_triangles.empty() && !obj_bottom.empty()) - bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, true); + bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, ! left_handed); // calculate support bottom cap if (bottom_sup_triangles.empty() && !sup_bottom.empty()) - bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, true); + bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, ! left_handed); } if (slice_high.is_valid()) { @@ -5057,49 +5045,35 @@ void GLCanvas3D::_render_sla_slices() const const ExPolygons& sup_top = slice_high.get_slice(soSupport); // calculate model top cap if (top_obj_triangles.empty() && !obj_top.empty()) - top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, false); + top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, left_handed); // calculate support top cap if (top_sup_triangles.empty() && !sup_top.empty()) - top_sup_triangles = triangulate_expolygons_3d(sup_top, clip_max_z + plane_shift_z, false); + top_sup_triangles = triangulate_expolygons_3d(sup_top, clip_max_z + plane_shift_z, left_handed); } } if (!bottom_obj_triangles.empty() || !top_obj_triangles.empty() || !bottom_sup_triangles.empty() || !top_sup_triangles.empty()) { - for (const InstanceTransform& inst : instance_transforms) + for (const SLAPrintObject::Instance& inst : obj->instances()) { ::glPushMatrix(); - ::glTranslated(inst.offset(0), inst.offset(1), inst.offset(2)); - ::glRotatef(inst.rotation, 0.0, 0.0, 1.0); - - ::glBegin(GL_TRIANGLES); - + ::glTranslated(unscale(inst.shift.x()), unscale(inst.shift.y()), 0); + ::glRotatef(Geometry::rad2deg(inst.rotation), 0.0, 0.0, 1.0); + if (obj->is_left_handed()) + // The polygons are mirrored by X. + ::glScalef(-1.0, 1.0, 1.0); ::glColor3f(1.0f, 0.37f, 0.0f); - - for (const Vec3d& v : bottom_obj_triangles) - { - ::glVertex3dv((GLdouble*)v.data()); - } - - for (const Vec3d& v : top_obj_triangles) - { - ::glVertex3dv((GLdouble*)v.data()); - } - - ::glColor3f(1.0f, 0.0f, 0.37f); - - for (const Vec3d& v : bottom_sup_triangles) - { - ::glVertex3dv((GLdouble*)v.data()); - } - - for (const Vec3d& v : top_sup_triangles) - { - ::glVertex3dv((GLdouble*)v.data()); - } - - ::glEnd(); - + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)bottom_obj_triangles.front().data()); + ::glDrawArrays(GL_TRIANGLES, 0, bottom_obj_triangles.size()); + ::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)top_obj_triangles.front().data()); + ::glDrawArrays(GL_TRIANGLES, 0, top_obj_triangles.size()); + ::glColor3f(1.0f, 0.0f, 0.37f); + ::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)bottom_sup_triangles.front().data()); + ::glDrawArrays(GL_TRIANGLES, 0, bottom_sup_triangles.size()); + ::glVertexPointer(3, GL_DOUBLE, 0, (GLdouble*)top_sup_triangles.front().data()); + ::glDrawArrays(GL_TRIANGLES, 0, top_sup_triangles.size()); + ::glDisableClientState(GL_VERTEX_ARRAY); ::glPopMatrix(); } } @@ -6217,6 +6191,8 @@ void GLCanvas3D::_load_shells_fff() void GLCanvas3D::_load_shells_sla() { + //FIXME use reload_scene +#if 1 const SLAPrint* print = this->sla_print(); if (print->objects().empty()) // nothing to render, return @@ -6240,9 +6216,7 @@ void GLCanvas3D::_load_shells_sla() m_volumes.load_object(model_obj, obj_idx, instance_idxs, "object", m_use_VBOs && m_initialized); - const std::vector& instances = obj->instances(); - - for (const SLAPrintObject::Instance& instance : instances) + for (const SLAPrintObject::Instance& instance : obj->instances()) { Vec3d offset = unscale(instance.shift(0), instance.shift(1), 0); Vec3d rotation(0.0, 0.0, (double)instance.rotation); @@ -6265,6 +6239,7 @@ void GLCanvas3D::_load_shells_sla() v.composite_id.volume_id = -1; v.set_instance_offset(offset); v.set_instance_rotation(rotation); + v.set_instance_mirror(X, obj->is_left_handed() ? -1. : 1.); } // add pad @@ -6283,6 +6258,7 @@ void GLCanvas3D::_load_shells_sla() v.composite_id.volume_id = -1; v.set_instance_offset(offset); v.set_instance_rotation(rotation); + v.set_instance_mirror(X, obj->is_left_handed() ? -1. : 1.); } // finalize volumes and sends geometry to gpu @@ -6305,6 +6281,9 @@ void GLCanvas3D::_load_shells_sla() } update_volumes_colors_by_extruder(); +#else + this->reload_scene(true, true); +#endif } void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 2b1061c95f..7e414d52a8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -634,6 +634,7 @@ public: m_sla_caps[id].reset(); } } + void reset_clipping_planes_cache() { m_sla_caps[0].triangles.clear(); m_sla_caps[1].triangles.clear(); } void set_use_clipping_planes(bool use) { m_use_clipping_planes = use; } void set_color_by(const std::string& value); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 14d19e2514..438e9d236a 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -27,7 +27,7 @@ namespace Slic3r { namespace GUI { - View3D::View3D(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) +View3D::View3D(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) : m_canvas_widget(nullptr) , m_canvas(nullptr) { @@ -155,7 +155,9 @@ void View3D::render() m_canvas->set_as_dirty(); } -Preview::Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process_func) +Preview::Preview( + wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, + BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process_func) : m_canvas_widget(nullptr) , m_canvas(nullptr) , m_double_slider_sizer(nullptr) @@ -179,14 +181,14 @@ Preview::Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_t , m_volumes_cleanup_required(false) #endif // __linux__ { - if (init(parent, bed, camera, view_toolbar)) + if (init(parent, bed, camera, view_toolbar, model)) { show_hide_ui_elements("none"); load_print(); } } -bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar) +bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model) { if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */)) return false; @@ -196,6 +198,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_canvas = _3DScene::get_canvas(this->m_canvas_widget); m_canvas->allow_multisample(GLCanvas3DManager::can_multisample()); m_canvas->set_config(m_config); + m_canvas->set_model(model); m_canvas->set_process(m_process); m_canvas->enable_legend_texture(true); m_canvas->enable_dynamic_background(true); @@ -781,6 +784,8 @@ void Preview::load_print_as_sla() } sort_remove_duplicates(zs); + m_canvas->reset_clipping_planes_cache(); + n_layers = (unsigned int)zs.size(); if (n_layers == 0) { diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 96c49e54ff..a2929f2e6f 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -102,7 +102,8 @@ class Preview : public wxPanel PrusaDoubleSlider* m_slider {nullptr}; public: - Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = [](){}); + Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, + BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = [](){}); virtual ~Preview(); wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } @@ -120,7 +121,7 @@ public: void refresh_print(); private: - bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar); + bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model); void bind_event_handlers(); void unbind_event_handlers(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c92c22c50c..435d9548f8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1350,7 +1350,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this); view3D = new View3D(q, bed, camera, view_toolbar, &model, config, &background_process); - preview = new Preview(q, bed, camera, view_toolbar, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); + preview = new Preview(q, bed, camera, view_toolbar, &model, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); panels.push_back(view3D); panels.push_back(preview);