diff options
24 files changed, 832 insertions, 2768 deletions
diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 157e7229c..23decaa37 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -16,102 +16,10 @@ use strict; use warnings; use Wx qw(wxTheApp :timer :bitmap :icon :dialog); -#============================================================================================================================== -#use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER); # must load OpenGL *before* Wx::GLCanvas use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); use base qw(Wx::GLCanvas Class::Accessor); -#============================================================================================================================== -#use Math::Trig qw(asin tan); -#use List::Util qw(reduce min max first); -#use Slic3r::Geometry qw(X Y normalize scale unscale scaled_epsilon); -#use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl JT_ROUND); -#============================================================================================================================== use Wx::GLCanvas qw(:all); -#============================================================================================================================== -#use Slic3r::Geometry qw(PI); -#============================================================================================================================== - -# volumes: reference to vector of Slic3r::GUI::3DScene::Volume. -#============================================================================================================================== -#__PACKAGE__->mk_accessors( qw(_quat _dirty init -# enable_picking -# enable_moving -# use_plain_shader -# on_viewport_changed -# on_hover -# on_select -# on_double_click -# on_right_click -# on_move -# on_model_update -# volumes -# _sphi _stheta -# cutting_plane_z -# cut_lines_vertices -# bed_shape -# bed_triangles -# bed_grid_lines -# bed_polygon -# background -# origin -# _mouse_pos -# _hover_volume_idx -# -# _drag_volume_idx -# _drag_start_pos -# _drag_volume_center_offset -# _drag_start_xy -# _dragged -# -# _layer_height_edited -# -# _camera_type -# _camera_target -# _camera_distance -# _zoom -# -# _legend_enabled -# _warning_enabled -# _apply_zoom_to_volumes_filter -# _mouse_dragging -# -# ) ); -# -#use constant TRACKBALLSIZE => 0.8; -#use constant TURNTABLE_MODE => 1; -#use constant GROUND_Z => -0.02; -## For mesh selection: Not selected - bright yellow. -#use constant DEFAULT_COLOR => [1,1,0]; -## For mesh selection: Selected - bright green. -#use constant SELECTED_COLOR => [0,1,0,1]; -## For mesh selection: Mouse hovers over the object, but object not selected yet - dark green. -#use constant HOVER_COLOR => [0.4,0.9,0,1]; -# -## phi / theta angles to orient the camera. -#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]; -#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 MANIPULATION_IDLE => 0; -#use constant MANIPULATION_DRAGGING => 1; -#use constant MANIPULATION_LAYER_HEIGHT => 2; -# -#use constant GIMBALL_LOCK_THETA_MAX => 180; -# -#use constant VARIABLE_LAYER_THICKNESS_BAR_WIDTH => 70; -#use constant VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT => 22; -# -## make OpenGL::Array thread-safe -#{ -# no warnings 'redefine'; -# *OpenGL::Array::CLONE_SKIP = sub { 1 }; -#} -#============================================================================================================================== sub new { my ($class, $parent) = @_; @@ -135,2097 +43,28 @@ sub new { # we request a depth buffer explicitely because it looks like it's not created by # default on Linux, causing transparency issues my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "", $attrib); -#============================================================================================================================== -# if (Wx::wxVERSION >= 3.000003) { -# # Wx 3.0.3 contains an ugly hack to support some advanced OpenGL attributes through the attribute list. -# # The attribute list is transferred between the wxGLCanvas and wxGLContext constructors using a single static array s_wglContextAttribs. -# # Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs. -# $self->GetContext(); -# } -#============================================================================================================================== -#============================================================================================================================== Slic3r::GUI::_3DScene::add_canvas($self); Slic3r::GUI::_3DScene::allow_multisample($self, $can_multisample); -# my $context = $self->GetContext; -# $self->SetCurrent($context); -# Slic3r::GUI::_3DScene::add_canvas($self, $context); -# -# $self->{can_multisample} = $can_multisample; -# $self->background(1); -# $self->_quat((0, 0, 0, 1)); -# $self->_stheta(45); -# $self->_sphi(45); -# $self->_zoom(1); -# $self->_legend_enabled(0); -# $self->_warning_enabled(0); -# $self->use_plain_shader(0); -# $self->_apply_zoom_to_volumes_filter(0); -# $self->_mouse_dragging(0); -# -# # Collection of GLVolume objects -# $self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new); -# -# # 3D point in model space -# $self->_camera_type('ortho'); -## $self->_camera_type('perspective'); -# $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); -# $self->_camera_distance(0.); -# $self->layer_editing_enabled(0); -# $self->{layer_height_edit_band_width} = 2.; -# $self->{layer_height_edit_strength} = 0.005; -# $self->{layer_height_edit_last_object_id} = -1; -# $self->{layer_height_edit_last_z} = 0.; -# $self->{layer_height_edit_last_action} = 0; -# -# $self->reset_objects; -# -# EVT_PAINT($self, sub { -# my $dc = Wx::PaintDC->new($self); -# $self->Render($dc); -# }); -# EVT_SIZE($self, sub { $self->_dirty(1) }); -# EVT_IDLE($self, sub { -# return unless $self->_dirty; -# return if !$self->IsShownOnScreen; -# $self->Resize( $self->GetSizeWH ); -# $self->Refresh; -# }); -# EVT_MOUSEWHEEL($self, \&mouse_wheel_event); -# EVT_MOUSE_EVENTS($self, \&mouse_event); -## EVT_KEY_DOWN($self, sub { -# EVT_CHAR($self, sub { -# my ($s, $event) = @_; -# if ($event->HasModifiers) { -# $event->Skip; -# } else { -# my $key = $event->GetKeyCode; -# if ($key == ord('0')) { -# $self->select_view('iso'); -# } elsif ($key == ord('1')) { -# $self->select_view('top'); -# } elsif ($key == ord('2')) { -# $self->select_view('bottom'); -# } elsif ($key == ord('3')) { -# $self->select_view('front'); -# } elsif ($key == ord('4')) { -# $self->select_view('rear'); -# } elsif ($key == ord('5')) { -# $self->select_view('left'); -# } elsif ($key == ord('6')) { -# $self->select_view('right'); -# } elsif ($key == ord('z')) { -# $self->zoom_to_volumes; -# } elsif ($key == ord('b')) { -# $self->zoom_to_bed; -# } else { -# $event->Skip; -# } -# } -# }); -# -# $self->{layer_height_edit_timer_id} = &Wx::NewId(); -# $self->{layer_height_edit_timer} = Wx::Timer->new($self, $self->{layer_height_edit_timer_id}); -# EVT_TIMER($self, $self->{layer_height_edit_timer_id}, sub { -# my ($self, $event) = @_; -# return if $self->_layer_height_edited != 1; -# $self->_variable_layer_thickness_action(undef); -# }); -#============================================================================================================================== return $self; } -#============================================================================================================================== -#sub set_legend_enabled { -# my ($self, $value) = @_; -# $self->_legend_enabled($value); -#} -# -#sub set_warning_enabled { -# my ($self, $value) = @_; -# $self->_warning_enabled($value); -#} -#============================================================================================================================== - sub Destroy { my ($self) = @_; -#============================================================================================================================== Slic3r::GUI::_3DScene::remove_canvas($self); -# $self->{layer_height_edit_timer}->Stop; -# $self->DestroyGL; -#============================================================================================================================== return $self->SUPER::Destroy; } -#============================================================================================================================== -#sub layer_editing_enabled { -# my ($self, $value) = @_; -# if (@_ == 2) { -# $self->{layer_editing_enabled} = $value; -# if ($value) { -# if (! $self->{layer_editing_initialized}) { -# # Enabling the layer editing for the first time. This triggers compilation of the necessary OpenGL shaders. -# # If compilation fails, a message box is shown with the error codes. -# $self->SetCurrent($self->GetContext); -# my $shader = new Slic3r::GUI::_3DScene::GLShader; -# my $error_message; -# if (! $shader->load_from_text($self->_fragment_shader_variable_layer_height, $self->_vertex_shader_variable_layer_height)) { -# # Compilation or linking of the shaders failed. -# $error_message = "Cannot compile an OpenGL Shader, therefore the Variable Layer Editing will be disabled.\n\n" -# . $shader->last_error; -# $shader = undef; -# } else { -# $self->{layer_height_edit_shader} = $shader; -# ($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_WRAP_S, GL_CLAMP); -# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); -# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); -# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); -# glBindTexture(GL_TEXTURE_2D, 0); -# } -# if (defined($error_message)) { -# # Don't enable the layer editing tool. -# $self->{layer_editing_enabled} = 0; -# # 2 means failed -# $self->{layer_editing_initialized} = 2; -# # Show the error message. -# Wx::MessageBox($error_message, "Slic3r Error", wxOK | wxICON_EXCLAMATION, $self); -# } else { -# $self->{layer_editing_initialized} = 1; -# } -# } elsif ($self->{layer_editing_initialized} == 2) { -# # Initilization failed before. Don't try to initialize and disable layer editing. -# $self->{layer_editing_enabled} = 0; -# } -# } -# } -# return $self->{layer_editing_enabled}; -#} -# -#sub layer_editing_allowed { -# my ($self) = @_; -# # Allow layer editing if either the shaders were not initialized yet and we don't know -# # whether it will be possible to initialize them, -# # or if the initialization was done already and it failed. -# return ! (defined($self->{layer_editing_initialized}) && $self->{layer_editing_initialized} == 2); -#} -# -#sub _first_selected_object_id_for_variable_layer_height_editing { -# my ($self) = @_; -# for my $i (0..$#{$self->volumes}) { -# if ($self->volumes->[$i]->selected) { -# my $object_id = int($self->volumes->[$i]->select_group_id / 1000000); -# # Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy. -# return ($object_id >= $self->{print}->object_count) ? -1 : $object_id -# if $object_id < 10000; -# } -# } -# return -1; -#} -# -## Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen. -#sub _variable_layer_thickness_bar_rect_screen { -# my ($self) = @_; -# my ($cw, $ch) = $self->GetSizeWH; -# return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0, $cw, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT); -#} -# -#sub _variable_layer_thickness_bar_rect_viewport { -# my ($self) = @_; -# my ($cw, $ch) = $self->GetSizeWH; -# return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom, $cw/(2*$self->_zoom), $ch/(2*$self->_zoom)); -#} -# -## Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen. -#sub _variable_layer_thickness_reset_rect_screen { -# my ($self) = @_; -# my ($cw, $ch) = $self->GetSizeWH; -# return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, $cw, $ch); -#} -# -#sub _variable_layer_thickness_reset_rect_viewport { -# my ($self) = @_; -# my ($cw, $ch) = $self->GetSizeWH; -# return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, -$ch/(2*$self->_zoom), $cw/(2*$self->_zoom), (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom); -#} -# -#sub _variable_layer_thickness_bar_rect_mouse_inside { -# my ($self, $mouse_evt) = @_; -# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; -# return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; -#} -# -#sub _variable_layer_thickness_reset_rect_mouse_inside { -# my ($self, $mouse_evt) = @_; -# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_reset_rect_screen; -# return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; -#} -# -#sub _variable_layer_thickness_bar_mouse_cursor_z_relative { -# my ($self) = @_; -# my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); -# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; -# return ($mouse_pos->x >= $bar_left && $mouse_pos->x <= $bar_right && $mouse_pos->y >= $bar_top && $mouse_pos->y <= $bar_bottom) ? -# # Inside the bar. -# ($bar_bottom - $mouse_pos->y - 1.) / ($bar_bottom - $bar_top - 1) : -# # Outside the bar. -# -1000.; -#} -# -#sub _variable_layer_thickness_action { -# my ($self, $mouse_event, $do_modification) = @_; -# # A volume is selected. Test, whether hovering over a layer thickness bar. -# return if $self->{layer_height_edit_last_object_id} == -1; -# if (defined($mouse_event)) { -# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; -# $self->{layer_height_edit_last_z} = unscale($self->{print}->get_object($self->{layer_height_edit_last_object_id})->size->z) -# * ($bar_bottom - $mouse_event->GetY - 1.) / ($bar_bottom - $bar_top); -# $self->{layer_height_edit_last_action} = $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1); -# } -# # Mark the volume as modified, so Print will pick its layer height profile? Where to mark it? -# # Start a timer to refresh the print? schedule_background_process() ? -# # The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself. -# $self->{print}->get_object($self->{layer_height_edit_last_object_id})->adjust_layer_height_profile( -# $self->{layer_height_edit_last_z}, -# $self->{layer_height_edit_strength}, -# $self->{layer_height_edit_band_width}, -# $self->{layer_height_edit_last_action}); -# -# $self->volumes->[$self->{layer_height_edit_last_object_id}]->generate_layer_height_texture( -# $self->{print}->get_object($self->{layer_height_edit_last_object_id}), 1); -# $self->Refresh; -# # Automatic action on mouse down with the same coordinate. -# $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS); -#} -# -#sub mouse_event { -# my ($self, $e) = @_; -# -# my $pos = Slic3r::Pointf->new($e->GetPositionXY); -# my $object_idx_selected = $self->{layer_height_edit_last_object_id} = ($self->layer_editing_enabled && $self->{print}) ? $self->_first_selected_object_id_for_variable_layer_height_editing : -1; -# -# $self->_mouse_dragging($e->Dragging); -# -# if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) { -# # wxMSW needs focus in order to catch mouse wheel events -# $self->SetFocus; -# $self->_drag_start_xy(undef); -# } elsif ($e->LeftDClick) { -# if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { -# } elsif ($self->on_double_click) { -# $self->on_double_click->(); -# } -# } elsif ($e->LeftDown || $e->RightDown) { -# # 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; -# $self->_layer_height_edited(0); -# if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { -# # A volume is selected and the mouse is hovering over a layer thickness bar. -# # Start editing the layer height. -# $self->_layer_height_edited(1); -# $self->_variable_layer_thickness_action($e); -# } elsif ($object_idx_selected != -1 && $self->_variable_layer_thickness_reset_rect_mouse_inside($e)) { -# $self->{print}->get_object($object_idx_selected)->reset_layer_height_profile; -# # Index 2 means no editing, just wait for mouse up event. -# $self->_layer_height_edited(2); -# $self->Refresh; -# $self->Update; -# } else { -# # The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate $pos->x,y, -# # an converts the screen space coordinate to unscaled object space. -# my $pos3d = ($volume_idx == -1) ? undef : $self->mouse_to_3d(@$pos); -# -# # 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; -# $self->Update; -# } -# -# # propagate event through callback -# $self->on_select->($volume_idx) -# if $self->on_select; -# -# if ($volume_idx != -1) { -# if ($e->LeftDown && $self->enable_moving) { -# # Only accept the initial position, if it is inside the volume bounding box. -# my $volume_bbox = $self->volumes->[$volume_idx]->transformed_bounding_box; -# $volume_bbox->offset(1.); -# if ($volume_bbox->contains_point($pos3d)) { -# # The dragging operation is initiated. -# $self->_drag_volume_idx($volume_idx); -# $self->_drag_start_pos($pos3d); -# # Remember the shift to to the object center. The object center will later be used -# # to limit the object placement close to the bed. -# $self->_drag_volume_center_offset($pos3d->vector_to($volume_bbox->center)); -# } -# } 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 && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) { -# # Get new position at the same Z of the initial click point. -# my $cur_pos = Slic3r::Linef3->new( -# $self->mouse_to_3d($e->GetX, $e->GetY, 0), -# $self->mouse_to_3d($e->GetX, $e->GetY, 1)) -# ->intersect_plane($self->_drag_start_pos->z); -# -# # Clip the new position, so the object center remains close to the bed. -# { -# $cur_pos->translate(@{$self->_drag_volume_center_offset}); -# my $cur_pos2 = Slic3r::Point->new(scale($cur_pos->x), scale($cur_pos->y)); -# if (! $self->bed_polygon->contains_point($cur_pos2)) { -# my $ip = $self->bed_polygon->point_projection($cur_pos2); -# $cur_pos->set_x(unscale($ip->x)); -# $cur_pos->set_y(unscale($ip->y)); -# } -# $cur_pos->translate(@{$self->_drag_volume_center_offset->negative}); -# } -# # Calculate the translation vector. -# my $vector = $self->_drag_start_pos->vector_to($cur_pos); -# # Get the volume being dragged. -# my $volume = $self->volumes->[$self->_drag_volume_idx]; -# # Get all volumes belonging to the same group, if any. -# my @volumes = ($volume->drag_group_id == -1) ? -# ($volume) : -# grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes}; -# # Apply new temporary volume origin and ignore Z. -# $_->translate($vector->x, $vector->y, 0) for @volumes; -# $self->_drag_start_pos($cur_pos); -# $self->_dragged(1); -# $self->Refresh; -# $self->Update; -# } elsif ($e->Dragging) { -# if ($self->_layer_height_edited && $object_idx_selected != -1) { -# $self->_variable_layer_thickness_action($e) if ($self->_layer_height_edited == 1); -# } 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) { -# # Turntable mode is enabled by default. -# $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE); -# $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #- -# $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; -# my @quat = trackball( -# $orig->x / ($size->width / 2) - 1, -# 1 - $orig->y / ($size->height / 2), #/ -# $pos->x / ($size->width / 2) - 1, -# 1 - $pos->y / ($size->height / 2), #/ -# ); -# $self->_quat(mulquats($self->_quat, \@quat)); -# } -# $self->on_viewport_changed->() if $self->on_viewport_changed; -# $self->Refresh; -# $self->Update; -# } -# $self->_drag_start_pos($pos); -# } elsif ($e->MiddleIsDown || $e->RightIsDown) { -# # If dragging over blank area with right button, pan. -# if (defined $self->_drag_start_xy) { -# # get point in model space at Z = 0 -# my $cur_pos = $self->mouse_to_3d($e->GetX, $e->GetY, 0); -# my $orig = $self->mouse_to_3d($self->_drag_start_xy->x, $self->_drag_start_xy->y, 0); -# $self->_camera_target->translate(@{$orig->vector_to($cur_pos)->negative}); -# $self->on_viewport_changed->() if $self->on_viewport_changed; -# $self->Refresh; -# $self->Update; -# } -# $self->_drag_start_xy($pos); -# } -# } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) { -# if ($self->_layer_height_edited) { -# $self->_layer_height_edited(undef); -# $self->{layer_height_edit_timer}->Stop; -# $self->on_model_update->() -# if ($object_idx_selected != -1 && $self->on_model_update); -# } elsif ($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) { -# # get all volumes belonging to the same group, if any -# my @volume_idxs; -# my $group_id = $self->volumes->[$self->_drag_volume_idx]->drag_group_id; -# if ($group_id == -1) { -# @volume_idxs = ($self->_drag_volume_idx); -# } else { -# @volume_idxs = grep $self->volumes->[$_]->drag_group_id == $group_id, -# 0..$#{$self->volumes}; -# } -# $self->on_move->(@volume_idxs); -# } -# $self->_drag_volume_idx(undef); -# $self->_drag_start_pos(undef); -# $self->_drag_start_xy(undef); -# $self->_dragged(undef); -# } elsif ($e->Moving) { -# $self->_mouse_pos($pos); -# # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor -# # hovers over. -# if ($self->enable_picking) { -# $self->Update; -# $self->Refresh; -# } -# } else { -# $e->Skip(); -# } -#} -# -#sub mouse_wheel_event { -# my ($self, $e) = @_; -# -# if ($e->MiddleIsDown) { -# # Ignore the wheel events if the middle button is pressed. -# return; -# } -# if ($self->layer_editing_enabled && $self->{print}) { -# my $object_idx_selected = $self->_first_selected_object_id_for_variable_layer_height_editing; -# if ($object_idx_selected != -1) { -# # A volume is selected. Test, whether hovering over a layer thickness bar. -# if ($self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { -# # Adjust the width of the selection. -# $self->{layer_height_edit_band_width} = max(min($self->{layer_height_edit_band_width} * (1 + 0.1 * $e->GetWheelRotation() / $e->GetWheelDelta()), 10.), 1.5); -# $self->Refresh; -# return; -# } -# } -# } -# -# # Calculate the zoom delta and apply it to the current zoom factor -# my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta(); -# $zoom = max(min($zoom, 4), -4); -# $zoom /= 10; -# $zoom = $self->_zoom / (1-$zoom); -# # Don't allow to zoom too far outside the scene. -# my $zoom_min = $self->get_zoom_to_bounding_box_factor($self->max_bounding_box); -# $zoom_min *= 0.4 if defined $zoom_min; -# $zoom = $zoom_min if defined $zoom_min && $zoom < $zoom_min; -# $self->_zoom($zoom); -# -## # In order to zoom around the mouse point we need to translate -## # the camera target -## my $size = Slic3r::Pointf->new($self->GetSizeWH); -## my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #- -## $self->_camera_target->translate( -## # ($pos - $size/2) represents the vector from the viewport center -## # to the mouse point. By multiplying it by $zoom we get the new, -## # transformed, length of such vector. -## # Since we want that point to stay fixed, we move our camera target -## # in the opposite direction by the delta of the length of such vector -## # ($zoom - 1). We then scale everything by 1/$self->_zoom since -## # $self->_camera_target is expressed in terms of model units. -## -($pos->x - $size->x/2) * ($zoom) / $self->_zoom, -## -($pos->y - $size->y/2) * ($zoom) / $self->_zoom, -## 0, -## ) if 0; -# -# $self->on_viewport_changed->() if $self->on_viewport_changed; -# $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen; -# $self->Refresh; -#} -# -## Reset selection. -#sub reset_objects { -# my ($self) = @_; -# if ($self->GetContext) { -# $self->SetCurrent($self->GetContext); -# $self->volumes->release_geometry; -# } -# $self->volumes->erase; -# $self->_dirty(1); -#} -# -## Setup camera to view all objects. -#sub set_viewport_from_scene { -# my ($self, $scene) = @_; -# -# $self->_sphi($scene->_sphi); -# $self->_stheta($scene->_stheta); -# $self->_camera_target($scene->_camera_target); -# $self->_zoom($scene->_zoom); -# $self->_quat($scene->_quat); -# $self->_dirty(1); -#} -# -## Set the camera to a default orientation, -## zoom to volumes. -#sub select_view { -# my ($self, $direction) = @_; -# -# my $dirvec; -# if (ref($direction)) { -# $dirvec = $direction; -# } else { -# if ($direction eq 'iso') { -# $dirvec = VIEW_DEFAULT; -# } elsif ($direction eq 'left') { -# $dirvec = VIEW_LEFT; -# } elsif ($direction eq 'right') { -# $dirvec = VIEW_RIGHT; -# } elsif ($direction eq 'top') { -# $dirvec = VIEW_TOP; -# } elsif ($direction eq 'bottom') { -# $dirvec = VIEW_BOTTOM; -# } elsif ($direction eq 'front') { -# $dirvec = VIEW_FRONT; -# } elsif ($direction eq 'rear') { -# $dirvec = VIEW_REAR; -# } -# } -# 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; -# $self->on_viewport_changed->() if $self->on_viewport_changed; -# $self->Refresh; -# } -#} -# -#sub get_zoom_to_bounding_box_factor { -# my ($self, $bb) = @_; -# my $max_bb_size = max(@{ $bb->size }); -# return undef if ($max_bb_size == 0); -# -# # project the bbox vertices on a plane perpendicular to the camera forward axis -# # then calculates the vertices coordinate on this plane along the camera xy axes -# -# # we need the view matrix, we let opengl calculate it (same as done in render sub) -# glMatrixMode(GL_MODELVIEW); -# glLoadIdentity(); -# -# if (!TURNTABLE_MODE) { -# # Shift the perspective camera. -# my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance); -# glTranslatef(@$camera_pos); -# } -# -# if (TURNTABLE_MODE) { -# # Turntable mode is enabled by default. -# glRotatef(-$self->_stheta, 1, 0, 0); # pitch -# glRotatef($self->_sphi, 0, 0, 1); # yaw -# } else { -# # Shift the perspective camera. -# my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance); -# glTranslatef(@$camera_pos); -# my @rotmat = quat_to_rotmatrix($self->quat); -# glMultMatrixd_p(@rotmat[0..15]); -# } -# glTranslatef(@{ $self->_camera_target->negative }); -# -# # get the view matrix back from opengl -# my @matrix = glGetFloatv_p(GL_MODELVIEW_MATRIX); -# -# # camera axes -# my $right = Slic3r::Pointf3->new($matrix[0], $matrix[4], $matrix[8]); -# my $up = Slic3r::Pointf3->new($matrix[1], $matrix[5], $matrix[9]); -# my $forward = Slic3r::Pointf3->new($matrix[2], $matrix[6], $matrix[10]); -# -# my $bb_min = $bb->min_point(); -# my $bb_max = $bb->max_point(); -# my $bb_center = $bb->center(); -# -# # bbox vertices in world space -# my @vertices = (); -# push(@vertices, $bb_min); -# push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_min->y(), $bb_min->z())); -# push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_max->y(), $bb_min->z())); -# push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_max->y(), $bb_min->z())); -# push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_min->y(), $bb_max->z())); -# push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_min->y(), $bb_max->z())); -# push(@vertices, $bb_max); -# push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_max->y(), $bb_max->z())); -# -# my $max_x = 0.0; -# my $max_y = 0.0; -# -# # margin factor to give some empty space around the bbox -# my $margin_factor = 1.25; -# -# foreach my $v (@vertices) { -# # project vertex on the plane perpendicular to camera forward axis -# my $pos = Slic3r::Pointf3->new($v->x() - $bb_center->x(), $v->y() - $bb_center->y(), $v->z() - $bb_center->z()); -# my $proj_on_normal = $pos->x() * $forward->x() + $pos->y() * $forward->y() + $pos->z() * $forward->z(); -# my $proj_on_plane = Slic3r::Pointf3->new($pos->x() - $proj_on_normal * $forward->x(), $pos->y() - $proj_on_normal * $forward->y(), $pos->z() - $proj_on_normal * $forward->z()); -# -# # calculates vertex coordinate along camera xy axes -# my $x_on_plane = $proj_on_plane->x() * $right->x() + $proj_on_plane->y() * $right->y() + $proj_on_plane->z() * $right->z(); -# my $y_on_plane = $proj_on_plane->x() * $up->x() + $proj_on_plane->y() * $up->y() + $proj_on_plane->z() * $up->z(); -# -# $max_x = max($max_x, $margin_factor * 2 * abs($x_on_plane)); -# $max_y = max($max_y, $margin_factor * 2 * abs($y_on_plane)); -# } -# -# return undef if (($max_x == 0) || ($max_y == 0)); -# -# my ($cw, $ch) = $self->GetSizeWH; -# my $min_ratio = min($cw / $max_x, $ch / $max_y); -# -# return $min_ratio; -#} -# -#sub zoom_to_bounding_box { -# my ($self, $bb) = @_; -# # Calculate the zoom factor needed to adjust viewport to bounding box. -# my $zoom = $self->get_zoom_to_bounding_box_factor($bb); -# if (defined $zoom) { -# $self->_zoom($zoom); -# # center view around bounding box center -# $self->_camera_target($bb->center); -# $self->on_viewport_changed->() if $self->on_viewport_changed; -# $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen; -# $self->Refresh; -# } -#} -# -#sub zoom_to_bed { -# my ($self) = @_; -# -# if ($self->bed_shape) { -# $self->zoom_to_bounding_box($self->bed_bounding_box); -# } -#} -# -#sub zoom_to_volume { -# my ($self, $volume_idx) = @_; -# -# my $volume = $self->volumes->[$volume_idx]; -# my $bb = $volume->transformed_bounding_box; -# $self->zoom_to_bounding_box($bb); -#} -# -#sub zoom_to_volumes { -# my ($self) = @_; -# -# $self->_apply_zoom_to_volumes_filter(1); -# $self->zoom_to_bounding_box($self->volumes_bounding_box); -# $self->_apply_zoom_to_volumes_filter(0); -#} -# -#sub volumes_bounding_box { -# my ($self) = @_; -# -# my $bb = Slic3r::Geometry::BoundingBoxf3->new; -# foreach my $v (@{$self->volumes}) { -# $bb->merge($v->transformed_bounding_box) if (! $self->_apply_zoom_to_volumes_filter || $v->zoom_to_volumes); -# } -# return $bb; -#} -# -#sub bed_bounding_box { -# my ($self) = @_; -# -# my $bb = Slic3r::Geometry::BoundingBoxf3->new; -# if ($self->bed_shape) { -# $bb->merge_point(Slic3r::Pointf3->new(@$_, 0)) for @{$self->bed_shape}; -# } -# return $bb; -#} -# -#sub max_bounding_box { -# my ($self) = @_; -# -# my $bb = $self->bed_bounding_box; -# $bb->merge($self->volumes_bounding_box); -# return $bb; -#} -# -## Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane -## to support the scene objects. -#sub set_auto_bed_shape { -# my ($self, $bed_shape) = @_; -# -# # draw a default square bed around object center -# my $max_size = max(@{ $self->volumes_bounding_box->size }); -# my $center = $self->volumes_bounding_box->center; -# $self->set_bed_shape([ -# [ $center->x - $max_size, $center->y - $max_size ], #-- -# [ $center->x + $max_size, $center->y - $max_size ], #-- -# [ $center->x + $max_size, $center->y + $max_size ], #++ -# [ $center->x - $max_size, $center->y + $max_size ], #++ -# ]); -# # Set the origin for painting of the coordinate system axes. -# $self->origin(Slic3r::Pointf->new(@$center[X,Y])); -#} -# -## Set the bed shape to a single closed 2D polygon (array of two element arrays), -## triangulate the bed and store the triangles into $self->bed_triangles, -## fills the $self->bed_grid_lines and sets $self->origin. -## Sets $self->bed_polygon to limit the object placement. -#sub set_bed_shape { -# my ($self, $bed_shape) = @_; -# -# $self->bed_shape($bed_shape); -# -# # triangulate bed -# my $expolygon = Slic3r::ExPolygon->new([ map [map scale($_), @$_], @$bed_shape ]); -# my $bed_bb = $expolygon->bounding_box; -# -# { -# my @points = (); -# foreach my $triangle (@{ $expolygon->triangulate }) { -# push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$triangle; -# } -# $self->bed_triangles(OpenGL::Array->new_list(GL_FLOAT, @points)); -# } -# -# { -# my @polylines = (); -# for (my $x = $bed_bb->x_min; $x <= $bed_bb->x_max; $x += scale 10) { -# push @polylines, Slic3r::Polyline->new([$x,$bed_bb->y_min], [$x,$bed_bb->y_max]); -# } -# for (my $y = $bed_bb->y_min; $y <= $bed_bb->y_max; $y += scale 10) { -# push @polylines, Slic3r::Polyline->new([$bed_bb->x_min,$y], [$bed_bb->x_max,$y]); -# } -# # clip with a slightly grown expolygon because our lines lay on the contours and -# # may get erroneously clipped -# my @lines = map Slic3r::Line->new(@$_[0,-1]), -# @{intersection_pl(\@polylines, [ @{$expolygon->offset(+scaled_epsilon)} ])}; -# -# # append bed contours -# push @lines, map @{$_->lines}, @$expolygon; -# -# my @points = (); -# foreach my $line (@lines) { -# push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$line; #)) -# } -# $self->bed_grid_lines(OpenGL::Array->new_list(GL_FLOAT, @points)); -# } -# -# # Set the origin for painting of the coordinate system axes. -# $self->origin(Slic3r::Pointf->new(0,0)); -# -# $self->bed_polygon(offset_ex([$expolygon->contour], $bed_bb->radius * 1.7, JT_ROUND, scale(0.5))->[0]->contour->clone); -#} -# -#sub deselect_volumes { -# my ($self) = @_; -# $_->set_selected(0) for @{$self->volumes}; -#} -# -#sub select_volume { -# my ($self, $volume_idx) = @_; -# -# return if ($volume_idx >= scalar(@{$self->volumes})); -# -# $self->volumes->[$volume_idx]->set_selected(1) -# if $volume_idx != -1; -#} -# -#sub SetCuttingPlane { -# my ($self, $z, $expolygons) = @_; -# -# $self->cutting_plane_z($z); -# -# # grow slices in order to display them better -# $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1); -# -# my @verts = (); -# foreach my $line (map @{$_->lines}, map @$_, @$expolygons) { -# push @verts, ( -# unscale($line->a->x), unscale($line->a->y), $z, #)) -# unscale($line->b->x), unscale($line->b->y), $z, #)) -# ); -# } -# $self->cut_lines_vertices(OpenGL::Array->new_list(GL_FLOAT, @verts)); -#} -# -## Given an axis and angle, compute quaternion. -#sub axis_to_quat { -# my ($ax, $phi) = @_; -# -# my $lena = sqrt(reduce { $a + $b } (map { $_ * $_ } @$ax)); -# my @q = map { $_ * (1 / $lena) } @$ax; -# @q = map { $_ * sin($phi / 2.0) } @q; -# $q[$#q + 1] = cos($phi / 2.0); -# return @q; -#} -# -## Project a point on the virtual trackball. -## If it is inside the sphere, map it to the sphere, if it outside map it -## to a hyperbola. -#sub project_to_sphere { -# my ($r, $x, $y) = @_; -# -# my $d = sqrt($x * $x + $y * $y); -# if ($d < $r * 0.70710678118654752440) { # Inside sphere -# return sqrt($r * $r - $d * $d); -# } else { # On hyperbola -# my $t = $r / 1.41421356237309504880; -# return $t * $t / $d; -# } -#} -# -#sub cross { -# my ($v1, $v2) = @_; -# -# return (@$v1[1] * @$v2[2] - @$v1[2] * @$v2[1], -# @$v1[2] * @$v2[0] - @$v1[0] * @$v2[2], -# @$v1[0] * @$v2[1] - @$v1[1] * @$v2[0]); -#} -# -## Simulate a track-ball. Project the points onto the virtual trackball, -## then figure out the axis of rotation, which is the cross product of -## P1 P2 and O P1 (O is the center of the ball, 0,0,0) Note: This is a -## deformed trackball-- is a trackball in the center, but is deformed -## into a hyperbolic sheet of rotation away from the center. -## It is assumed that the arguments to this routine are in the range -## (-1.0 ... 1.0). -#sub trackball { -# my ($p1x, $p1y, $p2x, $p2y) = @_; -# -# if ($p1x == $p2x && $p1y == $p2y) { -# # zero rotation -# return (0.0, 0.0, 0.0, 1.0); -# } -# -# # First, figure out z-coordinates for projection of P1 and P2 to -# # deformed sphere -# my @p1 = ($p1x, $p1y, project_to_sphere(TRACKBALLSIZE, $p1x, $p1y)); -# my @p2 = ($p2x, $p2y, project_to_sphere(TRACKBALLSIZE, $p2x, $p2y)); -# -# # axis of rotation (cross product of P1 and P2) -# my @a = cross(\@p2, \@p1); -# -# # Figure out how much to rotate around that axis. -# my @d = map { $_ * $_ } (map { $p1[$_] - $p2[$_] } 0 .. $#p1); -# my $t = sqrt(reduce { $a + $b } @d) / (2.0 * TRACKBALLSIZE); -# -# # Avoid problems with out-of-control values... -# $t = 1.0 if ($t > 1.0); -# $t = -1.0 if ($t < -1.0); -# my $phi = 2.0 * asin($t); -# -# return axis_to_quat(\@a, $phi); -#} -# -## Build a rotation matrix, given a quaternion rotation. -#sub quat_to_rotmatrix { -# my ($q) = @_; -# -# my @m = (); -# -# $m[0] = 1.0 - 2.0 * (@$q[1] * @$q[1] + @$q[2] * @$q[2]); -# $m[1] = 2.0 * (@$q[0] * @$q[1] - @$q[2] * @$q[3]); -# $m[2] = 2.0 * (@$q[2] * @$q[0] + @$q[1] * @$q[3]); -# $m[3] = 0.0; -# -# $m[4] = 2.0 * (@$q[0] * @$q[1] + @$q[2] * @$q[3]); -# $m[5] = 1.0 - 2.0 * (@$q[2] * @$q[2] + @$q[0] * @$q[0]); -# $m[6] = 2.0 * (@$q[1] * @$q[2] - @$q[0] * @$q[3]); -# $m[7] = 0.0; -# -# $m[8] = 2.0 * (@$q[2] * @$q[0] - @$q[1] * @$q[3]); -# $m[9] = 2.0 * (@$q[1] * @$q[2] + @$q[0] * @$q[3]); -# $m[10] = 1.0 - 2.0 * (@$q[1] * @$q[1] + @$q[0] * @$q[0]); -# $m[11] = 0.0; -# -# $m[12] = 0.0; -# $m[13] = 0.0; -# $m[14] = 0.0; -# $m[15] = 1.0; -# -# return @m; -#} -# -#sub mulquats { -# my ($q1, $rq) = @_; -# -# return (@$q1[3] * @$rq[0] + @$q1[0] * @$rq[3] + @$q1[1] * @$rq[2] - @$q1[2] * @$rq[1], -# @$q1[3] * @$rq[1] + @$q1[1] * @$rq[3] + @$q1[2] * @$rq[0] - @$q1[0] * @$rq[2], -# @$q1[3] * @$rq[2] + @$q1[2] * @$rq[3] + @$q1[0] * @$rq[1] - @$q1[1] * @$rq[0], -# @$q1[3] * @$rq[3] - @$q1[0] * @$rq[0] - @$q1[1] * @$rq[1] - @$q1[2] * @$rq[2]) -#} -# -## Convert the screen space coordinate to an object space coordinate. -## If the Z screen space coordinate is not provided, a depth buffer value is substituted. -#sub mouse_to_3d { -# my ($self, $x, $y, $z) = @_; -# -# return unless $self->GetContext; -# $self->SetCurrent($self->GetContext); -# -# my @viewport = glGetIntegerv_p(GL_VIEWPORT); # 4 items -# my @mview = glGetDoublev_p(GL_MODELVIEW_MATRIX); # 16 items -# my @proj = glGetDoublev_p(GL_PROJECTION_MATRIX); # 16 items -# -# $y = $viewport[3] - $y; -# $z //= glReadPixels_p($x, $y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT); -# my @projected = gluUnProject_p($x, $y, $z, @mview, @proj, @viewport); -# return Slic3r::Pointf3->new(@projected); -#} -# -#sub GetContext { -# my ($self) = @_; -# return $self->{context} ||= Wx::GLContext->new($self); -#} -# -#sub SetCurrent { -# my ($self, $context) = @_; -# return $self->SUPER::SetCurrent($context); -#} -# -#sub UseVBOs { -# my ($self) = @_; -# -# if (! defined ($self->{use_VBOs})) { -# my $use_legacy = wxTheApp->{app_config}->get('use_legacy_opengl'); -# if ($use_legacy eq '1') { -# # Disable OpenGL 2.0 rendering. -# $self->{use_VBOs} = 0; -# # Don't enable the layer editing tool. -# $self->{layer_editing_enabled} = 0; -# # 2 means failed -# $self->{layer_editing_initialized} = 2; -# return 0; -# } -# # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized -# # first when an OpenGL widget is shown for the first time. How ugly. -# return 0 if (! $self->init && $^O eq 'linux'); -# # Don't use VBOs if anything fails. -# $self->{use_VBOs} = 0; -# if ($self->GetContext) { -# $self->SetCurrent($self->GetContext); -# Slic3r::GUI::_3DScene::_glew_init; -# my @gl_version = split(/\./, glGetString(GL_VERSION)); -# $self->{use_VBOs} = int($gl_version[0]) >= 2; -# # print "UseVBOs $self OpenGL major: $gl_version[0], minor: $gl_version[1]. Use VBOs: ", $self->{use_VBOs}, "\n"; -# } -# } -# return $self->{use_VBOs}; -#} -# -#sub Resize { -# my ($self, $x, $y) = @_; -# -# return unless $self->GetContext; -# $self->_dirty(0); -# -# $self->SetCurrent($self->GetContext); -# glViewport(0, 0, $x, $y); -# -# $x /= $self->_zoom; -# $y /= $self->_zoom; -# -# glMatrixMode(GL_PROJECTION); -# glLoadIdentity(); -# if ($self->_camera_type eq 'ortho') { -# #FIXME setting the size of the box 10x larger than necessary -# # is only a workaround for an incorrectly set camera. -# # This workaround harms Z-buffer accuracy! -## my $depth = 1.05 * $self->max_bounding_box->radius(); -# my $depth = 5.0 * max(@{ $self->max_bounding_box->size }); -# glOrtho( -# -$x/2, $x/2, -$y/2, $y/2, -# -$depth, $depth, -# ); -# } else { -# die "Invalid camera type: ", $self->_camera_type, "\n" if ($self->_camera_type ne 'perspective'); -# my $bbox_r = $self->max_bounding_box->radius(); -# my $fov = PI * 45. / 180.; -# my $fov_tan = tan(0.5 * $fov); -# my $cam_distance = 0.5 * $bbox_r / $fov_tan; -# $self->_camera_distance($cam_distance); -# my $nr = $cam_distance - $bbox_r * 1.1; -# my $fr = $cam_distance + $bbox_r * 1.1; -# $nr = 1 if ($nr < 1); -# $fr = $nr + 1 if ($fr < $nr + 1); -# my $h2 = $fov_tan * $nr; -# my $w2 = $h2 * $x / $y; -# glFrustum(-$w2, $w2, -$h2, $h2, $nr, $fr); -# } -# glMatrixMode(GL_MODELVIEW); -#} -# -#sub InitGL { -# my $self = shift; -# -# return if $self->init; -# return unless $self->GetContext; -# $self->init(1); -# -## # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized -## # first when an OpenGL widget is shown for the first time. How ugly. -## # In that case the volumes are wainting to be moved to Vertex Buffer Objects -## # after the OpenGL context is being initialized. -## $self->volumes->finalize_geometry(1) -## if ($^O eq 'linux' && $self->UseVBOs); -# -# $self->zoom_to_bed; -# -# glClearColor(0, 0, 0, 1); -# glColor3f(1, 0, 0); -# glEnable(GL_DEPTH_TEST); -# glClearDepth(1.0); -# glDepthFunc(GL_LEQUAL); -# glEnable(GL_CULL_FACE); -# glEnable(GL_BLEND); -# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -# -# # Set antialiasing/multisampling -# glDisable(GL_LINE_SMOOTH); -# glDisable(GL_POLYGON_SMOOTH); -# -# # See "GL_MULTISAMPLE and GL_ARRAY_BUFFER_ARB messages on failed launch" -# # https://github.com/alexrj/Slic3r/issues/4085 -# eval { -# # Disable the multi sampling by default, so the picking by color will work correctly. -# glDisable(GL_MULTISAMPLE); -# }; -# # Disable multi sampling if the eval failed. -# $self->{can_multisample} = 0 if $@; -# -# # ambient lighting -# glLightModelfv_p(GL_LIGHT_MODEL_AMBIENT, 0.3, 0.3, 0.3, 1); -# -# glEnable(GL_LIGHTING); -# glEnable(GL_LIGHT0); -# glEnable(GL_LIGHT1); -# -# # light from camera -# glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0); -# glLightfv_p(GL_LIGHT1, GL_SPECULAR, 0.3, 0.3, 0.3, 1); -# glLightfv_p(GL_LIGHT1, GL_DIFFUSE, 0.2, 0.2, 0.2, 1); -# -# # Enables Smooth Color Shading; try GL_FLAT for (lack of) fun. -# glShadeModel(GL_SMOOTH); -# -## glMaterialfv_p(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, 0.5, 0.3, 0.3, 1); -## glMaterialfv_p(GL_FRONT_AND_BACK, GL_SPECULAR, 1, 1, 1, 1); -## glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50); -## glMaterialfv_p(GL_FRONT_AND_BACK, GL_EMISSION, 0.1, 0, 0, 0.9); -# -# # A handy trick -- have surface material mirror the color. -# glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); -# glEnable(GL_COLOR_MATERIAL); -# glEnable(GL_MULTISAMPLE) if ($self->{can_multisample}); -# -# if ($self->UseVBOs) { -# my $shader = new Slic3r::GUI::_3DScene::GLShader; -## if (! $shader->load($self->_fragment_shader_Phong, $self->_vertex_shader_Phong)) { -# print "Compilaton of path shader failed: \n" . $shader->last_error . "\n"; -# $shader = undef; -# } else { -# $self->{plain_shader} = $shader; -# } -# } -#} -# -#sub DestroyGL { -# my $self = shift; -# if ($self->GetContext) { -# $self->SetCurrent($self->GetContext); -# if ($self->{plain_shader}) { -# $self->{plain_shader}->release; -# delete $self->{plain_shader}; -# } -# if ($self->{layer_height_edit_shader}) { -# $self->{layer_height_edit_shader}->release; -# delete $self->{layer_height_edit_shader}; -# } -# $self->volumes->release_geometry; -# } -#} -# -#sub Render { -# my ($self, $dc) = @_; -# -# # prevent calling SetCurrent() when window is not shown yet -# return unless $self->IsShownOnScreen; -# return unless my $context = $self->GetContext; -# $self->SetCurrent($context); -# $self->InitGL; -# -# glClearColor(1, 1, 1, 1); -# glClearDepth(1); -# glDepthFunc(GL_LESS); -# glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -# -# glMatrixMode(GL_MODELVIEW); -# glLoadIdentity(); -# -# if (!TURNTABLE_MODE) { -# # Shift the perspective camera. -# my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance); -# glTranslatef(@$camera_pos); -# } -# -# if (TURNTABLE_MODE) { -# # Turntable mode is enabled by default. -# glRotatef(-$self->_stheta, 1, 0, 0); # pitch -# glRotatef($self->_sphi, 0, 0, 1); # yaw -# } else { -# my @rotmat = quat_to_rotmatrix($self->quat); -# glMultMatrixd_p(@rotmat[0..15]); -# } -# -# glTranslatef(@{ $self->_camera_target->negative }); -# -# # light from above -# 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 && !$self->_mouse_dragging) { -# if (my $pos = $self->_mouse_pos) { -# # 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. -# glPushAttrib(GL_ENABLE_BIT); -# glDisable(GL_MULTISAMPLE) if ($self->{can_multisample}); -# glDisable(GL_LIGHTING); -# glDisable(GL_BLEND); -# $self->draw_volumes(1); -# glPopAttrib(); -# glFlush(); -# my $col = [ glReadPixels_p($pos->x, $self->GetSize->GetHeight - $pos->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE) ]; -# my $volume_idx = $col->[0] + $col->[1]*256 + $col->[2]*256*256; -# $self->_hover_volume_idx(undef); -# $_->set_hover(0) for @{$self->volumes}; -# if ($volume_idx <= $#{$self->volumes}) { -# $self->_hover_volume_idx($volume_idx); -# -# $self->volumes->[$volume_idx]->set_hover(1); -# my $group_id = $self->volumes->[$volume_idx]->select_group_id; -# if ($group_id != -1) { -# $_->set_hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes}; -# } -# -# $self->on_hover->($volume_idx) if $self->on_hover; -# } -# glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -# } -# } -# -# # draw fixed background -# if ($self->background) { -# glDisable(GL_LIGHTING); -# glPushMatrix(); -# glLoadIdentity(); -# -# glMatrixMode(GL_PROJECTION); -# glPushMatrix(); -# glLoadIdentity(); -# -# # Draws a bluish bottom to top gradient over the complete screen. -# glDisable(GL_DEPTH_TEST); -# glBegin(GL_QUADS); -# glColor3f(0.0,0.0,0.0); -# glVertex3f(-1.0,-1.0, 1.0); -# glVertex3f( 1.0,-1.0, 1.0); -# glColor3f(10/255,98/255,144/255); -# glVertex3f( 1.0, 1.0, 1.0); -# glVertex3f(-1.0, 1.0, 1.0); -# glEnd(); -# glPopMatrix(); -# glEnable(GL_DEPTH_TEST); -# -# glMatrixMode(GL_MODELVIEW); -# glPopMatrix(); -# glEnable(GL_LIGHTING); -# } -# -# # draw ground and axes -# glDisable(GL_LIGHTING); -# -# # draw ground -# my $ground_z = GROUND_Z; -# -# if ($self->bed_triangles) { -# glDisable(GL_DEPTH_TEST); -# -# glEnable(GL_BLEND); -# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -# -# glEnableClientState(GL_VERTEX_ARRAY); -# glColor4f(0.8, 0.6, 0.5, 0.4); -# glNormal3d(0,0,1); -# glVertexPointer_c(3, GL_FLOAT, 0, $self->bed_triangles->ptr()); -# glDrawArrays(GL_TRIANGLES, 0, $self->bed_triangles->elements / 3); -# glDisableClientState(GL_VERTEX_ARRAY); -# -# # we need depth test for grid, otherwise it would disappear when looking -# # the object from below -# glEnable(GL_DEPTH_TEST); -# -# # draw grid -# glLineWidth(3); -# glColor4f(0.2, 0.2, 0.2, 0.4); -# glEnableClientState(GL_VERTEX_ARRAY); -# glVertexPointer_c(3, GL_FLOAT, 0, $self->bed_grid_lines->ptr()); -# glDrawArrays(GL_LINES, 0, $self->bed_grid_lines->elements / 3); -# glDisableClientState(GL_VERTEX_ARRAY); -# -# glDisable(GL_BLEND); -# } -# -# my $volumes_bb = $self->volumes_bounding_box; -# -# { -# # draw axes -# # disable depth testing so that axes are not covered by ground -# glDisable(GL_DEPTH_TEST); -# my $origin = $self->origin; -# my $axis_len = $self->use_plain_shader ? 0.3 * max(@{ $self->bed_bounding_box->size }) : 2 * max(@{ $volumes_bb->size }); -# glLineWidth(2); -# glBegin(GL_LINES); -# # draw line for x axis -# glColor3f(1, 0, 0); -# glVertex3f(@$origin, $ground_z); -# glVertex3f($origin->x + $axis_len, $origin->y, $ground_z); #,, -# # draw line for y axis -# glColor3f(0, 1, 0); -# glVertex3f(@$origin, $ground_z); -# glVertex3f($origin->x, $origin->y + $axis_len, $ground_z); #++ -# glEnd(); -# # draw line for Z axis -# # (re-enable depth test so that axis is correctly shown when objects are behind it) -# glEnable(GL_DEPTH_TEST); -# glBegin(GL_LINES); -# glColor3f(0, 0, 1); -# glVertex3f(@$origin, $ground_z); -# glVertex3f(@$origin, $ground_z+$axis_len); -# glEnd(); -# } -# -# glEnable(GL_LIGHTING); -# -# # draw objects -# if (! $self->use_plain_shader) { -# $self->draw_volumes; -# } elsif ($self->UseVBOs) { -# if ($self->enable_picking) { -# $self->mark_volumes_for_layer_height; -# $self->volumes->set_print_box($self->bed_bounding_box->x_min, $self->bed_bounding_box->y_min, 0.0, $self->bed_bounding_box->x_max, $self->bed_bounding_box->y_max, $self->{config}->get('max_print_height')); -# $self->volumes->check_outside_state($self->{config}); -# # do not cull backfaces to show broken geometry, if any -# glDisable(GL_CULL_FACE); -# } -# $self->{plain_shader}->enable if $self->{plain_shader}; -# $self->volumes->render_VBOs; -# $self->{plain_shader}->disable; -# glEnable(GL_CULL_FACE) if ($self->enable_picking); -# } else { -# # do not cull backfaces to show broken geometry, if any -# glDisable(GL_CULL_FACE) if ($self->enable_picking); -# $self->volumes->render_legacy; -# glEnable(GL_CULL_FACE) if ($self->enable_picking); -# } -# -# if (defined $self->cutting_plane_z) { -# # draw cutting plane -# my $plane_z = $self->cutting_plane_z; -# my $bb = $volumes_bb; -# glDisable(GL_CULL_FACE); -# glDisable(GL_LIGHTING); -# glEnable(GL_BLEND); -# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -# glBegin(GL_QUADS); -# glColor4f(0.8, 0.8, 0.8, 0.5); -# glVertex3f($bb->x_min-20, $bb->y_min-20, $plane_z); -# glVertex3f($bb->x_max+20, $bb->y_min-20, $plane_z); -# glVertex3f($bb->x_max+20, $bb->y_max+20, $plane_z); -# glVertex3f($bb->x_min-20, $bb->y_max+20, $plane_z); -# glEnd(); -# glEnable(GL_CULL_FACE); -# glDisable(GL_BLEND); -# -# # draw cutting contours -# glEnableClientState(GL_VERTEX_ARRAY); -# glLineWidth(2); -# glColor3f(0, 0, 0); -# glVertexPointer_c(3, GL_FLOAT, 0, $self->cut_lines_vertices->ptr()); -# glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3); -# glVertexPointer_c(3, GL_FLOAT, 0, 0); -# glDisableClientState(GL_VERTEX_ARRAY); -# } -# -# # draw warning message -# $self->draw_warning; -# -# # draw gcode preview legend -# $self->draw_legend; -# -# $self->draw_active_object_annotations; -# -# $self->SwapBuffers(); -#} -# -#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) = @_; -# -# # do not cull backfaces to show broken geometry, if any -# glDisable(GL_CULL_FACE); -# -# glEnable(GL_BLEND); -# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -# -# glEnableClientState(GL_VERTEX_ARRAY); -# glEnableClientState(GL_NORMAL_ARRAY); -# -# foreach my $volume_idx (0..$#{$self->volumes}) { -# my $volume = $self->volumes->[$volume_idx]; -# -# if ($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; -# my $b = ($volume_idx & 0x00FF0000) >> 16; -# glColor4f($r/255.0, $g/255.0, $b/255.0, 1); -# } elsif ($volume->selected) { -# glColor4f(@{ &SELECTED_COLOR }); -# } elsif ($volume->hover) { -# glColor4f(@{ &HOVER_COLOR }); -# } else { -# glColor4f(@{ $volume->color }); -# } -# -# $volume->render; -# } -# glDisableClientState(GL_NORMAL_ARRAY); -# glDisableClientState(GL_VERTEX_ARRAY); -# -# glDisable(GL_BLEND); -# glEnable(GL_CULL_FACE); -#} -# -#sub mark_volumes_for_layer_height { -# my ($self) = @_; -# -# foreach my $volume_idx (0..$#{$self->volumes}) { -# my $volume = $self->volumes->[$volume_idx]; -# my $object_id = int($volume->select_group_id / 1000000); -# if ($self->layer_editing_enabled && $volume->selected && $self->{layer_height_edit_shader} && -# $volume->has_layer_height_texture && $object_id < $self->{print}->object_count) { -# $volume->set_layer_height_texture_data($self->{layer_preview_z_texture_id}, $self->{layer_height_edit_shader}->shader_program_id, -# $self->{print}->get_object($object_id), $self->_variable_layer_thickness_bar_mouse_cursor_z_relative, $self->{layer_height_edit_band_width}); -# } else { -# $volume->reset_layer_height_texture_data(); -# } -# } -#} -# -#sub _load_image_set_texture { -# my ($self, $file_name) = @_; -# # Load a PNG with an alpha channel. -# my $img = Wx::Image->new; -# $img->LoadFile(Slic3r::var($file_name), wxBITMAP_TYPE_PNG); -# # Get RGB & alpha raw data from wxImage, interleave them into a Perl array. -# my @rgb = unpack 'C*', $img->GetData(); -# my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3); -# my $n_pixels = int(@alpha); -# my @data = (0)x($n_pixels * 4); -# for (my $i = 0; $i < $n_pixels; $i += 1) { -# $data[$i*4 ] = $rgb[$i*3]; -# $data[$i*4+1] = $rgb[$i*3+1]; -# $data[$i*4+2] = $rgb[$i*3+2]; -# $data[$i*4+3] = $alpha[$i]; -# } -# # Initialize a raw bitmap data. -# my $params = { -# loaded => 1, -# valid => $n_pixels > 0, -# width => $img->GetWidth, -# height => $img->GetHeight, -# data => OpenGL::Array->new_list(GL_UNSIGNED_BYTE, @data), -# texture_id => glGenTextures_p(1) -# }; -# # Create and initialize a texture with the raw data. -# glBindTexture(GL_TEXTURE_2D, $params->{texture_id}); -# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); -# glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $params->{width}, $params->{height}, 0, GL_RGBA, GL_UNSIGNED_BYTE, $params->{data}->ptr); -# glBindTexture(GL_TEXTURE_2D, 0); -# return $params; -#} -# -#sub _variable_layer_thickness_load_overlay_image { -# my ($self) = @_; -# $self->{layer_preview_annotation} = $self->_load_image_set_texture('variable_layer_height_tooltip.png') -# if (! $self->{layer_preview_annotation}->{loaded}); -# return $self->{layer_preview_annotation}->{valid}; -#} -# -#sub _variable_layer_thickness_load_reset_image { -# my ($self) = @_; -# $self->{layer_preview_reset_image} = $self->_load_image_set_texture('variable_layer_height_reset.png') -# if (! $self->{layer_preview_reset_image}->{loaded}); -# return $self->{layer_preview_reset_image}->{valid}; -#} -# -## Paint the tooltip. -#sub _render_image { -# my ($self, $image, $l, $r, $b, $t) = @_; -# $self->_render_texture($image->{texture_id}, $l, $r, $b, $t); -#} -# -#sub _render_texture { -# my ($self, $tex_id, $l, $r, $b, $t) = @_; -# -# glColor4f(1.,1.,1.,1.); -# glDisable(GL_LIGHTING); -# glEnable(GL_BLEND); -# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -# glEnable(GL_TEXTURE_2D); -# glBindTexture(GL_TEXTURE_2D, $tex_id); -# glBegin(GL_QUADS); -# glTexCoord2d(0.,1.); glVertex3f($l, $b, 0); -# glTexCoord2d(1.,1.); glVertex3f($r, $b, 0); -# glTexCoord2d(1.,0.); glVertex3f($r, $t, 0); -# glTexCoord2d(0.,0.); glVertex3f($l, $t, 0); -# glEnd(); -# glBindTexture(GL_TEXTURE_2D, 0); -# glDisable(GL_TEXTURE_2D); -# glDisable(GL_BLEND); -# glEnable(GL_LIGHTING); -#} -# -#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->{layer_height_edit_shader} || ! $self->layer_editing_enabled); -# -# # Find the selected volume, over which the layer editing is active. -# my $volume; -# foreach my $volume_idx (0..$#{$self->volumes}) { -# my $v = $self->volumes->[$volume_idx]; -# if ($v->selected && $v->has_layer_height_texture) { -# $volume = $v; -# last; -# } -# } -# return if (! $volume); -# -# # If the active object was not allocated at the Print, go away. This should only be a momentary case between an object addition / deletion -# # and an update by Platter::async_apply_config. -# my $object_idx = int($volume->select_group_id / 1000000); -# return if $object_idx >= $self->{print}->object_count; -# -# # 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 ($bar_left, $bar_bottom, $bar_right, $bar_top) = $self->_variable_layer_thickness_bar_rect_viewport; -# my ($reset_left, $reset_bottom, $reset_right, $reset_top) = $self->_variable_layer_thickness_reset_rect_viewport; -# my $z_cursor_relative = $self->_variable_layer_thickness_bar_mouse_cursor_z_relative; -# -# my $print_object = $self->{print}->get_object($object_idx); -# my $z_max = $print_object->model_object->bounding_box->z_max; -# -# $self->{layer_height_edit_shader}->enable; -# $self->{layer_height_edit_shader}->set_uniform('z_to_texture_row', $volume->layer_height_texture_z_to_row_id); -# $self->{layer_height_edit_shader}->set_uniform('z_texture_row_to_normalized', 1. / $volume->layer_height_texture_height); -# $self->{layer_height_edit_shader}->set_uniform('z_cursor', $z_max * $z_cursor_relative); -# $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width}); -# glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); -# glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, -# 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); -# glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, -# 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); -# glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $volume->layer_height_texture_width, $volume->layer_height_texture_height, -# GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level0); -# glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, -# GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level1); -# -# # Render the color bar. -# glDisable(GL_DEPTH_TEST); -# # 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, $z_max); -# glVertex3f($bar_left, $bar_top, $z_max); -# glEnd(); -# glBindTexture(GL_TEXTURE_2D, 0); -# $self->{layer_height_edit_shader}->disable; -# -# # Paint the tooltip. -# if ($self->_variable_layer_thickness_load_overlay_image) -# my $gap = 10/$self->_zoom; -# my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$self->_zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$self->_zoom + $gap, $reset_bottom + $gap); -# $self->_render_image($self->{layer_preview_annotation}, $l, $r, $t, $b); -# } -# -# # Paint the reset button. -# if ($self->_variable_layer_thickness_load_reset_image) { -# $self->_render_image($self->{layer_preview_reset_image}, $reset_left, $reset_right, $reset_bottom, $reset_top); -# } -# -# # Paint the graph. -# #FIXME show some kind of legend. -# my $max_z = unscale($print_object->size->z); -# my $profile = $print_object->model_object->layer_height_profile; -# my $layer_height = $print_object->config->get('layer_height'); -# my $layer_height_max = 10000000000.; -# { -# # Get a maximum layer height value. -# #FIXME This is a duplicate code of Slicing.cpp. -# my $nozzle_diameters = $print_object->print->config->get('nozzle_diameter'); -# my $layer_heights_min = $print_object->print->config->get('min_layer_height'); -# my $layer_heights_max = $print_object->print->config->get('max_layer_height'); -# for (my $i = 0; $i < scalar(@{$nozzle_diameters}); $i += 1) { -# my $lh_min = ($layer_heights_min->[$i] == 0.) ? 0.07 : max(0.01, $layer_heights_min->[$i]); -# my $lh_max = ($layer_heights_max->[$i] == 0.) ? (0.75 * $nozzle_diameters->[$i]) : $layer_heights_max->[$i]; -# $layer_height_max = min($layer_height_max, max($lh_min, $lh_max)); -# } -# } -# # Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region. -# $layer_height_max *= 1.12; -# # Baseline -# glColor3f(0., 0., 0.); -# glBegin(GL_LINE_STRIP); -# glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / $layer_height_max, $bar_bottom); -# glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / $layer_height_max, $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) / $layer_height_max, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z); -# } -# glEnd(); -# # Revert the matrices. -# glPopMatrix(); -# glEnable(GL_DEPTH_TEST); -#} -# -#sub draw_legend { -# my ($self) = @_; -# -# if (!$self->_legend_enabled) { -# return; -# } -# -# # If the legend texture has not been loaded into the GPU, do it now. -# my $tex_id = Slic3r::GUI::_3DScene::finalize_legend_texture; -# if ($tex_id > 0) -# { -# my $tex_w = Slic3r::GUI::_3DScene::get_legend_texture_width; -# my $tex_h = Slic3r::GUI::_3DScene::get_legend_texture_height; -# if (($tex_w > 0) && ($tex_h > 0)) -# { -# glDisable(GL_DEPTH_TEST); -# glPushMatrix(); -# glLoadIdentity(); -# -# my ($cw, $ch) = $self->GetSizeWH; -# -# my $l = (-0.5 * $cw) / $self->_zoom; -# my $t = (0.5 * $ch) / $self->_zoom; -# my $r = $l + $tex_w / $self->_zoom; -# my $b = $t - $tex_h / $self->_zoom; -# $self->_render_texture($tex_id, $l, $r, $b, $t); -# -# glPopMatrix(); -# glEnable(GL_DEPTH_TEST); -# } -# } -#} -# -#sub draw_warning { -# my ($self) = @_; -# -# if (!$self->_warning_enabled) { -# return; -# } -# -# # If the warning texture has not been loaded into the GPU, do it now. -# my $tex_id = Slic3r::GUI::_3DScene::finalize_warning_texture; -# if ($tex_id > 0) -# { -# my $tex_w = Slic3r::GUI::_3DScene::get_warning_texture_width; -# my $tex_h = Slic3r::GUI::_3DScene::get_warning_texture_height; -# if (($tex_w > 0) && ($tex_h > 0)) -# { -# glDisable(GL_DEPTH_TEST); -# glPushMatrix(); -# glLoadIdentity(); -# -# my ($cw, $ch) = $self->GetSizeWH; -# -# my $l = (-0.5 * $tex_w) / $self->_zoom; -# my $t = (-0.5 * $ch + $tex_h) / $self->_zoom; -# my $r = $l + $tex_w / $self->_zoom; -# my $b = $t - $tex_h / $self->_zoom; -# $self->_render_texture($tex_id, $l, $r, $b, $t); -# -# glPopMatrix(); -# glEnable(GL_DEPTH_TEST); -# } -# } -#} -# -#sub update_volumes_colors_by_extruder { -# my ($self, $config) = @_; -# $self->volumes->update_colors_by_extruder($config); -#} -# -#sub opengl_info -#{ -# my ($self, %params) = @_; -# my %tag = Slic3r::tags($params{format}); -# -# my $gl_version = glGetString(GL_VERSION); -# my $gl_vendor = glGetString(GL_VENDOR); -# my $gl_renderer = glGetString(GL_RENDERER); -# my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION); -# -# my $out = ''; -# $out .= "$tag{h2start}OpenGL installation$tag{h2end}$tag{eol}"; -# $out .= " $tag{bstart}Using POGL$tag{bend} v$OpenGL::BUILD_VERSION$tag{eol}"; -# $out .= " $tag{bstart}GL version: $tag{bend}${gl_version}$tag{eol}"; -# $out .= " $tag{bstart}vendor: $tag{bend}${gl_vendor}$tag{eol}"; -# $out .= " $tag{bstart}renderer: $tag{bend}${gl_renderer}$tag{eol}"; -# $out .= " $tag{bstart}GLSL version: $tag{bend}${glsl_version}$tag{eol}"; -# -# # Check for other OpenGL extensions -# $out .= "$tag{h2start}Installed extensions (* implemented in the module):$tag{h2end}$tag{eol}"; -# my $extensions = glGetString(GL_EXTENSIONS); -# my @extensions = split(' ',$extensions); -# foreach my $ext (sort @extensions) { -# my $stat = glpCheckExtension($ext); -# $out .= sprintf("%s ${ext}$tag{eol}", $stat?' ':'*'); -# $out .= sprintf(" ${stat}$tag{eol}") if ($stat && $stat !~ m|^$ext |); -# } -# -# return $out; -#} -# -#sub _report_opengl_state -#{ -# my ($self, $comment) = @_; -# my $err = glGetError(); -# return 0 if ($err == 0); -# -# # gluErrorString() hangs. Don't use it. -## my $errorstr = gluErrorString(); -# my $errorstr = ''; -# if ($err == 0x0500) { -# $errorstr = 'GL_INVALID_ENUM'; -# } elsif ($err == GL_INVALID_VALUE) { -# $errorstr = 'GL_INVALID_VALUE'; -# } elsif ($err == GL_INVALID_OPERATION) { -# $errorstr = 'GL_INVALID_OPERATION'; -# } elsif ($err == GL_STACK_OVERFLOW) { -# $errorstr = 'GL_STACK_OVERFLOW'; -# } elsif ($err == GL_OUT_OF_MEMORY) { -# $errorstr = 'GL_OUT_OF_MEMORY'; -# } else { -# $errorstr = 'unknown'; -# } -# if (defined($comment)) { -# printf("OpenGL error at %s, nr %d (0x%x): %s\n", $comment, $err, $err, $errorstr); -# } else { -# printf("OpenGL error nr %d (0x%x): %s\n", $err, $err, $errorstr); -# } -#} -# -#sub _vertex_shader_Gouraud { -# return <<'VERTEX'; -##version 110 -# -##define INTENSITY_CORRECTION 0.6 -# -#// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -#const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -##define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -##define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -##define LIGHT_TOP_SHININESS 20.0 -# -#// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -#const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); -##define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -#//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) -#//#define LIGHT_FRONT_SHININESS 5.0 -# -##define INTENSITY_AMBIENT 0.3 -# -#const vec3 ZERO = vec3(0.0, 0.0, 0.0); -# -#struct PrintBoxDetection -#{ -# vec3 min; -# vec3 max; -# // xyz contains the offset, if w == 1.0 detection needs to be performed -# vec4 volume_origin; -#}; -# -#uniform PrintBoxDetection print_box; -# -#// x = tainted, y = specular; -#varying vec2 intensity; -# -#varying vec3 delta_box_min; -#varying vec3 delta_box_max; -# -#void main() -#{ -# // First transform the normal into camera space and normalize the result. -# vec3 normal = normalize(gl_NormalMatrix * gl_Normal); -# -# // 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. -# float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); -# -# intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; -# intensity.y = 0.0; -# -# if (NdotL > 0.0) -# intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); -# -# // Perform the same lighting calculation for the 2nd light source (no specular applied). -# NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); -# intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; -# -# // compute deltas for out of print volume detection (world coordinates) -# if (print_box.volume_origin.w == 1.0) -# { -# vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz; -# delta_box_min = v - print_box.min; -# delta_box_max = v - print_box.max; -# } -# else -# { -# delta_box_min = ZERO; -# delta_box_max = ZERO; -# } -# -# gl_Position = ftransform(); -#} -# -#VERTEX -#} -# -#sub _fragment_shader_Gouraud { -# return <<'FRAGMENT'; -##version 110 -# -#const vec3 ZERO = vec3(0.0, 0.0, 0.0); -# -#// x = tainted, y = specular; -#varying vec2 intensity; -# -#varying vec3 delta_box_min; -#varying vec3 delta_box_max; -# -#uniform vec4 uniform_color; -# -#void main() -#{ -# // if the fragment is outside the print volume -> use darker color -# vec3 color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(uniform_color.rgb, ZERO, 0.3333) : uniform_color.rgb; -# gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + color * intensity.x, uniform_color.a); -#} -# -#FRAGMENT -#} -# -#sub _vertex_shader_Phong { -# return <<'VERTEX'; -##version 110 -# -#varying vec3 normal; -#varying vec3 eye; -#void main(void) -#{ -# eye = normalize(vec3(gl_ModelViewMatrix * gl_Vertex)); -# normal = normalize(gl_NormalMatrix * gl_Normal); -# gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; -#} -#VERTEX -#} -# -#sub _fragment_shader_Phong { -# return <<'FRAGMENT'; -##version 110 -# -##define INTENSITY_CORRECTION 0.7 -# -##define LIGHT_TOP_DIR -0.6/1.31, 0.6/1.31, 1./1.31 -##define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -##define LIGHT_TOP_SPECULAR (0.5 * INTENSITY_CORRECTION) -#//#define LIGHT_TOP_SHININESS 50. -##define LIGHT_TOP_SHININESS 10. -# -##define LIGHT_FRONT_DIR 1./1.43, 0.2/1.43, 1./1.43 -##define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -##define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) -##define LIGHT_FRONT_SHININESS 50. -# -##define INTENSITY_AMBIENT 0.0 -# -#varying vec3 normal; -#varying vec3 eye; -#uniform vec4 uniform_color; -#void main() { -# -# float intensity_specular = 0.; -# float intensity_tainted = 0.; -# float intensity = max(dot(normal,vec3(LIGHT_TOP_DIR)), 0.0); -# // if the vertex is lit compute the specular color -# if (intensity > 0.0) { -# intensity_tainted = LIGHT_TOP_DIFFUSE * intensity; -# // compute the half vector -# vec3 h = normalize(vec3(LIGHT_TOP_DIR) + eye); -# // compute the specular term into spec -# intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(h, normal), 0.0), LIGHT_TOP_SHININESS); -# } -# intensity = max(dot(normal,vec3(LIGHT_FRONT_DIR)), 0.0); -# // if the vertex is lit compute the specular color -# if (intensity > 0.0) { -# intensity_tainted += LIGHT_FRONT_DIFFUSE * intensity; -# // compute the half vector -#// vec3 h = normalize(vec3(LIGHT_FRONT_DIR) + eye); -# // compute the specular term into spec -#// intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(h,normal), 0.0), LIGHT_FRONT_SHININESS); -# } -# -# gl_FragColor = max( -# vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted, -# INTENSITY_AMBIENT * uniform_color); -# gl_FragColor.a = uniform_color.a; -#} -#FRAGMENT -#} -# -#sub _vertex_shader_variable_layer_height { -# return <<'VERTEX'; -##version 110 -# -##define INTENSITY_CORRECTION 0.6 -# -#const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -##define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -##define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -##define LIGHT_TOP_SHININESS 20.0 -# -#const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); -##define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -#//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) -#//#define LIGHT_FRONT_SHININESS 5.0 -# -##define INTENSITY_AMBIENT 0.3 -# -#// x = tainted, y = specular; -#varying vec2 intensity; -# -#varying float object_z; -# -#void main() -#{ -# // First transform the normal into camera space and normalize the result. -# vec3 normal = normalize(gl_NormalMatrix * gl_Normal); -# -# // 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. -# float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); -# -# intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; -# intensity.y = 0.0; -# -# if (NdotL > 0.0) -# intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); -# -# // Perform the same lighting calculation for the 2nd light source (no specular) -# NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); -# -# intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; -# -# // Scaled to widths of the Z texture. -# object_z = gl_Vertex.z; -# -# gl_Position = ftransform(); -#} -# -#VERTEX -#} -# -#sub _fragment_shader_variable_layer_height { -# 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; -#uniform float z_cursor; -#uniform float z_cursor_band_width; -# -#// x = tainted, y = specular; -#varying vec2 intensity; -# -#varying float object_z; -# -#void main() -#{ -# 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.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25; -# // Calculate level of detail from the object Z coordinate. -# // This makes the slowly sloping surfaces to be show with high detail (with stripes), -# // and the vertical surfaces to be shown with low detail (no stripes) -# float z_in_cells = object_z_row * 190.; -# // Gradient of Z projected on the screen. -# float dx_vtc = dFdx(z_in_cells); -# float dy_vtc = dFdy(z_in_cells); -# float lod = clamp(0.5 * log2(max(dx_vtc*dx_vtc, dy_vtc*dy_vtc)), 0., 1.); -# // Sample the Z texture. Texture coordinates are normalized to <0, 1>. -# vec4 color = -# mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.), -# texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); -# -# // Mix the final color. -# gl_FragColor = -# vec4(intensity.y, intensity.y, intensity.y, 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); -#} -# -#FRAGMENT -#} -#=================================================================================================================================== - # The 3D canvas to display objects and tool paths. package Slic3r::GUI::3DScene; use base qw(Slic3r::GUI::3DScene::Base); -#=================================================================================================================================== -#use OpenGL qw(:glconstants :gluconstants :glufunctions); -#use List::Util qw(first min max); -#use Slic3r::Geometry qw(scale unscale epsilon); -#use Slic3r::Print::State ':steps'; -#=================================================================================================================================== - -#=================================================================================================================================== -#__PACKAGE__->mk_accessors(qw( -# color_by -# select_by -# drag_by -#)); -#=================================================================================================================================== - sub new { my $class = shift; - my $self = $class->SUPER::new(@_); -#=================================================================================================================================== -# $self->color_by('volume'); # object | volume -# $self->select_by('object'); # object | volume | instance -# $self->drag_by('instance'); # object | instance -#=================================================================================================================================== - + my $self = $class->SUPER::new(@_); return $self; } -#============================================================================================================================== -#sub load_object { -# my ($self, $model, $print, $obj_idx, $instance_idxs) = @_; -# -# $self->SetCurrent($self->GetContext) if $useVBOs; -# -# my $model_object; -# if ($model->isa('Slic3r::Model::Object')) { -# $model_object = $model; -# $model = $model_object->model; -# $obj_idx = 0; -# } else { -# $model_object = $model->get_object($obj_idx); -# } -# -# $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, -# $self->UseVBOs); -# return @{$volume_indices}; -#} -# -## Create 3D thick extrusion lines for a skirt and brim. -## Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes. -#sub load_print_toolpaths { -# my ($self, $print, $colors) = @_; -# -# $self->SetCurrent($self->GetContext) if $self->UseVBOs; -# Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes, $colors, $self->UseVBOs) -# if ($print->step_done(STEP_SKIRT) && $print->step_done(STEP_BRIM)); -#} -# -## 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. -#sub load_print_object_toolpaths { -# my ($self, $object, $colors) = @_; -# -# $self->SetCurrent($self->GetContext) if $self->UseVBOs; -# Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $colors, $self->UseVBOs); -#} -# -## Create 3D thick extrusion lines for wipe tower extrusions. -#sub load_wipe_tower_toolpaths { -# my ($self, $print, $colors) = @_; -# -# $self->SetCurrent($self->GetContext) if $self->UseVBOs; -# Slic3r::GUI::_3DScene::_load_wipe_tower_toolpaths($print, $self->volumes, $colors, $self->UseVBOs) -# if ($print->step_done(STEP_WIPE_TOWER)); -#} -# -#sub load_gcode_preview { -# my ($self, $print, $gcode_preview_data, $colors) = @_; -# -# $self->SetCurrent($self->GetContext) if $self->UseVBOs; -# Slic3r::GUI::_3DScene::load_gcode_preview($print, $gcode_preview_data, $self->volumes, $colors, $self->UseVBOs); -#} -# -#sub set_toolpaths_range { -# my ($self, $min_z, $max_z) = @_; -# $self->volumes->set_range($min_z, $max_z); -#} -# -#sub reset_legend_texture { -# Slic3r::GUI::_3DScene::reset_legend_texture(); -#} -# -#sub get_current_print_zs { -# my ($self, $active_only) = @_; -# return $self->volumes->get_current_print_zs($active_only); -#} -#============================================================================================================================== - 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index eb5255b8c..aef452f5e 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -14,6 +14,7 @@ use Wx qw(:button :colour :cursor :dialog :filedialog :keycode :icon :font :id : 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_LEFT_DOWN EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED); +use Slic3r::Geometry qw(PI); use base 'Wx::Panel'; use constant TB_ADD => &Wx::NewId; @@ -116,6 +117,8 @@ sub new { my $model_object = $self->{model}->objects->[$obj_idx]; my $model_instance = $model_object->instances->[0]; + + $self->stop_background_process; my $variation = $scale / $model_instance->scaling_factor; #FIXME Scale the layer height profile? @@ -127,7 +130,6 @@ sub new { $object->transform_thumbnail($self->{model}, $obj_idx); #update print and start background processing - $self->stop_background_process; $self->{print}->add_model_object($model_object, $obj_idx); $self->selection_changed(1); # refresh info (size, volume etc.) @@ -135,6 +137,27 @@ sub new { $self->schedule_background_process; }; + # callback to react to gizmo rotate + my $on_gizmo_rotate = sub { + my ($angle_z) = @_; + $self->rotate(rad2deg($angle_z), Z, 'absolute'); + }; + + # callback to update object's geometry info while using gizmos + my $on_update_geometry_info = sub { + my ($size_x, $size_y, $size_z, $scale_factor) = @_; + + my ($obj_idx, $object) = $self->selected_object; + + if ((defined $obj_idx) && ($self->{object_info_size})) { # have we already loaded the info pane? + $self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", $size_x, $size_y, $size_z)); + my $model_object = $self->{model}->objects->[$obj_idx]; + if (my $stats = $model_object->mesh_stats) { + $self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * $scale_factor**3)); + } + } + }; + # Initialize 3D plater if ($Slic3r::GUI::have_OpenGL) { $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config}); @@ -152,7 +175,9 @@ sub new { Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved); Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly); -# Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); + Slic3r::GUI::_3DScene::register_on_gizmo_rotate_callback($self->{canvas3D}, $on_gizmo_rotate); + Slic3r::GUI::_3DScene::register_on_update_geometry_info_callback($self->{canvas3D}, $on_update_geometry_info); + Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); @@ -1055,7 +1080,17 @@ sub rotate { if ($axis == Z) { my $new_angle = deg2rad($angle); - $_->set_rotation(($relative ? $_->rotation : 0.) + $new_angle) for @{ $model_object->instances }; + foreach my $inst (@{ $model_object->instances }) { + my $rotation = ($relative ? $inst->rotation : 0.) + $new_angle; + while ($rotation > 2.0 * PI) { + $rotation -= 2.0 * PI; + } + while ($rotation < 0.0) { + $rotation += 2.0 * PI; + } + $inst->set_rotation($rotation); + Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D}); + } $object->transform_thumbnail($self->{model}, $obj_idx); } else { # rotation around X and Y needs to be performed on mesh @@ -1659,34 +1694,6 @@ sub export_object_stl { $self->statusbar->SetStatusText(L("STL file exported to ").$output_file); } -sub fix_through_netfabb { - my ($self) = @_; - my ($obj_idx, $object) = $self->selected_object; - return if !defined $obj_idx; - my $model_object = $self->{model}->objects->[$obj_idx]; - my $model_fixed = Slic3r::Model->new; - Slic3r::GUI::fix_model_by_win10_sdk_gui($model_object, $self->{print}, $model_fixed); - - my @new_obj_idx = $self->load_model_objects(@{$model_fixed->objects}); - return if !@new_obj_idx; - - foreach my $new_obj_idx (@new_obj_idx) { - my $o = $self->{model}->objects->[$new_obj_idx]; - $o->clear_instances; - $o->add_instance($_) for @{$model_object->instances}; - #$o->invalidate_bounding_box; - - if ($o->volumes_count == $model_object->volumes_count) { - for my $i (0..($o->volumes_count-1)) { - $o->get_volume($i)->config->apply($model_object->get_volume($i)->config); - } - } - #FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid, - } - - $self->remove($obj_idx); -} - sub export_amf { my ($self) = @_; return if !@{$self->{objects}}; @@ -2265,11 +2272,6 @@ sub object_menu { $frame->_append_menu_item($menu, L("Export object as STL…"), L('Export this single object as STL file'), sub { $self->export_object_stl; }, undef, 'brick_go.png'); - if (Slic3r::GUI::is_windows10) { - $frame->_append_menu_item($menu, L("Fix STL through Netfabb"), L('Fix the model by sending it to a Netfabb cloud service through Windows 10 API'), sub { - $self->fix_through_netfabb; - }, undef, 'brick_go.png'); - } return $menu; } diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 7f83e0f57..0b770b31c 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -5,25 +5,13 @@ use utf8; use List::Util qw(); use Wx qw(:misc :pen :brush :sizer :font :cursor :keycode wxTAB_TRAVERSAL); -#============================================================================================================================== -#use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR); -#============================================================================================================================== use base qw(Slic3r::GUI::3DScene Class::Accessor); -#============================================================================================================================== -#use Wx::Locale gettext => 'L'; -# -#__PACKAGE__->mk_accessors(qw( -# on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly -# on_remove_object on_increase_objects on_decrease_objects on_enable_action_buttons)); -#============================================================================================================================== - sub new { my $class = shift; my ($parent, $objects, $model, $print, $config) = @_; my $self = $class->SUPER::new($parent); -#============================================================================================================================== Slic3r::GUI::_3DScene::enable_picking($self, 1); Slic3r::GUI::_3DScene::enable_moving($self, 1); Slic3r::GUI::_3DScene::set_select_by($self, 'object'); @@ -31,253 +19,8 @@ sub new { Slic3r::GUI::_3DScene::set_model($self, $model); Slic3r::GUI::_3DScene::set_print($self, $print); Slic3r::GUI::_3DScene::set_config($self, $config); -# $self->enable_picking(1); -# $self->enable_moving(1); -# $self->select_by('object'); -# $self->drag_by('instance'); -# -# $self->{objects} = $objects; -# $self->{model} = $model; -# $self->{print} = $print; -# $self->{config} = $config; -# $self->{on_select_object} = sub {}; -# $self->{on_instances_moved} = sub {}; -# $self->{on_wipe_tower_moved} = sub {}; -# -# $self->{objects_volumes_idxs} = []; -# -# $self->on_select(sub { -# my ($volume_idx) = @_; -# $self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx) -# if ($self->{on_select_object}); -# }); -# -# $self->on_move(sub { -# my @volume_idxs = @_; -# my %done = (); # prevent moving instances twice -# my $object_moved; -# my $wipe_tower_moved; -# foreach my $volume_idx (@volume_idxs) { -# my $volume = $self->volumes->[$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; -# if ($obj_idx < 1000) { -# # Move a regular object. -# my $model_object = $self->{model}->get_object($obj_idx); -# $model_object -# ->instances->[$instance_idx] -# ->offset -# ->translate($volume->origin->x, $volume->origin->y); #)) -# $model_object->invalidate_bounding_box; -# $object_moved = 1; -# } elsif ($obj_idx == 1000) { -# # Move a wipe tower proxy. -# $wipe_tower_moved = $volume->origin; -# } -# } -# -# $self->{on_instances_moved}->() -# if $object_moved && $self->{on_instances_moved}; -# $self->{on_wipe_tower_moved}->($wipe_tower_moved) -# if $wipe_tower_moved && $self->{on_wipe_tower_moved}; -# }); -# -# EVT_KEY_DOWN($self, sub { -# my ($s, $event) = @_; -# if ($event->HasModifiers) { -# $event->Skip; -# } else { -# my $key = $event->GetKeyCode; -# if ($key == WXK_DELETE) { -# $self->on_remove_object->() if $self->on_remove_object; -# } else { -# $event->Skip; -# } -# } -# }); -# -# EVT_CHAR($self, sub { -# my ($s, $event) = @_; -# if ($event->HasModifiers) { -# $event->Skip; -# } else { -# my $key = $event->GetKeyCode; -# if ($key == ord('a')) { -# $self->on_arrange->() if $self->on_arrange; -# } elsif ($key == ord('l')) { -# $self->on_rotate_object_left->() if $self->on_rotate_object_left; -# } elsif ($key == ord('r')) { -# $self->on_rotate_object_right->() if $self->on_rotate_object_right; -# } elsif ($key == ord('s')) { -# $self->on_scale_object_uniformly->() if $self->on_scale_object_uniformly; -# } elsif ($key == ord('+')) { -# $self->on_increase_objects->() if $self->on_increase_objects; -# } elsif ($key == ord('-')) { -# $self->on_decrease_objects->() if $self->on_decrease_objects; -# } else { -# $event->Skip; -# } -# } -# }); -#============================================================================================================================== return $self; } -#============================================================================================================================== -#sub set_on_select_object { -# my ($self, $cb) = @_; -# $self->{on_select_object} = $cb; -#} -# -#sub set_on_double_click { -# my ($self, $cb) = @_; -# $self->on_double_click($cb); -#} -# -#sub set_on_right_click { -# my ($self, $cb) = @_; -# $self->on_right_click($cb); -#} -# -#sub set_on_arrange { -# my ($self, $cb) = @_; -# $self->on_arrange($cb); -#} -# -#sub set_on_rotate_object_left { -# my ($self, $cb) = @_; -# $self->on_rotate_object_left($cb); -#} -# -#sub set_on_rotate_object_right { -# my ($self, $cb) = @_; -# $self->on_rotate_object_right($cb); -#} -# -#sub set_on_scale_object_uniformly { -# my ($self, $cb) = @_; -# $self->on_scale_object_uniformly($cb); -#} -# -#sub set_on_increase_objects { -# my ($self, $cb) = @_; -# $self->on_increase_objects($cb); -#} -# -#sub set_on_decrease_objects { -# my ($self, $cb) = @_; -# $self->on_decrease_objects($cb); -#} -# -#sub set_on_remove_object { -# my ($self, $cb) = @_; -# $self->on_remove_object($cb); -#} -# -#sub set_on_instances_moved { -# my ($self, $cb) = @_; -# $self->{on_instances_moved} = $cb; -#} -# -#sub set_on_wipe_tower_moved { -# my ($self, $cb) = @_; -# $self->{on_wipe_tower_moved} = $cb; -#} -# -#sub set_on_model_update { -# my ($self, $cb) = @_; -# $self->on_model_update($cb); -#} -# -#sub set_on_enable_action_buttons { -# my ($self, $cb) = @_; -# $self->on_enable_action_buttons($cb); -#} -# -#sub update_volumes_selection { -# my ($self) = @_; -# -# foreach my $obj_idx (0..$#{$self->{model}->objects}) { -# if ($self->{objects}[$obj_idx]->selected) { -# my $volume_idxs = $self->{objects_volumes_idxs}->[$obj_idx]; -# $self->select_volume($_) for @{$volume_idxs}; -# } -# } -#} -# -#sub reload_scene { -# my ($self, $force) = @_; -# -# $self->reset_objects; -# $self->update_bed_size; -# -# if (! $self->IsShown && ! $force) { -# $self->{reload_delayed} = 1; -# return; -# } -# -# $self->{reload_delayed} = 0; -# -# $self->{objects_volumes_idxs} = []; -# foreach my $obj_idx (0..$#{$self->{model}->objects}) { -# my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx); -# push(@{$self->{objects_volumes_idxs}}, \@volume_idxs); -# } -# -# $self->update_volumes_selection; -# -# if (defined $self->{config}->nozzle_diameter) { -# # Should the wipe tower be visualized? -# my $extruders_count = scalar @{ $self->{config}->nozzle_diameter }; -# # Height of a print. -# my $height = $self->{model}->bounding_box->z_max; -# # Show at least a slab. -# $height = 10 if $height < 10; -# if ($extruders_count > 1 && $self->{config}->single_extruder_multi_material && $self->{config}->wipe_tower && -# ! $self->{config}->complete_objects) { -# $self->volumes->load_wipe_tower_preview(1000, -# $self->{config}->wipe_tower_x, $self->{config}->wipe_tower_y, $self->{config}->wipe_tower_width, -# #$self->{config}->wipe_tower_per_color_wipe# 15 * ($extruders_count - 1), # this is just a hack when the config parameter became obsolete -# 15 * ($extruders_count - 1), -# $self->{model}->bounding_box->z_max, $self->{config}->wipe_tower_rotation_angle, $self->UseVBOs); -# } -# } -# -# $self->update_volumes_colors_by_extruder($self->{config}); -# -# # checks for geometry outside the print volume to render it accordingly -# if (scalar @{$self->volumes} > 0) -# { -# my $contained = $self->volumes->check_outside_state($self->{config}); -# if (!$contained) { -# $self->set_warning_enabled(1); -# Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume")); -# $self->on_enable_action_buttons->(0) if ($self->on_enable_action_buttons); -# } else { -# $self->set_warning_enabled(0); -# $self->volumes->reset_outside_state(); -# Slic3r::GUI::_3DScene::reset_warning_texture(); -# $self->on_enable_action_buttons->(scalar @{$self->{model}->objects} > 0) if ($self->on_enable_action_buttons); -# } -# } else { -# $self->set_warning_enabled(0); -# Slic3r::GUI::_3DScene::reset_warning_texture(); -# } -#} -# -#sub update_bed_size { -# my ($self) = @_; -# $self->set_bed_shape($self->{config}->bed_shape); -#} -# -## Called by the Platter wxNotebook when this page is activated. -#sub OnActivate { -# my ($self) = @_; -# $self->reload_scene(1) if ($self->{reload_delayed}); -#} -#============================================================================================================================== - 1; diff --git a/resources/shaders/gouraud.vs b/resources/shaders/gouraud.vs index 22ba91a93..ea7e46e79 100644 --- a/resources/shaders/gouraud.vs +++ b/resources/shaders/gouraud.vs @@ -22,8 +22,8 @@ struct PrintBoxDetection { vec3 min; vec3 max; - // xyz contains the offset, if w == 1.0 detection needs to be performed - vec4 volume_origin; + bool volume_detection; + mat4 volume_world_matrix; }; uniform PrintBoxDetection print_box; @@ -54,9 +54,9 @@ void main() intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; // compute deltas for out of print volume detection (world coordinates) - if (print_box.volume_origin.w == 1.0) + if (print_box.volume_detection) { - vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz; + vec3 v = (print_box.volume_world_matrix * gl_Vertex).xyz; delta_box_min = v - print_box.min; delta_box_max = v - print_box.max; } diff --git a/resources/shaders/variable_layer_height.vs b/resources/shaders/variable_layer_height.vs index 2c918c0d4..9763859d0 100644 --- a/resources/shaders/variable_layer_height.vs +++ b/resources/shaders/variable_layer_height.vs @@ -14,6 +14,8 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); #define INTENSITY_AMBIENT 0.3 +uniform mat4 volume_world_matrix; + // x = tainted, y = specular; varying vec2 intensity; @@ -38,9 +40,8 @@ void main() NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - - // Scaled to widths of the Z texture. - object_z = gl_Vertex.z; + // Scaled to widths of the Z texture. + object_z = (volume_world_matrix * gl_Vertex).z; gl_Position = ftransform(); } diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp index ceb968a50..4355cd61b 100644 --- a/xs/src/libslic3r/BoundingBox.cpp +++ b/xs/src/libslic3r/BoundingBox.cpp @@ -2,6 +2,8 @@ #include <algorithm> #include <assert.h> +#include <Eigen/Dense> + namespace Slic3r { template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points); @@ -251,4 +253,41 @@ void BoundingBox::align_to_grid(const coord_t cell_size) } } +BoundingBoxf3 BoundingBoxf3::transformed(const std::vector<float>& matrix) const +{ + Eigen::Matrix<float, 3, 8> vertices; + + vertices(0, 0) = (float)min.x; vertices(1, 0) = (float)min.y; vertices(2, 0) = (float)min.z; + vertices(0, 1) = (float)max.x; vertices(1, 1) = (float)min.y; vertices(2, 1) = (float)min.z; + vertices(0, 2) = (float)max.x; vertices(1, 2) = (float)max.y; vertices(2, 2) = (float)min.z; + vertices(0, 3) = (float)min.x; vertices(1, 3) = (float)max.y; vertices(2, 3) = (float)min.z; + vertices(0, 4) = (float)min.x; vertices(1, 4) = (float)min.y; vertices(2, 4) = (float)max.z; + vertices(0, 5) = (float)max.x; vertices(1, 5) = (float)min.y; vertices(2, 5) = (float)max.z; + vertices(0, 6) = (float)max.x; vertices(1, 6) = (float)max.y; vertices(2, 6) = (float)max.z; + vertices(0, 7) = (float)min.x; vertices(1, 7) = (float)max.y; vertices(2, 7) = (float)max.z; + + Eigen::Transform<float, 3, Eigen::Affine> m; + ::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float)); + Eigen::Matrix<float, 3, 8> transf_vertices = m * vertices.colwise().homogeneous(); + + float min_x = transf_vertices(0, 0); + float max_x = transf_vertices(0, 0); + float min_y = transf_vertices(1, 0); + float max_y = transf_vertices(1, 0); + float min_z = transf_vertices(2, 0); + float max_z = transf_vertices(2, 0); + + for (int i = 1; i < 8; ++i) + { + min_x = std::min(min_x, transf_vertices(0, i)); + max_x = std::max(max_x, transf_vertices(0, i)); + min_y = std::min(min_y, transf_vertices(1, i)); + max_y = std::max(max_y, transf_vertices(1, i)); + min_z = std::min(min_z, transf_vertices(2, i)); + max_z = std::max(max_z, transf_vertices(2, i)); + } + + return BoundingBoxf3(Pointf3((coordf_t)min_x, (coordf_t)min_y, (coordf_t)min_z), Pointf3((coordf_t)max_x, (coordf_t)max_y, (coordf_t)max_z)); +} + } diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index 5de94aa9c..1f71536ee 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -148,6 +148,8 @@ public: BoundingBoxf3() : BoundingBox3Base<Pointf3>() {}; BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base<Pointf3>(pmin, pmax) {}; BoundingBoxf3(const std::vector<Pointf3> &points) : BoundingBox3Base<Pointf3>(points) {}; + + BoundingBoxf3 transformed(const std::vector<float>& matrix) const; }; template<typename VT> diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index 263363756..21d4b4d3b 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -13,9 +13,7 @@ #include <boost/filesystem/operations.hpp> #include <boost/algorithm/string.hpp> -//############################################################################################################################################ #include <boost/nowide/fstream.hpp> -//############################################################################################################################################ #include <miniz/miniz_zip.h> #if 0 diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index fc49d68af..73234d3eb 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -21,6 +21,7 @@ // #include <benchmark.h> #include "SVG.hpp" +#include <Eigen/Dense> namespace Slic3r { @@ -990,10 +991,7 @@ void ModelObject::clear_instances() // Returns the bounding box of the transformed instances. // This bounding box is approximate and not snug. -//======================================================================================================== const BoundingBoxf3& ModelObject::bounding_box() const -//const BoundingBoxf3& ModelObject::bounding_box() -//======================================================================================================== { if (! m_bounding_box_valid) { BoundingBoxf3 raw_bbox; @@ -1435,32 +1433,16 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const { - // rotate around mesh origin - double c = cos(this->rotation); - double s = sin(this->rotation); - Pointf3 pts[4] = { - bbox.min, - bbox.max, - Pointf3(bbox.min.x, bbox.max.y, bbox.min.z), - Pointf3(bbox.max.x, bbox.min.y, bbox.max.z) - }; - BoundingBoxf3 out; - for (int i = 0; i < 4; ++ i) { - Pointf3 &v = pts[i]; - double xold = v.x; - double yold = v.y; - v.x = float(c * xold - s * yold); - v.y = float(s * xold + c * yold); - v.x *= this->scaling_factor; - v.y *= this->scaling_factor; - v.z *= this->scaling_factor; - if (! dont_translate) { - v.x += this->offset.x; - v.y += this->offset.y; - } - out.merge(v); - } - return out; + Eigen::Transform<float, 3, Eigen::Affine> matrix = Eigen::Transform<float, 3, Eigen::Affine>::Identity(); + if (!dont_translate) + matrix.translate(Eigen::Vector3f((float)offset.x, (float)offset.y, 0.0f)); + + matrix.rotate(Eigen::AngleAxisf(rotation, Eigen::Vector3f::UnitZ())); + matrix.scale(scaling_factor); + + std::vector<float> m(16, 0.0f); + ::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float)); + return bbox.transformed(m); } void ModelInstance::transform_polygon(Polygon* polygon) const diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 3337f4d9a..5003f8330 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -103,10 +103,7 @@ public: // Returns the bounding box of the transformed instances. // This bounding box is approximate and not snug. // This bounding box is being cached. -//======================================================================================================== const BoundingBoxf3& bounding_box() const; -// const BoundingBoxf3& bounding_box(); -//======================================================================================================== void invalidate_bounding_box() { m_bounding_box_valid = false; } // Returns a snug bounding box of the transformed instances. // This bounding box is not being cached. @@ -148,10 +145,9 @@ private: // Parent object, owning this ModelObject. Model *m_model; // Bounding box, cached. -//======================================================================================================== + mutable BoundingBoxf3 m_bounding_box; mutable bool m_bounding_box_valid; -//======================================================================================================== }; // An object STL, or a modifier volume, over which a different set of parameters shall be applied. diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 921841a27..a501fa4d3 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -96,7 +96,8 @@ public: void call(int i, int j) const; void call(const std::vector<int>& ints) const; void call(double d) const; - void call(double x, double y) const; + void call(double a, double b) const; + void call(double a, double b, double c, double d) const; void call(bool b) const; private: void *m_callback; diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 991118c14..13ec1d066 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -266,7 +266,7 @@ void PerlCallback::call(double d) const LEAVE; } -void PerlCallback::call(double x, double y) const +void PerlCallback::call(double a, double b) const { if (!m_callback) return; @@ -274,8 +274,26 @@ void PerlCallback::call(double x, double y) const ENTER; SAVETMPS; PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVnv(x))); - XPUSHs(sv_2mortal(newSVnv(y))); + XPUSHs(sv_2mortal(newSVnv(a))); + XPUSHs(sv_2mortal(newSVnv(b))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + +void PerlCallback::call(double a, double b, double c, double d) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(a))); + XPUSHs(sv_2mortal(newSVnv(b))); + XPUSHs(sv_2mortal(newSVnv(c))); + XPUSHs(sv_2mortal(newSVnv(d))); PUTBACK; perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); FREETMPS; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 09e10ac28..a52946afb 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2,7 +2,6 @@ #include "3DScene.hpp" -#include "../../libslic3r/libslic3r.h" #include "../../libslic3r/ExtrusionEntity.hpp" #include "../../libslic3r/ExtrusionEntityCollection.hpp" #include "../../libslic3r/Geometry.hpp" @@ -28,8 +27,15 @@ #include <wx/image.h> #include <wx/settings.h> +#include <Eigen/Dense> + #include "GUI.hpp" +static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + namespace Slic3r { void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh) @@ -198,6 +204,34 @@ const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; +GLVolume::GLVolume(float r, float g, float b, float a) + : m_angle_z(0.0f) + , m_scale_factor(1.0f) + , m_dirty(true) + , composite_id(-1) + , select_group_id(-1) + , drag_group_id(-1) + , extruder_id(0) + , selected(false) + , is_active(true) + , zoom_to_volumes(true) + , outside_printer_detection_enabled(true) + , is_outside(false) + , hover(false) + , is_modifier(false) + , is_wipe_tower(false) + , tverts_range(0, size_t(-1)) + , qverts_range(0, size_t(-1)) +{ + m_world_mat = std::vector<float>(UNIT_MATRIX, std::end(UNIT_MATRIX)); + + color[0] = r; + color[1] = g; + color[2] = b; + color[3] = a; + set_render_color(r, g, b, a); +} + void GLVolume::set_render_color(float r, float g, float b, float a) { render_color[0] = r; @@ -218,12 +252,7 @@ void GLVolume::set_render_color(const float* rgba, unsigned int size) void GLVolume::set_render_color() { if (selected) - { - if (is_outside) - set_render_color(SELECTED_OUTSIDE_COLOR, 4); - else - set_render_color(SELECTED_COLOR, 4); - } + set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); else if (hover) set_render_color(HOVER_COLOR, 4); else if (is_outside) @@ -232,6 +261,52 @@ void GLVolume::set_render_color() set_render_color(color, 4); } +const Pointf3& GLVolume::get_origin() const +{ + return m_origin; +} + +void GLVolume::set_origin(const Pointf3& origin) +{ + m_origin = origin; + m_dirty = true; +} + +void GLVolume::set_angle_z(float angle_z) +{ + m_angle_z = angle_z; + m_dirty = true; +} + +void GLVolume::set_scale_factor(float scale_factor) +{ + m_scale_factor = scale_factor; + m_dirty = true; +} + +const std::vector<float>& GLVolume::world_matrix() const +{ + if (m_dirty) + { + Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity(); + m.translate(Eigen::Vector3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z)); + m.rotate(Eigen::AngleAxisf(m_angle_z, Eigen::Vector3f::UnitZ())); + m.scale(m_scale_factor); + ::memcpy((void*)m_world_mat.data(), (const void*)m.data(), 16 * sizeof(float)); + m_dirty = false; + } + + return m_world_mat; +} + +BoundingBoxf3 GLVolume::transformed_bounding_box() const +{ + if (m_dirty) + m_transformed_bounding_box = bounding_box.transformed(world_matrix()); + + return m_transformed_bounding_box; +} + void GLVolume::set_range(double min_z, double max_z) { this->qverts_range.first = 0; @@ -272,14 +347,16 @@ void GLVolume::render() const if (!is_active) return; - glCullFace(GL_BACK); - glPushMatrix(); - glTranslated(this->origin.x, this->origin.y, this->origin.z); + ::glCullFace(GL_BACK); + ::glPushMatrix(); + ::glTranslated(m_origin.x, m_origin.y, m_origin.z); + ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f); + ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor); if (this->indexed_vertex_array.indexed()) this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); else this->indexed_vertex_array.render(); - glPopMatrix(); + ::glPopMatrix(); } void GLVolume::render_using_layer_height() const @@ -297,6 +374,7 @@ void GLVolume::render_using_layer_height() const GLint z_texture_row_to_normalized_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_texture_row_to_normalized") : -1; GLint z_cursor_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor") : -1; GLint z_cursor_band_width_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor_band_width") : -1; + GLint world_matrix_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "volume_world_matrix") : -1; if (z_to_texture_row_id >= 0) glUniform1f(z_to_texture_row_id, (GLfloat)layer_height_texture_z_to_row_id()); @@ -310,14 +388,20 @@ void GLVolume::render_using_layer_height() const if (z_cursor_band_width_id >= 0) glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width); - unsigned int w = layer_height_texture_width(); - unsigned int h = layer_height_texture_height(); + if (world_matrix_id >= 0) + ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); + GLsizei w = (GLsizei)layer_height_texture_width(); + GLsizei h = (GLsizei)layer_height_texture_height(); + GLsizei half_w = w / 2; + GLsizei half_h = h / 2; + + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBindTexture(GL_TEXTURE_2D, layer_height_texture_data.texture_id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, w / 2, h / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level0()); - glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, w / 2, h / 2, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1()); + glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1()); render(); @@ -327,6 +411,128 @@ void GLVolume::render_using_layer_height() const glUseProgram(current_program_id); } +void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const +{ + if (!is_active) + return; + + if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id) + return; + + if (layer_height_texture_data.can_use()) + { + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + render_using_layer_height(); + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + return; + } + + GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first)); + GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first)); + if (n_triangles + n_quads == 0) + { + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + + if (color_id >= 0) + { + float color[4]; + ::memcpy((void*)color, (const void*)render_color, 4 * sizeof(float)); + ::glUniform4fv(color_id, 1, (const GLfloat*)color); + } + else + ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + + if (detection_id != -1) + ::glUniform1i(detection_id, outside_printer_detection_enabled ? 1 : 0); + + if (worldmatrix_id != -1) + ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); + + render(); + + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + + return; + } + + if (color_id >= 0) + ::glUniform4fv(color_id, 1, (const GLfloat*)render_color); + else + ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + + if (detection_id != -1) + ::glUniform1i(detection_id, outside_printer_detection_enabled ? 1 : 0); + + if (worldmatrix_id != -1) + ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); + + ::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); + ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); + ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); + + ::glPushMatrix(); + ::glTranslated(m_origin.x, m_origin.y, m_origin.z); + ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f); + ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor); + + if (n_triangles > 0) + { + ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.triangle_indices_VBO_id); + ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)); + } + if (n_quads > 0) + { + ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.quad_indices_VBO_id); + ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)); + } + + ::glPopMatrix(); +} + +void GLVolume::render_legacy() const +{ + assert(!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); + if (!is_active) + return; + + GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first)); + GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first)); + if (n_triangles + n_quads == 0) + { + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + + ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + render(); + + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + + return; + } + + ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3); + ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data()); + + ::glPushMatrix(); + ::glTranslated(m_origin.x, m_origin.y, m_origin.z); + ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f); + ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor); + + if (n_triangles > 0) + ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first); + + if (n_quads > 0) + ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, indexed_vertex_array.quad_indices.data() + qverts_range.first); + + ::glPopMatrix(); +} + double GLVolume::layer_height_texture_z_to_row_id() const { return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max.z); @@ -399,7 +605,6 @@ std::vector<int> GLVolumeCollection::load_object( 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); @@ -434,13 +639,15 @@ std::vector<int> GLVolumeCollection::load_object( } v.is_modifier = model_volume->modifier; v.outside_printer_detection_enabled = !model_volume->modifier; + v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0)); + v.set_angle_z(instance->rotation); + v.set_scale_factor(instance->scaling_factor); } } return volumes_idx; } - int GLVolumeCollection::load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs) { @@ -461,7 +668,8 @@ int GLVolumeCollection::load_wipe_tower_preview( else v.indexed_vertex_array.load_mesh_flat_shading(mesh); - v.origin = Pointf3(pos_x, pos_y, 0.); + v.set_origin(Pointf3(pos_x, pos_y, 0.)); + // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); @@ -486,102 +694,23 @@ void GLVolumeCollection::render_VBOs() const GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1; GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1; GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1; - GLint print_box_origin_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_origin") : -1; + GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1; + GLint print_box_worldmatrix_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1; - for (GLVolume *volume : this->volumes) { - if (!volume->is_active) - continue; + if (print_box_min_id != -1) + ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min); - if (!volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id) - continue; + if (print_box_max_id != -1) + ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max); + for (GLVolume *volume : this->volumes) + { if (volume->layer_height_texture_data.can_use()) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); volume->generate_layer_height_texture(volume->layer_height_texture_data.print_object, false); - volume->render_using_layer_height(); - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - continue; - } - - volume->set_render_color(); - - GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first)); - GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first)); - if (n_triangles + n_quads == 0) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - - if (color_id >= 0) - { - float color[4]; - ::memcpy((void*)color, (const void*)volume->render_color, 4 * sizeof(float)); - ::glUniform4fv(color_id, 1, (const GLfloat*)color); - } - else - ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); - - if (print_box_min_id != -1) - ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min); - - if (print_box_max_id != -1) - ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max); - - if (print_box_origin_id != -1) - { - float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f }; - ::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin); - } - - volume->render(); - - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - - continue; - } - - if (color_id >= 0) - ::glUniform4fv(color_id, 1, (const GLfloat*)volume->render_color); else - ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); - - if (print_box_min_id != -1) - ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min); - - if (print_box_max_id != -1) - ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max); - - if (print_box_origin_id != -1) - { - float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f }; - ::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin); - } + volume->set_render_color(); - ::glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); - ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); - ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); - - bool has_offset = (volume->origin.x != 0) || (volume->origin.y != 0) || (volume->origin.z != 0); - if (has_offset) { - ::glPushMatrix(); - ::glTranslated(volume->origin.x, volume->origin.y, volume->origin.z); - } - - if (n_triangles > 0) { - ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id); - ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(volume->tverts_range.first * 4)); - } - if (n_quads > 0) { - ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id); - ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(volume->qverts_range.first * 4)); - } - - if (has_offset) - ::glPopMatrix(); + volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id); } ::glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -602,43 +731,10 @@ void GLVolumeCollection::render_legacy() const glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); - for (GLVolume *volume : this->volumes) { - assert(! volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); - if (!volume->is_active) - continue; - + for (GLVolume *volume : this->volumes) + { volume->set_render_color(); - - GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first)); - GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first)); - if (n_triangles + n_quads == 0) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - - ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); - volume->render(); - - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - - continue; - } - - glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); - glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data() + 3); - glNormalPointer(GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data()); - bool has_offset = volume->origin.x != 0 || volume->origin.y != 0 || volume->origin.z != 0; - if (has_offset) { - glPushMatrix(); - glTranslated(volume->origin.x, volume->origin.y, volume->origin.z); - } - if (n_triangles > 0) - glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, volume->indexed_vertex_array.triangle_indices.data() + volume->tverts_range.first); - if (n_quads > 0) - glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, volume->indexed_vertex_array.quad_indices.data() + volume->qverts_range.first); - if (has_offset) - glPopMatrix(); + volume->render_legacy(); } glDisableClientState(GL_VERTEX_ARRAY); @@ -1486,11 +1582,12 @@ GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; unsigned int _3DScene::TextureBase::finalize() { - if (!m_data.empty()) { + if ((m_tex_id == 0) && !m_data.empty()) { // sends buffer to gpu + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); ::glGenTextures(1, &m_tex_id); - ::glBindTexture(GL_TEXTURE_2D, m_tex_id); - ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)m_data.data()); + ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_tex_id); + ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)m_data.data()); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); @@ -1949,6 +2046,11 @@ void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas) s_canvas_mgr.update_volumes_colors_by_extruder(canvas); } +void _3DScene::update_gizmos_data(wxGLCanvas* canvas) +{ + s_canvas_mgr.update_gizmos_data(canvas); +} + void _3DScene::render(wxGLCanvas* canvas) { s_canvas_mgr.render(canvas); @@ -2044,6 +2146,16 @@ void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, vo s_canvas_mgr.register_on_gizmo_scale_uniformly_callback(canvas, callback); } +void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback); +} + +void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback); +} + static inline int hex_digit_to_int(const char c) { return diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 9016f984d..f7fc75db5 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -240,7 +240,7 @@ class GLVolume { edit_band_width = 0.0f; } - bool can_use() { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); } + bool can_use() const { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); } }; public: @@ -249,44 +249,27 @@ public: static const float OUTSIDE_COLOR[4]; static const float SELECTED_OUTSIDE_COLOR[4]; - 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), - extruder_id(0), - selected(false), - is_active(true), - zoom_to_volumes(true), - outside_printer_detection_enabled(true), - is_outside(false), - hover(false), - is_modifier(false), - is_wipe_tower(false), - tverts_range(0, size_t(-1)), - qverts_range(0, size_t(-1)) - { - color[0] = r; - color[1] = g; - color[2] = b; - color[3] = a; - set_render_color(r, g, b, a); - } + GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); 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); +private: + // Offset of the volume to be rendered. + Pointf3 m_origin; + // Rotation around Z axis of the volume to be rendered. + float m_angle_z; + // Scale factor of the volume to be rendered. + float m_scale_factor; + // World matrix of the volume to be rendered. + std::vector<float> m_world_mat; + // Bounding box of this volume, in unscaled coordinates. + mutable BoundingBoxf3 m_transformed_bounding_box; + // Whether or not is needed to recalculate the world matrix. + mutable bool m_dirty; - int load_wipe_tower_preview( - int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs); +public: // 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]; // Color used to render this volume. @@ -333,10 +316,17 @@ public: // Sets render color in dependence of current state void set_render_color(); + const Pointf3& get_origin() const; + void set_origin(const Pointf3& origin); + void set_angle_z(float angle_z); + void set_scale_factor(float scale_factor); + 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; } + + const std::vector<float>& world_matrix() const; + BoundingBoxf3 transformed_bounding_box() const; bool empty() const { return this->indexed_vertex_array.empty(); } bool indexed() const { return this->indexed_vertex_array.indexed(); } @@ -344,6 +334,9 @@ public: void set_range(coordf_t low, coordf_t high); void render() const; void render_using_layer_height() const; + void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const; + void render_legacy() const; + void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); } void release_geometry() { this->indexed_vertex_array.release_geometry(); } @@ -568,6 +561,7 @@ public: static void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other); static void update_volumes_colors_by_extruder(wxGLCanvas* canvas); + static void update_gizmos_data(wxGLCanvas* canvas); static void render(wxGLCanvas* canvas); @@ -590,6 +584,8 @@ public: static void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); + static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); + static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); static std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs); static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index adea27fa4..208125a15 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,5 +1,6 @@ #include "GLCanvas3D.hpp" +#include "../../libslic3r/libslic3r.h" #include "../../slic3r/GUI/3DScene.hpp" #include "../../slic3r/GUI/GLShader.hpp" #include "../../slic3r/GUI/GUI.hpp" @@ -41,6 +42,11 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f }; static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f; static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; +static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + namespace Slic3r { namespace GUI { @@ -493,6 +499,7 @@ void GLCanvas3D::Bed::_render_prusa(float theta) const ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ::glEnable(GL_TEXTURE_2D); + ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); ::glEnableClientState(GL_VERTEX_ARRAY); ::glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -500,7 +507,6 @@ void GLCanvas3D::Bed::_render_prusa(float theta) const if (theta > 90.0f) ::glFrontFace(GL_CW); - ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); ::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id()); ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()); ::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords()); @@ -553,6 +559,7 @@ void GLCanvas3D::Bed::_render_custom() const ::glDisableClientState(GL_VERTEX_ARRAY); ::glDisable(GL_BLEND); + ::glDisable(GL_LIGHTING); } } @@ -577,7 +584,6 @@ GLCanvas3D::Axes::Axes() void GLCanvas3D::Axes::render(bool depth_test) const { - ::glDisable(GL_LIGHTING); if (depth_test) ::glEnable(GL_DEPTH_TEST); else @@ -623,7 +629,6 @@ bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons) void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) const { - ::glDisable(GL_LIGHTING); _render_plane(bb); _render_contour(); } @@ -730,6 +735,12 @@ void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const m_shader->set_uniform(name.c_str(), value); } +void GLCanvas3D::Shader::set_uniform(const std::string& name, const float* matrix) const +{ + if (m_shader != nullptr) + m_shader->set_uniform(name.c_str(), matrix); +} + const GLShader* GLCanvas3D::Shader::get_shader() const { return m_shader; @@ -963,15 +974,18 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height()); m_shader.set_uniform("z_cursor", max_z * get_cursor_z_relative(canvas)); m_shader.set_uniform("z_cursor_band_width", band_width); + // The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix + m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX); GLsizei w = (GLsizei)volume.layer_height_texture_width(); GLsizei h = (GLsizei)volume.layer_height_texture_height(); GLsizei half_w = w / 2; GLsizei half_h = h / 2; + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); ::glBindTexture(GL_TEXTURE_2D, m_z_texture_id); - ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - ::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + ::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); ::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level0()); ::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level1()); @@ -1053,7 +1067,9 @@ const Pointf3 GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MA GLCanvas3D::Mouse::Drag::Drag() : start_position_2D(Invalid_2D_Point) , start_position_3D(Invalid_3D_Point) - , volume_idx(-1) + , move_with_shift(false) + , move_volume_idx(-1) + , gizmo_volume_idx(-1) { } @@ -1083,8 +1099,9 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const return (drag.start_position_3D != Drag::Invalid_3D_Point); } -const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f; -const float GLCanvas3D::Gizmos::OverlayGapY = 10.0f; +const float GLCanvas3D::Gizmos::OverlayTexturesScale = 0.75f; +const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale; +const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale; GLCanvas3D::Gizmos::Gizmos() : m_enabled(false) @@ -1150,7 +1167,7 @@ void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Poin if (it->second == nullptr) continue; - float tex_size = (float)it->second->get_textures_size(); + float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius @@ -1176,7 +1193,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Poi if (it->second == nullptr) continue; - float tex_size = (float)it->second->get_textures_size(); + float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius @@ -1242,7 +1259,7 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const if (it->second == nullptr) continue; - float tex_size = (float)it->second->get_textures_size(); + float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius @@ -1274,14 +1291,19 @@ void GLCanvas3D::Gizmos::update(const Pointf& mouse_pos) curr->update(mouse_pos); } -void GLCanvas3D::Gizmos::update_data(float scale) +void GLCanvas3D::Gizmos::refresh() { if (!m_enabled) return; - GizmosMap::const_iterator it = m_gizmos.find(Scale); - if (it != m_gizmos.end()) - reinterpret_cast<GLGizmoScale*>(it->second)->set_scale(scale); + GLGizmoBase* curr = _get_current(); + if (curr != nullptr) + curr->refresh(); +} + +GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const +{ + return m_current; } bool GLCanvas3D::Gizmos::is_running() const @@ -1309,6 +1331,9 @@ void GLCanvas3D::Gizmos::start_dragging() void GLCanvas3D::Gizmos::stop_dragging() { m_dragging = false; + GLGizmoBase* curr = _get_current(); + if (curr != nullptr) + curr->stop_dragging(); } float GLCanvas3D::Gizmos::get_scale() const @@ -1320,6 +1345,35 @@ float GLCanvas3D::Gizmos::get_scale() const return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale*>(it->second)->get_scale() : 1.0f; } +void GLCanvas3D::Gizmos::set_scale(float scale) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(Scale); + if (it != m_gizmos.end()) + reinterpret_cast<GLGizmoScale*>(it->second)->set_scale(scale); +} + +float GLCanvas3D::Gizmos::get_angle_z() const +{ + if (!m_enabled) + return 0.0f; + + GizmosMap::const_iterator it = m_gizmos.find(Rotate); + return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoRotate*>(it->second)->get_angle_z() : 0.0f; +} + +void GLCanvas3D::Gizmos::set_angle_z(float angle_z) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(Rotate); + if (it != m_gizmos.end()) + reinterpret_cast<GLGizmoRotate*>(it->second)->set_angle_z(angle_z); +} + void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const { if (!m_enabled) @@ -1375,8 +1429,8 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const float scaled_gap_y = OverlayGapY * inv_zoom; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - float tex_size = (float)it->second->get_textures_size() * inv_zoom; - GLTexture::render_texture(it->second->get_textures_id(), top_x, top_x + tex_size, top_y - tex_size, top_y); + float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale * inv_zoom; + GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y); top_y -= (tex_size + scaled_gap_y); } } @@ -1849,6 +1903,42 @@ void GLCanvas3D::update_volumes_colors_by_extruder() m_volumes.update_colors_by_extruder(m_config); } +void GLCanvas3D::update_gizmos_data() +{ + if (!m_gizmos.is_running()) + return; + + int id = _get_first_selected_object_id(); + if ((id != -1) && (m_model != nullptr)) + { + ModelObject* model_object = m_model->objects[id]; + if (model_object != nullptr) + { + ModelInstance* model_instance = model_object->instances[0]; + if (model_instance != nullptr) + { + switch (m_gizmos.get_current_type()) + { + case Gizmos::Scale: + { + m_gizmos.set_scale(model_instance->scaling_factor); + break; + } + case Gizmos::Rotate: + { + m_gizmos.set_angle_z(model_instance->rotation); + break; + } + default: + { + break; + } + } + } + } + } +} + void GLCanvas3D::render() { if (m_canvas == nullptr) @@ -1961,6 +2051,7 @@ void GLCanvas3D::reload_scene(bool force) m_objects_volumes_idxs.push_back(load_object(*m_model, obj_idx)); } + update_gizmos_data(); update_volumes_selection(m_objects_selections); if (m_config->has("nozzle_diameter")) @@ -2512,6 +2603,18 @@ void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback) m_on_gizmo_scale_uniformly_callback.register_callback(callback); } +void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback) +{ + if (callback != nullptr) + m_on_gizmo_rotate_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_update_geometry_info_callback(void* callback) +{ + if (callback != nullptr) + m_on_update_geometry_info_callback.register_callback(callback); +} + void GLCanvas3D::bind_event_handlers() { if (m_canvas != nullptr) @@ -2729,14 +2832,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse) { + update_gizmos_data(); m_gizmos.update_on_off_state(*this, m_mouse.position); - _update_gizmos_data(); m_dirty = true; } else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse()) - { - _update_gizmos_data(); + { + update_gizmos_data(); m_gizmos.start_dragging(); + m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(); m_dirty = true; } else @@ -2761,9 +2865,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } - if (m_gizmos.is_running()) - _update_gizmos_data(); - + update_gizmos_data(); + m_gizmos.refresh(); m_dirty = true; } } @@ -2786,7 +2889,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (volume_bbox.contains(pos3d)) { // The dragging operation is initiated. - m_mouse.drag.volume_idx = volume_idx; + m_mouse.drag.move_with_shift = evt.ShiftDown(); + m_mouse.drag.move_volume_idx = volume_idx; m_mouse.drag.start_position_3D = pos3d; // Remember the shift to to the object center.The object center will later be used // to limit the object placement close to the bed. @@ -2802,7 +2906,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } } - else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1)) + else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1)) { m_mouse.dragging = true; @@ -2825,27 +2929,34 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Calculate the translation vector. Vectorf3 vector = m_mouse.drag.start_position_3D.vector_to(cur_pos); // Get the volume being dragged. - GLVolume* volume = m_volumes.volumes[m_mouse.drag.volume_idx]; + GLVolume* volume = m_volumes.volumes[m_mouse.drag.move_volume_idx]; // Get all volumes belonging to the same group, if any. std::vector<GLVolume*> volumes; - if (volume->drag_group_id == -1) + int group_id = m_mouse.drag.move_with_shift ? volume->select_group_id : volume->drag_group_id; + if (group_id == -1) volumes.push_back(volume); else { for (GLVolume* v : m_volumes.volumes) { - if ((v != nullptr) && (v->drag_group_id == volume->drag_group_id)) - volumes.push_back(v); + if (v != nullptr) + { + if ((m_mouse.drag.move_with_shift && (v->select_group_id == group_id)) || (v->drag_group_id == group_id)) + volumes.push_back(v); + } } } // Apply new temporary volume origin and ignore Z. for (GLVolume* v : volumes) { - v->origin.translate(vector.x, vector.y, 0.0); + Pointf3 origin = v->get_origin(); + origin.translate(vector.x, vector.y, 0.0); + v->set_origin(origin); } m_mouse.drag.start_position_3D = cur_pos; + m_gizmos.refresh(); m_dirty = true; } @@ -2856,7 +2967,59 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) const Pointf3& cur_pos = _mouse_to_bed_3d(pos); m_gizmos.update(Pointf(cur_pos.x, cur_pos.y)); - m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); + std::vector<GLVolume*> volumes; + if (m_mouse.drag.gizmo_volume_idx != -1) + { + GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx]; + // Get all volumes belonging to the same group, if any. + if (volume->select_group_id == -1) + volumes.push_back(volume); + else + { + for (GLVolume* v : m_volumes.volumes) + { + if ((v != nullptr) && (v->select_group_id == volume->select_group_id)) + volumes.push_back(v); + } + } + } + + switch (m_gizmos.get_current_type()) + { + case Gizmos::Scale: + { + // Apply new temporary scale factor + float scale_factor = m_gizmos.get_scale(); + for (GLVolume* v : volumes) + { + v->set_scale_factor(scale_factor); + } + break; + } + case Gizmos::Rotate: + { + // Apply new temporary angle_z + float angle_z = m_gizmos.get_angle_z(); + for (GLVolume* v : volumes) + { + v->set_angle_z(angle_z); + } + break; + } + default: + break; + } + + if (!volumes.empty()) + { + const BoundingBoxf3& bb = volumes[0]->transformed_bounding_box(); + const Pointf3& size = bb.size(); + m_on_update_geometry_info_callback.call(size.x, size.y, size.z, m_gizmos.get_scale()); + } + + if ((m_gizmos.get_current_type() != Gizmos::Rotate) && (volumes.size() > 1)) + m_gizmos.refresh(); + m_dirty = true; } else if (evt.Dragging() && !gizmos_overlay_contains_mouse) @@ -2914,19 +3077,19 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (layer_editing_object_idx != -1) m_on_model_update_callback.call(); } - else if ((m_mouse.drag.volume_idx != -1) && m_mouse.dragging) + else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging) { // get all volumes belonging to the same group, if any std::vector<int> volume_idxs; - int vol_id = m_mouse.drag.volume_idx; - int group_id = m_volumes.volumes[vol_id]->drag_group_id; + int vol_id = m_mouse.drag.move_volume_idx; + int group_id = m_mouse.drag.move_with_shift ? m_volumes.volumes[vol_id]->select_group_id : m_volumes.volumes[vol_id]->drag_group_id; if (group_id == -1) volume_idxs.push_back(vol_id); else { for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) { - if (m_volumes.volumes[i]->drag_group_id == group_id) + if ((m_mouse.drag.move_with_shift && (m_volumes.volumes[i]->select_group_id == group_id)) || (m_volumes.volumes[i]->drag_group_id == group_id)) volume_idxs.push_back(i); } } @@ -2944,10 +3107,26 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } else if (evt.LeftUp() && m_gizmos.is_dragging()) { + switch (m_gizmos.get_current_type()) + { + case Gizmos::Scale: + { + m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); + break; + } + case Gizmos::Rotate: + { + m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); + break; + } + default: + break; + } m_gizmos.stop_dragging(); } - m_mouse.drag.volume_idx = -1; + m_mouse.drag.move_volume_idx = -1; + m_mouse.drag.gizmo_volume_idx = -1; m_mouse.set_start_position_3D_as_invalid(); m_mouse.set_start_position_2D_as_invalid(); m_mouse.dragging = false; @@ -3200,6 +3379,8 @@ void GLCanvas3D::_deregister_callbacks() m_on_wipe_tower_moved_callback.deregister_callback(); m_on_enable_action_buttons_callback.deregister_callback(); m_on_gizmo_scale_uniformly_callback.deregister_callback(); + m_on_gizmo_rotate_callback.deregister_callback(); + m_on_update_geometry_info_callback.deregister_callback(); } void GLCanvas3D::_mark_volumes_for_layer_height() const @@ -3259,7 +3440,6 @@ void GLCanvas3D::_picking_pass() const if (m_multisample_allowed) ::glDisable(GL_MULTISAMPLE); - ::glDisable(GL_LIGHTING); ::glDisable(GL_BLEND); ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -3315,8 +3495,6 @@ void GLCanvas3D::_render_background() const static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f }; - ::glDisable(GL_LIGHTING); - ::glPushMatrix(); ::glLoadIdentity(); ::glMatrixMode(GL_PROJECTION); @@ -3395,6 +3573,8 @@ void GLCanvas3D::_render_objects() const if (m_picking_enabled) ::glEnable(GL_CULL_FACE); } + + ::glDisable(GL_LIGHTING); } void GLCanvas3D::_render_cutting_plane() const @@ -3459,6 +3639,7 @@ void GLCanvas3D::_render_legend_texture() const float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom; float r = l + (float)w * inv_zoom; float b = t - (float)h * inv_zoom; + GLTexture::render_texture(tex_id, l, r, b, t); ::glPopMatrix(); @@ -3503,9 +3684,7 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const { static const GLfloat INV_255 = 1.0f / 255.0f; - if (fake_colors) - ::glDisable(GL_LIGHTING); - else + if (!fake_colors) ::glEnable(GL_LIGHTING); // do not cull backfaces to show broken geometry, if any @@ -3543,6 +3722,9 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const ::glDisable(GL_BLEND); ::glEnable(GL_CULL_FACE); + + if (!fake_colors) + ::glDisable(GL_LIGHTING); } void GLCanvas3D::_render_gizmo() const @@ -3668,6 +3850,35 @@ int GLCanvas3D::_get_first_selected_object_id() const return -1; } +int GLCanvas3D::_get_first_selected_volume_id() const +{ + if (m_print != nullptr) + { + int objects_count = (int)m_print->objects.size(); + + for (const GLVolume* vol : m_volumes.volumes) + { + if ((vol != nullptr) && vol->selected) + { + int object_id = vol->select_group_id / 1000000; + // Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy. + if ((object_id < 10000) && (object_id < objects_count)) + { + int volume_id = 0; + for (int i = 0; i < object_id; ++i) + { + const PrintObject* obj = m_print->objects[i]; + const ModelObject* model = obj->model_object(); + volume_id += model->instances.size(); + } + return volume_id; + } + } + } + } + return -1; +} + static inline int hex_digit_to_int(const char c) { return @@ -4273,13 +4484,14 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs) { // Move a regular object. ModelObject* model_object = m_model->objects[obj_idx]; - model_object->instances[instance_idx]->offset.translate(volume->origin.x, volume->origin.y); + const Pointf3& origin = volume->get_origin(); + model_object->instances[instance_idx]->offset = Pointf(origin.x, origin.y); model_object->invalidate_bounding_box(); object_moved = true; } else if (obj_idx == 1000) // Move a wipe tower proxy. - wipe_tower_origin = volume->origin; + wipe_tower_origin = volume->get_origin(); } if (object_moved) @@ -4302,21 +4514,6 @@ void GLCanvas3D::_on_select(int volume_idx) m_on_select_object_callback.call(id); } -void GLCanvas3D::_update_gizmos_data() -{ - int id = _get_first_selected_object_id(); - if ((id != -1) && (m_model != nullptr)) - { - ModelObject* model_object = m_model->objects[id]; - if (model_object != nullptr) - { - ModelInstance* model_instance = model_object->instances[0]; - if (model_instance != nullptr) - m_gizmos.update_data(model_instance->scaling_factor); - } - } -} - std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors) { static const float INV_255 = 1.0f / 255.0f; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index b0706a05d..cc998226b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -225,6 +225,7 @@ public: void stop_using() const; void set_uniform(const std::string& name, float value) const; + void set_uniform(const std::string& name, const float* matrix) const; const GLShader* get_shader() const; @@ -302,7 +303,10 @@ public: Point start_position_2D; Pointf3 start_position_3D; Vectorf3 volume_center_offset; - int volume_idx; + + bool move_with_shift; + int move_volume_idx; + int gizmo_volume_idx; public: Drag(); @@ -323,6 +327,7 @@ public: class Gizmos { + static const float OverlayTexturesScale; static const float OverlayOffsetX; static const float OverlayGapY; @@ -360,14 +365,21 @@ public: bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const; bool grabber_contains_mouse() const; void update(const Pointf& mouse_pos); - void update_data(float scale); + void refresh(); + + EType get_current_type() const; bool is_running() const; + bool is_dragging() const; void start_dragging(); void stop_dragging(); float get_scale() const; + void set_scale(float scale); + + float get_angle_z() const; + void set_angle_z(float angle_z); void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const; void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const; @@ -439,6 +451,8 @@ private: PerlCallback m_on_wipe_tower_moved_callback; PerlCallback m_on_enable_action_buttons_callback; PerlCallback m_on_gizmo_scale_uniformly_callback; + PerlCallback m_on_gizmo_rotate_callback; + PerlCallback m_on_update_geometry_info_callback; public: GLCanvas3D(wxGLCanvas* canvas); @@ -507,6 +521,7 @@ public: void set_viewport_from_scene(const GLCanvas3D& other); void update_volumes_colors_by_extruder(); + void update_gizmos_data(); void render(); @@ -545,6 +560,8 @@ public: void register_on_wipe_tower_moved_callback(void* callback); void register_on_enable_action_buttons_callback(void* callback); void register_on_gizmo_scale_uniformly_callback(void* callback); + void register_on_gizmo_rotate_callback(void* callback); + void register_on_update_geometry_info_callback(void* callback); void bind_event_handlers(); void unbind_event_handlers(); @@ -605,6 +622,7 @@ private: void _stop_timer(); int _get_first_selected_object_id() const; + int _get_first_selected_volume_id() const; // generates gcode extrusion paths geometry void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors); @@ -625,8 +643,6 @@ private: void _on_move(const std::vector<int>& volume_idxs); void _on_select(int volume_idx); - void _update_gizmos_data(); - static std::vector<float> _parse_colors(const std::vector<std::string>& colors); }; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index ec4ac1606..7a68cbc81 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -464,6 +464,13 @@ void GLCanvas3DManager::update_volumes_colors_by_extruder(wxGLCanvas* canvas) it->second->update_volumes_colors_by_extruder(); } +void GLCanvas3DManager::update_gizmos_data(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->update_gizmos_data(); +} + void GLCanvas3DManager::render(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -655,6 +662,20 @@ void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* c it->second->register_on_gizmo_scale_uniformly_callback(callback); } +void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_gizmo_rotate_callback(callback); +} + +void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_update_geometry_info_callback(callback); +} + GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 9d9285601..c813fd477 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -120,6 +120,7 @@ public: void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other); void update_volumes_colors_by_extruder(wxGLCanvas* canvas); + void update_gizmos_data(wxGLCanvas* canvas); void render(wxGLCanvas* canvas) const; @@ -152,6 +153,8 @@ public: void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); + void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); + void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index d3aae33e8..47b01e8a2 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -90,9 +90,10 @@ GLGizmoBase::EState GLGizmoBase::get_state() const void GLGizmoBase::set_state(GLGizmoBase::EState state) { m_state = state; + on_set_state(); } -unsigned int GLGizmoBase::get_textures_id() const +unsigned int GLGizmoBase::get_texture_id() const { return m_textures[m_state].get_id(); } @@ -118,12 +119,22 @@ void GLGizmoBase::start_dragging() on_start_dragging(); } +void GLGizmoBase::stop_dragging() +{ + on_stop_dragging(); +} + void GLGizmoBase::update(const Pointf& mouse_pos) { if (m_hover_id != -1) on_update(mouse_pos); } +void GLGizmoBase::refresh() +{ + on_refresh(); +} + void GLGizmoBase::render(const BoundingBoxf3& box) const { on_render(box); @@ -134,13 +145,29 @@ void GLGizmoBase::render_for_picking(const BoundingBoxf3& box) const on_render_for_picking(box); } +void GLGizmoBase::on_set_state() +{ + // do nothing +} + void GLGizmoBase::on_start_dragging() { + // do nothing +} + +void GLGizmoBase::on_stop_dragging() +{ + // do nothing +} + +void GLGizmoBase::on_refresh() +{ + // do nothing } void GLGizmoBase::render_grabbers() const { - for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) + for (int i = 0; i < (int)m_grabbers.size(); ++i) { m_grabbers[i].render(m_hover_id == i); } @@ -162,9 +189,23 @@ GLGizmoRotate::GLGizmoRotate() , m_angle_z(0.0f) , m_center(Pointf(0.0, 0.0)) , m_radius(0.0f) + , m_keep_radius(false) { } +float GLGizmoRotate::get_angle_z() const +{ + return m_angle_z; +} + +void GLGizmoRotate::set_angle_z(float angle_z) +{ + if (std::abs(angle_z - 2.0f * PI) < EPSILON) + angle_z = 0.0f; + + m_angle_z = angle_z; +} + bool GLGizmoRotate::on_init() { std::string path = resources_dir() + "/icons/overlay/"; @@ -186,6 +227,11 @@ bool GLGizmoRotate::on_init() return true; } +void GLGizmoRotate::on_set_state() +{ + m_keep_radius = (m_state == On) ? false : true; +} + void GLGizmoRotate::on_update(const Pointf& mouse_pos) { Vectorf orig_dir(1.0, 0.0); @@ -194,6 +240,7 @@ void GLGizmoRotate::on_update(const Pointf& mouse_pos) if (cross(orig_dir, new_dir) < 0.0) theta = 2.0 * (coordf_t)PI - theta; + // snap if (length(m_center.vector_to(mouse_pos)) < 2.0 * (double)m_radius / 3.0) { coordf_t step = 2.0 * (coordf_t)PI / (coordf_t)SnapRegionsCount; @@ -202,18 +249,26 @@ void GLGizmoRotate::on_update(const Pointf& mouse_pos) if (theta == 2.0 * (coordf_t)PI) theta = 0.0; - + m_angle_z = (float)theta; } +void GLGizmoRotate::on_refresh() +{ + m_keep_radius = false; +} + void GLGizmoRotate::on_render(const BoundingBoxf3& box) const { - ::glDisable(GL_LIGHTING); ::glDisable(GL_DEPTH_TEST); const Pointf3& size = box.size(); m_center = box.center(); - m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y)); + if (!m_keep_radius) + { + m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y)); + m_keep_radius = true; + } ::glLineWidth(2.0f); ::glColor3fv(BaseColor); @@ -230,7 +285,6 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const { - ::glDisable(GL_LIGHTING); ::glDisable(GL_DEPTH_TEST); m_grabbers[0].color[0] = 1.0f; @@ -399,7 +453,6 @@ void GLGizmoScale::on_update(const Pointf& mouse_pos) void GLGizmoScale::on_render(const BoundingBoxf3& box) const { - ::glDisable(GL_LIGHTING); ::glDisable(GL_DEPTH_TEST); coordf_t min_x = box.min.x - (coordf_t)Offset; @@ -438,7 +491,6 @@ void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const { static const GLfloat INV_255 = 1.0f / 255.0f; - ::glDisable(GL_LIGHTING); ::glDisable(GL_DEPTH_TEST); for (unsigned int i = 0; i < 4; ++i) diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 2baec8f9b..506b3972e 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -57,22 +57,27 @@ public: EState get_state() const; void set_state(EState state); - unsigned int get_textures_id() const; + unsigned int get_texture_id() const; int get_textures_size() const; int get_hover_id() const; void set_hover_id(int id); void start_dragging(); + void stop_dragging(); void update(const Pointf& mouse_pos); + void refresh(); void render(const BoundingBoxf3& box) const; void render_for_picking(const BoundingBoxf3& box) const; protected: virtual bool on_init() = 0; + virtual void on_set_state(); virtual void on_start_dragging(); + virtual void on_stop_dragging(); virtual void on_update(const Pointf& mouse_pos) = 0; + virtual void on_refresh(); virtual void on_render(const BoundingBoxf3& box) const = 0; virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; @@ -96,13 +101,19 @@ class GLGizmoRotate : public GLGizmoBase mutable Pointf m_center; mutable float m_radius; + mutable bool m_keep_radius; public: GLGizmoRotate(); + float get_angle_z() const; + void set_angle_z(float angle_z); + protected: virtual bool on_init(); + virtual void on_set_state(); virtual void on_update(const Pointf& mouse_pos); + virtual void on_refresh(); virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; @@ -120,9 +131,9 @@ class GLGizmoScale : public GLGizmoBase static const float Offset; float m_scale; + float m_starting_scale; Pointf m_starting_drag_position; - float m_starting_scale; public: GLGizmoScale(); diff --git a/xs/src/slic3r/GUI/GLShader.cpp b/xs/src/slic3r/GUI/GLShader.cpp index 903f6c347..e2995f7c3 100644 --- a/xs/src/slic3r/GUI/GLShader.cpp +++ b/xs/src/slic3r/GUI/GLShader.cpp @@ -214,6 +214,17 @@ bool GLShader::set_uniform(const char *name, float value) const return false; } +bool GLShader::set_uniform(const char* name, const float* matrix) const +{ + int id = get_uniform_location(name); + if (id >= 0) + { + ::glUniformMatrix4fv(id, 1, GL_FALSE, (const GLfloat*)matrix); + return true; + } + return false; +} + /* # Set shader vector sub SetVector diff --git a/xs/src/slic3r/GUI/GLShader.hpp b/xs/src/slic3r/GUI/GLShader.hpp index 032640d8d..803b2f154 100644 --- a/xs/src/slic3r/GUI/GLShader.hpp +++ b/xs/src/slic3r/GUI/GLShader.hpp @@ -25,6 +25,7 @@ public: int get_uniform_location(const char *name) const; bool set_uniform(const char *name, float value) const; + bool set_uniform(const char* name, const float* matrix) const; void enable() const; void disable() const; diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/xs/src/slic3r/GUI/GLTexture.cpp index 924920bd8..2af555707 100644 --- a/xs/src/slic3r/GUI/GLTexture.cpp +++ b/xs/src/slic3r/GUI/GLTexture.cpp @@ -72,9 +72,10 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap } // sends data to gpu + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); ::glGenTextures(1, &m_id); ::glBindTexture(GL_TEXTURE_2D, m_id); - ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); if (generate_mipmaps) { // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards @@ -127,27 +128,25 @@ const std::string& GLTexture::get_source() const void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) { - ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - ::glDisable(GL_LIGHTING); ::glEnable(GL_BLEND); ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + ::glEnable(GL_TEXTURE_2D); + ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); ::glBegin(GL_QUADS); - ::glTexCoord2d(0.0f, 1.0f); ::glVertex3f(left, bottom, 0.0f); - ::glTexCoord2d(1.0f, 1.0f); ::glVertex3f(right, bottom, 0.0f); - ::glTexCoord2d(1.0f, 0.0f); ::glVertex3f(right, top, 0.0f); - ::glTexCoord2d(0.0f, 0.0f); ::glVertex3f(left, top, 0.0f); + ::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(left, bottom); + ::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(right, bottom); + ::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(right, top); + ::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(left, top); ::glEnd(); ::glBindTexture(GL_TEXTURE_2D, 0); ::glDisable(GL_TEXTURE_2D); ::glDisable(GL_BLEND); - ::glEnable(GL_LIGHTING); } void GLTexture::_generate_mipmaps(wxImage& image) @@ -182,7 +181,7 @@ void GLTexture::_generate_mipmaps(wxImage& image) data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; } - ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); } } diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 48bd30cd1..65dfc8e8e 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -56,9 +56,13 @@ int volume_idx() const; int instance_idx() const; Clone<Pointf3> origin() const - %code%{ RETVAL = THIS->origin; %}; + %code%{ RETVAL = THIS->get_origin(); %}; void translate(double x, double y, double z) - %code%{ THIS->origin.translate(x, y, z); %}; + %code%{ + Pointf3 o = THIS->get_origin(); + o.translate(x, y, z); + THIS->set_origin(o); + %}; Clone<BoundingBoxf3> bounding_box() const %code%{ RETVAL = THIS->bounding_box; %}; Clone<BoundingBoxf3> transformed_bounding_box() const; @@ -470,6 +474,12 @@ update_volumes_colors_by_extruder(canvas) _3DScene::update_volumes_colors_by_extruder((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); void +update_gizmos_data(canvas) + SV *canvas; + CODE: + _3DScene::update_gizmos_data((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + +void render(canvas) SV *canvas; CODE: @@ -604,6 +614,20 @@ register_on_gizmo_scale_uniformly_callback(canvas, callback) CODE: _3DScene::register_on_gizmo_scale_uniformly_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_gizmo_rotate_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_rotate_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_update_geometry_info_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_update_geometry_info_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + unsigned int finalize_legend_texture() CODE: |