From e6fddd364d876c3970a440a7c37f8a6221388fea Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 13 Mar 2017 16:02:17 +0100 Subject: [PATCH] Volume rewritten from Perl to C++, generation of vertex arrays from paths rewritten from Perl to C++, parallelized. --- lib/Slic3r/GUI/3DScene.pm | 622 +++------------------------------- lib/Slic3r/Print.pm | 7 - xs/lib/Slic3r/XS.pm | 7 +- xs/src/libslic3r/Slicing.cpp | 2 +- xs/src/perlglue.cpp | 3 +- xs/src/slic3r/GUI/3DScene.cpp | 574 ++++++++++++++++++++++++++----- xs/src/slic3r/GUI/3DScene.hpp | 182 +++++++++- xs/xsp/GUI_3DScene.xsp | 105 +++++- xs/xsp/Print.xsp | 14 - xs/xsp/my.map | 5 +- xs/xsp/typemap.xspt | 5 +- 11 files changed, 825 insertions(+), 701 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index aa2e9bca5..d63398aef 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1,12 +1,8 @@ # Implements pure perl packages # # Slic3r::GUI::3DScene::Base; -# Slic3r::GUI::3DScene::Volume; # Slic3r::GUI::3DScene; # -# It uses static methods of a C++ class Slic3r::GUI::_3DScene::GLVertexArray -# for efficient building of vertex arrays for OpenGL rendering. -# # Slic3r::GUI::Plater::3D derives from Slic3r::GUI::3DScene, # Slic3r::GUI::Plater::3DPreview, Slic3r::GUI::Plater::3DToolpaths, # Slic3r::GUI::Plater::ObjectCutDialog and Slic3r::GUI::Plater::ObjectPartsPanel @@ -136,18 +132,17 @@ sub new { $self->_stheta(45); $self->_sphi(45); $self->_zoom(1); - + + # Collection of GLVolume objects + $self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new); + # 3D point in model space $self->_camera_type('ortho'); # $self->_camera_type('perspective'); $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); $self->_camera_distance(0.); - # Size of a layer height texture, used by a shader to color map the object print layers. $self->layer_editing_enabled(0); - # 512x512 bitmaps are supported everywhere, but that may not be sufficent for super large print volumes. - $self->{layer_preview_z_texture_width} = 1024; - $self->{layer_preview_z_texture_height} = 1024; $self->{layer_height_edit_band_width} = 2.; $self->{layer_height_edit_strength} = 0.005; $self->{layer_height_edit_last_object_id} = -1; @@ -327,10 +322,8 @@ sub _variable_layer_thickness_action { $self->{layer_height_edit_strength}, $self->{layer_height_edit_band_width}, $self->{layer_height_edit_last_action}); - $self->{print}->get_object($self->{layer_height_edit_last_object_id})->generate_layer_height_texture( - $self->volumes->[$self->{layer_height_edit_last_object_id}]->layer_height_texture_data->ptr, - $self->{layer_preview_z_texture_height}, - $self->{layer_preview_z_texture_width}); + $self->volumes->[$self->{layer_height_edit_last_object_id}]->generate_layer_height_texture( + $self->{print}->get_object($self->{layer_height_edit_last_object_id}), 1); $self->Refresh; # Automatic action on mouse down with the same coordinate. $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS); @@ -545,8 +538,7 @@ sub mouse_wheel_event { # Reset selection. sub reset_objects { my ($self) = @_; - - $self->volumes([]); + $self->volumes->erase; $self->_dirty(1); } @@ -724,13 +716,12 @@ sub set_bed_shape { sub deselect_volumes { my ($self) = @_; - $_->selected(0) for @{$self->volumes}; + $_->set_selected(0) for @{$self->volumes}; } sub select_volume { my ($self, $volume_idx) = @_; - - $self->volumes->[$volume_idx]->selected(1) + $self->volumes->[$volume_idx]->set_selected(1) if $volume_idx != -1; } @@ -1053,14 +1044,14 @@ sub Render { my $col = [ glReadPixels_p($pos->x, $self->GetSize->GetHeight - $pos->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE) ]; my $volume_idx = $col->[0] + $col->[1]*256 + $col->[2]*256*256; $self->_hover_volume_idx(undef); - $_->hover(0) for @{$self->volumes}; + $_->set_hover(0) for @{$self->volumes}; if ($volume_idx <= $#{$self->volumes}) { $self->_hover_volume_idx($volume_idx); - $self->volumes->[$volume_idx]->hover(1); + $self->volumes->[$volume_idx]->set_hover(1); my $group_id = $self->volumes->[$volume_idx]->select_group_id; if ($group_id != -1) { - $_->hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes}; + $_->set_hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes}; } $self->on_hover->($volume_idx) if $self->on_hover; @@ -1213,17 +1204,10 @@ sub draw_volumes { my $volume = $self->volumes->[$volume_idx]; my $shader_active = 0; - if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->{layer_height_texture_data}) { - my $print_object = $self->{print}->get_object(int($volume->select_group_id / 1000000)); - { - # Update the height texture if the ModelObject::layer_height_texture is invalid. - my $ncells = $print_object->generate_layer_height_texture( - $volume->{layer_height_texture_data}->ptr, - $self->{layer_preview_z_texture_height}, - $self->{layer_preview_z_texture_width}, - !defined($volume->{layer_height_texture_cells})); - $volume->{layer_height_texture_cells} = $ncells if $ncells > 0; - } + if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->has_layer_height_texture) { + # Update the height texture if the ModelObject::layer_height_texture is invalid. + $volume->generate_layer_height_texture( + $self->{print}->get_object(int($volume->select_group_id / 1000000)), 0); $self->{shader}->Enable; my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row'); my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized'); @@ -1233,35 +1217,23 @@ sub draw_volumes { die if ! defined($z_texture_row_to_normalized_id); die if ! defined($z_cursor_id); die if ! defined($z_cursor_band_width_id); - my $ncells = $volume->{layer_height_texture_cells}; - my $z_max = $volume->{bounding_box}->z_max; - glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max)); - glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height}); - glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative); + glUniform1fARB($z_to_texture_row_id, $volume->layer_height_texture_z_to_row_id); + glUniform1fARB($z_texture_row_to_normalized_id, 1. / $volume->layer_height_texture_height); + glUniform1fARB($z_cursor_id, $volume->bounding_box->z_max * $z_cursor_relative); glUniform1fARB($z_cursor_band_width_id, $self->{layer_height_edit_band_width}); glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LEVEL, 0); # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - if (1) { - glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, - 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, - 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); # glPixelStorei(GL_UNPACK_ALIGNMENT, 1); # glPixelStorei(GL_UNPACK_ROW_LENGTH, $self->{layer_preview_z_texture_width}); - glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, - GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr); - glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, - GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4)); - } else { - glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, - 0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr); - glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width}/2, $self->{layer_preview_z_texture_height}/2, - 0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr + $self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4); - } - -# my $nlines = ceil($ncells / ($self->{layer_preview_z_texture_width} - 1)); - + glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $volume->layer_height_texture_width, $volume->layer_height_texture_height, + GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level0); + glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, + GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level1); $shader_active = 1; } elsif ($fakecolor) { # Object picking mode. Render the object with a color encoding the object index. @@ -1277,68 +1249,21 @@ sub draw_volumes { glColor4f(@{ $volume->color }); } - my $qverts_begin = 0; - my $qverts_end = defined($volume->qverts) ? $volume->qverts->size() : 0; - my $tverts_begin = 0; - my $tverts_end = defined($volume->tverts) ? $volume->tverts->size() : 0; - my $n_offsets = ($volume->range && $volume->offsets) ? scalar(@{$volume->offsets}) : 0; - if ($n_offsets) { - # The Z layer range is specified. - # First test whether the Z span of this object is not out of ($min_z, $max_z) completely. - my ($min_z, $max_z) = @{$volume->range}; - next if ($volume->offsets->[0] > $max_z || $volume->offsets->[-3] < $min_z); - # Then find the lowest layer to be displayed. - my $i = 0; - while ($i < $n_offsets && $volume->offsets->[$i] < $min_z) { - $i += 3; - } - # This shall not happen. - next if ($i == $n_offsets); - # Remember start of the layer. - $qverts_begin = $volume->offsets->[$i+1]; - $tverts_begin = $volume->offsets->[$i+2]; - # Some layers are above $min_z. Which? - while ($i < $n_offsets && $volume->offsets->[$i] <= $max_z) { - $i += 3; - } - if ($i < $n_offsets) { - $qverts_end = $volume->offsets->[$i+1]; - $tverts_end = $volume->offsets->[$i+2]; - } - } - glPushMatrix(); glTranslatef(@{$volume->origin}); - glCullFace(GL_BACK); - if ($qverts_begin < $qverts_end) { - glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr); - glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_ptr); - $qverts_begin /= 3; - $qverts_end /= 3; - my $nvertices = $qverts_end-$qverts_begin; - while ($nvertices > 0) { - my $nvertices_this = ($nvertices > 4096) ? 4096 : $nvertices; - glDrawArrays(GL_QUADS, $qverts_begin, $nvertices_this); - $qverts_begin += $nvertices_this; - $nvertices -= $nvertices_this; - } + my $qverts_cnt = $volume->qverts_to_render_cnt; + if ($qverts_cnt) { + glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts_to_render_ptr); + glNormalPointer_c(GL_FLOAT, 0, $volume->qnorms_to_render_ptr); + glDrawArrays(GL_QUADS, 0, $qverts_cnt / 3); } - - if ($tverts_begin < $tverts_end) { - glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr); - glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_ptr); - $tverts_begin /= 3; - $tverts_end /= 3; - my $nvertices = $tverts_end-$tverts_begin; - while ($nvertices > 0) { - my $nvertices_this = ($nvertices > 4095) ? 4095 : $nvertices; - glDrawArrays(GL_TRIANGLES, $tverts_begin, $nvertices_this); - $tverts_begin += $nvertices_this; - $nvertices -= $nvertices_this; - } + my $tverts_cnt = $volume->tverts_to_render_cnt; + if ($tverts_cnt) { + glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts_to_render_ptr); + glNormalPointer_c(GL_FLOAT, 0, $volume->tnorms_to_render_ptr); + glDrawArrays(GL_TRIANGLES, 0, $tverts_cnt / 3); } - glVertexPointer_c(3, GL_FLOAT, 0, 0); glNormalPointer_c(GL_FLOAT, 0, 0); glPopMatrix(); @@ -1369,7 +1294,6 @@ sub _load_image_set_texture { # Get RGB & alpha raw data from wxImage, interleave them into a Perl array. my @rgb = unpack 'C*', $img->GetData(); my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3); -# my @alpha = unpack 'C*', $img->GetAlpha(); my $n_pixels = int(@alpha); my @data = (0)x($n_pixels * 4); for (my $i = 0; $i < $n_pixels; $i += 1) { @@ -1441,7 +1365,7 @@ sub draw_active_object_annotations { my $volume; foreach my $volume_idx (0..$#{$self->volumes}) { my $v = $self->volumes->[$volume_idx]; - if ($v->selected && $v->{layer_height_texture_data} && $v->{layer_height_texture_cells}) { + if ($v->selected && $v->has_layer_height_texture) { $volume = $v; last; } @@ -1458,20 +1382,18 @@ sub draw_active_object_annotations { my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row'); my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized'); my $z_cursor_id = $self->{shader}->Map('z_cursor'); - my $ncells = $volume->{layer_height_texture_cells}; - my $z_max = $volume->{bounding_box}->z_max; - glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max)); - glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height}); - glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative); + glUniform1fARB($z_to_texture_row_id, $volume->layer_height_texture_z_to_row_id); + glUniform1fARB($z_texture_row_to_normalized_id, 1. / $volume->layer_height_texture_height); + glUniform1fARB($z_cursor_id, $volume->bounding_box->z_max * $z_cursor_relative); glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); - glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, + glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, + glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, - GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr); - glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, - GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4)); + glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $volume->layer_height_texture_width, $volume->layer_height_texture_height, + GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level0); + glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, + GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level1); # Render the color bar. glDisable(GL_DEPTH_TEST); @@ -1483,8 +1405,8 @@ sub draw_active_object_annotations { glBegin(GL_QUADS); glVertex3f($bar_left, $bar_bottom, 0); glVertex3f($bar_right, $bar_bottom, 0); - glVertex3f($bar_right, $bar_top, $volume->{bounding_box}->z_max); - glVertex3f($bar_left, $bar_top, $volume->{bounding_box}->z_max); + glVertex3f($bar_right, $bar_top, $volume->bounding_box->z_max); + glVertex3f($bar_left, $bar_top, $volume->bounding_box->z_max); glEnd(); glBindTexture(GL_TEXTURE_2D, 0); $self->{shader}->Disable; @@ -1715,65 +1637,6 @@ void main() FRAGMENT } -# Container for object geometry and selection status. -package Slic3r::GUI::3DScene::Volume; -use Moo; - -has 'bounding_box' => (is => 'ro', required => 1); -has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); -has 'color' => (is => 'ro', required => 1); -# An ID containing the object ID, volume ID and instance ID. -has 'composite_id' => (is => 'rw', default => sub { -1 }); -# An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance. -has 'select_group_id' => (is => 'rw', default => sub { -1 }); -# An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance. -has 'drag_group_id' => (is => 'rw', default => sub { -1 }); -# Boolean: Is this object selected? -has 'selected' => (is => 'rw', default => sub { 0 }); -# Boolean: Is mouse over this object? -has 'hover' => (is => 'rw', default => sub { 0 }); -# Vector of two values: a span in the Z axis. Meaningful for a display of layers. -has 'range' => (is => 'rw'); - -# Geometric data. -# Quads: GLVertexArray object: C++ class maintaining an std::vector for coords and normals. -has 'qverts' => (is => 'rw'); -# Triangles: GLVertexArray object -has 'tverts' => (is => 'rw'); -# 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. -has 'offsets' => (is => 'rw'); - -# RGBA texture along the Z axis of an object, to visualize layers by stripes colored by their height. -has 'layer_height_texture_data' => (is => 'rw'); -# Number of texture cells. -has 'layer_height_texture_cells' => (is => 'rw'); - -sub object_idx { - my ($self) = @_; - return int($self->composite_id / 1000000); -} - -sub volume_idx { - my ($self) = @_; - return ($self->composite_id / 1000) % 1000; -} - -sub instance_idx { - my ($self) = @_; - return $self->composite_id % 1000; -} - -sub transformed_bounding_box { - my ($self) = @_; - - my $bb = $self->bounding_box; - $bb->translate(@{$self->origin}); - return $bb; -} - - # The 3D canvas to display objects and tool paths. package Slic3r::GUI::3DScene; use base qw(Slic3r::GUI::3DScene::Base); @@ -1783,9 +1646,6 @@ use List::Util qw(first min max); use Slic3r::Geometry qw(scale unscale epsilon); use Slic3r::Print::State ':steps'; -# Perimeter: yellow, Infill: redish, Suport: greenish, last: blueish, -use constant COLORS => [ [1,1,0,1], [1,0.5,0.5,1], [0.5,1,0.5,1], [0.5,0.5,1,1] ]; - __PACKAGE__->mk_accessors(qw( color_by select_by @@ -1816,177 +1676,17 @@ sub load_object { } $instance_idxs ||= [0..$#{$model_object->instances}]; - - # Object will have a single common layer height texture for all volumes. - my $layer_height_texture_data; - my $layer_height_texture_cells; - if ($print && $obj_idx < $print->object_count) { - # Generate the layer height texture. Allocate data for the 0th and 1st mipmap levels. - $layer_height_texture_data = OpenGL::Array->new($self->{layer_preview_z_texture_width}*$self->{layer_preview_z_texture_height}*5, GL_UNSIGNED_BYTE); - } - - my @volumes_idx = (); - foreach my $volume_idx (0..$#{$model_object->volumes}) { - my $volume = $model_object->volumes->[$volume_idx]; - foreach my $instance_idx (@$instance_idxs) { - my $instance = $model_object->instances->[$instance_idx]; - my $mesh = $volume->mesh->clone; - $instance->transform_mesh($mesh); - - my $color_idx; - if ($self->color_by eq 'volume') { - $color_idx = $volume_idx; - } elsif ($self->color_by eq 'object') { - $color_idx = $obj_idx; - } - - # Using the colors 'yellowish', 'greenish', 'blueish' for both the extrusion paths - # and the volumes of a single multi-color object. - #FIXME so for 4 or more color print, there will be only 3 colors displayed, which will - # not correspond to the color of the filament. - my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; - $color->[3] = $volume->modifier ? 0.5 : 1; - push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( - bounding_box => $mesh->bounding_box, - color => $color, - ); - $v->composite_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx); - if ($self->select_by eq 'object') { - $v->select_group_id($obj_idx*1000000); - } elsif ($self->select_by eq 'volume') { - $v->select_group_id($obj_idx*1000000 + $volume_idx*1000); - } elsif ($self->select_by eq 'instance') { - $v->select_group_id($v->composite_id); - } - if ($self->drag_by eq 'object') { - $v->drag_group_id($obj_idx*1000); - } elsif ($self->drag_by eq 'instance') { - $v->drag_group_id($obj_idx*1000 + $instance_idx); - } - push @volumes_idx, my $scene_volume_idx = $#{$self->volumes}; - - my $verts = Slic3r::GUI::_3DScene::GLVertexArray->new; - $verts->load_mesh($mesh); - $v->tverts($verts); - - if (! $volume->modifier) { - $v->layer_height_texture_data($layer_height_texture_data); - $v->layer_height_texture_cells($layer_height_texture_cells); - } - } - } - - return @volumes_idx; -} - -# Called possibly by utils/view-toolpaths.pl, likely broken. -sub load_print_object_slices { - my ($self, $object) = @_; - - my @verts = (); - my @norms = (); - my @quad_verts = (); - my @quad_norms = (); - foreach my $layer (@{$object->layers}) { - my $gap = 0; - my $top_z = $layer->print_z; - my $bottom_z = $layer->print_z - $layer->height + $gap; - - foreach my $copy (@{ $object->_shifted_copies }) { - { - my @expolygons = map $_->clone, @{$layer->slices}; - $_->translate(@$copy) for @expolygons; - $self->_expolygons_to_verts(\@expolygons, $layer->print_z, \@verts, \@norms); - } - foreach my $slice (@{$layer->slices}) { - foreach my $polygon (@$slice) { - foreach my $line (@{$polygon->lines}) { - $line->translate(@$copy); - - push @quad_norms, (0,0,-1), (0,0,-1); - push @quad_verts, (map unscale($_), @{$line->a}), $bottom_z; - push @quad_verts, (map unscale($_), @{$line->b}), $bottom_z; - push @quad_norms, (0,0,1), (0,0,1); - push @quad_verts, (map unscale($_), @{$line->b}), $top_z; - push @quad_verts, (map unscale($_), @{$line->a}), $top_z; - - # We'll use this for the middle normal when using 4 quads: - #my $xy_normal = $line->normal; - #$_xynormal->scale(1/$line->length); - } - } - } - } - } - - my $obb = $object->bounding_box; - my $bb = Slic3r::Geometry::BoundingBoxf3->new; - $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->min_point}, 0)); - $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->max_point}, $object->size->z)); - - push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[0], - verts => OpenGL::Array->new_list(GL_FLOAT, @verts), - norms => OpenGL::Array->new_list(GL_FLOAT, @norms), - quad_verts => OpenGL::Array->new_list(GL_FLOAT, @quad_verts), - quad_norms => OpenGL::Array->new_list(GL_FLOAT, @quad_norms), - ); + my $volume_indices = $self->volumes->load_object($model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by); + return @{$volume_indices}; } # Create 3D thick extrusion lines for a skirt and brim. # Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes. sub load_print_toolpaths { my ($self, $print) = @_; - - return if !$print->step_done(STEP_SKIRT); - return if !$print->step_done(STEP_BRIM); - return if !$print->has_skirt && $print->config->brim_width == 0; - - my $qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - my $tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - my $offsets = []; # triples stored in a linear array, sorted by print_z: print_z, qverts, tverts - - my $skirt_height = 0; # number of layers - if ($print->has_infinite_skirt) { - $skirt_height = $print->total_layer_count; - } else { - $skirt_height = min($print->config->skirt_height, $print->total_layer_count); - } - $skirt_height ||= 1 if $print->config->brim_width > 0; - - # get first $skirt_height layers (maybe this should be moved to a PrintObject method?) - my $object0 = $print->get_object(0); - my @layers = (); - push @layers, map $object0->get_layer($_-1), 1..min($skirt_height, $object0->layer_count); - push @layers, map $object0->get_support_layer($_-1), 1..min($skirt_height, $object0->support_layer_count); - @layers = sort { $a->print_z <=> $b->print_z } @layers; - @layers = @layers[0..($skirt_height-1)]; - - foreach my $i (0..($skirt_height-1)) { - my $top_z = $layers[$i]->print_z; - push @$offsets, ($top_z, $qverts->size, $tverts->size); - - if ($i == 0) { - $self->_extrusionentity_to_verts($print->brim, $top_z, Slic3r::Point->new(0,0), $qverts, $tverts); - } - - $self->_extrusionentity_to_verts($print->skirt, $top_z, Slic3r::Point->new(0,0), $qverts, $tverts); - } - my $bb = Slic3r::Geometry::BoundingBoxf3->new; - { - my $pbb = $print->bounding_box; - $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$pbb->min_point})); - $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$pbb->max_point})); - } - push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[2], - qverts => $qverts, - tverts => $tverts, - offsets => $offsets, - ); + Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes) + if ($print->step_done(STEP_SKIRT) && $print->step_done(STEP_BRIM)); } # Create 3D thick extrusion lines for object forming extrusions. @@ -1994,225 +1694,13 @@ sub load_print_toolpaths { # one for perimeters, one for infill and one for supports. sub load_print_object_toolpaths { my ($self, $object) = @_; - - my $perim_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - my $perim_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - my $infill_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - my $infill_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - my $support_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - my $support_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - - my $perim_offsets = []; # triples of (print_z, qverts, tverts), stored linearly, sorted by print_z - my $infill_offsets = []; - my $support_offsets = []; - - # order layers by print_z - my @layers = sort { $a->print_z <=> $b->print_z } - @{$object->layers}, @{$object->support_layers}; - - # Bounding box of the object and its copies. - my $bb = Slic3r::Geometry::BoundingBoxf3->new; - { - my $obb = $object->bounding_box; - foreach my $copy (@{ $object->_shifted_copies }) { - my $cbb = $obb->clone; - $cbb->translate(@$copy); - $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$cbb->min_point}, 0)); - $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$cbb->max_point}, $object->size->z)); - } - } - # Maximum size of an allocation block: 32MB / sizeof(float) - my $alloc_size_max = 32 * 1048576 / 4; - - foreach my $layer (@layers) { - my $top_z = $layer->print_z; - - push @$perim_offsets, ($top_z, $perim_qverts->size, $perim_tverts->size) - if (!@$perim_offsets || $perim_offsets->[-3] != $top_z); - push @$infill_offsets, ($top_z, $infill_qverts->size, $infill_tverts->size) - if (!@$infill_offsets || $infill_offsets->[-3] != $top_z); - push @$support_offsets, ($top_z, $support_qverts->size, $support_tverts->size) - if (!@$support_offsets || $support_offsets->[-3] != $top_z); - - foreach my $copy (@{ $object->_shifted_copies }) { - foreach my $layerm (@{$layer->regions}) { - if ($object->step_done(STEP_PERIMETERS)) { - $self->_extrusionentity_to_verts($layerm->perimeters, $top_z, $copy, - $perim_qverts, $perim_tverts); - } - - if ($object->step_done(STEP_INFILL)) { - $self->_extrusionentity_to_verts($layerm->fills, $top_z, $copy, - $infill_qverts, $infill_tverts); - } - } - - if ($layer->isa('Slic3r::Layer::Support') && $object->step_done(STEP_SUPPORTMATERIAL)) { - $self->_extrusionentity_to_verts($layer->support_fills, $top_z, $copy, - $support_qverts, $support_tverts); - - $self->_extrusionentity_to_verts($layer->support_interface_fills, $top_z, $copy, - $support_qverts, $support_tverts); - } - } - - if ($perim_qverts->size() > $alloc_size_max || $perim_tverts->size() > $alloc_size_max) { - # Store the vertex arrays and restart their containers. - push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[0], - qverts => $perim_qverts, - tverts => $perim_tverts, - offsets => $perim_offsets, - ); - $perim_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - $perim_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - $perim_offsets = []; - } - - if ($infill_qverts->size() > $alloc_size_max || $infill_tverts->size() > $alloc_size_max) { - # Store the vertex arrays and restart their containers. - push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[1], - qverts => $infill_qverts, - tverts => $infill_tverts, - offsets => $infill_offsets, - ); - $infill_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - $infill_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - $infill_offsets = []; - } - - if ($support_qverts->size() > $alloc_size_max || $support_tverts->size() > $alloc_size_max) { - # Store the vertex arrays and restart their containers. - push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[2], - qverts => $support_qverts, - tverts => $support_tverts, - offsets => $support_offsets, - ); - $support_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - $support_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; - $support_offsets = []; - } - } - - if ($perim_qverts->size() > 0 || $perim_tverts->size() > 0) { - push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[0], - qverts => $perim_qverts, - tverts => $perim_tverts, - offsets => $perim_offsets, - ); - } - - if ($infill_qverts->size() > 0 || $infill_tverts->size() > 0) { - push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[1], - qverts => $infill_qverts, - tverts => $infill_tverts, - offsets => $infill_offsets, - ); - } - - if ($support_qverts->size() > 0 || $support_tverts->size() > 0) { - push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[2], - qverts => $support_qverts, - tverts => $support_tverts, - offsets => $support_offsets, - ); - } + Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes); } sub set_toolpaths_range { my ($self, $min_z, $max_z) = @_; - - foreach my $volume (@{$self->volumes}) { - $volume->range([ $min_z, $max_z ]); - } -} - -# called by load_print_object_slices, probably not used. -sub _expolygons_to_verts { - my ($self, $expolygons, $z, $verts, $norms) = @_; - - my $tess = gluNewTess(); - gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_END, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_VERTEX, sub { - my ($x, $y, $z) = @_; - push @$verts, $x, $y, $z; - push @$norms, (0,0,1), (0,0,1), (0,0,1); - }); - gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT'); - - foreach my $expolygon (@$expolygons) { - gluTessBeginPolygon($tess); - foreach my $polygon (@$expolygon) { - gluTessBeginContour($tess); - gluTessVertex_p($tess, (map unscale($_), @$_), $z) for @$polygon; - gluTessEndContour($tess); - } - gluTessEndPolygon($tess); - } - - gluDeleteTess($tess); -} - -# Fill in the $qverts and $tverts with quads and triangles -# for the extrusion $entity. -sub _extrusionentity_to_verts { - my ($self, $entity, $top_z, $copy, $qverts, $tverts) = @_; - - my ($lines, $widths, $heights, $closed); - if ($entity->isa('Slic3r::ExtrusionPath::Collection')) { - $self->_extrusionentity_to_verts($_, $top_z, $copy, $qverts, $tverts) - for @$entity; - return; - } elsif ($entity->isa('Slic3r::ExtrusionPath')) { - my $polyline = $entity->polyline->clone; - $polyline->remove_duplicate_points; - $polyline->translate(@$copy); - $lines = $polyline->lines; - $widths = [ map $entity->width, 0..$#$lines ]; - $heights = [ map $entity->height, 0..$#$lines ]; - $closed = 0; - } else { - $lines = []; - $widths = []; - $heights = []; - # $entity is either of type Slic3r::ExtrusionLoop or Slic3r::ExtrusionMultiPath. - $closed = $entity->isa('Slic3r::ExtrusionLoop') ? 1 : 0; - foreach my $path (@$entity) { - my $polyline = $path->polyline->clone; - $polyline->remove_duplicate_points; - $polyline->translate(@$copy); - my $path_lines = $polyline->lines; - push @$lines, @$path_lines; - push @$widths, map $path->width, 0..$#$path_lines; - push @$heights, map $path->height, 0..$#$path_lines; - } - } - # Calling the C++ implementation Slic3r::_3DScene::_extrusionentity_to_verts_do() - # This adds new vertices to the $qverts and $tverts. - Slic3r::GUI::_3DScene::_extrusionentity_to_verts_do($lines, $widths, $heights, - $closed, - # Top height of the extrusion. - $top_z, - # $copy is not used here. - $copy, - # GLVertexArray object: C++ class maintaining an std::vector for coords and normals. - $qverts, - $tverts); + $self->volumes->set_range($min_z, $max_z); } 1; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 25d68b388..1a4aad5b3 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -26,13 +26,6 @@ sub status_cb { return $status_cb // sub {}; } -# this value is not supposed to be compared with $layer->id -# since they have different semantics -sub total_layer_count { - my $self = shift; - return max(map $_->total_layer_count, @{$self->objects}); -} - sub size { my $self = shift; return $self->bounding_box->size; diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index a66b8ad49..3baf0dfe3 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -247,7 +247,11 @@ sub new { ); } -package Slic3r::GUI::_3DScene::GLVertexArray; +package Slic3r::GUI::_3DScene::GLVolume::Collection; +use overload + '@{}' => sub { $_[0]->arrayref }, + 'fallback' => 1; + sub CLONE_SKIP { 1 } package main; @@ -279,6 +283,7 @@ for my $class (qw( Slic3r::Geometry::BoundingBox Slic3r::Geometry::BoundingBoxf Slic3r::Geometry::BoundingBoxf3 + Slic3r::GUI::_3DScene::GLVolume Slic3r::Layer Slic3r::Layer::Region Slic3r::Layer::Support diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp index ea43e704f..5303d74a4 100644 --- a/xs/src/libslic3r/Slicing.cpp +++ b/xs/src/libslic3r/Slicing.cpp @@ -593,7 +593,7 @@ int generate_layer_height_texture( #endif // Clear the main texture and the 2nd LOD level. - memset(data, 0, rows * cols * 5); +// memset(data, 0, rows * cols * (level_of_detail_2nd_level ? 5 : 4)); // 2nd LOD level data start unsigned char *data1 = reinterpret_cast(data) + rows * cols * 4; int ncells = std::min((cols-1) * rows, int(ceil(16. * (slicing_params.object_print_z_height() / slicing_params.min_layer_height)))); diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 552524eaa..d8f9db480 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -60,7 +60,8 @@ REGISTER_CLASS(Surface, "Surface"); REGISTER_CLASS(SurfaceCollection, "Surface::Collection"); REGISTER_CLASS(PrintObjectSupportMaterial, "Print::SupportMaterial2"); REGISTER_CLASS(TriangleMesh, "TriangleMesh"); -REGISTER_CLASS(GLVertexArray, "GUI::_3DScene::GLVertexArray"); +REGISTER_CLASS(GLVolume, "GUI::_3DScene::GLVolume"); +REGISTER_CLASS(GLVolumeCollection, "GUI::_3DScene::GLVolume::Collection"); SV* ConfigBase__as_hash(ConfigBase* THIS) { diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 323b4240b..82ebbfeed 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1,21 +1,172 @@ #include "3DScene.hpp" +#include "../../libslic3r/libslic3r.h" +#include "../../libslic3r/ExtrusionEntity.hpp" +#include "../../libslic3r/ExtrusionEntityCollection.hpp" +#include "../../libslic3r/Print.hpp" +#include "../../libslic3r/Slicing.hpp" + +#include +#include +#include #include +#include + +#include + +#include +#include namespace Slic3r { +void GLVertexArray::load_mesh(const TriangleMesh &mesh) +{ + this->reserve_more(3 * 3 * mesh.facets_count()); + + for (int i = 0; i < mesh.stl.stats.number_of_facets; ++ i) { + stl_facet &facet = mesh.stl.facet_start[i]; + for (int j = 0; j < 3; ++ j) { + this->push_norm(facet.normal.x, facet.normal.y, facet.normal.z); + this->push_vert(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z); + } + } +} + +void GLVolume::set_range(double min_z, double max_z) +{ + this->qverts_range.first = 0; + this->qverts_range.second = this->qverts.size(); + this->tverts_range.first = 0; + this->tverts_range.second = this->tverts.size(); + if (! this->print_zs.empty()) { + // The Z layer range is specified. + // First test whether the Z span of this object is not out of (min_z, max_z) completely. + if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) { + this->qverts_range.second = 0; + this->tverts_range.second = 0; + } else { + // Then find the lowest layer to be displayed. + size_t i = 0; + for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++ i); + if (i == this->print_zs.size()) { + // This shall not happen. + this->qverts_range.second = 0; + this->tverts_range.second = 0; + } else { + // Remember start of the layer. + this->qverts_range.first = this->offsets[i * 2]; + this->tverts_range.first = this->offsets[i * 2 + 1]; + // Some layers are above $min_z. Which? + for (; i < this->print_zs.size() && this->print_zs[i] <= max_z; ++ i); + if (i < this->print_zs.size()) { + this->qverts_range.second = this->offsets[i * 2]; + this->tverts_range.second = this->offsets[i * 2 + 1]; + } + } + } + } +} + +void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool force) +{ + GLTexture *tex = this->layer_height_texture.get(); + if (tex == nullptr) + // No layer_height_texture is assigned to this GLVolume, therefore the layer height texture cannot be filled. + return; + + // Always try to update the layer height profile. + bool update = print_object->update_layer_height_profile(print_object->model_object()->layer_height_profile) || force; + // Update if the layer height profile was changed, or when the texture is not valid. + if (! update && ! tex->data.empty() && tex->cells > 0) + // Texture is valid, don't update. + return; + + if (tex->data.empty()) { + tex->width = 1024; + tex->height = 1024; + tex->levels = 2; + tex->data.assign(tex->width * tex->height * 5, 0); + } + + SlicingParameters slicing_params = print_object->slicing_parameters(); + bool level_of_detail_2nd_level = true; + tex->cells = Slic3r::generate_layer_height_texture( + slicing_params, + Slic3r::generate_object_layers(slicing_params, print_object->model_object()->layer_height_profile), + tex->data.data(), tex->height, tex->width, level_of_detail_2nd_level); +} + +// 512x512 bitmaps are supported everywhere, but that may not be sufficent for super large print volumes. +#define LAYER_HEIGHT_TEXTURE_WIDTH 1024 +#define LAYER_HEIGHT_TEXTURE_HEIGHT 1024 + +std::vector GLVolumeCollection::load_object( + const ModelObject *model_object, + int obj_idx, + const std::vector &instance_idxs, + const std::string &color_by, + const std::string &select_by, + const std::string &drag_by) +{ + static float colors[4][4] = { + { 1.0f, 1.0f, 0.0f, 1.f }, + { 1.0f, 0.5f, 0.5f, 1.f }, + { 0.5f, 1.0f, 0.5f, 1.f }, + { 0.5f, 0.5f, 1.0f, 1.f } + }; + + // Object will have a single common layer height texture for all volumes. + std::shared_ptr layer_height_texture = std::make_shared(); + + std::vector volumes_idx; + for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) { + const ModelVolume *model_volume = model_object->volumes[volume_idx]; + for (int instance_idx : instance_idxs) { + const ModelInstance *instance = model_object->instances[instance_idx]; + TriangleMesh mesh = model_volume->mesh; + instance->transform_mesh(&mesh); + volumes_idx.push_back(int(this->volumes.size())); + float color[4]; + memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); + color[3] = model_volume->modifier ? 0.5f : 1.f; + this->volumes.emplace_back(new GLVolume(color)); + GLVolume &v = *this->volumes.back(); + v.tverts.load_mesh(mesh); + v.bounding_box = v.tverts.bounding_box(); + v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; + if (select_by == "object") + v.select_group_id = obj_idx * 1000000; + else if (select_by == "volume") + v.select_group_id = obj_idx * 1000000 + volume_idx * 1000; + else if (select_by == "instance") + v.select_group_id = v.composite_id; + if (drag_by == "object") + v.drag_group_id = obj_idx * 1000; + else if (drag_by == "instance") + v.drag_group_id = obj_idx * 1000 + instance_idx; + if (! model_volume->modifier) + v.layer_height_texture = layer_height_texture; + } + } + + return volumes_idx; +} + // caller is responsible for supplying NO lines with zero length -void -_3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector &widths, - const std::vector &heights, bool closed, double top_z, const Point ©, - GLVertexArray* qverts, GLVertexArray* tverts) +static void thick_lines_to_verts( + const Lines &lines, + const std::vector &widths, + const std::vector &heights, + bool closed, + double top_z, + GLVolume &volume) { /* It looks like it's faster without reserving capacity... // each segment has 4 quads, thus 16 vertices; + 2 caps - qverts->reserve_more(3 * 4 * (4 * lines.size() + 2)); + volume.qverts.reserve_more(3 * 4 * (4 * lines.size() + 2)); - // two triangles for each corner - tverts->reserve_more(3 * 3 * 2 * (lines.size() + 1)); + // two triangles for each corner + volume.tverts.reserve_more(3 * 3 * 2 * (lines.size() + 1)); */ assert(! lines.empty()); @@ -69,59 +220,59 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vectorpush_norm(prev_xy_right_normal); - tverts->push_vert(prev_b1.x, prev_b1.y, middle_z); + 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 - tverts->push_norm(xy_right_normal); - tverts->push_vert(a1.x, a1.y, middle_z); + volume.tverts.push_norm(xy_right_normal); + volume.tverts.push_vert(a1.x, a1.y, middle_z); // normal going upwards - tverts->push_norm(0,0,1); - tverts->push_vert(a.x, a.y, top_z); + volume.tverts.push_norm(0,0,1); + 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 - tverts->push_norm(prev_xy_right_normal); - tverts->push_vert(prev_b1.x, prev_b1.y, middle_z); + volume.tverts.push_norm(prev_xy_right_normal); + volume.tverts.push_vert(prev_b1.x, prev_b1.y, middle_z); // normal going downwards - tverts->push_norm(0,0,-1); - tverts->push_vert(a.x, a.y, bottom_z); + volume.tverts.push_norm(0,0,-1); + volume.tverts.push_vert(a.x, a.y, bottom_z); // use the normal going to the right calculated for this line - tverts->push_norm(xy_right_normal); - tverts->push_vert(a1.x, a1.y, middle_z); + 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 - tverts->push_norm(prev_xy_left_normal); - tverts->push_vert(prev_b2.x, prev_b2.y, middle_z); + volume.tverts.push_norm(prev_xy_left_normal); + volume.tverts.push_vert(prev_b2.x, prev_b2.y, middle_z); // normal going upwards - tverts->push_norm(0,0,1); - tverts->push_vert(a.x, a.y, top_z); + volume.tverts.push_norm(0,0,1); + volume.tverts.push_vert(a.x, a.y, top_z); // use the normal going to the right calculated for this line - tverts->push_norm(xy_left_normal); - tverts->push_vert(a2.x, a2.y, middle_z); + 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 - tverts->push_norm(prev_xy_left_normal); - tverts->push_vert(prev_b2.x, prev_b2.y, middle_z); + 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 - tverts->push_norm(xy_left_normal); - tverts->push_vert(a2.x, a2.y, middle_z); + volume.tverts.push_norm(xy_left_normal); + volume.tverts.push_vert(a2.x, a2.y, middle_z); // normal going downwards - tverts->push_norm(0,0,-1); - tverts->push_vert(a.x, a.y, bottom_z); + volume.tverts.push_norm(0,0,-1); + volume.tverts.push_vert(a.x, a.y, bottom_z); } } } @@ -139,112 +290,359 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vectorpush_norm(0,0,-1); - qverts->push_vert(a.x, a.y, bottom_z); + volume.qverts.push_norm(0,0,-1); + volume.qverts.push_vert(a.x, a.y, bottom_z); // normal pointing to the right - qverts->push_norm(xy_right_normal); - qverts->push_vert(a1.x, a1.y, middle_z); + volume.qverts.push_norm(xy_right_normal); + volume.qverts.push_vert(a1.x, a1.y, middle_z); // normal pointing upwards - qverts->push_norm(0,0,1); - qverts->push_vert(a.x, a.y, top_z); + volume.qverts.push_norm(0,0,1); + volume.qverts.push_vert(a.x, a.y, top_z); // normal pointing to the left - qverts->push_norm(xy_left_normal); - qverts->push_vert(a2.x, a2.y, middle_z); + 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 == lines.size()-1) { // normal pointing downwards - qverts->push_norm(0,0,-1); - qverts->push_vert(b.x, b.y, bottom_z); + volume.qverts.push_norm(0,0,-1); + volume.qverts.push_vert(b.x, b.y, bottom_z); // normal pointing to the left - qverts->push_norm(xy_left_normal); - qverts->push_vert(b2.x, b2.y, middle_z); + volume.qverts.push_norm(xy_left_normal); + volume.qverts.push_vert(b2.x, b2.y, middle_z); // normal pointing upwards - qverts->push_norm(0,0,1); - qverts->push_vert(b.x, b.y, top_z); + volume.qverts.push_norm(0,0,1); + volume.qverts.push_vert(b.x, b.y, top_z); // normal pointing to the right - qverts->push_norm(xy_right_normal); - qverts->push_vert(b1.x, b1.y, middle_z); + volume.qverts.push_norm(xy_right_normal); + volume.qverts.push_vert(b1.x, b1.y, middle_z); } } // bottom-right face { // normal going downwards - qverts->push_norm(0,0,-1); - qverts->push_norm(0,0,-1); - qverts->push_vert(a.x, a.y, bottom_z); - qverts->push_vert(b.x, b.y, bottom_z); + 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); - qverts->push_norm(xy_right_normal); - qverts->push_norm(xy_right_normal); - qverts->push_vert(b1.x, b1.y, middle_z); - qverts->push_vert(a1.x, a1.y, middle_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 { - qverts->push_norm(xy_right_normal); - qverts->push_norm(xy_right_normal); - qverts->push_vert(a1.x, a1.y, middle_z); - qverts->push_vert(b1.x, b1.y, middle_z); + 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 - qverts->push_norm(0,0,1); - qverts->push_norm(0,0,1); - qverts->push_vert(b.x, b.y, top_z); - qverts->push_vert(a.x, a.y, top_z); + 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 { - qverts->push_norm(0,0,1); - qverts->push_norm(0,0,1); - qverts->push_vert(a.x, a.y, top_z); - qverts->push_vert(b.x, b.y, top_z); + 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); - qverts->push_norm(xy_left_normal); - qverts->push_norm(xy_left_normal); - qverts->push_vert(b2.x, b2.y, middle_z); - qverts->push_vert(a2.x, a2.y, middle_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 { - qverts->push_norm(xy_left_normal); - qverts->push_norm(xy_left_normal); - qverts->push_vert(a2.x, a2.y, middle_z); - qverts->push_vert(b2.x, b2.y, middle_z); + 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 - qverts->push_norm(0,0,-1); - qverts->push_norm(0,0,-1); - qverts->push_vert(b.x, b.y, bottom_z); - qverts->push_vert(a.x, a.y, bottom_z); + 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); } first_done = true; } } -void -GLVertexArray::load_mesh(const TriangleMesh &mesh) +// Fill in the qverts and tverts with quads and triangles for the extrusion_path. +static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) { - this->reserve_more(3 * 3 * mesh.facets_count()); - - for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { - stl_facet &facet = mesh.stl.facet_start[i]; - for (int j = 0; j <= 2; ++j) { - this->push_norm(facet.normal.x, facet.normal.y, facet.normal.z); - this->push_vert(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z); + Polyline polyline = extrusion_path.polyline; + polyline.remove_duplicate_points(); + polyline.translate(copy); + Lines lines = polyline.lines(); + std::vector widths(lines.size(), extrusion_path.width); + std::vector heights(lines.size(), extrusion_path.height); + thick_lines_to_verts(lines, widths, heights, false, print_z, volume); +} + +// Fill in the qverts and tverts with quads and triangles for the extrusion_loop. +static inline void extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) +{ + Lines lines; + std::vector widths; + std::vector heights; + for (const ExtrusionPath &extrusion_path : extrusion_loop.paths) { + Polyline polyline = extrusion_path.polyline; + polyline.remove_duplicate_points(); + polyline.translate(copy); + Lines lines_this = polyline.lines(); + append(lines, lines_this); + widths.insert(widths.end(), lines_this.size(), extrusion_path.width); + heights.insert(heights.end(), lines_this.size(), extrusion_path.height); + } + thick_lines_to_verts(lines, widths, heights, true, print_z, volume); +} + +// Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path. +static inline void extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) +{ + Lines lines; + std::vector widths; + std::vector heights; + for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths) { + Polyline polyline = extrusion_path.polyline; + polyline.remove_duplicate_points(); + polyline.translate(copy); + Lines lines_this = polyline.lines(); + append(lines, lines_this); + widths.insert(widths.end(), lines_this.size(), extrusion_path.width); + heights.insert(heights.end(), lines_this.size(), extrusion_path.height); + } + thick_lines_to_verts(lines, widths, heights, false, print_z, volume); +} + +static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume); + +static inline void extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) +{ + for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities) + extrusionentity_to_verts(extrusion_entity, print_z, copy, volume); +} + +static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) +{ + if (extrusion_entity != nullptr) { + auto *extrusion_path = dynamic_cast(extrusion_entity); + if (extrusion_path != nullptr) + extrusionentity_to_verts(*extrusion_path, print_z, copy, volume); + else { + auto *extrusion_loop = dynamic_cast(extrusion_entity); + if (extrusion_loop != nullptr) + extrusionentity_to_verts(*extrusion_loop, print_z, copy, volume); + else { + auto *extrusion_multi_path = dynamic_cast(extrusion_entity); + if (extrusion_multi_path != nullptr) + extrusionentity_to_verts(*extrusion_multi_path, print_z, copy, volume); + else { + auto *extrusion_entity_collection = dynamic_cast(extrusion_entity); + if (extrusion_entity_collection != nullptr) + extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume); + else { + CONFESS("Unexpected extrusion_entity type in to_verts()"); + } + } + } } } } +// 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) +{ + if (! print->has_skirt() && print->config.brim_width.value == 0) + return; + + const float color[] = { 0.5f, 1.0f, 0.5f, 1.f }; // greenish + + // number of skirt layers + size_t total_layer_count = 0; + for (const PrintObject *print_object : print->objects) + total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); + size_t skirt_height = print->has_infinite_skirt() ? + total_layer_count : + std::min(print->config.skirt_height.value, total_layer_count); + if (skirt_height == 0 && print->config.brim_width.value > 0) + skirt_height = 1; + + // get first skirt_height layers (maybe this should be moved to a PrintObject method?) + const PrintObject *object0 = print->objects.front(); + std::vector print_zs; + print_zs.reserve(skirt_height * 2); + for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++ i) + print_zs.push_back(float(object0->layers[i]->print_z)); + //FIXME why there are support layers? + for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++ i) + print_zs.push_back(float(object0->support_layers[i]->print_z)); + std::sort(print_zs.begin(), print_zs.end()); + print_zs.erase(std::unique(print_zs.begin(), print_zs.end()), print_zs.end()); + if (print_zs.size() > skirt_height) + print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); + + volumes->volumes.emplace_back(new GLVolume(color)); + GLVolume &volume = *volumes->volumes.back(); + for (size_t i = 0; i < skirt_height; ++ i) { + volume.print_zs.push_back(print_zs[i]); + volume.offsets.push_back(volume.qverts.size()); + volume.offsets.push_back(volume.tverts.size()); + if (i == 0) + extrusionentity_to_verts(print->brim, print_zs[i], Point(0, 0), volume); + extrusionentity_to_verts(print->skirt, print_zs[i], Point(0, 0), volume); + } + auto bb = print->bounding_box(); + volume.bounding_box.merge(Pointf3(unscale(bb.min.x), unscale(bb.min.y), 0.f)); + volume.bounding_box.merge(Pointf3(unscale(bb.max.x), unscale(bb.max.y), 0.f)); +} + +// Create 3D thick extrusion lines for object forming extrusions. +// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, +// one for perimeters, one for infill and one for supports. +void _3DScene::_load_print_object_toolpaths( + const PrintObject *print_object, + GLVolumeCollection *volumes) +{ + struct Ctxt + { + const Points *shifted_copies; + std::vector layers; + // Bounding box of the object and its copies. + BoundingBoxf3 bbox; + bool has_perimeters; + bool has_infill; + bool has_support; + +// static const size_t alloc_size_max () { return 32 * 1048576 / 4; } + static const size_t alloc_size_max () { return 4 * 1048576 / 4; } + static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } + + static const float* color_perimeters () { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow + static const float* color_infill () { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish + static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish + } ctxt; + + ctxt.shifted_copies = &print_object->_shifted_copies; + + // order layers by print_z + ctxt.layers.reserve(print_object->layers.size() + print_object->support_layers.size()); + for (const Layer *layer : print_object->layers) + ctxt.layers.push_back(layer); + for (const Layer *layer : print_object->support_layers) + ctxt.layers.push_back(layer); + std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); + + for (const Point ©: print_object->_shifted_copies) { + BoundingBox cbb = print_object->bounding_box(); + cbb.translate(copy.x, copy.y); + ctxt.bbox.merge(Pointf3(unscale(cbb.min.x), unscale(cbb.min.y), 0.f)); + ctxt.bbox.merge(Pointf3(unscale(cbb.max.x), unscale(cbb.max.y), 0.f)); + } + + // Maximum size of an allocation block: 32MB / sizeof(float) + ctxt.has_perimeters = print_object->state.is_done(posPerimeters); + ctxt.has_infill = print_object->state.is_done(posInfill); + ctxt.has_support = print_object->state.is_done(posSupportMaterial); + + BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; + + //FIXME Improve the heuristics for a grain size. + size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); + std::vector volumes_per_thread(ctxt.layers.size(), GLVolumeCollection()); + tbb::parallel_for( + tbb::blocked_range(0, ctxt.layers.size(), grain_size), + [&ctxt, &volumes_per_thread](const tbb::blocked_range& range) { + std::vector &volumes = volumes_per_thread[range.begin()].volumes; + volumes.emplace_back(new GLVolume(ctxt.color_perimeters())); + volumes.emplace_back(new GLVolume(ctxt.color_infill())); + volumes.emplace_back(new GLVolume(ctxt.color_support())); + size_t vols[3] = { 0, 1, 2 }; + for (size_t i = 0; i < 3; ++ i) { + GLVolume &volume = *volumes[i]; + volume.bounding_box = ctxt.bbox; + volume.qverts.reserve(ctxt.alloc_size_reserve()); + volume.tverts.reserve(ctxt.alloc_size_reserve()); + } + for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { + const Layer *layer = ctxt.layers[idx_layer]; + for (size_t i = 0; i < 3; ++ i) { + GLVolume &vol = *volumes[vols[i]]; + if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) { + vol.print_zs.push_back(layer->print_z); + vol.offsets.push_back(vol.qverts.size()); + vol.offsets.push_back(vol.tverts.size()); + } + } + for (const Point ©: *ctxt.shifted_copies) { + for (const LayerRegion *layerm : layer->regions) { + if (ctxt.has_perimeters) + extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, *volumes[vols[0]]); + if (ctxt.has_infill) + extrusionentity_to_verts(layerm->fills, float(layer->print_z), copy, *volumes[vols[1]]); + } + if (ctxt.has_support) { + const SupportLayer *support_layer = dynamic_cast(layer); + if (support_layer) { + extrusionentity_to_verts(support_layer->support_fills, float(layer->print_z), copy, *volumes[vols[2]]); + extrusionentity_to_verts(support_layer->support_interface_fills, float(layer->print_z), copy, *volumes[vols[2]]); + } + } + } + for (size_t i = 0; i < 3; ++ i) { + GLVolume &vol = *volumes[vols[i]]; + if (vol.qverts.size() > ctxt.alloc_size_max() || vol.tverts.size() > ctxt.alloc_size_max()) { + // Shrink the old vectors to preserve memory. + vol.qverts.shrink_to_fit(); + vol.tverts.shrink_to_fit(); + // Store the vertex arrays and restart their containers. + vols[i] = volumes.size(); + volumes.emplace_back(new GLVolume(vol.color)); + GLVolume &vol_new = *volumes.back(); + vol_new.bounding_box = ctxt.bbox; + vol_new.qverts.reserve(ctxt.alloc_size_reserve()); + vol_new.tverts.reserve(ctxt.alloc_size_reserve()); + } + } + } + while (! volumes.empty() && volumes.back()->empty()) { + delete volumes.back(); + volumes.pop_back(); + } + }); + + BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - merging results"; + + size_t volume_ptr = volumes->volumes.size(); + size_t num_volumes = volume_ptr; + for (const GLVolumeCollection &v : volumes_per_thread) + num_volumes += v.volumes.size(); + volumes->volumes.resize(num_volumes, nullptr); + for (GLVolumeCollection &v : volumes_per_thread) { + memcpy(volumes->volumes.data() + volume_ptr, v.volumes.data(), v.volumes.size() * sizeof(void*)); + volume_ptr += v.volumes.size(); + v.volumes.clear(); + } + + BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end"; +} + } diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 85b6d08ec..d3fb7cffe 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -8,8 +8,20 @@ namespace Slic3r { +class Print; +class PrintObject; +class Model; +class ModelObject; + class GLVertexArray { - public: +public: + GLVertexArray() {} + GLVertexArray(const GLVertexArray &rhs) : verts(rhs.verts), norms(rhs.norms) {} + GLVertexArray(GLVertexArray &&rhs) : verts(std::move(rhs.verts)), norms(std::move(rhs.norms)) {} + + GLVertexArray& operator=(const GLVertexArray &rhs) { verts = rhs.verts; norms = rhs.norms; return *this; } + GLVertexArray& operator=(GLVertexArray &&rhs) { verts = std::move(rhs.verts); norms = std::move(rhs.norms); return *this; } + std::vector verts, norms; void reserve(size_t len) { @@ -41,14 +53,174 @@ class GLVertexArray { this->norms.push_back(z); }; void load_mesh(const TriangleMesh &mesh); + + size_t size() const { return verts.size(); } + bool empty() const { return verts.empty(); } + void shrink_to_fit() { this->verts.shrink_to_fit(); this->norms.shrink_to_fit(); } + + BoundingBoxf3 bounding_box() const { + BoundingBoxf3 bbox; + if (! this->verts.empty()) { + bbox.min.x = bbox.max.x = this->verts[0]; + bbox.min.y = bbox.max.y = this->verts[1]; + bbox.min.z = bbox.max.z = this->verts[2]; + for (size_t i = 3; i < this->verts.size(); i += 3) { + bbox.min.x = std::min(bbox.min.x, this->verts[i + 0]); + bbox.min.y = std::min(bbox.min.y, this->verts[i + 1]); + bbox.min.z = std::min(bbox.min.z, this->verts[i + 2]); + bbox.max.x = std::max(bbox.max.x, this->verts[i + 0]); + bbox.max.y = std::max(bbox.max.y, this->verts[i + 1]); + bbox.max.z = std::max(bbox.max.z, this->verts[i + 2]); + } + } + return bbox; + } +}; + +class GLTexture +{ +public: + GLTexture() : width(0), height(0), levels(0), cells(0) {} + + // Texture data + std::vector data; + // Width of the texture, top level. + size_t width; + // Height of the texture, top level. + size_t height; + // For how many levels of detail is the data allocated? + size_t levels; + // Number of texture cells allocated for the height texture. + size_t cells; +}; + +class GLVolume { +public: + GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) : + composite_id(-1), + select_group_id(-1), + drag_group_id(-1), + selected(false), + hover(false), + qverts_range(0, size_t(-1)), + tverts_range(0, size_t(-1)) + { + color[0] = r; + color[1] = g; + color[2] = b; + color[3] = a; + } + GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} + + std::vector load_object( + const ModelObject *model_object, + const std::vector &instance_idxs, + const std::string &color_by, + const std::string &select_by, + const std::string &drag_by); + + // Bounding box of this volume, in unscaled coordinates. + BoundingBoxf3 bounding_box; + // Offset of the volume to be rendered. + Pointf3 origin; + // Color of the triangles / quads held by this volume. + float color[4]; + + // An ID containing the object ID, volume ID and instance ID. + int composite_id; + // An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance. + int select_group_id; + // An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance. + int drag_group_id; + // Is this object selected? + bool selected; + // Boolean: Is mouse over this object? + bool hover; + + // Geometric data. + // Quad vertices. + GLVertexArray qverts; + std::pair qverts_range; + // Triangle vertices. + GLVertexArray tverts; + std::pair tverts_range; + // 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; + std::vector offsets; + + int object_idx() const { return this->composite_id / 1000000; } + int volume_idx() const { return (this->composite_id / 1000) % 1000; } + int instance_idx() const { return this->composite_id % 1000; } + BoundingBoxf3 transformed_bounding_box() const { BoundingBoxf3 bb = this->bounding_box; bb.translate(this->origin); return bb; } + bool empty() const { return qverts.size() < 4 && tverts.size() < 3; } + + void set_range(coordf_t low, coordf_t high); + + void* qverts_to_render_ptr() { return qverts.verts.data() + qverts_range.first; } + void* qnorms_to_render_ptr() { return qverts.norms.data() + qverts_range.first; } + size_t qverts_to_render_cnt() { return std::min(qverts.verts.size(), qverts_range.second - qverts_range.first); } + void* tverts_to_render_ptr() { return tverts.verts.data() + tverts_range.first; } + 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); } + + std::shared_ptr layer_height_texture; + + bool has_layer_height_texture() const + { return this->layer_height_texture.get() != nullptr; } + size_t layer_height_texture_width() const + { return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->width; } + size_t layer_height_texture_height() const + { return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->height; } + size_t layer_height_texture_cells() const + { return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->cells; } + void* layer_height_texture_data_ptr_level0() { + return (layer_height_texture.get() == nullptr) ? 0 : + (void*)layer_height_texture->data.data(); + } + void* layer_height_texture_data_ptr_level1() { + return (layer_height_texture.get() == nullptr) ? 0 : + (void*)(layer_height_texture->data.data() + layer_height_texture->width * layer_height_texture->height * 4); + } + double layer_height_texture_z_to_row_id() const { + return (this->layer_height_texture.get() == nullptr) ? 0. : + double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * bounding_box.max.z); + } + void generate_layer_height_texture(PrintObject *print_object, bool force); +}; + +class GLVolumeCollection +{ +public: + std::vector volumes; + + GLVolumeCollection() {}; + ~GLVolumeCollection() { clear(); }; + + std::vector load_object( + const ModelObject *model_object, + int obj_idx, + const std::vector &instance_idxs, + const std::string &color_by, + const std::string &select_by, + const std::string &drag_by); + + 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); } }; class _3DScene { - public: - static void _extrusionentity_to_verts_do(const Lines &lines, const std::vector &widths, - const std::vector &heights, bool closed, double top_z, const Point ©, - GLVertexArray* qverts, GLVertexArray* tverts); +public: + static void _load_print_toolpaths( + const Print *print, + GLVolumeCollection *volumes); + + static void _load_print_object_toolpaths( + const PrintObject *print_object, + GLVolumeCollection *volumes); }; } diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 4ff101937..82ed0a893 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -3,26 +3,101 @@ #include #include "slic3r/GUI/3DScene.hpp" -%name{Slic3r::GUI::_3DScene::GLVertexArray} class GLVertexArray { - GLVertexArray(); - ~GLVertexArray(); - void load_mesh(TriangleMesh* mesh) const - %code%{ THIS->load_mesh(*mesh); %}; - size_t size() const - %code%{ RETVAL = THIS->verts.size(); %}; - void* verts_ptr() const - %code%{ RETVAL = THIS->verts.empty() ? 0 : &THIS->verts.front(); %}; - void* norms_ptr() const - %code%{ RETVAL = THIS->verts.empty() ? 0 : &THIS->norms.front(); %}; +%name{Slic3r::GUI::_3DScene::GLVolume} class GLVolume { + GLVolume(); + ~GLVolume(); + + std::vector color() + %code%{ RETVAL.reserve(4); RETVAL.push_back(THIS->color[0]); RETVAL.push_back(THIS->color[1]); RETVAL.push_back(THIS->color[2]); RETVAL.push_back(THIS->color[3]); %}; + int composite_id() + %code%{ RETVAL = THIS->composite_id; %}; + int select_group_id() + %code%{ RETVAL = THIS->select_group_id; %}; + int drag_group_id() + %code%{ RETVAL = THIS->drag_group_id; %}; + int selected() + %code%{ RETVAL = THIS->selected; %}; + void set_selected(int i) + %code%{ THIS->selected = i; %}; + int hover() + %code%{ RETVAL = THIS->hover; %}; + void set_hover(int i) + %code%{ THIS->hover = i; %}; + + int object_idx() const; + int volume_idx() const; + int instance_idx() const; + bool empty() const; + Clone origin() const + %code%{ RETVAL = THIS->origin; %}; + Clone bounding_box() const + %code%{ RETVAL = THIS->bounding_box; %}; + Clone transformed_bounding_box() const; + + void* qverts_to_render_ptr(); + void* qnorms_to_render_ptr(); + int qverts_to_render_cnt(); + void* tverts_to_render_ptr(); + void* tnorms_to_render_ptr(); + int tverts_to_render_cnt(); + + bool has_layer_height_texture(); + int layer_height_texture_width(); + int layer_height_texture_height(); + int layer_height_texture_cells(); + void* layer_height_texture_data_ptr_level0(); + void* layer_height_texture_data_ptr_level1(); + double layer_height_texture_z_to_row_id() const; + void generate_layer_height_texture(PrintObject *print_object, bool force); +}; + + +%name{Slic3r::GUI::_3DScene::GLVolume::Collection} class GLVolumeCollection { + GLVolumeCollection(); + ~GLVolumeCollection(); + + std::vector load_object(ModelObject *object, int obj_idx, std::vector instance_idxs, std::string color_by, std::string select_by, std::string drag_by); + + void erase() + %code{% THIS->clear(); %}; + + int count() + %code{% RETVAL = THIS->volumes.size(); %}; + + void set_range(double low, double high); +%{ + +SV* +GLVolumeCollection::arrayref() + CODE: + AV* av = newAV(); + av_fill(av, THIS->volumes.size()-1); + int i = 0; + for (GLVolume *v : THIS->volumes) { + av_store(av, i++, perl_to_SV_ref(*v)); + } + RETVAL = newRV_noinc((SV*)av); + OUTPUT: + RETVAL + +%} }; %package{Slic3r::GUI::_3DScene}; %{ void -_extrusionentity_to_verts_do(Lines lines, std::vector widths, std::vector heights, bool closed, double top_z, Point* copy, GLVertexArray* qverts, GLVertexArray* tverts) +_load_print_toolpaths(print, volumes) + Print *print; + GLVolumeCollection *volumes; CODE: - _3DScene::_extrusionentity_to_verts_do(lines, widths, heights, closed, - top_z, *copy, qverts, tverts); + _3DScene::_load_print_toolpaths(print, volumes); -%} \ No newline at end of file +void +_load_print_object_toolpaths(print_object, volumes) + PrintObject *print_object; + GLVolumeCollection *volumes; + CODE: + _3DScene::_load_print_object_toolpaths(print_object, volumes); + +%} diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 70fed730f..618347b50 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -143,20 +143,6 @@ _constant() void reset_layer_height_profile(); - int generate_layer_height_texture(void *data, int rows, int cols, bool force = true) - %code%{ - force |= THIS->update_layer_height_profile(THIS->model_object()->layer_height_profile); - if (force) { - SlicingParameters slicing_params = THIS->slicing_parameters(); - bool level_of_detail_2nd_level = true; - RETVAL = generate_layer_height_texture( - slicing_params, - generate_object_layers(slicing_params, THIS->model_object()->layer_height_profile), - data, rows, cols, level_of_detail_2nd_level); - } else - RETVAL = 0; - %}; - int ptr() %code%{ RETVAL = (int)(intptr_t)THIS; %}; }; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 8f5f67d4e..75c73d93d 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -233,7 +233,10 @@ PrintObjectSupportMaterial* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T -GLVertexArray* O_OBJECT_SLIC3R +GLVolume* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +GLVolumeCollection* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T Axis T_UV ExtrusionLoopRole T_UV diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 57109055a..8e22ddbce 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -211,7 +211,10 @@ %typemap{ModelInstancePtrs*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; -%typemap{GLVertexArray*}; +%typemap{GLVolume*}; +%typemap{Ref}{simple}; +%typemap{GLVolumeCollection*}; +%typemap{Ref}{simple}; %typemap{PrintRegionPtrs*}; %typemap{PrintObjectPtrs*};