diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index d63398aef..ec6fe575b 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -148,6 +148,8 @@ sub new { $self->{layer_height_edit_last_object_id} = -1; $self->{layer_height_edit_last_z} = 0.; $self->{layer_height_edit_last_action} = 0; + + $self->{use_VBOs} = 0; $self->reset_objects; @@ -415,7 +417,7 @@ sub mouse_event { } # apply new temporary volume origin and ignore Z - $_->origin->translate($vector->x, $vector->y, 0) for @volumes; #,, + $_->translate($vector->x, $vector->y, 0) for @volumes; #,, $self->_drag_start_pos($cur_pos); $self->_dragged(1); $self->Refresh; @@ -1685,7 +1687,8 @@ sub load_object { sub load_print_toolpaths { my ($self, $print) = @_; - Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes) + $self->GetContext if ($self->{use_VBOs}); + Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes, $self->{use_VBOs}) if ($print->step_done(STEP_SKIRT) && $print->step_done(STEP_BRIM)); } @@ -1695,7 +1698,8 @@ sub load_print_toolpaths { sub load_print_object_toolpaths { my ($self, $object) = @_; - Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes); + $self->GetContext if ($self->{use_VBOs}); + Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $self->{use_VBOs}); } sub set_toolpaths_range { diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index aab6f5c98..96f14ac32 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -237,7 +237,7 @@ sub selection_changed { # deselect all meshes if ($self->{canvas}) { - $_->selected(0) for @{$self->{canvas}->volumes}; + $_->set_selected(0) for @{$self->{canvas}->volumes}; } # disable things as if nothing is selected @@ -265,7 +265,7 @@ sub selection_changed { if ($itemData->{type} eq 'volume') { # select volume in 3D preview if ($self->{canvas}) { - $self->{canvas}->volumes->[ $itemData->{volume_id} ]{selected} = 1; + $self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1); } $self->{btn_delete}->Enable; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 82ebbfeed..0c81bba8b 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -178,22 +178,23 @@ static void thick_lines_to_verts( Vectorf3 prev_xy_left_normal, prev_xy_right_normal; // loop once more in case of closed loops - bool first_done = false; - for (size_t i = 0; i <= lines.size(); ++i) { - if (i == lines.size()) i = 0; - - const Line &line = lines.at(i); - if (i == 0 && first_done && !closed) break; + for (size_t ii = 0; ii <= lines.size(); ++ ii) { + size_t i = ii; + if (ii == lines.size()) { + if (! closed) + break; + i = 0; + } + const Line &line = lines[i]; double len = line.length(); double unscaled_len = unscale(len); - - double bottom_z = top_z - heights.at(i); + double bottom_z = top_z - heights[i]; double middle_z = (top_z + bottom_z) / 2; double dist = widths.at(i)/2; // scaled Vectorf v = Vectorf::new_unscale(line.vector()); - v.scale(1/unscaled_len); + v.scale(1. / unscaled_len); Pointf a = Pointf::new_unscale(line.a); Pointf b = Pointf::new_unscale(line.b); @@ -209,11 +210,11 @@ static void thick_lines_to_verts( // calculate new XY normals Vector n = line.normal(); Vectorf3 xy_right_normal = Vectorf3::new_unscale(n.x, n.y, 0); - xy_right_normal.scale(1/unscaled_len); + xy_right_normal.scale(1.f / unscaled_len); Vectorf3 xy_left_normal = xy_right_normal; - xy_left_normal.scale(-1); + xy_left_normal.scale(-1.f); - if (first_done) { + if (ii > 0) { // if we're making a ccw turn, draw the triangles on the right side, otherwise draw them on the left side double ccw = line.b.ccw(prev_line); if (ccw > EPSILON) { @@ -228,7 +229,7 @@ static void thick_lines_to_verts( volume.tverts.push_vert(a1.x, a1.y, middle_z); // normal going upwards - volume.tverts.push_norm(0,0,1); + volume.tverts.push_norm(0.f, 0.f, 1.f); volume.tverts.push_vert(a.x, a.y, top_z); } // bottom-right vertex triangle between previous line and this one @@ -238,7 +239,7 @@ static void thick_lines_to_verts( volume.tverts.push_vert(prev_b1.x, prev_b1.y, middle_z); // normal going downwards - volume.tverts.push_norm(0,0,-1); + volume.tverts.push_norm(0.f, 0.f, -1.f); volume.tverts.push_vert(a.x, a.y, bottom_z); // use the normal going to the right calculated for this line @@ -253,7 +254,7 @@ static void thick_lines_to_verts( volume.tverts.push_vert(prev_b2.x, prev_b2.y, middle_z); // normal going upwards - volume.tverts.push_norm(0,0,1); + volume.tverts.push_norm(0.f, 0.f, 1.f); volume.tverts.push_vert(a.x, a.y, top_z); // use the normal going to the right calculated for this line @@ -271,14 +272,15 @@ static void thick_lines_to_verts( volume.tverts.push_vert(a2.x, a2.y, middle_z); // normal going downwards - volume.tverts.push_norm(0,0,-1); + volume.tverts.push_norm(0.f, 0.f, -1.f); volume.tverts.push_vert(a.x, a.y, bottom_z); } } } // if this was the extra iteration we were only interested in the triangles - if (first_done && i == 0) break; + if (ii == lines.size()) + break; prev_line = line; prev_b1 = b1; @@ -286,7 +288,7 @@ static void thick_lines_to_verts( prev_xy_right_normal = xy_right_normal; prev_xy_left_normal = xy_left_normal; - if (!closed) { + if (! closed) { // terminate open paths with caps if (i == 0) { // normal pointing downwards @@ -306,7 +308,7 @@ static void thick_lines_to_verts( volume.qverts.push_vert(a2.x, a2.y, middle_z); } // we don't use 'else' because both cases are true if we have only one line - if (i == lines.size()-1) { + if (i + 1 == lines.size()) { // normal pointing downwards volume.qverts.push_norm(0,0,-1); volume.qverts.push_vert(b.x, b.y, bottom_z); @@ -379,8 +381,230 @@ static void thick_lines_to_verts( volume.qverts.push_vert(b.x, b.y, bottom_z); volume.qverts.push_vert(a.x, a.y, bottom_z); } + } +} + +// caller is responsible for supplying NO lines with zero length +static void thick_lines_to_VBOs( + const Lines &lines, + const std::vector &widths, + const std::vector &heights, + bool closed, + double top_z, + GLVolume &volume) +{ + assert(! lines.empty()); + if (lines.empty()) + return; + + Line prev_line; + Pointf prev_b1, prev_b2; + Vectorf3 prev_xy_left_normal, prev_xy_right_normal; + + // loop once more in case of closed loops + for (size_t ii = 0; ii <= lines.size(); ++ ii) { + size_t i = ii; + if (ii == lines.size()) { + if (! closed) + break; + i = 0; + } - first_done = true; + const Line &line = lines[i]; + double len = line.length(); + double unscaled_len = unscale(len); + double bottom_z = top_z - heights[i]; + double middle_z = (top_z + bottom_z) / 2; + double dist = widths.at(i)/2; // scaled + + Vectorf v = Vectorf::new_unscale(line.vector()); + v.scale(1. / unscaled_len); + + Pointf a = Pointf::new_unscale(line.a); + Pointf b = Pointf::new_unscale(line.b); + Pointf a1 = a; + Pointf a2 = a; + a1.translate(+dist*v.y, -dist*v.x); + a2.translate(-dist*v.y, +dist*v.x); + Pointf b1 = b; + Pointf b2 = b; + b1.translate(+dist*v.y, -dist*v.x); + b2.translate(-dist*v.y, +dist*v.x); + + // calculate new XY normals + Vector n = line.normal(); + Vectorf3 xy_right_normal = Vectorf3::new_unscale(n.x, n.y, 0); + xy_right_normal.scale(1.f / unscaled_len); + Vectorf3 xy_left_normal = xy_right_normal; + xy_left_normal.scale(-1.f); + + if (ii > 0) { + // if we're making a ccw turn, draw the triangles on the right side, otherwise draw them on the left side + double ccw = line.b.ccw(prev_line); + if (ccw > EPSILON) { + // top-right vertex triangle between previous line and this one + { + // use the normal going to the right calculated for the previous line + volume.tverts.push_norm(prev_xy_right_normal); + volume.tverts.push_vert(prev_b1.x, prev_b1.y, middle_z); + + // use the normal going to the right calculated for this line + volume.tverts.push_norm(xy_right_normal); + volume.tverts.push_vert(a1.x, a1.y, middle_z); + + // normal going upwards + volume.tverts.push_norm(0.f, 0.f, 1.f); + volume.tverts.push_vert(a.x, a.y, top_z); + } + // bottom-right vertex triangle between previous line and this one + { + // use the normal going to the right calculated for the previous line + volume.tverts.push_norm(prev_xy_right_normal); + volume.tverts.push_vert(prev_b1.x, prev_b1.y, middle_z); + + // normal going downwards + volume.tverts.push_norm(0.f, 0.f, -1.f); + volume.tverts.push_vert(a.x, a.y, bottom_z); + + // use the normal going to the right calculated for this line + volume.tverts.push_norm(xy_right_normal); + volume.tverts.push_vert(a1.x, a1.y, middle_z); + } + } else if (ccw < -EPSILON) { + // top-left vertex triangle between previous line and this one + { + // use the normal going to the left calculated for the previous line + volume.tverts.push_norm(prev_xy_left_normal); + volume.tverts.push_vert(prev_b2.x, prev_b2.y, middle_z); + + // normal going upwards + volume.tverts.push_norm(0.f, 0.f, 1.f); + volume.tverts.push_vert(a.x, a.y, top_z); + + // use the normal going to the right calculated for this line + volume.tverts.push_norm(xy_left_normal); + volume.tverts.push_vert(a2.x, a2.y, middle_z); + } + // bottom-left vertex triangle between previous line and this one + { + // use the normal going to the left calculated for the previous line + volume.tverts.push_norm(prev_xy_left_normal); + volume.tverts.push_vert(prev_b2.x, prev_b2.y, middle_z); + + // use the normal going to the right calculated for this line + volume.tverts.push_norm(xy_left_normal); + volume.tverts.push_vert(a2.x, a2.y, middle_z); + + // normal going downwards + volume.tverts.push_norm(0.f, 0.f, -1.f); + volume.tverts.push_vert(a.x, a.y, bottom_z); + } + } + } + + // if this was the extra iteration we were only interested in the triangles + if (ii == lines.size()) + break; + + prev_line = line; + prev_b1 = b1; + prev_b2 = b2; + prev_xy_right_normal = xy_right_normal; + prev_xy_left_normal = xy_left_normal; + + if (! closed) { + // terminate open paths with caps + if (i == 0) { + // normal pointing downwards + volume.qverts.push_norm(0,0,-1); + volume.qverts.push_vert(a.x, a.y, bottom_z); + + // normal pointing to the right + volume.qverts.push_norm(xy_right_normal); + volume.qverts.push_vert(a1.x, a1.y, middle_z); + + // normal pointing upwards + volume.qverts.push_norm(0,0,1); + volume.qverts.push_vert(a.x, a.y, top_z); + + // normal pointing to the left + volume.qverts.push_norm(xy_left_normal); + volume.qverts.push_vert(a2.x, a2.y, middle_z); + } + // we don't use 'else' because both cases are true if we have only one line + if (i + 1 == lines.size()) { + // normal pointing downwards + volume.qverts.push_norm(0,0,-1); + volume.qverts.push_vert(b.x, b.y, bottom_z); + + // normal pointing to the left + volume.qverts.push_norm(xy_left_normal); + volume.qverts.push_vert(b2.x, b2.y, middle_z); + + // normal pointing upwards + volume.qverts.push_norm(0,0,1); + volume.qverts.push_vert(b.x, b.y, top_z); + + // normal pointing to the right + volume.qverts.push_norm(xy_right_normal); + volume.qverts.push_vert(b1.x, b1.y, middle_z); + } + } + + // bottom-right face + { + // normal going downwards + volume.qverts.push_norm(0,0,-1); + volume.qverts.push_norm(0,0,-1); + volume.qverts.push_vert(a.x, a.y, bottom_z); + volume.qverts.push_vert(b.x, b.y, bottom_z); + + volume.qverts.push_norm(xy_right_normal); + volume.qverts.push_norm(xy_right_normal); + volume.qverts.push_vert(b1.x, b1.y, middle_z); + volume.qverts.push_vert(a1.x, a1.y, middle_z); + } + + // top-right face + { + volume.qverts.push_norm(xy_right_normal); + volume.qverts.push_norm(xy_right_normal); + volume.qverts.push_vert(a1.x, a1.y, middle_z); + volume.qverts.push_vert(b1.x, b1.y, middle_z); + + // normal going upwards + volume.qverts.push_norm(0,0,1); + volume.qverts.push_norm(0,0,1); + volume.qverts.push_vert(b.x, b.y, top_z); + volume.qverts.push_vert(a.x, a.y, top_z); + } + + // top-left face + { + volume.qverts.push_norm(0,0,1); + volume.qverts.push_norm(0,0,1); + volume.qverts.push_vert(a.x, a.y, top_z); + volume.qverts.push_vert(b.x, b.y, top_z); + + volume.qverts.push_norm(xy_left_normal); + volume.qverts.push_norm(xy_left_normal); + volume.qverts.push_vert(b2.x, b2.y, middle_z); + volume.qverts.push_vert(a2.x, a2.y, middle_z); + } + + // bottom-left face + { + volume.qverts.push_norm(xy_left_normal); + volume.qverts.push_norm(xy_left_normal); + volume.qverts.push_vert(a2.x, a2.y, middle_z); + volume.qverts.push_vert(b2.x, b2.y, middle_z); + + // normal going downwards + volume.qverts.push_norm(0,0,-1); + volume.qverts.push_norm(0,0,-1); + volume.qverts.push_vert(b.x, b.y, bottom_z); + volume.qverts.push_vert(a.x, a.y, bottom_z); + } } } @@ -469,7 +693,10 @@ static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, fl // Create 3D thick extrusion lines for a skirt and brim. // Adds a new Slic3r::GUI::3DScene::Volume to volumes. -void _3DScene::_load_print_toolpaths(const Print *print, GLVolumeCollection *volumes) +void _3DScene::_load_print_toolpaths( + const Print *print, + GLVolumeCollection *volumes, + bool use_VBOs) { if (! print->has_skirt() && print->config.brim_width.value == 0) return; @@ -520,7 +747,8 @@ void _3DScene::_load_print_toolpaths(const Print *print, GLVolumeCollection *vol // one for perimeters, one for infill and one for supports. void _3DScene::_load_print_object_toolpaths( const PrintObject *print_object, - GLVolumeCollection *volumes) + GLVolumeCollection *volumes, + bool use_VBOs) { struct Ctxt { diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index d3fb7cffe..77ffb1fa6 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -103,7 +103,10 @@ public: selected(false), hover(false), qverts_range(0, size_t(-1)), - tverts_range(0, size_t(-1)) + tverts_range(0, size_t(-1)), + name_vertex_buffer(0), + name_normal_buffer(0), + name_index_buffer(0) { color[0] = r; color[1] = g; @@ -144,10 +147,17 @@ public: // Triangle vertices. GLVertexArray tverts; std::pair tverts_range; + // OpenGL buffers for vertices and their normals. + int name_vertex_buffer; + int name_normal_buffer; + // OpenGL buffer of the indices. + int name_index_buffer; + // Triangle indices for the vertex buffer object. + std::vector triangle_indices; // If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts // of the extrusions per layer. - // The offsets stores tripples of (z_top, qverts_idx, tverts_idx) in a linear array. std::vector print_zs; + // Offset into qverts & tverts, or offsets into indices stored into an OpenGL name_index_buffer. std::vector offsets; int object_idx() const { return this->composite_id / 1000000; } @@ -165,6 +175,8 @@ public: void* tnorms_to_render_ptr() { return tverts.norms.data() + tverts_range.first; } size_t tverts_to_render_cnt() { return std::min(tverts.verts.size(), tverts_range.second - tverts_range.first); } + void render_VBOs() const; + std::shared_ptr layer_height_texture; bool has_layer_height_texture() const @@ -209,6 +221,7 @@ public: void clear() { for (auto *v : volumes) delete v; volumes.clear(); } bool empty() const { return volumes.empty(); } void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } + void render_VBOs() const { for (GLVolume *vol : this->volumes) vol->render_VBOs(); } }; class _3DScene @@ -216,11 +229,13 @@ class _3DScene public: static void _load_print_toolpaths( const Print *print, - GLVolumeCollection *volumes); + GLVolumeCollection *volumes, + bool use_VBOs); static void _load_print_object_toolpaths( const PrintObject *print_object, - GLVolumeCollection *volumes); + GLVolumeCollection *volumes, + bool use_VBOs); }; } diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 82ed0a893..e06b640a7 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -30,6 +30,8 @@ bool empty() const; Clone origin() const %code%{ RETVAL = THIS->origin; %}; + void translate(double x, double y, double z) + %code%{ THIS->origin.translate(x, y, z); %}; Clone bounding_box() const %code%{ RETVAL = THIS->bounding_box; %}; Clone transformed_bounding_box() const; @@ -87,17 +89,19 @@ GLVolumeCollection::arrayref() %{ void -_load_print_toolpaths(print, volumes) +_load_print_toolpaths(print, volumes, use_VBOs) Print *print; GLVolumeCollection *volumes; + bool use_VBOs; CODE: - _3DScene::_load_print_toolpaths(print, volumes); + _3DScene::_load_print_toolpaths(print, volumes, use_VBOs); void -_load_print_object_toolpaths(print_object, volumes) +_load_print_object_toolpaths(print_object, volumes, use_VBOs) PrintObject *print_object; GLVolumeCollection *volumes; + bool use_VBOs; CODE: - _3DScene::_load_print_object_toolpaths(print_object, volumes); + _3DScene::_load_print_object_toolpaths(print_object, volumes, use_VBOs); %}