Volume rewritten from Perl to C++,
generation of vertex arrays from paths rewritten from Perl to C++, parallelized.
This commit is contained in:
parent
50976e1b5a
commit
e6fddd364d
@ -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
|
||||||
@ -137,17 +133,16 @@ sub new {
|
|||||||
$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,122 +1676,8 @@ 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.
|
||||||
@ -1939,54 +1685,8 @@ sub load_print_object_slices {
|
|||||||
sub load_print_toolpaths {
|
sub load_print_toolpaths {
|
||||||
my ($self, $print) = @_;
|
my ($self, $print) = @_;
|
||||||
|
|
||||||
return if !$print->step_done(STEP_SKIRT);
|
Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes)
|
||||||
return if !$print->step_done(STEP_BRIM);
|
if ($print->step_done(STEP_SKIRT) && $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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create 3D thick extrusion lines for object forming extrusions.
|
# Create 3D thick extrusion lines for object forming extrusions.
|
||||||
@ -1995,224 +1695,12 @@ sub load_print_toolpaths {
|
|||||||
sub load_print_object_toolpaths {
|
sub load_print_object_toolpaths {
|
||||||
my ($self, $object) = @_;
|
my ($self, $object) = @_;
|
||||||
|
|
||||||
my $perim_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes);
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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))));
|
||||||
|
@ -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) {
|
||||||
|
@ -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 ©,
|
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 ©, GLVolume &volume)
|
||||||
{
|
{
|
||||||
this->reserve_more(3 * 3 * mesh.facets_count());
|
Polyline polyline = extrusion_path.polyline;
|
||||||
|
polyline.remove_duplicate_points();
|
||||||
|
polyline.translate(copy);
|
||||||
|
Lines lines = polyline.lines();
|
||||||
|
std::vector<double> widths(lines.size(), extrusion_path.width);
|
||||||
|
std::vector<double> heights(lines.size(), extrusion_path.height);
|
||||||
|
thick_lines_to_verts(lines, widths, heights, false, print_z, volume);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
|
// Fill in the qverts and tverts with quads and triangles for the extrusion_loop.
|
||||||
stl_facet &facet = mesh.stl.facet_start[i];
|
static inline void extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume)
|
||||||
for (int j = 0; j <= 2; ++j) {
|
{
|
||||||
this->push_norm(facet.normal.x, facet.normal.y, facet.normal.z);
|
Lines lines;
|
||||||
this->push_vert(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z);
|
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 ©, 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 ©, 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<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 ©: 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 ©: *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";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 ©,
|
const Print *print,
|
||||||
GLVertexArray* qverts, GLVertexArray* tverts);
|
GLVolumeCollection *volumes);
|
||||||
|
|
||||||
|
static void _load_print_object_toolpaths(
|
||||||
|
const PrintObject *print_object,
|
||||||
|
GLVolumeCollection *volumes);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
%}
|
%}
|
@ -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; %};
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
@ -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*};
|
||||||
|
Loading…
Reference in New Issue
Block a user