User interface of the variable layer thickness. Certainly not finished yet,

but sufficient for evaluation of the prints.
This commit is contained in:
bubnikv 2016-12-12 18:02:24 +01:00
parent 2d030f3a3c
commit 46b44fc141
5 changed files with 413 additions and 133 deletions

View File

@ -53,10 +53,15 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
origin
_mouse_pos
_hover_volume_idx
_drag_volume_idx
_drag_start_pos
_drag_start_xy
_dragged
layer_editing_enabled
_layer_height_edited
_camera_type
_camera_target
_camera_distance
@ -74,7 +79,7 @@ use constant SELECTED_COLOR => [0,1,0,1];
use constant HOVER_COLOR => [0.4,0.9,0,1];
# phi / theta angles to orient the camera.
use constant VIEW_ISO => [45.0,45.0];
use constant VIEW_DEFAULT => [45.0,45.0];
use constant VIEW_LEFT => [90.0,90.0];
use constant VIEW_RIGHT => [-90.0,90.0];
use constant VIEW_TOP => [0.0,0.0];
@ -82,7 +87,11 @@ use constant VIEW_BOTTOM => [0.0,180.0];
use constant VIEW_FRONT => [0.0,90.0];
use constant VIEW_REAR => [180.0,90.0];
use constant GIMBAL_LOCK_THETA_MAX => 170;
use constant MANIPULATION_IDLE => 0;
use constant MANIPULATION_DRAGGING => 1;
use constant MANIPULATION_LAYER_HEIGHT => 2;
use constant GIMBALL_LOCK_THETA_MAX => 170;
# make OpenGL::Array thread-safe
{
@ -129,6 +138,11 @@ sub new {
# $self->_camera_type('perspective');
$self->_camera_target(Slic3r::Pointf3->new(0,0,0));
$self->_camera_distance(0.);
# Size of a layer height texture, used by a shader to color map the object print layers.
$self->{layer_preview_z_texture_width} = 512;
$self->{layer_preview_z_texture_height} = 512;
$self->{layer_height_edit_band_width} = 2.;
$self->reset_objects;
@ -177,6 +191,16 @@ sub new {
return $self;
}
sub _first_selected_object_id {
my ($self) = @_;
for my $i (0..$#{$self->volumes}) {
if ($self->volumes->[$i]->selected) {
return int($self->volumes->[$i]->select_group_id / 1000000);
}
}
return -1;
}
sub mouse_event {
my ($self, $e) = @_;
@ -191,40 +215,65 @@ sub mouse_event {
# If user pressed left or right button we first check whether this happened
# on a volume or not.
my $volume_idx = $self->_hover_volume_idx // -1;
# select volume in this 3D canvas
if ($self->enable_picking) {
$self->deselect_volumes;
$self->select_volume($volume_idx);
if ($volume_idx != -1) {
my $group_id = $self->volumes->[$volume_idx]->select_group_id;
my @volumes;
if ($group_id != -1) {
$self->select_volume($_)
for grep $self->volumes->[$_]->select_group_id == $group_id,
0..$#{$self->volumes};
$self->_layer_height_edited(0);
if ($self->layer_editing_enabled && $self->{print}) {
my $object_idx_selected = $self->_first_selected_object_id;
if ($object_idx_selected != -1) {
# A volume is selected. Test, whether hovering over a layer thickness bar.
my ($cw, $ch) = $self->GetSizeWH;
my $bar_width = 70;
if ($e->GetX >= $cw - $bar_width) {
# Start editing the layer height.
$self->_layer_height_edited(1);
my $z = unscale($self->{print}->get_object($object_idx_selected)->size->z) * ($ch - $e->GetY - 1.) / ($ch - 1);
# print "Modifying height profile at $z\n";
# $self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile($z, $e->RightDown ? - 0.05 : 0.05, 2., 0);
$self->{print}->get_object($object_idx_selected)->generate_layer_height_texture(
$self->volumes->[$object_idx_selected]->layer_height_texture_data->ptr,
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
$self->Refresh;
}
}
$self->Refresh;
}
# propagate event through callback
$self->on_select->($volume_idx)
if $self->on_select;
if ($volume_idx != -1) {
if ($e->LeftDown && $self->enable_moving) {
$self->_drag_volume_idx($volume_idx);
$self->_drag_start_pos($self->mouse_to_3d(@$pos));
} elsif ($e->RightDown) {
# if right clicking on volume, propagate event through callback
$self->on_right_click->($e->GetPosition)
if $self->on_right_click;
if (! $self->_layer_height_edited) {
# Select volume in this 3D canvas.
# Don't deselect a volume if layer editing is enabled. We want the object to stay selected
# during the scene manipulation.
if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) {
$self->deselect_volumes;
$self->select_volume($volume_idx);
if ($volume_idx != -1) {
my $group_id = $self->volumes->[$volume_idx]->select_group_id;
my @volumes;
if ($group_id != -1) {
$self->select_volume($_)
for grep $self->volumes->[$_]->select_group_id == $group_id,
0..$#{$self->volumes};
}
}
$self->Refresh;
}
# propagate event through callback
$self->on_select->($volume_idx)
if $self->on_select;
if ($volume_idx != -1) {
if ($e->LeftDown && $self->enable_moving) {
$self->_drag_volume_idx($volume_idx);
$self->_drag_start_pos($self->mouse_to_3d(@$pos));
} elsif ($e->RightDown) {
# if right clicking on volume, propagate event through callback
$self->on_right_click->($e->GetPosition)
if $self->on_right_click;
}
}
}
} elsif ($e->Dragging && $e->LeftIsDown && defined($self->_drag_volume_idx)) {
} elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) {
# get new position at the same Z of the initial click point
my $mouse_ray = $self->mouse_ray($e->GetX, $e->GetY);
my $cur_pos = $mouse_ray->intersect_plane($self->_drag_start_pos->z);
@ -249,14 +298,29 @@ sub mouse_event {
$self->_dragged(1);
$self->Refresh;
} elsif ($e->Dragging) {
if ($e->LeftIsDown) {
if ($self->_layer_height_edited) {
my $object_idx_selected = $self->_first_selected_object_id;
if ($object_idx_selected != -1) {
# A volume is selected. Test, whether hovering over a layer thickness bar.
my ($cw, $ch) = $self->GetSizeWH;
my $z = unscale($self->{print}->get_object($object_idx_selected)->size->z) * ($ch - $e->GetY - 1.) / ($ch - 1);
# print "Modifying height profile at $z\n";
my $strength = 0.005;
$self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile($z, $e->RightIsDown ? - $strength : $strength, 2., $e->ShiftDown ? 1 : 0);
$self->{print}->get_object($object_idx_selected)->generate_layer_height_texture(
$self->volumes->[$object_idx_selected]->layer_height_texture_data->ptr,
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
$self->Refresh;
}
} elsif ($e->LeftIsDown) {
# if dragging over blank area with left button, rotate
if (defined $self->_drag_start_pos) {
my $orig = $self->_drag_start_pos;
if (TURNTABLE_MODE) {
$self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE);
$self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #-
$self->_stheta(GIMBAL_LOCK_THETA_MAX) if $self->_stheta > GIMBAL_LOCK_THETA_MAX;
$self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
$self->_stheta(0) if $self->_stheta < 0;
} else {
my $size = $self->GetClientSize;
@ -304,6 +368,7 @@ sub mouse_event {
$self->_drag_start_pos(undef);
$self->_drag_start_xy(undef);
$self->_dragged(undef);
$self->_layer_height_edited(undef);
} elsif ($e->Moving) {
$self->_mouse_pos($pos);
$self->Refresh;
@ -340,8 +405,8 @@ sub select_view {
if (ref($direction)) {
$dirvec = $direction;
} else {
if ($direction eq 'iso') {
$dirvec = VIEW_ISO;
if ($direction eq 'default') {
$dirvec = VIEW_DEFAULT;
} elsif ($direction eq 'left') {
$dirvec = VIEW_LEFT;
} elsif ($direction eq 'right') {
@ -356,22 +421,18 @@ sub select_view {
$dirvec = VIEW_REAR;
}
}
$self->_sphi($dirvec->[0]);
$self->_stheta($dirvec->[1]);
# Avoid gimbal lock.
$self->_stheta(GIMBAL_LOCK_THETA_MAX) if $self->_stheta > GIMBAL_LOCK_THETA_MAX;
$self->_stheta(0) if $self->_stheta < 0;
# View everything.
$self->volumes_bounding_box->defined
? $self->zoom_to_volumes
: $self->zoom_to_bed;
$self->on_viewport_changed->() if $self->on_viewport_changed;
$self->_dirty(1);
$self->Refresh;
my $bb = $self->volumes_bounding_box;
if (! $bb->empty) {
$self->_sphi($dirvec->[0]);
$self->_stheta($dirvec->[1]);
# Avoid gimball lock.
$self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
$self->_stheta(0) if $self->_stheta < 0;
# View everything.
$self->zoom_to_bounding_box($bb);
$self->on_viewport_changed->() if $self->on_viewport_changed;
$self->Refresh;
}
}
sub zoom_to_bounding_box {
@ -380,7 +441,7 @@ sub zoom_to_bounding_box {
# calculate the zoom factor needed to adjust viewport to
# bounding box
my $max_size = max(@{$bb->size}) * 1.05;
my $max_size = max(@{$bb->size}) * 2;
my $min_viewport_size = min($self->GetSizeWH);
$self->_zoom($min_viewport_size / $max_size);
@ -725,12 +786,19 @@ sub InitGL {
$self->init(1);
my $shader;
# $shader = $self->{shader} = new Slic3r::GUI::GLShader;
$shader = $self->{shader} = new Slic3r::GUI::GLShader;
if ($self->{shader}) {
my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader);
print $info if $info;
($self->{layer_preview_z_texture_id}) = glGenTextures_p(1);
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
glBindTexture(GL_TEXTURE_2D, 0);
}
glClearColor(0, 0, 0, 1);
glColor3f(1, 0, 0);
glEnable(GL_DEPTH_TEST);
@ -808,11 +876,15 @@ sub Render {
glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0);
glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1);
glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1);
# Head light
glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0);
if ($self->enable_picking) {
# Render the object for picking.
# FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing.
# Better to use software ray-casting on a bounding-box hierarchy.
glDisable(GL_MULTISAMPLE);
glDisable(GL_LIGHTING);
$self->draw_volumes(1);
glFlush();
@ -839,6 +911,7 @@ sub Render {
glFlush();
glFinish();
glEnable(GL_LIGHTING);
glEnable(GL_MULTISAMPLE);
}
# draw fixed background
@ -938,7 +1011,7 @@ sub Render {
# draw objects
$self->draw_volumes;
# draw cutting plane
if (defined $self->cutting_plane_z) {
my $plane_z = $self->cutting_plane_z;
@ -957,6 +1030,8 @@ sub Render {
glEnable(GL_CULL_FACE);
glDisable(GL_BLEND);
}
$self->draw_active_object_annotations;
glFlush();
@ -967,18 +1042,64 @@ sub draw_volumes {
# $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking.
my ($self, $fakecolor) = @_;
$self->{shader}->Enable if (! $fakecolor && $self->{shader});
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
# The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
# where x, y is the window size divided by $self->_zoom.
my ($cw, $ch) = $self->GetSizeWH;
my $bar_width = 70;
my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom));
my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom));
my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition());
my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. :
($ch - $mouse_pos->y - 1.) / ($ch - 1);
foreach my $volume_idx (0..$#{$self->volumes}) {
my $volume = $self->volumes->[$volume_idx];
if ($fakecolor) {
my $shader_active = 0;
if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->{layer_height_texture_data} && $volume->{layer_height_texture_cells}) {
$self->{shader}->Enable;
my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row');
my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized');
my $z_cursor_id = $self->{shader}->Map('z_cursor');
die if ! defined($z_to_texture_row_id);
die if ! defined($z_texture_row_to_normalized_id);
die if ! defined($z_cursor_id);
my $ncells = $volume->{layer_height_texture_cells};
my $z_max = $volume->{bounding_box}->z_max;
glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max));
glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height});
glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative);
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LEVEL, 0);
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
if (1) {
glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
# glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
# glPixelStorei(GL_UNPACK_ROW_LENGTH, $self->{layer_preview_z_texture_width});
glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr);
glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4));
} else {
glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr);
glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width}/2, $self->{layer_preview_z_texture_height}/2,
0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr + $self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4);
}
# my $nlines = ceil($ncells / ($self->{layer_preview_z_texture_width} - 1));
$shader_active = 1;
} elsif ($fakecolor) {
# Object picking mode. Render the object with a color encoding the object index.
my $r = ($volume_idx & 0x000000FF) >> 0;
my $g = ($volume_idx & 0x0000FF00) >> 8;
@ -1055,8 +1176,13 @@ sub draw_volumes {
}
glVertexPointer_c(3, GL_FLOAT, 0, 0);
glNormalPointer_c(GL_FLOAT, 0, 0);
glNormalPointer_c(GL_FLOAT, 0, 0);
glPopMatrix();
if ($shader_active) {
glBindTexture(GL_TEXTURE_2D, 0);
$self->{shader}->Disable;
}
}
glDisableClientState(GL_NORMAL_ARRAY);
glDisable(GL_BLEND);
@ -1069,8 +1195,93 @@ sub draw_volumes {
glVertexPointer_c(3, GL_FLOAT, 0, 0);
}
glDisableClientState(GL_VERTEX_ARRAY);
}
$self->{shader}->Disable if (! $fakecolor && $self->{shader});
sub draw_active_object_annotations {
# $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking.
my ($self) = @_;
return if (! $self->{shader} || ! $self->layer_editing_enabled);
my $volume;
foreach my $volume_idx (0..$#{$self->volumes}) {
my $v = $self->volumes->[$volume_idx];
if ($v->selected && $v->{layer_height_texture_data} && $v->{layer_height_texture_cells}) {
$volume = $v;
last;
}
}
return if (! $volume);
# The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
# where x, y is the window size divided by $self->_zoom.
my ($cw, $ch) = $self->GetSizeWH;
my $bar_width = 70;
my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom));
my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom));
my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition());
my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. :
($ch - $mouse_pos->y - 1.) / ($ch - 1);
$self->{shader}->Enable;
my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row');
my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized');
my $z_cursor_id = $self->{shader}->Map('z_cursor');
my $ncells = $volume->{layer_height_texture_cells};
my $z_max = $volume->{bounding_box}->z_max;
glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max));
glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height});
glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative);
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},
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr);
glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4));
# Render the color bar.
glDisable(GL_DEPTH_TEST);
# The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
# where x, y is the window size divided by $self->_zoom.
glPushMatrix();
glLoadIdentity();
# Paint the overlay.
glBegin(GL_QUADS);
glVertex3f($bar_left, $bar_bottom, 0);
glVertex3f($bar_right, $bar_bottom, 0);
glVertex3f($bar_right, $bar_top, $volume->{bounding_box}->z_max);
glVertex3f($bar_left, $bar_top, $volume->{bounding_box}->z_max);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
$self->{shader}->Disable;
# Paint the graph.
my $object_idx = int($volume->select_group_id / 1000000);
my $print_object = $self->{print}->get_object($object_idx);
my $max_z = unscale($print_object->size->z);
my $profile = $print_object->layer_height_profile;
my $layer_height = $print_object->config->get('layer_height');
# Baseline
glColor3f(0., 0., 0.);
glBegin(GL_LINE_STRIP);
glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45, $bar_bottom);
glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45, $bar_top);
glEnd();
# Curve
glColor3f(0., 0., 1.);
glBegin(GL_LINE_STRIP);
for (my $i = 0; $i < int(@{$profile}); $i += 2) {
my $z = $profile->[$i];
my $h = $profile->[$i+1];
glVertex3f($bar_left + $h * ($bar_right - $bar_left) / 0.45, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z);
}
glEnd();
# Revert the matrices.
glPopMatrix();
glEnable(GL_DEPTH_TEST);
}
sub _report_opengl_state
@ -1106,57 +1317,61 @@ sub _vertex_shader {
return <<'VERTEX';
#version 110
#define LIGHT_TOP_DIR 0., 1., 0.
#define LIGHT_TOP_DIFFUSE 0.2
#define LIGHT_TOP_SPECULAR 0.3
#define LIGHT_FRONT_DIR 0., 0., 1.
#define LIGHT_FRONT_DIFFUSE 0.5
#define LIGHT_FRONT_SPECULAR 0.3
#define INTENSITY_AMBIENT 0.1
uniform float z_to_texture_row;
varying float intensity_specular;
varying float intensity_tainted;
varying float object_z;
void main()
{
vec3 normal, lightDir, viewVector, halfVector;
vec4 diffuse, ambient, globalAmbient, specular = vec4(0.0);
float NdotL,NdotHV;
vec3 eye, normal, lightDir, viewVector, halfVector;
float NdotL, NdotHV;
// eye = gl_ModelViewMatrixInverse[3].xyz;
eye = vec3(0., 0., 1.);
// First transform the normal into eye space and normalize the result.
normal = normalize(gl_NormalMatrix * gl_Normal);
// Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space.
// Also since we're talking about a directional light, the position field is actually direction.
lightDir = normalize(vec3(gl_LightSource[0].position));
lightDir = vec3(LIGHT_TOP_DIR);
halfVector = normalize(lightDir + eye);
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
// Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
NdotL = max(dot(normal, lightDir), 0.0);
// Compute the diffuse, ambient and globalAmbient terms.
// diffuse = NdotL * (gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse);
// ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
diffuse = NdotL * (gl_Color * gl_LightSource[0].diffuse);
ambient = gl_Color * gl_LightSource[0].ambient;
globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient;
// compute the specular term if NdotL is larger than zero
if (NdotL > 0.0) {
NdotHV = max(dot(normal, normalize(gl_LightSource[0].halfVector.xyz)),0.0);
specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess);
}
intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
intensity_specular = 0.;
// if (NdotL > 0.0)
// intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), gl_FrontMaterial.shininess);
// Perform the same lighting calculation for the 2nd light source.
lightDir = normalize(vec3(gl_LightSource[1].position));
lightDir = vec3(LIGHT_FRONT_DIR);
halfVector = normalize(lightDir + eye);
NdotL = max(dot(normal, lightDir), 0.0);
// diffuse += NdotL * (gl_FrontMaterial.diffuse * gl_LightSource[1].diffuse);
// ambient += gl_FrontMaterial.ambient * gl_LightSource[1].ambient;
diffuse += NdotL * (gl_Color * gl_LightSource[1].diffuse);
ambient += gl_Color * gl_LightSource[1].ambient;
intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE;
// compute the specular term if NdotL is larger than zero
if (NdotL > 0.0) {
NdotHV = max(dot(normal, normalize(gl_LightSource[1].halfVector.xyz)),0.0);
specular += gl_FrontMaterial.specular * gl_LightSource[1].specular * pow(NdotHV,gl_FrontMaterial.shininess);
}
gl_FrontColor = globalAmbient + diffuse + ambient + specular;
gl_FrontColor.a = 1.;
gl_Position = ftransform();
// compute the specular term if NdotL is larger than zero
if (NdotL > 0.0)
intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(normal, halfVector), 0.0), gl_FrontMaterial.shininess);
// Scaled to widths of the Z texture.
object_z = gl_Vertex.z / gl_Vertex.w;
gl_Position = ftransform();
}
VERTEX
@ -1165,17 +1380,39 @@ VERTEX
sub _fragment_shader {
return <<'FRAGMENT';
#version 110
#define M_PI 3.1415926535897932384626433832795
// 2D texture (1D texture split by the rows) of color along the object Z axis.
uniform sampler2D z_texture;
// Scaling from the Z texture rows coordinate to the normalized texture row coordinate.
uniform float z_to_texture_row;
uniform float z_texture_row_to_normalized;
varying float intensity_specular;
varying float intensity_tainted;
varying float object_z;
uniform float z_cursor;
uniform float z_cursor_band_width;
void main()
{
float layer_height = 0.25;
float layer_height2 = 0.5 * layer_height;
float layer_center = floor(object_z / layer_height) * layer_height + layer_height2;
float intensity = cos(M_PI * 0.7 * (layer_center - object_z) / layer_height);
gl_FragColor = gl_Color * intensity;
float object_z_row = z_to_texture_row * object_z;
// Index of the row in the texture.
float z_texture_row = floor(object_z_row);
// Normalized coordinate from 0. to 1.
float z_texture_col = object_z_row - z_texture_row;
// float z_blend = 0.5 + 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) / 3.)));
// float z_blend = 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor)))) + 0.5;
float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor)))) + 0.25;
// Scale z_texture_row to normalized coordinates.
// Sample the Z texture.
gl_FragColor =
vec4(intensity_specular, intensity_specular, intensity_specular, 1.) +
// intensity_tainted * texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5)), -2.5);
(1. - z_blend) * intensity_tainted * texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5)), -200.) +
z_blend * vec4(1., 1., 0., 0.);
// and reset the transparency.
gl_FragColor.a = 1.;
}
@ -1189,6 +1426,8 @@ 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.
@ -1210,6 +1449,26 @@ has 'tverts' => (is => 'rw');
# 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 $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) = @_;
@ -1235,8 +1494,6 @@ __PACKAGE__->mk_accessors(qw(
color_by
select_by
drag_by
volumes_by_object
_objects_by_volumes
));
sub new {
@ -1246,14 +1503,12 @@ sub new {
$self->color_by('volume'); # object | volume
$self->select_by('object'); # object | volume | instance
$self->drag_by('instance'); # object | instance
$self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ]
$self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ]
return $self;
}
sub load_object {
my ($self, $model, $obj_idx, $instance_idxs) = @_;
my ($self, $model, $print, $obj_idx, $instance_idxs) = @_;
my $model_object;
if ($model->isa('Slic3r::Model::Object')) {
@ -1265,6 +1520,19 @@ sub load_object {
}
$instance_idxs ||= [0..$#{$model_object->instances}];
# Object will have a single common layer height texture for all volumes.
my $layer_height_texture_data;
my $layer_height_texture_cells;
if ($print && $obj_idx < $print->object_count) {
# Generate the layer height texture. Allocate data for the 0th and 1st mipmap levels.
$layer_height_texture_data = OpenGL::Array->new($self->{layer_preview_z_texture_width}*$self->{layer_preview_z_texture_height}*5, GL_UNSIGNED_BYTE);
# $print->get_object($obj_idx)->update_layer_height_profile_from_ranges();
$layer_height_texture_cells = $print->get_object($obj_idx)->generate_layer_height_texture(
$layer_height_texture_data->ptr,
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
}
my @volumes_idx = ();
foreach my $volume_idx (0..$#{$model_object->volumes}) {
@ -1287,16 +1555,18 @@ sub load_object {
# not correspond to the color of the filament.
my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ];
$color->[3] = $volume->modifier ? 0.5 : 1;
print "Reloading object $volume_idx, $instance_idx\n";
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($obj_idx*1000000 + $volume_idx*1000 + $instance_idx);
$v->select_group_id($v->composite_id);
}
if ($self->drag_by eq 'object') {
$v->drag_group_id($obj_idx*1000);
@ -1304,15 +1574,18 @@ sub load_object {
$v->drag_group_id($obj_idx*1000 + $instance_idx);
}
push @volumes_idx, my $scene_volume_idx = $#{$self->volumes};
$self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $volume_idx, $instance_idx ];
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);
}
}
}
$self->volumes_by_object->{$obj_idx} = [@volumes_idx];
return @volumes_idx;
}
@ -1651,19 +1924,4 @@ sub _extrusionentity_to_verts {
$tverts);
}
sub object_idx {
my ($self, $volume_idx) = @_;
return $self->_objects_by_volumes->{$volume_idx}[0];
}
sub volume_idx {
my ($self, $volume_idx) = @_;
return $self->_objects_by_volumes->{$volume_idx}[1];
}
sub instance_idx {
my ($self, $volume_idx) = @_;
return $self->_objects_by_volumes->{$volume_idx}[2];
}
1;

View File

@ -8,10 +8,11 @@ use utf8;
use File::Basename qw(basename dirname);
use List::Util qw(sum first max);
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad);
use LWP::UserAgent;
use threads::shared qw(shared_clone);
use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
:panel :sizer :toolbar :window wxTheApp :notebook :combobox);
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
use base 'Wx::Panel';
@ -30,6 +31,7 @@ use constant TB_SCALE => &Wx::NewId;
use constant TB_SPLIT => &Wx::NewId;
use constant TB_CUT => &Wx::NewId;
use constant TB_SETTINGS => &Wx::NewId;
use constant TB_LAYER_EDITING => &Wx::NewId;
# package variables to avoid passing lexicals to threads
our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType;
@ -94,7 +96,7 @@ sub new {
# Initialize 3D plater
if ($Slic3r::GUI::have_OpenGL) {
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config});
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config});
$self->{preview_notebook}->AddPage($self->{canvas3D}, '3D');
$self->{canvas3D}->set_on_select_object($on_select_object);
$self->{canvas3D}->set_on_double_click($on_double_click);
@ -154,6 +156,9 @@ sub new {
$self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddSeparator;
$self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), '');
# FIXME add a button for layer editing
$self->{htoolbar}->AddCheckTool(TB_LAYER_EDITING, "Layer editing", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), '');
} else {
my %tbar_buttons = (
add => "Add…",
@ -168,12 +173,15 @@ sub new {
split => "Split",
cut => "Cut…",
settings => "Settings…",
layer_editing => "Layer editing",
);
$self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) {
$self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
$self->{btoolbar}->Add($self->{"btn_$_"});
}
$self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
$self->{btoolbar}->Add($self->{"btn_layer_editing"});
}
$self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize,
@ -256,6 +264,7 @@ sub new {
EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; });
EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog });
EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog });
EVT_TOOL($self, TB_LAYER_EDITING, sub { $self->on_layer_editing_toggled($self->{htoolbar}->GetToolState(TB_LAYER_EDITING)); });
} else {
EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; });
EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove
@ -269,6 +278,7 @@ sub new {
EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; });
EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog });
EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog });
EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); });
}
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
@ -464,6 +474,13 @@ sub _on_select_preset {
$self->on_config_change($self->GetFrame->config);
}
sub on_layer_editing_toggled {
my ($self, $new_state) = @_;
print "on_layer_editing_toggled $new_state\n";
$self->{canvas3D}->layer_editing_enabled($new_state);
$self->{canvas3D}->update;
}
sub GetFrame {
my ($self) = @_;
return &Wx::GetTopLevelParent($self);
@ -1481,6 +1498,8 @@ sub on_thumbnail_made {
# (i.e. when an object is added/removed/moved/rotated/scaled)
sub update {
my ($self, $force_autocenter) = @_;
print "Platter - update\n";
if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) {
$self->{model}->center_instances_around_point($self->bed_centerf);
@ -1679,7 +1698,7 @@ sub object_list_changed {
my $have_objects = @{$self->{objects}} ? 1 : 0;
my $method = $have_objects ? 'Enable' : 'Disable';
$self->{"btn_$_"}->$method
for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode);
for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode layer_editing);
if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) {
$self->{btn_reslice}->Disable;
@ -1712,6 +1731,7 @@ sub selection_changed {
if ($self->{object_info_size}) { # have we already loaded the info pane?
if ($have_sel) {
my $model_object = $self->{model}->objects->[$obj_idx];
$model_object->print_info;
my $model_instance = $model_object->instances->[0];
$self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size}));
$self->{object_info_materials}->SetLabel($model_object->materials_count);

View File

@ -12,7 +12,7 @@ use base qw(Slic3r::GUI::3DScene Class::Accessor);
sub new {
my $class = shift;
my ($parent, $objects, $model, $config) = @_;
my ($parent, $objects, $model, $print, $config) = @_;
my $self = $class->SUPER::new($parent);
$self->enable_picking(1);
@ -22,6 +22,7 @@ sub new {
$self->{objects} = $objects;
$self->{model} = $model;
$self->{print} = $print;
$self->{config} = $config;
$self->{on_select_object} = sub {};
$self->{on_instances_moved} = sub {};
@ -31,7 +32,7 @@ sub new {
my $obj_idx = undef;
if ($volume_idx != -1) {
$obj_idx = $self->object_idx($volume_idx);
$obj_idx = $self->volumes->[$volume_idx]->object_idx;
}
$self->{on_select_object}->($obj_idx)
if $self->{on_select_object};
@ -42,8 +43,8 @@ sub new {
my %done = (); # prevent moving instances twice
foreach my $volume_idx (@volume_idxs) {
my $volume = $self->volumes->[$volume_idx];
my $obj_idx = $self->object_idx($volume_idx);
my $instance_idx = $self->instance_idx($volume_idx);
my $obj_idx = $volume->object_idx;
my $instance_idx = $volume->instance_idx;
next if $done{"${obj_idx}_${instance_idx}"};
$done{"${obj_idx}_${instance_idx}"} = 1;
@ -89,7 +90,7 @@ sub update {
$self->update_bed_size;
foreach my $obj_idx (0..$#{$self->{model}->objects}) {
my @volume_idxs = $self->load_object($self->{model}, $obj_idx);
my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx);
if ($self->{objects}[$obj_idx]->selected) {
$self->select_volume($_) for @volume_idxs;

View File

@ -114,7 +114,7 @@ sub new {
my $canvas;
if ($Slic3r::GUI::have_OpenGL) {
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
$canvas->load_object($self->{model_object}, undef, [0]);
$canvas->load_object($self->{model_object}, undef, undef, [0]);
$canvas->set_auto_bed_shape;
$canvas->SetSize([500,500]);
$canvas->SetMinSize($canvas->GetSize);
@ -244,7 +244,7 @@ sub _update {
}
$self->{canvas}->reset_objects;
$self->{canvas}->load_object($_, undef, [0]) for @objects;
$self->{canvas}->load_object($_, undef, undef, [0]) for @objects;
$self->{canvas}->SetCuttingPlane(
$self->{cut_options}{z},
[@expolygons],

View File

@ -22,6 +22,7 @@ sub new {
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
my $object = $self->{model_object} = $params{model_object};
my $print_object = $self->{print_object} = $params{print_object};
# create TreeCtrl
my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],
@ -82,7 +83,7 @@ sub new {
$self->reload_tree($canvas->volume_idx($volume_idx));
});
$canvas->load_object($self->{model_object}, undef, [0]);
$canvas->load_object($self->{model_object}, undef, undef, [0]);
$canvas->set_auto_bed_shape;
$canvas->SetSize([500,500]);
$canvas->zoom_to_volumes;