Volume rewritten from Perl to C++,

generation of vertex arrays from paths rewritten from Perl to C++,
parallelized.
This commit is contained in:
bubnikv 2017-03-13 16:02:17 +01:00
parent 50976e1b5a
commit e6fddd364d
11 changed files with 825 additions and 701 deletions

View File

@ -1,12 +1,8 @@
# Implements pure perl packages # Implements pure perl packages
# #
# Slic3r::GUI::3DScene::Base; # Slic3r::GUI::3DScene::Base;
# Slic3r::GUI::3DScene::Volume;
# Slic3r::GUI::3DScene; # 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::3D derives from Slic3r::GUI::3DScene,
# Slic3r::GUI::Plater::3DPreview, Slic3r::GUI::Plater::3DToolpaths, # Slic3r::GUI::Plater::3DPreview, Slic3r::GUI::Plater::3DToolpaths,
# Slic3r::GUI::Plater::ObjectCutDialog and Slic3r::GUI::Plater::ObjectPartsPanel # Slic3r::GUI::Plater::ObjectCutDialog and Slic3r::GUI::Plater::ObjectPartsPanel
@ -136,18 +132,17 @@ sub new {
$self->_stheta(45); $self->_stheta(45);
$self->_sphi(45); $self->_sphi(45);
$self->_zoom(1); $self->_zoom(1);
# Collection of GLVolume objects
$self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new);
# 3D point in model space # 3D point in model space
$self->_camera_type('ortho'); $self->_camera_type('ortho');
# $self->_camera_type('perspective'); # $self->_camera_type('perspective');
$self->_camera_target(Slic3r::Pointf3->new(0,0,0)); $self->_camera_target(Slic3r::Pointf3->new(0,0,0));
$self->_camera_distance(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); $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_band_width} = 2.;
$self->{layer_height_edit_strength} = 0.005; $self->{layer_height_edit_strength} = 0.005;
$self->{layer_height_edit_last_object_id} = -1; $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_strength},
$self->{layer_height_edit_band_width}, $self->{layer_height_edit_band_width},
$self->{layer_height_edit_last_action}); $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}]->generate_layer_height_texture(
$self->volumes->[$self->{layer_height_edit_last_object_id}]->layer_height_texture_data->ptr, $self->{print}->get_object($self->{layer_height_edit_last_object_id}), 1);
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
$self->Refresh; $self->Refresh;
# Automatic action on mouse down with the same coordinate. # Automatic action on mouse down with the same coordinate.
$self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS); $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS);
@ -545,8 +538,7 @@ sub mouse_wheel_event {
# Reset selection. # Reset selection.
sub reset_objects { sub reset_objects {
my ($self) = @_; my ($self) = @_;
$self->volumes->erase;
$self->volumes([]);
$self->_dirty(1); $self->_dirty(1);
} }
@ -724,13 +716,12 @@ sub set_bed_shape {
sub deselect_volumes { sub deselect_volumes {
my ($self) = @_; my ($self) = @_;
$_->selected(0) for @{$self->volumes}; $_->set_selected(0) for @{$self->volumes};
} }
sub select_volume { sub select_volume {
my ($self, $volume_idx) = @_; my ($self, $volume_idx) = @_;
$self->volumes->[$volume_idx]->set_selected(1)
$self->volumes->[$volume_idx]->selected(1)
if $volume_idx != -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 $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; my $volume_idx = $col->[0] + $col->[1]*256 + $col->[2]*256*256;
$self->_hover_volume_idx(undef); $self->_hover_volume_idx(undef);
$_->hover(0) for @{$self->volumes}; $_->set_hover(0) for @{$self->volumes};
if ($volume_idx <= $#{$self->volumes}) { if ($volume_idx <= $#{$self->volumes}) {
$self->_hover_volume_idx($volume_idx); $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; my $group_id = $self->volumes->[$volume_idx]->select_group_id;
if ($group_id != -1) { 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; $self->on_hover->($volume_idx) if $self->on_hover;
@ -1213,17 +1204,10 @@ sub draw_volumes {
my $volume = $self->volumes->[$volume_idx]; my $volume = $self->volumes->[$volume_idx];
my $shader_active = 0; my $shader_active = 0;
if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->{layer_height_texture_data}) { if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->has_layer_height_texture) {
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.
{ $volume->generate_layer_height_texture(
# Update the height texture if the ModelObject::layer_height_texture is invalid. $self->{print}->get_object(int($volume->select_group_id / 1000000)), 0);
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;
}
$self->{shader}->Enable; $self->{shader}->Enable;
my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row'); 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_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_texture_row_to_normalized_id);
die if ! defined($z_cursor_id); die if ! defined($z_cursor_id);
die if ! defined($z_cursor_band_width_id); die if ! defined($z_cursor_band_width_id);
my $ncells = $volume->{layer_height_texture_cells}; glUniform1fARB($z_to_texture_row_id, $volume->layer_height_texture_z_to_row_id);
my $z_max = $volume->{bounding_box}->z_max; glUniform1fARB($z_texture_row_to_normalized_id, 1. / $volume->layer_height_texture_height);
glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max)); glUniform1fARB($z_cursor_id, $volume->bounding_box->z_max * $z_cursor_relative);
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_cursor_band_width_id, $self->{layer_height_edit_band_width}); glUniform1fARB($z_cursor_band_width_id, $self->{layer_height_edit_band_width});
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); 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_MIN_LEVEL, 0);
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
if (1) { glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height,
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);
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,
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);
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
# glPixelStorei(GL_UNPACK_ALIGNMENT, 1); # glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
# glPixelStorei(GL_UNPACK_ROW_LENGTH, $self->{layer_preview_z_texture_width}); # 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}, 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); GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level0);
glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, 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}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4)); GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level1);
} 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));
$shader_active = 1; $shader_active = 1;
} elsif ($fakecolor) { } elsif ($fakecolor) {
# Object picking mode. Render the object with a color encoding the object index. # Object picking mode. Render the object with a color encoding the object index.
@ -1277,68 +1249,21 @@ sub draw_volumes {
glColor4f(@{ $volume->color }); 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(); glPushMatrix();
glTranslatef(@{$volume->origin}); glTranslatef(@{$volume->origin});
glCullFace(GL_BACK); glCullFace(GL_BACK);
if ($qverts_begin < $qverts_end) { my $qverts_cnt = $volume->qverts_to_render_cnt;
glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr); if ($qverts_cnt) {
glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_ptr); glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts_to_render_ptr);
$qverts_begin /= 3; glNormalPointer_c(GL_FLOAT, 0, $volume->qnorms_to_render_ptr);
$qverts_end /= 3; glDrawArrays(GL_QUADS, 0, $qverts_cnt / 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 $tverts_cnt = $volume->tverts_to_render_cnt;
if ($tverts_begin < $tverts_end) { if ($tverts_cnt) {
glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr); glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts_to_render_ptr);
glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_ptr); glNormalPointer_c(GL_FLOAT, 0, $volume->tnorms_to_render_ptr);
$tverts_begin /= 3; glDrawArrays(GL_TRIANGLES, 0, $tverts_cnt / 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;
}
} }
glVertexPointer_c(3, GL_FLOAT, 0, 0); glVertexPointer_c(3, GL_FLOAT, 0, 0);
glNormalPointer_c(GL_FLOAT, 0, 0); glNormalPointer_c(GL_FLOAT, 0, 0);
glPopMatrix(); glPopMatrix();
@ -1369,7 +1294,6 @@ sub _load_image_set_texture {
# Get RGB & alpha raw data from wxImage, interleave them into a Perl array. # Get RGB & alpha raw data from wxImage, interleave them into a Perl array.
my @rgb = unpack 'C*', $img->GetData(); my @rgb = unpack 'C*', $img->GetData();
my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3); 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 $n_pixels = int(@alpha);
my @data = (0)x($n_pixels * 4); my @data = (0)x($n_pixels * 4);
for (my $i = 0; $i < $n_pixels; $i += 1) { for (my $i = 0; $i < $n_pixels; $i += 1) {
@ -1441,7 +1365,7 @@ sub draw_active_object_annotations {
my $volume; my $volume;
foreach my $volume_idx (0..$#{$self->volumes}) { foreach my $volume_idx (0..$#{$self->volumes}) {
my $v = $self->volumes->[$volume_idx]; 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; $volume = $v;
last; 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_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_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized');
my $z_cursor_id = $self->{shader}->Map('z_cursor'); my $z_cursor_id = $self->{shader}->Map('z_cursor');
my $ncells = $volume->{layer_height_texture_cells}; glUniform1fARB($z_to_texture_row_id, $volume->layer_height_texture_z_to_row_id);
my $z_max = $volume->{bounding_box}->z_max; glUniform1fARB($z_texture_row_to_normalized_id, 1. / $volume->layer_height_texture_height);
glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max)); glUniform1fARB($z_cursor_id, $volume->bounding_box->z_max * $z_cursor_relative);
glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height});
glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative);
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); 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); 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); 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}, 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); GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level0);
glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, 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}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4)); GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level1);
# Render the color bar. # Render the color bar.
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
@ -1483,8 +1405,8 @@ sub draw_active_object_annotations {
glBegin(GL_QUADS); glBegin(GL_QUADS);
glVertex3f($bar_left, $bar_bottom, 0); glVertex3f($bar_left, $bar_bottom, 0);
glVertex3f($bar_right, $bar_bottom, 0); glVertex3f($bar_right, $bar_bottom, 0);
glVertex3f($bar_right, $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); glVertex3f($bar_left, $bar_top, $volume->bounding_box->z_max);
glEnd(); glEnd();
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
$self->{shader}->Disable; $self->{shader}->Disable;
@ -1715,65 +1637,6 @@ void main()
FRAGMENT 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<float> 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. # The 3D canvas to display objects and tool paths.
package Slic3r::GUI::3DScene; package Slic3r::GUI::3DScene;
use base qw(Slic3r::GUI::3DScene::Base); 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::Geometry qw(scale unscale epsilon);
use Slic3r::Print::State ':steps'; 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( __PACKAGE__->mk_accessors(qw(
color_by color_by
select_by select_by
@ -1816,177 +1676,17 @@ sub load_object {
} }
$instance_idxs ||= [0..$#{$model_object->instances}]; $instance_idxs ||= [0..$#{$model_object->instances}];
my $volume_indices = $self->volumes->load_object($model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by);
# Object will have a single common layer height texture for all volumes. return @{$volume_indices};
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),
);
} }
# Create 3D thick extrusion lines for a skirt and brim. # Create 3D thick extrusion lines for a skirt and brim.
# Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes. # Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes.
sub load_print_toolpaths { sub load_print_toolpaths {
my ($self, $print) = @_; 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; Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes)
{ if ($print->step_done(STEP_SKIRT) && $print->step_done(STEP_BRIM));
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,
);
} }
# Create 3D thick extrusion lines for object forming extrusions. # 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. # one for perimeters, one for infill and one for supports.
sub load_print_object_toolpaths { sub load_print_object_toolpaths {
my ($self, $object) = @_; 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) Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes);
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,
);
}
} }
sub set_toolpaths_range { sub set_toolpaths_range {
my ($self, $min_z, $max_z) = @_; my ($self, $min_z, $max_z) = @_;
$self->volumes->set_range($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<float> for coords and normals.
$qverts,
$tverts);
} }
1; 1;

View File

@ -26,13 +26,6 @@ sub status_cb {
return $status_cb // sub {}; 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 { sub size {
my $self = shift; my $self = shift;
return $self->bounding_box->size; return $self->bounding_box->size;

View File

@ -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 } sub CLONE_SKIP { 1 }
package main; package main;
@ -279,6 +283,7 @@ for my $class (qw(
Slic3r::Geometry::BoundingBox Slic3r::Geometry::BoundingBox
Slic3r::Geometry::BoundingBoxf Slic3r::Geometry::BoundingBoxf
Slic3r::Geometry::BoundingBoxf3 Slic3r::Geometry::BoundingBoxf3
Slic3r::GUI::_3DScene::GLVolume
Slic3r::Layer Slic3r::Layer
Slic3r::Layer::Region Slic3r::Layer::Region
Slic3r::Layer::Support Slic3r::Layer::Support

View File

@ -593,7 +593,7 @@ int generate_layer_height_texture(
#endif #endif
// Clear the main texture and the 2nd LOD level. // 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 // 2nd LOD level data start
unsigned char *data1 = reinterpret_cast<unsigned char*>(data) + rows * cols * 4; unsigned char *data1 = reinterpret_cast<unsigned char*>(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)))); int ncells = std::min((cols-1) * rows, int(ceil(16. * (slicing_params.object_print_z_height() / slicing_params.min_layer_height))));

View File

@ -60,7 +60,8 @@ REGISTER_CLASS(Surface, "Surface");
REGISTER_CLASS(SurfaceCollection, "Surface::Collection"); REGISTER_CLASS(SurfaceCollection, "Surface::Collection");
REGISTER_CLASS(PrintObjectSupportMaterial, "Print::SupportMaterial2"); REGISTER_CLASS(PrintObjectSupportMaterial, "Print::SupportMaterial2");
REGISTER_CLASS(TriangleMesh, "TriangleMesh"); REGISTER_CLASS(TriangleMesh, "TriangleMesh");
REGISTER_CLASS(GLVertexArray, "GUI::_3DScene::GLVertexArray"); REGISTER_CLASS(GLVolume, "GUI::_3DScene::GLVolume");
REGISTER_CLASS(GLVolumeCollection, "GUI::_3DScene::GLVolume::Collection");
SV* SV*
ConfigBase__as_hash(ConfigBase* THIS) { ConfigBase__as_hash(ConfigBase* THIS) {

View File

@ -1,21 +1,172 @@
#include "3DScene.hpp" #include "3DScene.hpp"
#include "../../libslic3r/libslic3r.h"
#include "../../libslic3r/ExtrusionEntity.hpp"
#include "../../libslic3r/ExtrusionEntityCollection.hpp"
#include "../../libslic3r/Print.hpp"
#include "../../libslic3r/Slicing.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h> #include <assert.h>
#include <utility>
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
#include <tbb/atomic.h>
namespace Slic3r { 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<int> GLVolumeCollection::load_object(
const ModelObject *model_object,
int obj_idx,
const std::vector<int> &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<GLTexture> layer_height_texture = std::make_shared<GLTexture>();
std::vector<int> 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 // caller is responsible for supplying NO lines with zero length
void static void thick_lines_to_verts(
_3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector<double> &widths, const Lines &lines,
const std::vector<double> &heights, bool closed, double top_z, const Point &copy, const std::vector<double> &widths,
GLVertexArray* qverts, GLVertexArray* tverts) const std::vector<double> &heights,
bool closed,
double top_z,
GLVolume &volume)
{ {
/* It looks like it's faster without reserving capacity... /* It looks like it's faster without reserving capacity...
// each segment has 4 quads, thus 16 vertices; + 2 caps // 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 // two triangles for each corner
tverts->reserve_more(3 * 3 * 2 * (lines.size() + 1)); volume.tverts.reserve_more(3 * 3 * 2 * (lines.size() + 1));
*/ */
assert(! lines.empty()); assert(! lines.empty());
@ -69,59 +220,59 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector<dou
// top-right vertex triangle between previous line and this one // top-right vertex triangle between previous line and this one
{ {
// use the normal going to the right calculated for the previous line // use the normal going to the right calculated for the previous line
tverts->push_norm(prev_xy_right_normal); volume.tverts.push_norm(prev_xy_right_normal);
tverts->push_vert(prev_b1.x, prev_b1.y, middle_z); volume.tverts.push_vert(prev_b1.x, prev_b1.y, middle_z);
// use the normal going to the right calculated for this line // use the normal going to the right calculated for this line
tverts->push_norm(xy_right_normal); volume.tverts.push_norm(xy_right_normal);
tverts->push_vert(a1.x, a1.y, middle_z); volume.tverts.push_vert(a1.x, a1.y, middle_z);
// normal going upwards // normal going upwards
tverts->push_norm(0,0,1); volume.tverts.push_norm(0,0,1);
tverts->push_vert(a.x, a.y, top_z); volume.tverts.push_vert(a.x, a.y, top_z);
} }
// bottom-right vertex triangle between previous line and this one // bottom-right vertex triangle between previous line and this one
{ {
// use the normal going to the right calculated for the previous line // use the normal going to the right calculated for the previous line
tverts->push_norm(prev_xy_right_normal); volume.tverts.push_norm(prev_xy_right_normal);
tverts->push_vert(prev_b1.x, prev_b1.y, middle_z); volume.tverts.push_vert(prev_b1.x, prev_b1.y, middle_z);
// normal going downwards // normal going downwards
tverts->push_norm(0,0,-1); volume.tverts.push_norm(0,0,-1);
tverts->push_vert(a.x, a.y, bottom_z); volume.tverts.push_vert(a.x, a.y, bottom_z);
// use the normal going to the right calculated for this line // use the normal going to the right calculated for this line
tverts->push_norm(xy_right_normal); volume.tverts.push_norm(xy_right_normal);
tverts->push_vert(a1.x, a1.y, middle_z); volume.tverts.push_vert(a1.x, a1.y, middle_z);
} }
} else if (ccw < -EPSILON) { } else if (ccw < -EPSILON) {
// top-left vertex triangle between previous line and this one // top-left vertex triangle between previous line and this one
{ {
// use the normal going to the left calculated for the previous line // use the normal going to the left calculated for the previous line
tverts->push_norm(prev_xy_left_normal); volume.tverts.push_norm(prev_xy_left_normal);
tverts->push_vert(prev_b2.x, prev_b2.y, middle_z); volume.tverts.push_vert(prev_b2.x, prev_b2.y, middle_z);
// normal going upwards // normal going upwards
tverts->push_norm(0,0,1); volume.tverts.push_norm(0,0,1);
tverts->push_vert(a.x, a.y, top_z); volume.tverts.push_vert(a.x, a.y, top_z);
// use the normal going to the right calculated for this line // use the normal going to the right calculated for this line
tverts->push_norm(xy_left_normal); volume.tverts.push_norm(xy_left_normal);
tverts->push_vert(a2.x, a2.y, middle_z); volume.tverts.push_vert(a2.x, a2.y, middle_z);
} }
// bottom-left vertex triangle between previous line and this one // bottom-left vertex triangle between previous line and this one
{ {
// use the normal going to the left calculated for the previous line // use the normal going to the left calculated for the previous line
tverts->push_norm(prev_xy_left_normal); volume.tverts.push_norm(prev_xy_left_normal);
tverts->push_vert(prev_b2.x, prev_b2.y, middle_z); volume.tverts.push_vert(prev_b2.x, prev_b2.y, middle_z);
// use the normal going to the right calculated for this line // use the normal going to the right calculated for this line
tverts->push_norm(xy_left_normal); volume.tverts.push_norm(xy_left_normal);
tverts->push_vert(a2.x, a2.y, middle_z); volume.tverts.push_vert(a2.x, a2.y, middle_z);
// normal going downwards // normal going downwards
tverts->push_norm(0,0,-1); volume.tverts.push_norm(0,0,-1);
tverts->push_vert(a.x, a.y, bottom_z); volume.tverts.push_vert(a.x, a.y, bottom_z);
} }
} }
} }
@ -139,112 +290,359 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector<dou
// terminate open paths with caps // terminate open paths with caps
if (i == 0) { if (i == 0) {
// normal pointing downwards // normal pointing downwards
qverts->push_norm(0,0,-1); volume.qverts.push_norm(0,0,-1);
qverts->push_vert(a.x, a.y, bottom_z); volume.qverts.push_vert(a.x, a.y, bottom_z);
// normal pointing to the right // normal pointing to the right
qverts->push_norm(xy_right_normal); volume.qverts.push_norm(xy_right_normal);
qverts->push_vert(a1.x, a1.y, middle_z); volume.qverts.push_vert(a1.x, a1.y, middle_z);
// normal pointing upwards // normal pointing upwards
qverts->push_norm(0,0,1); volume.qverts.push_norm(0,0,1);
qverts->push_vert(a.x, a.y, top_z); volume.qverts.push_vert(a.x, a.y, top_z);
// normal pointing to the left // normal pointing to the left
qverts->push_norm(xy_left_normal); volume.qverts.push_norm(xy_left_normal);
qverts->push_vert(a2.x, a2.y, middle_z); 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 // we don't use 'else' because both cases are true if we have only one line
if (i == lines.size()-1) { if (i == lines.size()-1) {
// normal pointing downwards // normal pointing downwards
qverts->push_norm(0,0,-1); volume.qverts.push_norm(0,0,-1);
qverts->push_vert(b.x, b.y, bottom_z); volume.qverts.push_vert(b.x, b.y, bottom_z);
// normal pointing to the left // normal pointing to the left
qverts->push_norm(xy_left_normal); volume.qverts.push_norm(xy_left_normal);
qverts->push_vert(b2.x, b2.y, middle_z); volume.qverts.push_vert(b2.x, b2.y, middle_z);
// normal pointing upwards // normal pointing upwards
qverts->push_norm(0,0,1); volume.qverts.push_norm(0,0,1);
qverts->push_vert(b.x, b.y, top_z); volume.qverts.push_vert(b.x, b.y, top_z);
// normal pointing to the right // normal pointing to the right
qverts->push_norm(xy_right_normal); volume.qverts.push_norm(xy_right_normal);
qverts->push_vert(b1.x, b1.y, middle_z); volume.qverts.push_vert(b1.x, b1.y, middle_z);
} }
} }
// bottom-right face // bottom-right face
{ {
// normal going downwards // normal going downwards
qverts->push_norm(0,0,-1); volume.qverts.push_norm(0,0,-1);
qverts->push_norm(0,0,-1); volume.qverts.push_norm(0,0,-1);
qverts->push_vert(a.x, a.y, bottom_z); volume.qverts.push_vert(a.x, a.y, bottom_z);
qverts->push_vert(b.x, b.y, bottom_z); volume.qverts.push_vert(b.x, b.y, bottom_z);
qverts->push_norm(xy_right_normal); volume.qverts.push_norm(xy_right_normal);
qverts->push_norm(xy_right_normal); volume.qverts.push_norm(xy_right_normal);
qverts->push_vert(b1.x, b1.y, middle_z); volume.qverts.push_vert(b1.x, b1.y, middle_z);
qverts->push_vert(a1.x, a1.y, middle_z); volume.qverts.push_vert(a1.x, a1.y, middle_z);
} }
// top-right face // top-right face
{ {
qverts->push_norm(xy_right_normal); volume.qverts.push_norm(xy_right_normal);
qverts->push_norm(xy_right_normal); volume.qverts.push_norm(xy_right_normal);
qverts->push_vert(a1.x, a1.y, middle_z); volume.qverts.push_vert(a1.x, a1.y, middle_z);
qverts->push_vert(b1.x, b1.y, middle_z); volume.qverts.push_vert(b1.x, b1.y, middle_z);
// normal going upwards // normal going upwards
qverts->push_norm(0,0,1); volume.qverts.push_norm(0,0,1);
qverts->push_norm(0,0,1); volume.qverts.push_norm(0,0,1);
qverts->push_vert(b.x, b.y, top_z); volume.qverts.push_vert(b.x, b.y, top_z);
qverts->push_vert(a.x, a.y, top_z); volume.qverts.push_vert(a.x, a.y, top_z);
} }
// top-left face // top-left face
{ {
qverts->push_norm(0,0,1); volume.qverts.push_norm(0,0,1);
qverts->push_norm(0,0,1); volume.qverts.push_norm(0,0,1);
qverts->push_vert(a.x, a.y, top_z); volume.qverts.push_vert(a.x, a.y, top_z);
qverts->push_vert(b.x, b.y, top_z); volume.qverts.push_vert(b.x, b.y, top_z);
qverts->push_norm(xy_left_normal); volume.qverts.push_norm(xy_left_normal);
qverts->push_norm(xy_left_normal); volume.qverts.push_norm(xy_left_normal);
qverts->push_vert(b2.x, b2.y, middle_z); volume.qverts.push_vert(b2.x, b2.y, middle_z);
qverts->push_vert(a2.x, a2.y, middle_z); volume.qverts.push_vert(a2.x, a2.y, middle_z);
} }
// bottom-left face // bottom-left face
{ {
qverts->push_norm(xy_left_normal); volume.qverts.push_norm(xy_left_normal);
qverts->push_norm(xy_left_normal); volume.qverts.push_norm(xy_left_normal);
qverts->push_vert(a2.x, a2.y, middle_z); volume.qverts.push_vert(a2.x, a2.y, middle_z);
qverts->push_vert(b2.x, b2.y, middle_z); volume.qverts.push_vert(b2.x, b2.y, middle_z);
// normal going downwards // normal going downwards
qverts->push_norm(0,0,-1); volume.qverts.push_norm(0,0,-1);
qverts->push_norm(0,0,-1); volume.qverts.push_norm(0,0,-1);
qverts->push_vert(b.x, b.y, bottom_z); volume.qverts.push_vert(b.x, b.y, bottom_z);
qverts->push_vert(a.x, a.y, bottom_z); volume.qverts.push_vert(a.x, a.y, bottom_z);
} }
first_done = true; first_done = true;
} }
} }
void // Fill in the qverts and tverts with quads and triangles for the extrusion_path.
GLVertexArray::load_mesh(const TriangleMesh &mesh) static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point &copy, GLVolume &volume)
{ {
this->reserve_more(3 * 3 * mesh.facets_count()); Polyline polyline = extrusion_path.polyline;
polyline.remove_duplicate_points();
for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { polyline.translate(copy);
stl_facet &facet = mesh.stl.facet_start[i]; Lines lines = polyline.lines();
for (int j = 0; j <= 2; ++j) { std::vector<double> widths(lines.size(), extrusion_path.width);
this->push_norm(facet.normal.x, facet.normal.y, facet.normal.z); std::vector<double> heights(lines.size(), extrusion_path.height);
this->push_vert(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z); 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 &copy, GLVolume &volume)
{
Lines lines;
std::vector<double> widths;
std::vector<double> 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 &copy, GLVolume &volume)
{
Lines lines;
std::vector<double> widths;
std::vector<double> 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 &copy, GLVolume &volume);
static inline void extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point &copy, 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 &copy, GLVolume &volume)
{
if (extrusion_entity != nullptr) {
auto *extrusion_path = dynamic_cast<const ExtrusionPath*>(extrusion_entity);
if (extrusion_path != nullptr)
extrusionentity_to_verts(*extrusion_path, print_z, copy, volume);
else {
auto *extrusion_loop = dynamic_cast<const ExtrusionLoop*>(extrusion_entity);
if (extrusion_loop != nullptr)
extrusionentity_to_verts(*extrusion_loop, print_z, copy, volume);
else {
auto *extrusion_multi_path = dynamic_cast<const ExtrusionMultiPath*>(extrusion_entity);
if (extrusion_multi_path != nullptr)
extrusionentity_to_verts(*extrusion_multi_path, print_z, copy, volume);
else {
auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(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<size_t>(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<float> 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<const Layer*> 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 &copy: 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<GLVolumeCollection> volumes_per_thread(ctxt.layers.size(), GLVolumeCollection());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
[&ctxt, &volumes_per_thread](const tbb::blocked_range<size_t>& range) {
std::vector<GLVolume*> &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 &copy: *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<const SupportLayer*>(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";
}
} }

View File

@ -8,8 +8,20 @@
namespace Slic3r { namespace Slic3r {
class Print;
class PrintObject;
class Model;
class ModelObject;
class GLVertexArray { 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<float> verts, norms; std::vector<float> verts, norms;
void reserve(size_t len) { void reserve(size_t len) {
@ -41,14 +53,174 @@ class GLVertexArray {
this->norms.push_back(z); this->norms.push_back(z);
}; };
void load_mesh(const TriangleMesh &mesh); 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<coordf_t>(bbox.min.x, this->verts[i + 0]);
bbox.min.y = std::min<coordf_t>(bbox.min.y, this->verts[i + 1]);
bbox.min.z = std::min<coordf_t>(bbox.min.z, this->verts[i + 2]);
bbox.max.x = std::max<coordf_t>(bbox.max.x, this->verts[i + 0]);
bbox.max.y = std::max<coordf_t>(bbox.max.y, this->verts[i + 1]);
bbox.max.z = std::max<coordf_t>(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<char> 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<int> load_object(
const ModelObject *model_object,
const std::vector<int> &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<size_t, size_t> qverts_range;
// Triangle vertices.
GLVertexArray tverts;
std::pair<size_t, size_t> 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<coordf_t> print_zs;
std::vector<size_t> 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<GLTexture> 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<GLVolume*> volumes;
GLVolumeCollection() {};
~GLVolumeCollection() { clear(); };
std::vector<int> load_object(
const ModelObject *model_object,
int obj_idx,
const std::vector<int> &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 class _3DScene
{ {
public: public:
static void _extrusionentity_to_verts_do(const Lines &lines, const std::vector<double> &widths, static void _load_print_toolpaths(
const std::vector<double> &heights, bool closed, double top_z, const Point &copy, const Print *print,
GLVertexArray* qverts, GLVertexArray* tverts); GLVolumeCollection *volumes);
static void _load_print_object_toolpaths(
const PrintObject *print_object,
GLVolumeCollection *volumes);
}; };
} }

View File

@ -3,26 +3,101 @@
#include <xsinit.h> #include <xsinit.h>
#include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/3DScene.hpp"
%name{Slic3r::GUI::_3DScene::GLVertexArray} class GLVertexArray { %name{Slic3r::GUI::_3DScene::GLVolume} class GLVolume {
GLVertexArray(); GLVolume();
~GLVertexArray(); ~GLVolume();
void load_mesh(TriangleMesh* mesh) const
%code%{ THIS->load_mesh(*mesh); %}; std::vector<double> color()
size_t size() const %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]); %};
%code%{ RETVAL = THIS->verts.size(); %}; int composite_id()
void* verts_ptr() const %code%{ RETVAL = THIS->composite_id; %};
%code%{ RETVAL = THIS->verts.empty() ? 0 : &THIS->verts.front(); %}; int select_group_id()
void* norms_ptr() const %code%{ RETVAL = THIS->select_group_id; %};
%code%{ RETVAL = THIS->verts.empty() ? 0 : &THIS->norms.front(); %}; 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<Pointf3> origin() const
%code%{ RETVAL = THIS->origin; %};
Clone<BoundingBoxf3> bounding_box() const
%code%{ RETVAL = THIS->bounding_box; %};
Clone<BoundingBoxf3> 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<int> load_object(ModelObject *object, int obj_idx, std::vector<int> 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}; %package{Slic3r::GUI::_3DScene};
%{ %{
void void
_extrusionentity_to_verts_do(Lines lines, std::vector<double> widths, std::vector<double> heights, bool closed, double top_z, Point* copy, GLVertexArray* qverts, GLVertexArray* tverts) _load_print_toolpaths(print, volumes)
Print *print;
GLVolumeCollection *volumes;
CODE: CODE:
_3DScene::_extrusionentity_to_verts_do(lines, widths, heights, closed, _3DScene::_load_print_toolpaths(print, volumes);
top_z, *copy, qverts, tverts);
%} void
_load_print_object_toolpaths(print_object, volumes)
PrintObject *print_object;
GLVolumeCollection *volumes;
CODE:
_3DScene::_load_print_object_toolpaths(print_object, volumes);
%}

View File

@ -143,20 +143,6 @@ _constant()
void reset_layer_height_profile(); 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() int ptr()
%code%{ RETVAL = (int)(intptr_t)THIS; %}; %code%{ RETVAL = (int)(intptr_t)THIS; %};
}; };

View File

@ -233,7 +233,10 @@ PrintObjectSupportMaterial* O_OBJECT_SLIC3R
Ref<PrintObjectSupportMaterial> O_OBJECT_SLIC3R_T Ref<PrintObjectSupportMaterial> O_OBJECT_SLIC3R_T
Clone<PrintObjectSupportMaterial> O_OBJECT_SLIC3R_T Clone<PrintObjectSupportMaterial> O_OBJECT_SLIC3R_T
GLVertexArray* O_OBJECT_SLIC3R GLVolume* O_OBJECT_SLIC3R
Ref<GLVolume> O_OBJECT_SLIC3R_T
GLVolumeCollection* O_OBJECT_SLIC3R
Ref<GLVolumeCollection> O_OBJECT_SLIC3R_T
Axis T_UV Axis T_UV
ExtrusionLoopRole T_UV ExtrusionLoopRole T_UV

View File

@ -211,7 +211,10 @@
%typemap{ModelInstancePtrs*}; %typemap{ModelInstancePtrs*};
%typemap{Ref<ModelInstancePtrs>}{simple}; %typemap{Ref<ModelInstancePtrs>}{simple};
%typemap{Clone<ModelInstancePtrs>}{simple}; %typemap{Clone<ModelInstancePtrs>}{simple};
%typemap{GLVertexArray*}; %typemap{GLVolume*};
%typemap{Ref<GLVolume>}{simple};
%typemap{GLVolumeCollection*};
%typemap{Ref<GLVolumeCollection>}{simple};
%typemap{PrintRegionPtrs*}; %typemap{PrintRegionPtrs*};
%typemap{PrintObjectPtrs*}; %typemap{PrintObjectPtrs*};