2016-09-14 07:38:59 +00:00
# Implements pure perl packages
#
# Slic3r::GUI::3DScene::Base;
# Slic3r::GUI::3DScene;
#
# Slic3r::GUI::Plater::3D derives from Slic3r::GUI::3DScene,
2018-02-14 19:35:59 +00:00
# Slic3r::GUI::Plater::3DPreview,
2016-10-03 15:01:29 +00:00
# Slic3r::GUI::Plater::ObjectCutDialog and Slic3r::GUI::Plater::ObjectPartsPanel
2016-09-14 07:38:59 +00:00
# own $self->{canvas} of the Slic3r::GUI::3DScene type.
#
# Therefore the 3DScene supports renderng of STLs, extrusions and cutting planes,
# and camera manipulation.
2015-01-13 16:25:56 +00:00
package Slic3r::GUI :: 3 DScene:: Base ;
2013-05-16 10:01:38 +00:00
use strict ;
2013-05-17 12:14:33 +00:00
use warnings ;
2013-06-30 21:51:06 +00:00
2017-12-11 17:00:51 +00:00
use Wx qw( wxTheApp :timer :bitmap :icon :dialog ) ;
2017-07-21 14:29:40 +00:00
use Wx::Event qw( EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER ) ;
2013-05-16 10:01:38 +00:00
# must load OpenGL *before* Wx::GLCanvas
2014-12-15 14:19:42 +00:00
use OpenGL qw( :glconstants :glfunctions :glufunctions :gluconstants ) ;
2013-05-17 12:14:33 +00:00
use base qw( Wx::GLCanvas Class::Accessor ) ;
2016-09-14 07:38:59 +00:00
use Math::Trig qw( asin tan ) ;
2015-01-18 19:48:54 +00:00
use List::Util qw( reduce min max first ) ;
2017-07-21 14:29:40 +00:00
use Slic3r::Geometry qw( X Y normalize scale unscale scaled_epsilon ) ;
2017-06-08 10:23:50 +00:00
use Slic3r::Geometry::Clipper qw( offset_ex intersection_pl JT_ROUND ) ;
2013-05-16 10:01:38 +00:00
use Wx::GLCanvas qw( :all ) ;
2016-09-14 07:38:59 +00:00
use Slic3r::Geometry qw( PI ) ;
2016-05-27 21:38:45 +00:00
2016-09-14 07:38:59 +00:00
# _dirty: boolean flag indicating, that the screen has to be redrawn on EVT_IDLE.
# volumes: reference to vector of Slic3r::GUI::3DScene::Volume.
# _camera_type: 'perspective' or 'ortho'
2014-12-16 00:12:37 +00:00
__PACKAGE__ - > mk_accessors ( qw( _quat _dirty init
2014-12-13 19:41:03 +00:00
enable_picking
2014-12-13 21:18:43 +00:00
enable_moving
2017-03-28 12:18:13 +00:00
use_plain_shader
2015-02-15 23:37:36 +00:00
on_viewport_changed
2014-12-13 23:54:35 +00:00
on_hover
on_select
2014-12-13 19:41:03 +00:00
on_double_click
on_right_click
2014-12-16 00:12:37 +00:00
on_move
2017-02-09 13:56:13 +00:00
on_model_update
2014-12-15 00:28:11 +00:00
volumes
_sphi _stheta
2014-04-25 12:54:08 +00:00
cutting_plane_z
2014-07-12 15:35:17 +00:00
cut_lines_vertices
2014-12-16 00:12:37 +00:00
bed_shape
2014-07-15 19:58:03 +00:00
bed_triangles
bed_grid_lines
2017-06-08 09:02:29 +00:00
bed_polygon
2015-01-04 12:11:05 +00:00
background
2014-07-15 19:58:03 +00:00
origin
2014-12-13 23:54:35 +00:00
_mouse_pos
2014-12-16 00:12:37 +00:00
_hover_volume_idx
2016-12-12 17:02:24 +00:00
2014-12-13 21:18:43 +00:00
_drag_volume_idx
_drag_start_pos
2017-06-08 09:02:29 +00:00
_drag_volume_center_offset
2014-12-16 00:12:37 +00:00
_drag_start_xy
2014-12-22 18:34:19 +00:00
_dragged
2016-12-12 17:02:24 +00:00
_layer_height_edited
2016-09-14 07:38:59 +00:00
_camera_type
2014-12-15 00:28:11 +00:00
_camera_target
2016-09-14 07:38:59 +00:00
_camera_distance
2014-12-15 00:28:11 +00:00
_zoom
2018-01-16 13:59:06 +00:00
_legend_enabled
2018-03-09 09:40:42 +00:00
_warning_enabled
2018-02-19 10:28:56 +00:00
_apply_zoom_to_volumes_filter
2018-04-19 11:31:50 +00:00
_mouse_dragging
2018-01-16 13:59:06 +00:00
2014-04-25 12:54:08 +00:00
) ) ;
2018-01-16 13:59:06 +00:00
2015-01-13 16:25:56 +00:00
use constant TRACKBALLSIZE = > 0.8 ;
2013-08-25 00:07:51 +00:00
use constant TURNTABLE_MODE = > 1 ;
2015-01-04 12:32:00 +00:00
use constant GROUND_Z = > - 0.02 ;
2016-09-14 07:38:59 +00:00
# For mesh selection: Not selected - bright yellow.
2015-01-13 16:25:56 +00:00
use constant DEFAULT_COLOR = > [ 1 , 1 , 0 ] ;
2016-09-14 07:38:59 +00:00
# For mesh selection: Selected - bright green.
2014-01-22 20:09:32 +00:00
use constant SELECTED_COLOR = > [ 0 , 1 , 0 , 1 ] ;
2016-09-14 07:38:59 +00:00
# For mesh selection: Mouse hovers over the object, but object not selected yet - dark green.
2015-01-07 19:11:03 +00:00
use constant HOVER_COLOR = > [ 0.4 , 0.9 , 0 , 1 ] ;
2013-07-01 10:23:44 +00:00
2016-10-05 12:13:07 +00:00
# phi / theta angles to orient the camera.
2016-12-12 17:02:24 +00:00
use constant VIEW_DEFAULT = > [ 45.0 , 45.0 ] ;
2016-10-05 12:13:07 +00:00
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 ] ;
2016-12-12 17:02:24 +00:00
use constant MANIPULATION_IDLE = > 0 ;
use constant MANIPULATION_DRAGGING = > 1 ;
use constant MANIPULATION_LAYER_HEIGHT = > 2 ;
2018-03-23 14:40:26 +00:00
use constant GIMBALL_LOCK_THETA_MAX = > 180 ;
2016-11-16 08:24:27 +00:00
2017-02-09 13:56:13 +00:00
use constant VARIABLE_LAYER_THICKNESS_BAR_WIDTH = > 70 ;
use constant VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = > 22 ;
2014-05-02 11:13:13 +00:00
# make OpenGL::Array thread-safe
2014-07-03 07:24:19 +00:00
{
no warnings 'redefine' ;
* OpenGL::Array:: CLONE_SKIP = sub { 1 } ;
}
2014-05-02 11:13:13 +00:00
2013-05-16 10:01:38 +00:00
sub new {
2014-07-13 10:10:34 +00:00
my ( $ class , $ parent ) = @ _ ;
2014-11-08 13:37:37 +00:00
2016-05-27 21:38:45 +00:00
# We can only enable multi sample anti aliasing wih wxWidgets 3.0.3 and with a hacked Wx::GLCanvas,
# which exports some new WX_GL_XXX constants, namely WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES.
my $ can_multisample =
2017-12-12 19:47:36 +00:00
! wxTheApp - > { app_config } - > get ( 'use_legacy_opengl' ) &&
2016-05-27 21:38:45 +00:00
Wx:: wxVERSION >= 3.000003 &&
defined Wx::GLCanvas - > can ( 'WX_GL_SAMPLE_BUFFERS' ) &&
defined Wx::GLCanvas - > can ( 'WX_GL_SAMPLES' ) ;
my $ attrib = [ WX_GL_RGBA , WX_GL_DOUBLEBUFFER , WX_GL_DEPTH_SIZE , 24 ] ;
if ( $ can_multisample ) {
# Request a window with multi sampled anti aliasing. This is a new feature in Wx 3.0.3 (backported from 3.1.0).
# Use eval to avoid compilation, if the subs WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES are missing.
eval 'push(@$attrib, (WX_GL_SAMPLE_BUFFERS, 1, WX_GL_SAMPLES, 4));' ;
}
# wxWidgets expect the attrib list to be ended by zero.
push ( @$ attrib , 0 ) ;
2014-11-08 13:37:37 +00:00
# we request a depth buffer explicitely because it looks like it's not created by
# default on Linux, causing transparency issues
2016-05-27 21:38:45 +00:00
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 ( ) ;
}
2017-09-12 08:03:10 +00:00
$ self - > { can_multisample } = $ can_multisample ;
2015-01-04 12:11:05 +00:00
$ self - > background ( 1 ) ;
2014-12-15 00:28:11 +00:00
$ self - > _quat ( ( 0 , 0 , 0 , 1 ) ) ;
$ self - > _stheta ( 45 ) ;
$ self - > _sphi ( 45 ) ;
$ self - > _zoom ( 1 ) ;
2018-01-16 13:59:06 +00:00
$ self - > _legend_enabled ( 0 ) ;
2018-03-09 09:40:42 +00:00
$ self - > _warning_enabled ( 0 ) ;
2017-03-28 12:18:13 +00:00
$ self - > use_plain_shader ( 0 ) ;
2018-02-19 10:28:56 +00:00
$ self - > _apply_zoom_to_volumes_filter ( 0 ) ;
2018-04-19 11:31:50 +00:00
$ self - > _mouse_dragging ( 0 ) ;
2017-03-13 15:02:17 +00:00
# Collection of GLVolume objects
$ self - > volumes ( Slic3r::GUI::_3DScene::GLVolume::Collection - > new ) ;
2014-12-16 00:12:37 +00:00
# 3D point in model space
2016-09-14 07:38:59 +00:00
$ self - > _camera_type ( 'ortho' ) ;
# $self->_camera_type('perspective');
2014-12-16 00:12:37 +00:00
$ self - > _camera_target ( Slic3r::Pointf3 - > new ( 0 , 0 , 0 ) ) ;
2016-09-14 07:38:59 +00:00
$ self - > _camera_distance ( 0 . ) ;
2016-12-12 17:02:24 +00:00
2017-02-03 14:53:31 +00:00
$ self - > layer_editing_enabled ( 0 ) ;
2016-12-12 17:02:24 +00:00
$ self - > { layer_height_edit_band_width } = 2 . ;
2016-12-21 15:00:41 +00:00
$ 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 ;
2017-03-14 09:11:08 +00:00
2014-07-13 10:10:34 +00:00
$ self - > reset_objects ;
2013-05-17 12:14:33 +00:00
EVT_PAINT ( $ self , sub {
my $ dc = Wx::PaintDC - > new ( $ self ) ;
$ self - > Render ( $ dc ) ;
} ) ;
2014-12-15 00:28:11 +00:00
EVT_SIZE ( $ self , sub { $ self - > _dirty ( 1 ) } ) ;
2013-05-17 12:14:33 +00:00
EVT_IDLE ( $ self , sub {
2014-12-15 00:28:11 +00:00
return unless $ self - > _dirty ;
2013-05-17 12:14:33 +00:00
return if ! $ self - > IsShownOnScreen ;
$ self - > Resize ( $ self - > GetSizeWH ) ;
$ self - > Refresh ;
} ) ;
2016-12-21 15:00:41 +00:00
EVT_MOUSEWHEEL ( $ self , \ & mouse_wheel_event ) ;
2014-12-15 00:28:11 +00:00
EVT_MOUSE_EVENTS ( $ self , \ & mouse_event ) ;
2017-07-21 14:29:40 +00:00
# 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' ) ;
2018-04-23 06:44:24 +00:00
} elsif ( $ key == ord ( 'z' ) ) {
$ self - > zoom_to_volumes ;
} elsif ( $ key == ord ( 'b' ) ) {
$ self - > zoom_to_bed ;
2017-07-21 14:29:40 +00:00
} else {
$ event - > Skip ;
}
}
} ) ;
2014-12-15 00:28:11 +00:00
2016-12-21 15:00:41 +00:00
$ 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 ) = @ _ ;
2017-02-09 13:56:13 +00:00
return if $ self - > _layer_height_edited != 1 ;
2016-12-21 15:00:41 +00:00
return if $ self - > { layer_height_edit_last_object_id } == - 1 ;
2017-02-07 17:17:12 +00:00
$ self - > _variable_layer_thickness_action ( undef ) ;
2016-12-21 15:00:41 +00:00
} ) ;
2014-12-15 00:28:11 +00:00
return $ self ;
}
2018-01-16 13:59:06 +00:00
sub set_legend_enabled {
my ( $ self , $ value ) = @ _ ;
2018-03-09 09:40:42 +00:00
$ self - > _legend_enabled ( $ value ) ;
}
sub set_warning_enabled {
my ( $ self , $ value ) = @ _ ;
$ self - > _warning_enabled ( $ value ) ;
2018-01-16 13:59:06 +00:00
}
2016-12-14 14:36:18 +00:00
sub Destroy {
my ( $ self ) = @ _ ;
2016-12-21 15:00:41 +00:00
$ self - > { layer_height_edit_timer } - > Stop ;
2016-12-14 14:36:18 +00:00
$ self - > DestroyGL ;
return $ self - > SUPER:: Destroy ;
}
2017-02-17 14:11:59 +00:00
sub layer_editing_enabled {
my ( $ self , $ value ) = @ _ ;
if ( @ _ == 2 ) {
$ self - > { layer_editing_enabled } = $ value ;
2017-02-19 17:01:03 +00:00
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.
2017-03-20 14:55:22 +00:00
$ self - > SetCurrent ( $ self - > GetContext ) ;
2017-03-20 11:05:20 +00:00
my $ shader = new Slic3r::GUI::_3DScene:: GLShader ;
2017-02-19 17:01:03 +00:00
my $ error_message ;
2017-03-20 11:05:20 +00:00
if ( ! $ shader - > load ( $ 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 ;
2017-02-19 17:01:03 +00:00
} else {
2017-03-20 11:05:20 +00:00
$ 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 ) ;
2017-02-19 17:01:03 +00:00
}
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 ;
}
2017-02-17 14:11:59 +00:00
}
}
return $ self - > { layer_editing_enabled } ;
}
2017-02-19 17:01:03 +00:00
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 ) ;
}
2017-05-17 14:53:40 +00:00
sub _first_selected_object_id_for_variable_layer_height_editing {
2016-12-12 17:02:24 +00:00
my ( $ self ) = @ _ ;
for my $ i ( 0 .. $# { $ self - > volumes } ) {
if ( $ self - > volumes - > [ $ i ] - > selected ) {
2017-05-17 14:53:40 +00:00
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.
2017-06-13 11:42:38 +00:00
return ( $ object_id >= $ self - > { print } - > object_count ) ? - 1 : $ object_id
if $ object_id < 10000 ;
2016-12-12 17:02:24 +00:00
}
}
return - 1 ;
}
2016-12-21 15:00:41 +00:00
# Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen.
2017-02-09 13:56:13 +00:00
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 {
2016-12-21 15:00:41 +00:00
my ( $ self ) = @ _ ;
my ( $ cw , $ ch ) = $ self - > GetSizeWH ;
2017-02-09 13:56:13 +00:00
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 ) ;
2016-12-21 15:00:41 +00:00
}
sub _variable_layer_thickness_bar_rect_mouse_inside {
my ( $ self , $ mouse_evt ) = @ _ ;
2017-02-09 13:56:13 +00:00
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 ;
2016-12-21 15:00:41 +00:00
return $ mouse_evt - > GetX >= $ bar_left && $ mouse_evt - > GetX <= $ bar_right && $ mouse_evt - > GetY >= $ bar_top && $ mouse_evt - > GetY <= $ bar_bottom ;
}
2017-02-09 13:56:13 +00:00
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 . ;
}
2016-12-21 15:00:41 +00:00
sub _variable_layer_thickness_action {
my ( $ self , $ mouse_event , $ do_modification ) = @ _ ;
# A volume is selected. Test, whether hovering over a layer thickness bar.
2017-06-13 11:42:38 +00:00
return if $ self - > { layer_height_edit_last_object_id } == - 1 ;
2016-12-21 15:00:41 +00:00
if ( defined ( $ mouse_event ) ) {
2017-06-13 11:42:38 +00:00
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 ) ;
2016-12-21 15:00:41 +00:00
$ self - > { layer_height_edit_last_action } = $ mouse_event - > ShiftDown ? ( $ mouse_event - > RightIsDown ? 3 : 2 ) : ( $ mouse_event - > RightIsDown ? 0 : 1 ) ;
}
2017-06-13 11:42:38 +00:00
# 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 ) ;
2016-12-21 15:00:41 +00:00
}
2014-12-15 00:28:11 +00:00
sub mouse_event {
my ( $ self , $ e ) = @ _ ;
my $ pos = Slic3r::Pointf - > new ( $ e - > GetPositionXY ) ;
2017-05-17 14:53:40 +00:00
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 ;
2016-12-21 15:00:41 +00:00
2018-04-19 11:31:50 +00:00
$ self - > _mouse_dragging ( $ e - > Dragging ) ;
2018-04-27 12:08:22 +00:00
if ( $ e - > Entering && ( & Wx:: wxMSW || $^O eq 'linux' ) ) {
2014-12-25 18:51:53 +00:00
# wxMSW needs focus in order to catch mouse wheel events
$ self - > SetFocus ;
2017-06-08 10:01:35 +00:00
$ self - > _drag_start_xy ( undef ) ;
2014-12-25 18:51:53 +00:00
} elsif ( $ e - > LeftDClick ) {
2016-12-21 15:00:41 +00:00
if ( $ object_idx_selected != - 1 && $ self - > _variable_layer_thickness_bar_rect_mouse_inside ( $ e ) ) {
} elsif ( $ self - > on_double_click ) {
$ self - > on_double_click - > ( ) ;
}
2014-12-15 00:28:11 +00:00
} elsif ( $ e - > LeftDown || $ e - > RightDown ) {
# If user pressed left or right button we first check whether this happened
# on a volume or not.
2014-12-16 00:12:37 +00:00
my $ volume_idx = $ self - > _hover_volume_idx // - 1 ;
2016-12-12 17:02:24 +00:00
$ self - > _layer_height_edited ( 0 ) ;
2016-12-21 15:00:41 +00:00
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 ) ;
2017-02-07 17:17:12 +00:00
$ self - > _variable_layer_thickness_action ( $ e ) ;
2017-02-09 13:56:13 +00:00
} elsif ( $ object_idx_selected != - 1 && $ self - > _variable_layer_thickness_reset_rect_mouse_inside ( $ e ) ) {
2017-06-13 11:42:38 +00:00
$ self - > { print } - > get_object ( $ object_idx_selected ) - > reset_layer_height_profile ;
2017-02-09 13:56:13 +00:00
# Index 2 means no editing, just wait for mouse up event.
$ self - > _layer_height_edited ( 2 ) ;
$ self - > Refresh ;
2017-03-20 11:05:20 +00:00
$ self - > Update ;
2016-12-21 15:00:41 +00:00
} else {
2018-03-06 13:45:03 +00:00
# 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 ) ;
2016-12-12 17:02:24 +00:00
# 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 ;
2017-03-20 11:05:20 +00:00
$ self - > Update ;
2016-12-12 17:02:24 +00:00
}
# propagate event through callback
$ self - > on_select - > ( $ volume_idx )
if $ self - > on_select ;
if ( $ volume_idx != - 1 ) {
if ( $ e - > LeftDown && $ self - > enable_moving ) {
2017-06-08 09:02:29 +00:00
# Only accept the initial position, if it is inside the volume bounding box.
my $ volume_bbox = $ self - > volumes - > [ $ volume_idx ] - > transformed_bounding_box ;
2017-06-08 18:50:24 +00:00
$ volume_bbox - > offset ( 1 . ) ;
2017-06-08 09:02:29 +00:00
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 ) ) ;
}
2016-12-12 17:02:24 +00:00
} elsif ( $ e - > RightDown ) {
# if right clicking on volume, propagate event through callback
$ self - > on_right_click - > ( $ e - > GetPosition )
if $ self - > on_right_click ;
}
2014-12-13 19:41:03 +00:00
}
2014-12-15 00:28:11 +00:00
}
2016-12-12 17:02:24 +00:00
} elsif ( $ e - > Dragging && $ e - > LeftIsDown && ! $ self - > _layer_height_edited && defined ( $ self - > _drag_volume_idx ) ) {
2017-06-08 09:02:29 +00:00
# Get new position at the same Z of the initial click point.
2017-06-08 10:01:35 +00:00
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 ) ;
2017-06-08 09:02:29 +00:00
# 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.
2014-12-15 00:28:11 +00:00
my $ vector = $ self - > _drag_start_pos - > vector_to ( $ cur_pos ) ;
2017-06-08 09:02:29 +00:00
# Get the volume being dragged.
2014-12-30 13:21:10 +00:00
my $ volume = $ self - > volumes - > [ $ self - > _drag_volume_idx ] ;
2017-06-08 09:02:29 +00:00
# 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 ;
2014-12-16 00:12:37 +00:00
$ self - > _drag_start_pos ( $ cur_pos ) ;
2014-12-22 18:34:19 +00:00
$ self - > _dragged ( 1 ) ;
2014-12-15 00:28:11 +00:00
$ self - > Refresh ;
2017-03-20 11:05:20 +00:00
$ self - > Update ;
2015-01-07 10:13:56 +00:00
} elsif ( $ e - > Dragging ) {
2016-12-21 15:00:41 +00:00
if ( $ self - > _layer_height_edited && $ object_idx_selected != - 1 ) {
2017-02-09 13:56:13 +00:00
$ self - > _variable_layer_thickness_action ( $ e ) if ( $ self - > _layer_height_edited == 1 ) ;
2016-12-12 17:02:24 +00:00
} elsif ( $ e - > LeftIsDown ) {
2014-12-15 00:28:11 +00:00
# if dragging over blank area with left button, rotate
if ( defined $ self - > _drag_start_pos ) {
my $ orig = $ self - > _drag_start_pos ;
if ( TURNTABLE_MODE ) {
2017-06-08 09:02:29 +00:00
# Turntable mode is enabled by default.
2014-12-15 00:28:11 +00:00
$ self - > _sphi ( $ self - > _sphi + ( $ pos - > x - $ orig - > x ) * TRACKBALLSIZE ) ;
$ self - > _stheta ( $ self - > _stheta - ( $ pos - > y - $ orig - > y ) * TRACKBALLSIZE ) ; #-
2016-12-12 17:02:24 +00:00
$ self - > _stheta ( GIMBALL_LOCK_THETA_MAX ) if $ self - > _stheta > GIMBALL_LOCK_THETA_MAX ;
2014-12-16 00:12:37 +00:00
$ self - > _stheta ( 0 ) if $ self - > _stheta < 0 ;
2014-12-15 00:28:11 +00:00
} 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 ) ) ;
}
2015-02-15 23:37:36 +00:00
$ self - > on_viewport_changed - > ( ) if $ self - > on_viewport_changed ;
2014-12-15 00:28:11 +00:00
$ self - > Refresh ;
2017-03-20 11:05:20 +00:00
$ self - > Update ;
2014-12-13 19:41:03 +00:00
}
2014-12-15 00:28:11 +00:00
$ self - > _drag_start_pos ( $ pos ) ;
2014-12-25 19:08:47 +00:00
} elsif ( $ e - > MiddleIsDown || $ e - > RightIsDown ) {
2017-06-08 10:01:35 +00:00
# If dragging over blank area with right button, pan.
2014-12-16 00:12:37 +00:00
if ( defined $ self - > _drag_start_xy ) {
# get point in model space at Z = 0
2017-06-08 10:01:35 +00:00
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 } ) ;
2015-02-15 23:37:36 +00:00
$ self - > on_viewport_changed - > ( ) if $ self - > on_viewport_changed ;
2014-12-15 00:28:11 +00:00
$ self - > Refresh ;
2017-03-20 11:05:20 +00:00
$ self - > Update ;
2014-12-13 21:18:43 +00:00
}
2014-12-16 00:12:37 +00:00
$ self - > _drag_start_xy ( $ pos ) ;
2013-07-01 08:58:30 +00:00
}
2015-01-07 10:58:22 +00:00
} elsif ( $ e - > LeftUp || $ e - > MiddleUp || $ e - > RightUp ) {
2017-02-09 13:56:13 +00:00
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 ) {
2015-01-13 17:41:23 +00:00
# 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 ) ;
2014-12-15 00:28:11 +00:00
}
$ self - > _drag_volume_idx ( undef ) ;
$ self - > _drag_start_pos ( undef ) ;
2014-12-16 00:12:37 +00:00
$ self - > _drag_start_xy ( undef ) ;
2014-12-22 18:34:19 +00:00
$ self - > _dragged ( undef ) ;
2014-12-15 00:28:11 +00:00
} elsif ( $ e - > Moving ) {
$ self - > _mouse_pos ( $ pos ) ;
2016-12-20 11:19:13 +00:00
# Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor
# hovers over.
2017-03-20 11:05:20 +00:00
if ( $ self - > enable_picking ) {
$ self - > Update ;
$ self - > Refresh ;
}
2014-12-15 00:28:11 +00:00
} else {
$ e - > Skip ( ) ;
}
2013-05-16 10:01:38 +00:00
}
2013-06-29 12:16:56 +00:00
2016-12-21 15:00:41 +00:00
sub mouse_wheel_event {
my ( $ self , $ e ) = @ _ ;
2017-06-09 13:02:11 +00:00
if ( $ e - > MiddleIsDown ) {
# Ignore the wheel events if the middle button is pressed.
return ;
}
2016-12-21 15:00:41 +00:00
if ( $ self - > layer_editing_enabled && $ self - > { print } ) {
2017-05-17 14:53:40 +00:00
my $ object_idx_selected = $ self - > _first_selected_object_id_for_variable_layer_height_editing ;
2016-12-21 15:00:41 +00:00
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 ;
2017-06-09 13:02:11 +00:00
$ zoom = $ self - > _zoom / ( 1 - $ zoom ) ;
# Don't allow to zoom too far outside the scene.
2017-06-13 09:35:24 +00:00
my $ zoom_min = $ self - > get_zoom_to_bounding_box_factor ( $ self - > max_bounding_box ) ;
$ zoom_min *= 0.4 if defined $ zoom_min ;
2017-06-09 13:02:11 +00:00
$ zoom = $ zoom_min if defined $ zoom_min && $ zoom < $ zoom_min ;
$ self - > _zoom ( $ zoom ) ;
2016-12-21 15:00:41 +00:00
2018-04-23 06:44:24 +00:00
# # 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;
2016-12-21 15:00:41 +00:00
$ self - > on_viewport_changed - > ( ) if $ self - > on_viewport_changed ;
2017-02-09 13:56:13 +00:00
$ self - > Resize ( $ self - > GetSizeWH ) if $ self - > IsShownOnScreen ;
2016-12-21 15:00:41 +00:00
$ self - > Refresh ;
}
2016-09-14 07:38:59 +00:00
# Reset selection.
2014-07-13 10:10:34 +00:00
sub reset_objects {
my ( $ self ) = @ _ ;
2017-03-20 11:05:20 +00:00
if ( $ self - > GetContext ) {
$ self - > SetCurrent ( $ self - > GetContext ) ;
2017-03-16 13:02:28 +00:00
$ self - > volumes - > release_geometry ;
}
2017-03-13 15:02:17 +00:00
$ self - > volumes - > erase ;
2014-12-15 00:28:11 +00:00
$ self - > _dirty ( 1 ) ;
2014-07-13 10:10:34 +00:00
}
2016-09-14 07:38:59 +00:00
# Setup camera to view all objects.
2015-02-15 23:37:36 +00:00
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 ) ;
}
2016-10-05 12:13:07 +00:00
# Set the camera to a default orientation,
# zoom to volumes.
sub select_view {
my ( $ self , $ direction ) = @ _ ;
my $ dirvec ;
if ( ref ( $ direction ) ) {
$ dirvec = $ direction ;
} else {
2017-02-09 20:40:07 +00:00
if ( $ direction eq 'iso' ) {
2016-12-12 17:02:24 +00:00
$ dirvec = VIEW_DEFAULT ;
2016-10-05 12:13:07 +00:00
} 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 ;
}
}
2016-12-12 17:02:24 +00:00
my $ bb = $ self - > volumes_bounding_box ;
if ( ! $ bb - > empty ) {
$ self - > _sphi ( $ dirvec - > [ 0 ] ) ;
$ self - > _stheta ( $ dirvec - > [ 1 ] ) ;
# Avoid gimball lock.
$ self - > _stheta ( GIMBALL_LOCK_THETA_MAX ) if $ self - > _stheta > GIMBALL_LOCK_THETA_MAX ;
$ self - > _stheta ( 0 ) if $ self - > _stheta < 0 ;
# View everything.
$ self - > zoom_to_bounding_box ( $ bb ) ;
$ self - > on_viewport_changed - > ( ) if $ self - > on_viewport_changed ;
$ self - > Refresh ;
}
2016-10-05 12:13:07 +00:00
}
2017-06-09 13:02:11 +00:00
sub get_zoom_to_bounding_box_factor {
2018-04-26 10:14:49 +00:00
my ( $ self , $ bb ) = @ _ ;
2018-04-23 06:44:24 +00:00
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 ) ) ;
}
2018-04-26 10:14:49 +00:00
return undef if ( ( $ max_x == 0 ) || ( $ max_y == 0 ) ) ;
2018-04-23 06:44:24 +00:00
my ( $ cw , $ ch ) = $ self - > GetSizeWH ;
my $ min_ratio = min ( $ cw / $max_x, $ch / $ max_y ) ;
return $ min_ratio ;
2017-06-09 13:02:11 +00:00
}
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 ;
2018-04-23 06:44:24 +00:00
$ self - > Resize ( $ self - > GetSizeWH ) if $ self - > IsShownOnScreen ;
$ self - > Refresh ;
2017-06-09 13:02:11 +00:00
}
2014-12-16 00:12:37 +00:00
}
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 ] ;
2015-01-09 00:18:47 +00:00
my $ bb = $ volume - > transformed_bounding_box ;
2014-12-16 00:12:37 +00:00
$ self - > zoom_to_bounding_box ( $ bb ) ;
}
sub zoom_to_volumes {
my ( $ self ) = @ _ ;
2018-02-19 10:28:56 +00:00
$ self - > _apply_zoom_to_volumes_filter ( 1 ) ;
2014-12-16 00:12:37 +00:00
$ self - > zoom_to_bounding_box ( $ self - > volumes_bounding_box ) ;
2018-02-19 10:28:56 +00:00
$ self - > _apply_zoom_to_volumes_filter ( 0 ) ;
2014-12-16 00:12:37 +00:00
}
sub volumes_bounding_box {
my ( $ self ) = @ _ ;
my $ bb = Slic3r::Geometry::BoundingBoxf3 - > new ;
2018-02-19 10:28:56 +00:00
foreach my $ v ( @ { $ self - > volumes } ) {
$ bb - > merge ( $ v - > transformed_bounding_box ) if ( ! $ self - > _apply_zoom_to_volumes_filter || $ v - > zoom_to_volumes ) ;
}
2014-12-16 00:12:37 +00:00
return $ bb ;
}
sub bed_bounding_box {
my ( $ self ) = @ _ ;
my $ bb = Slic3r::Geometry::BoundingBoxf3 - > new ;
2015-12-19 22:15:37 +00:00
if ( $ self - > bed_shape ) {
$ bb - > merge_point ( Slic3r::Pointf3 - > new ( @$ _ , 0 ) ) for @ { $ self - > bed_shape } ;
}
2014-12-16 00:12:37 +00:00
return $ bb ;
}
sub max_bounding_box {
my ( $ self ) = @ _ ;
my $ bb = $ self - > bed_bounding_box ;
$ bb - > merge ( $ self - > volumes_bounding_box ) ;
return $ bb ;
2014-07-13 10:10:34 +00:00
}
2017-06-08 09:02:29 +00:00
# Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane
# to support the scene objects.
2014-07-15 19:58:03 +00:00
sub set_auto_bed_shape {
my ( $ self , $ bed_shape ) = @ _ ;
# draw a default square bed around object center
2014-12-16 00:12:37 +00:00
my $ max_size = max ( @ { $ self - > volumes_bounding_box - > size } ) ;
my $ center = $ self - > volumes_bounding_box - > center ;
2014-07-15 19:58:03 +00:00
$ 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 ] , #++
] ) ;
2017-06-08 09:02:29 +00:00
# Set the origin for painting of the coordinate system axes.
2014-07-15 19:58:03 +00:00
$ self - > origin ( Slic3r::Pointf - > new ( @$ center [ X , Y ] ) ) ;
}
2017-06-08 09:02:29 +00:00
# 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.
2014-07-15 19:58:03 +00:00
sub set_bed_shape {
my ( $ self , $ bed_shape ) = @ _ ;
2014-12-16 00:12:37 +00:00
$ self - > bed_shape ( $ bed_shape ) ;
2014-07-15 19:58:03 +00:00
# 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 } ) {
2017-06-08 09:02:29 +00:00
push @ points , map { + unscale ( $ _ - > x ) , unscale ( $ _ - > y ) , GROUND_Z } @$ triangle ;
2014-07-15 19:58:03 +00:00
}
$ self - > bed_triangles ( OpenGL::Array - > new_list ( GL_FLOAT , @ points ) ) ;
}
{
2015-02-22 16:09:08 +00:00
my @ polylines = ( ) ;
2014-07-15 19:58:03 +00:00
for ( my $ x = $ bed_bb - > x_min ; $ x <= $ bed_bb - > x_max ; $ x += scale 10 ) {
2015-02-22 16:09:08 +00:00
push @ polylines , Slic3r::Polyline - > new ( [ $ x , $ bed_bb - > y_min ] , [ $ x , $ bed_bb - > y_max ] ) ;
2014-07-15 19:58:03 +00:00
}
for ( my $ y = $ bed_bb - > y_min ; $ y <= $ bed_bb - > y_max ; $ y += scale 10 ) {
2015-02-22 16:09:08 +00:00
push @ polylines , Slic3r::Polyline - > new ( [ $ bed_bb - > x_min , $ y ] , [ $ bed_bb - > x_max , $ y ] ) ;
2014-07-15 19:58:03 +00:00
}
2015-01-04 12:17:20 +00:00
# clip with a slightly grown expolygon because our lines lay on the contours and
# may get erroneously clipped
2015-02-22 16:09:08 +00:00
my @ lines = map Slic3r::Line - > new ( @$ _ [ 0 , - 1 ] ) ,
@ { intersection_pl ( \ @ polylines , [ @ { $ expolygon - > offset ( + scaled_epsilon ) } ] ) } ;
2015-01-04 12:36:14 +00:00
# append bed contours
2015-02-22 16:09:08 +00:00
push @ lines , map @ { $ _ - > lines } , @$ expolygon ;
2015-01-04 12:36:14 +00:00
2014-07-15 19:58:03 +00:00
my @ points = ( ) ;
2015-02-22 16:09:08 +00:00
foreach my $ line ( @ lines ) {
push @ points , map { + unscale ( $ _ - > x ) , unscale ( $ _ - > y ) , GROUND_Z } @$ line ; #))
2014-07-15 19:58:03 +00:00
}
$ self - > bed_grid_lines ( OpenGL::Array - > new_list ( GL_FLOAT , @ points ) ) ;
}
2017-06-08 09:02:29 +00:00
# Set the origin for painting of the coordinate system axes.
2014-07-15 19:58:03 +00:00
$ self - > origin ( Slic3r::Pointf - > new ( 0 , 0 ) ) ;
2017-06-08 09:02:29 +00:00
2017-06-08 10:23:50 +00:00
$ self - > bed_polygon ( offset_ex ( [ $ expolygon - > contour ] , $ bed_bb - > radius * 1.7 , JT_ROUND , scale ( 0.5 ) ) - > [ 0 ] - > contour - > clone ) ;
2014-07-15 19:58:03 +00:00
}
2014-12-13 23:54:35 +00:00
sub deselect_volumes {
my ( $ self ) = @ _ ;
2017-03-13 15:02:17 +00:00
$ _ - > set_selected ( 0 ) for @ { $ self - > volumes } ;
2014-12-13 23:54:35 +00:00
}
2014-12-13 19:41:03 +00:00
sub select_volume {
my ( $ self , $ volume_idx ) = @ _ ;
2018-05-03 14:08:41 +00:00
return if ( $ volume_idx >= scalar ( @ { $ self - > volumes } ) ) ;
2017-03-13 15:02:17 +00:00
$ self - > volumes - > [ $ volume_idx ] - > set_selected ( 1 )
2014-12-13 19:41:03 +00:00
if $ volume_idx != - 1 ;
2014-01-18 17:43:55 +00:00
}
2014-04-25 12:54:08 +00:00
sub SetCuttingPlane {
2015-12-19 22:15:37 +00:00
my ( $ self , $ z , $ expolygons ) = @ _ ;
2014-07-12 15:35:17 +00:00
2014-04-25 12:54:08 +00:00
$ self - > cutting_plane_z ( $ z ) ;
2014-07-12 15:35:17 +00:00
2015-12-19 22:15:37 +00:00
# grow slices in order to display them better
$ expolygons = offset_ex ( [ map @$ _ , @$ expolygons ] , scale 0.1 ) ;
2014-07-12 15:35:17 +00:00
my @ verts = ( ) ;
2015-12-19 22:15:37 +00:00
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 , #))
) ;
2014-07-12 15:35:17 +00:00
}
$ self - > cut_lines_vertices ( OpenGL::Array - > new_list ( GL_FLOAT , @ verts ) ) ;
2014-04-25 12:54:08 +00:00
}
2013-07-01 10:23:44 +00:00
# Given an axis and angle, compute quaternion.
2013-06-30 21:51:06 +00:00
sub axis_to_quat {
2013-07-01 08:58:30 +00:00
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 ;
2013-06-30 21:51:06 +00:00
}
2013-07-01 10:23:44 +00:00
# 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.
2013-06-30 21:51:06 +00:00
sub project_to_sphere {
2013-07-01 08:58:30 +00:00
my ( $ r , $ x , $ y ) = @ _ ;
my $ d = sqrt ( $ x * $ x + $ y * $ y ) ;
2013-07-01 10:23:44 +00:00
if ( $ d < $ r * 0.70710678118654752440 ) { # Inside sphere
2013-06-30 21:51:06 +00:00
return sqrt ( $ r * $ r - $ d * $ d ) ;
2013-07-01 10:23:44 +00:00
} else { # On hyperbola
2013-07-01 08:58:30 +00:00
my $ t = $ r / 1.41421356237309504880 ;
return $ t * $ t / $ d ;
}
2013-06-30 21:51:06 +00:00
}
sub cross {
2013-07-01 08:58:30 +00:00
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 ] ) ;
2013-06-30 21:51:06 +00:00
}
2013-07-01 10:23:44 +00:00
# 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).
2013-06-29 16:31:06 +00:00
sub trackball {
2013-07-01 10:23:44 +00:00
my ( $ p1x , $ p1y , $ p2x , $ p2y ) = @ _ ;
2013-06-30 21:51:06 +00:00
if ( $ p1x == $ p2x && $ p1y == $ p2y ) {
2013-07-01 10:23:44 +00:00
# zero rotation
return ( 0.0 , 0.0 , 0.0 , 1.0 ) ;
2013-06-30 21:51:06 +00:00
}
2013-07-01 10:23:44 +00:00
# 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)
2013-06-30 21:51:06 +00:00
my @ a = cross ( \ @ p2 , \ @ p1 ) ;
2013-07-01 10:23:44 +00:00
# Figure out how much to rotate around that axis.
2013-06-30 21:51:06 +00:00
my @ d = map { $ _ * $ _ } ( map { $ p1 [ $ _ ] - $ p2 [ $ _ ] } 0 .. $# p1 ) ;
2013-07-01 10:23:44 +00:00
my $ t = sqrt ( reduce { $ a + $ b } @ d ) / ( 2.0 * TRACKBALLSIZE ) ;
# Avoid problems with out-of-control values...
2013-06-30 21:51:06 +00:00
$ 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 ) ;
}
2013-07-01 10:23:44 +00:00
# Build a rotation matrix, given a quaternion rotation.
2013-06-30 21:51:06 +00:00
sub quat_to_rotmatrix {
2013-07-01 08:58:30 +00:00
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 ;
2013-06-29 16:31:06 +00:00
}
2013-06-30 21:51:06 +00:00
sub mulquats {
2013-07-01 08:58:30 +00:00
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 ] )
2013-06-29 16:31:06 +00:00
}
2017-06-08 10:01:35 +00:00
# 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.
2013-06-29 12:16:56 +00:00
sub mouse_to_3d {
2014-12-16 00:12:37 +00:00
my ( $ self , $ x , $ y , $ z ) = @ _ ;
2013-07-01 08:58:30 +00:00
2018-03-06 13:45:03 +00:00
return unless $ self - > GetContext ;
$ self - > SetCurrent ( $ self - > GetContext ) ;
2013-07-01 08:58:30 +00:00
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
2014-07-15 19:58:03 +00:00
2014-12-15 00:28:11 +00:00
$ y = $ viewport [ 3 ] - $ y ;
2014-12-16 00:12:37 +00:00
$ z // = glReadPixels_p ( $ x , $ y , 1 , 1 , GL_DEPTH_COMPONENT , GL_FLOAT ) ;
2014-12-15 00:28:11 +00:00
my @ projected = gluUnProject_p ( $ x , $ y , $ z , @ mview , @ proj , @ viewport ) ;
return Slic3r::Pointf3 - > new ( @ projected ) ;
2013-06-29 12:16:56 +00:00
}
2013-05-16 10:01:38 +00:00
sub GetContext {
2013-05-17 12:14:33 +00:00
my ( $ self ) = @ _ ;
2017-08-03 15:47:18 +00:00
return $ self - > { context } || = Wx::GLContext - > new ( $ self ) ;
2013-05-16 10:01:38 +00:00
}
sub SetCurrent {
2013-05-17 12:14:33 +00:00
my ( $ self , $ context ) = @ _ ;
2017-08-03 15:47:18 +00:00
return $ self - > SUPER:: SetCurrent ( $ context ) ;
2013-05-16 10:01:38 +00:00
}
2013-06-30 21:51:06 +00:00
2017-03-20 11:05:20 +00:00
sub UseVBOs {
my ( $ self ) = @ _ ;
if ( ! defined ( $ self - > { use_VBOs } ) ) {
2017-12-11 17:00:51 +00:00
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 ;
}
2017-03-30 08:25:52 +00:00
# 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.
2017-05-30 15:24:50 +00:00
return 0 if ( ! $ self - > init && $^O eq 'linux' ) ;
2017-03-20 11:05:20 +00:00
# Don't use VBOs if anything fails.
$ self - > { use_VBOs } = 0 ;
if ( $ self - > GetContext ) {
$ self - > SetCurrent ( $ self - > GetContext ) ;
2017-05-30 15:24:50 +00:00
Slic3r::GUI::_3DScene:: _glew_init ;
2017-03-20 11:05:20 +00:00
my @ gl_version = split ( /\./ , glGetString ( GL_VERSION ) ) ;
$ self - > { use_VBOs } = int ( $ gl_version [ 0 ] ) >= 2 ;
2017-03-30 08:25:52 +00:00
# print "UseVBOs $self OpenGL major: $gl_version[0], minor: $gl_version[1]. Use VBOs: ", $self->{use_VBOs}, "\n";
2017-03-20 11:05:20 +00:00
}
}
return $ self - > { use_VBOs } ;
}
2013-05-16 10:01:38 +00:00
sub Resize {
2013-05-17 12:14:33 +00:00
my ( $ self , $ x , $ y ) = @ _ ;
2013-05-16 10:01:38 +00:00
return unless $ self - > GetContext ;
2014-12-15 00:28:11 +00:00
$ self - > _dirty ( 0 ) ;
2015-01-04 12:11:05 +00:00
2013-05-17 12:14:33 +00:00
$ self - > SetCurrent ( $ self - > GetContext ) ;
glViewport ( 0 , 0 , $ x , $ y ) ;
2013-05-16 10:01:38 +00:00
2014-12-15 00:28:11 +00:00
$ x /= $ self - > _zoom ;
$ y /= $ self - > _zoom ;
2013-05-16 10:01:38 +00:00
glMatrixMode ( GL_PROJECTION ) ;
glLoadIdentity ( ) ;
2016-09-14 07:38:59 +00:00
if ( $ self - > _camera_type eq 'ortho' ) {
2016-09-15 10:26:53 +00:00
#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!
2018-04-23 06:44:24 +00:00
# my $depth = 1.05 * $self->max_bounding_box->radius();
2018-04-26 09:03:15 +00:00
my $ depth = 5.0 * max ( @ { $ self - > max_bounding_box - > size } ) ;
2016-09-14 07:38:59 +00:00
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 ) ;
}
2013-05-16 10:01:38 +00:00
glMatrixMode ( GL_MODELVIEW ) ;
}
2016-09-14 07:38:59 +00:00
2013-05-16 10:01:38 +00:00
sub InitGL {
my $ self = shift ;
return if $ self - > init ;
return unless $ self - > GetContext ;
2013-05-17 12:14:33 +00:00
$ self - > init ( 1 ) ;
2016-12-02 10:14:24 +00:00
2017-03-30 08:25:52 +00:00
# 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 ) ;
2018-05-03 07:10:12 +00:00
$ self - > zoom_to_bed ;
2018-04-30 06:57:41 +00:00
2014-07-12 15:06:42 +00:00
glClearColor ( 0 , 0 , 0 , 1 ) ;
glColor3f ( 1 , 0 , 0 ) ;
2013-05-17 12:14:33 +00:00
glEnable ( GL_DEPTH_TEST ) ;
2014-07-12 15:06:42 +00:00
glClearDepth ( 1.0 ) ;
glDepthFunc ( GL_LEQUAL ) ;
glEnable ( GL_CULL_FACE ) ;
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
2013-05-16 11:42:19 +00:00
2014-12-15 14:19:42 +00:00
# Set antialiasing/multisampling
glDisable ( GL_LINE_SMOOTH ) ;
glDisable ( GL_POLYGON_SMOOTH ) ;
2017-09-12 08:14:24 +00:00
# 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 $@ ;
2014-12-15 14:19:42 +00:00
2014-07-12 15:06:42 +00:00
# ambient lighting
2015-01-07 19:11:03 +00:00
glLightModelfv_p ( GL_LIGHT_MODEL_AMBIENT , 0.3 , 0.3 , 0.3 , 1 ) ;
2014-07-12 15:06:42 +00:00
glEnable ( GL_LIGHTING ) ;
glEnable ( GL_LIGHT0 ) ;
glEnable ( GL_LIGHT1 ) ;
2015-01-07 19:11:03 +00:00
# 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 ) ;
2013-05-17 12:14:33 +00:00
# Enables Smooth Color Shading; try GL_FLAT for (lack of) fun.
glShadeModel ( GL_SMOOTH ) ;
2017-03-20 11:05:20 +00:00
# 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);
2014-07-12 15:06:42 +00:00
2013-05-17 12:14:33 +00:00
# A handy trick -- have surface material mirror the color.
glColorMaterial ( GL_FRONT_AND_BACK , GL_AMBIENT_AND_DIFFUSE ) ;
glEnable ( GL_COLOR_MATERIAL ) ;
2017-09-12 08:03:10 +00:00
glEnable ( GL_MULTISAMPLE ) if ( $ self - > { can_multisample } ) ;
2017-03-16 13:02:28 +00:00
2017-03-20 11:05:20 +00:00
if ( $ self - > UseVBOs ) {
my $ shader = new Slic3r::GUI::_3DScene:: GLShader ;
if ( ! $ shader - > load ( $ self - > _fragment_shader_Gouraud , $ self - > _vertex_shader_Gouraud ) ) {
# 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 ;
}
}
2013-05-16 10:01:38 +00:00
}
2016-12-14 14:36:18 +00:00
sub DestroyGL {
my $ self = shift ;
2017-03-20 11:05:20 +00:00
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 } ;
}
2017-03-16 13:02:28 +00:00
$ self - > volumes - > release_geometry ;
2016-12-14 14:36:18 +00:00
}
}
2013-05-16 10:01:38 +00:00
sub Render {
2013-05-17 12:14:33 +00:00
my ( $ self , $ dc ) = @ _ ;
2014-06-19 08:27:23 +00:00
# prevent calling SetCurrent() when window is not shown yet
return unless $ self - > IsShownOnScreen ;
return unless my $ context = $ self - > GetContext ;
2014-06-19 08:29:03 +00:00
$ self - > SetCurrent ( $ context ) ;
2013-05-16 10:01:38 +00:00
$ self - > InitGL ;
2014-07-12 15:06:42 +00:00
glClearColor ( 1 , 1 , 1 , 1 ) ;
glClearDepth ( 1 ) ;
glDepthFunc ( GL_LESS ) ;
2013-05-16 10:01:38 +00:00
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
2014-07-15 19:58:03 +00:00
glMatrixMode ( GL_MODELVIEW ) ;
2014-12-15 00:28:11 +00:00
glLoadIdentity ( ) ;
2016-09-14 07:38:59 +00:00
2018-04-23 06:44:24 +00:00
if ( ! TURNTABLE_MODE ) {
2016-09-14 07:38:59 +00:00
# Shift the perspective camera.
my $ camera_pos = Slic3r::Pointf3 - > new ( 0 , 0 , - $ self - > _camera_distance ) ;
glTranslatef ( @$ camera_pos ) ;
}
2014-07-15 19:58:03 +00:00
2014-12-15 00:28:11 +00:00
if ( TURNTABLE_MODE ) {
2017-06-08 09:02:29 +00:00
# Turntable mode is enabled by default.
2014-12-15 00:28:11 +00:00
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 ] ) ;
}
2014-12-16 00:12:37 +00:00
glTranslatef ( @ { $ self - > _camera_target - > negative } ) ;
2014-07-15 19:58:03 +00:00
2015-01-07 19:11:03 +00:00
# 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 ) ;
2016-12-12 17:02:24 +00:00
# Head light
glLightfv_p ( GL_LIGHT1 , GL_POSITION , 1 , 0 , 1 , 0 ) ;
2015-01-07 19:11:03 +00:00
2018-04-19 11:31:50 +00:00
if ( $ self - > enable_picking && ! $ self - > _mouse_dragging ) {
2014-12-13 23:54:35 +00:00
if ( my $ pos = $ self - > _mouse_pos ) {
2018-02-22 13:44:07 +00:00
# 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 ( ) ;
2014-12-13 23:54:35 +00:00
my $ col = [ glReadPixels_p ( $ pos - > x , $ self - > GetSize - > GetHeight - $ pos - > y , 1 , 1 , GL_RGBA , GL_UNSIGNED_BYTE ) ] ;
2014-12-13 19:41:03 +00:00
my $ volume_idx = $ col - > [ 0 ] + $ col - > [ 1 ] * 256 + $ col - > [ 2 ] * 256 * 256 ;
2014-12-16 00:12:37 +00:00
$ self - > _hover_volume_idx ( undef ) ;
2017-03-13 15:02:17 +00:00
$ _ - > set_hover ( 0 ) for @ { $ self - > volumes } ;
2014-12-13 19:41:03 +00:00
if ( $ volume_idx <= $# { $ self - > volumes } ) {
2014-12-16 00:12:37 +00:00
$ self - > _hover_volume_idx ( $ volume_idx ) ;
2015-01-13 17:41:23 +00:00
2017-03-13 15:02:17 +00:00
$ self - > volumes - > [ $ volume_idx ] - > set_hover ( 1 ) ;
2015-01-13 17:41:23 +00:00
my $ group_id = $ self - > volumes - > [ $ volume_idx ] - > select_group_id ;
if ( $ group_id != - 1 ) {
2017-03-13 15:02:17 +00:00
$ _ - > set_hover ( 1 ) for grep { $ _ - > select_group_id == $ group_id } @ { $ self - > volumes } ;
2015-01-13 17:41:23 +00:00
}
2014-12-13 23:54:35 +00:00
$ self - > on_hover - > ( $ volume_idx ) if $ self - > on_hover ;
2014-12-13 19:41:03 +00:00
}
2018-02-22 13:44:07 +00:00
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
2014-12-13 19:41:03 +00:00
}
}
2015-01-04 12:11:05 +00:00
# draw fixed background
if ( $ self - > background ) {
2015-01-07 19:11:03 +00:00
glDisable ( GL_LIGHTING ) ;
2015-01-04 12:11:05 +00:00
glPushMatrix ( ) ;
glLoadIdentity ( ) ;
glMatrixMode ( GL_PROJECTION ) ;
glPushMatrix ( ) ;
glLoadIdentity ( ) ;
2016-09-14 07:38:59 +00:00
# Draws a bluish bottom to top gradient over the complete screen.
glDisable ( GL_DEPTH_TEST ) ;
2015-01-04 12:11:05 +00:00
glBegin ( GL_QUADS ) ;
glColor3f ( 0.0 , 0.0 , 0.0 ) ;
2016-09-14 07:38:59 +00:00
glVertex3f ( - 1.0 , - 1.0 , 1.0 ) ;
glVertex3f ( 1.0 , - 1.0 , 1.0 ) ;
2015-01-04 12:11:05 +00:00
glColor3f ( 10 /255,98/ 255 , 144 / 255 ) ;
2016-09-14 07:38:59 +00:00
glVertex3f ( 1.0 , 1.0 , 1.0 ) ;
glVertex3f ( - 1.0 , 1.0 , 1.0 ) ;
2015-01-04 12:11:05 +00:00
glEnd ( ) ;
glPopMatrix ( ) ;
2016-09-14 07:38:59 +00:00
glEnable ( GL_DEPTH_TEST ) ;
2015-01-04 12:11:05 +00:00
glMatrixMode ( GL_MODELVIEW ) ;
glPopMatrix ( ) ;
2015-01-07 19:11:03 +00:00
glEnable ( GL_LIGHTING ) ;
2015-01-04 12:11:05 +00:00
}
2014-07-15 19:58:03 +00:00
# draw ground and axes
glDisable ( GL_LIGHTING ) ;
2015-01-07 18:46:37 +00:00
# draw ground
my $ ground_z = GROUND_Z ;
if ( $ self - > bed_triangles ) {
glDisable ( GL_DEPTH_TEST ) ;
2013-08-25 00:58:50 +00:00
2015-01-07 18:46:37 +00:00
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
2014-07-15 19:58:03 +00:00
2015-01-07 18:46:37 +00:00
glEnableClientState ( GL_VERTEX_ARRAY ) ;
glColor4f ( 0.8 , 0.6 , 0.5 , 0.4 ) ;
glNormal3d ( 0 , 0 , 1 ) ;
2017-01-20 15:56:37 +00:00
glVertexPointer_c ( 3 , GL_FLOAT , 0 , $ self - > bed_triangles - > ptr ( ) ) ;
2015-01-07 18:46:37 +00:00
glDrawArrays ( GL_TRIANGLES , 0 , $ self - > bed_triangles - > elements / 3 ) ;
glDisableClientState ( GL_VERTEX_ARRAY ) ;
2014-12-16 00:12:37 +00:00
2015-01-07 18:48:25 +00:00
# we need depth test for grid, otherwise it would disappear when looking
# the object from below
2015-01-07 18:46:37 +00:00
glEnable ( GL_DEPTH_TEST ) ;
# draw grid
glLineWidth ( 3 ) ;
glColor4f ( 0.2 , 0.2 , 0.2 , 0.4 ) ;
glEnableClientState ( GL_VERTEX_ARRAY ) ;
2017-01-20 15:56:37 +00:00
glVertexPointer_c ( 3 , GL_FLOAT , 0 , $ self - > bed_grid_lines - > ptr ( ) ) ;
2015-01-07 18:46:37 +00:00
glDrawArrays ( GL_LINES , 0 , $ self - > bed_grid_lines - > elements / 3 ) ;
glDisableClientState ( GL_VERTEX_ARRAY ) ;
2014-04-25 12:54:08 +00:00
2015-01-07 18:46:37 +00:00
glDisable ( GL_BLEND ) ;
}
my $ volumes_bb = $ self - > volumes_bounding_box ;
{
# draw axes
2015-01-08 14:31:13 +00:00
# disable depth testing so that axes are not covered by ground
2015-01-07 18:48:25 +00:00
glDisable ( GL_DEPTH_TEST ) ;
2015-01-07 18:46:37 +00:00
my $ origin = $ self - > origin ;
2018-04-18 08:17:22 +00:00
my $ axis_len = $ self - > use_plain_shader ? 0.3 * max ( @ { $ self - > bed_bounding_box - > size } ) : 2 * max ( @ { $ volumes_bb - > size } ) ;
2015-01-07 18:46:37 +00:00
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 ) ; #++
2015-01-08 14:31:13 +00:00
glEnd ( ) ;
2015-01-07 18:46:37 +00:00
# draw line for Z axis
2015-01-08 14:31:13 +00:00
# (re-enable depth test so that axis is correctly shown when objects are behind it)
glEnable ( GL_DEPTH_TEST ) ;
glBegin ( GL_LINES ) ;
2015-01-07 18:46:37 +00:00
glColor3f ( 0 , 0 , 1 ) ;
glVertex3f ( @$ origin , $ ground_z ) ;
glVertex3f ( @$ origin , $ ground_z + $ axis_len ) ;
glEnd ( ) ;
2013-08-25 00:58:50 +00:00
}
2014-07-12 15:06:42 +00:00
glEnable ( GL_LIGHTING ) ;
2018-03-09 09:40:42 +00:00
2015-01-04 12:32:00 +00:00
# draw objects
2017-03-28 12:18:13 +00:00
if ( ! $ self - > use_plain_shader ) {
2017-03-20 11:05:20 +00:00
$ self - > draw_volumes ;
} elsif ( $ self - > UseVBOs ) {
2018-03-09 09:40:42 +00:00
if ( $ self - > enable_picking ) {
2018-03-09 13:33:44 +00:00
$ self - > mark_volumes_for_layer_height ;
2018-03-09 09:40:42 +00:00
$ 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' ) ) ;
2018-03-13 13:37:59 +00:00
$ self - > volumes - > update_outside_state ( $ self - > { config } , 0 ) ;
2018-03-20 10:59:33 +00:00
# do not cull backfaces to show broken geometry, if any
glDisable ( GL_CULL_FACE ) ;
2018-03-09 09:40:42 +00:00
}
2017-03-20 11:05:20 +00:00
$ self - > { plain_shader } - > enable if $ self - > { plain_shader } ;
$ self - > volumes - > render_VBOs ;
$ self - > { plain_shader } - > disable ;
2018-03-20 10:59:33 +00:00
glEnable ( GL_CULL_FACE ) if ( $ self - > enable_picking ) ;
2017-03-20 11:05:20 +00:00
} else {
2018-03-20 10:59:33 +00:00
# do not cull backfaces to show broken geometry, if any
glDisable ( GL_CULL_FACE ) if ( $ self - > enable_picking ) ;
2017-03-20 11:05:20 +00:00
$ self - > volumes - > render_legacy ;
2018-03-20 10:59:33 +00:00
glEnable ( GL_CULL_FACE ) if ( $ self - > enable_picking ) ;
2017-03-20 11:05:20 +00:00
}
2016-12-12 17:02:24 +00:00
2015-01-07 18:46:37 +00:00
if ( defined $ self - > cutting_plane_z ) {
2018-04-18 07:44:49 +00:00
# draw cutting plane
2015-01-07 18:46:37 +00:00
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 ) ;
2018-04-18 07:44:49 +00:00
# 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 ) ;
2015-01-07 18:46:37 +00:00
}
2016-12-12 17:02:24 +00:00
2018-03-09 09:40:42 +00:00
# draw warning message
$ self - > draw_warning ;
2018-01-16 13:59:06 +00:00
# draw gcode preview legend
$ self - > draw_legend ;
2016-12-12 17:02:24 +00:00
$ self - > draw_active_object_annotations ;
2015-01-07 18:46:37 +00:00
2013-05-16 10:01:38 +00:00
$ self - > SwapBuffers ( ) ;
}
2014-12-13 21:18:43 +00:00
sub draw_volumes {
2016-09-14 07:38:59 +00:00
# $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking.
2014-12-13 19:41:03 +00:00
my ( $ self , $ fakecolor ) = @ _ ;
2013-05-17 12:14:33 +00:00
2018-03-20 10:59:33 +00:00
# do not cull backfaces to show broken geometry, if any
glDisable ( GL_CULL_FACE ) ;
2014-01-22 20:09:32 +00:00
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
2014-12-15 14:19:42 +00:00
2013-05-17 12:14:33 +00:00
glEnableClientState ( GL_VERTEX_ARRAY ) ;
glEnableClientState ( GL_NORMAL_ARRAY ) ;
2014-12-13 19:41:03 +00:00
foreach my $ volume_idx ( 0 .. $# { $ self - > volumes } ) {
my $ volume = $ self - > volumes - > [ $ volume_idx ] ;
2016-12-12 17:02:24 +00:00
2018-03-09 13:33:44 +00:00
if ( $ fakecolor ) {
2016-09-14 07:38:59 +00:00
# Object picking mode. Render the object with a color encoding the object index.
2014-12-13 19:41:03 +00:00
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 ) ;
2014-12-13 23:54:35 +00:00
} elsif ( $ volume - > selected ) {
2014-01-22 20:09:32 +00:00
glColor4f ( @ { & SELECTED_COLOR } ) ;
2014-12-13 23:54:35 +00:00
} elsif ( $ volume - > hover ) {
2014-12-13 19:41:03 +00:00
glColor4f ( @ { & HOVER_COLOR } ) ;
2014-01-17 18:33:13 +00:00
} else {
2014-12-13 23:54:35 +00:00
glColor4f ( @ { $ volume - > color } ) ;
2014-01-17 18:33:13 +00:00
}
2016-09-15 10:26:53 +00:00
2017-03-16 13:02:28 +00:00
$ volume - > render ;
2013-08-25 14:35:21 +00:00
}
2013-05-17 12:14:33 +00:00
glDisableClientState ( GL_NORMAL_ARRAY ) ;
glDisableClientState ( GL_VERTEX_ARRAY ) ;
2018-04-18 07:44:49 +00:00
glDisable ( GL_BLEND ) ;
glEnable ( GL_CULL_FACE ) ;
2016-12-12 17:02:24 +00:00
}
2018-03-09 13:33:44 +00:00
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 ( ) ;
}
}
}
2017-02-09 13:56:13 +00:00
sub _load_image_set_texture {
my ( $ self , $ file_name ) = @ _ ;
# Load a PNG with an alpha channel.
my $ img = Wx::Image - > new ;
2017-10-25 10:53:31 +00:00
$ img - > LoadFile ( Slic3r:: var ( $ file_name ) , wxBITMAP_TYPE_PNG ) ;
2017-02-09 13:56:13 +00:00
# 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 ;
}
2017-02-07 17:17:12 +00:00
sub _variable_layer_thickness_load_overlay_image {
my ( $ self ) = @ _ ;
2017-02-09 13:56:13 +00:00
$ 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 } ;
}
2017-02-07 17:17:12 +00:00
2017-02-09 13:56:13 +00:00
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 } ;
}
2017-02-07 17:17:12 +00:00
2017-02-09 13:56:13 +00:00
# Paint the tooltip.
sub _render_image {
my ( $ self , $ image , $ l , $ r , $ b , $ t ) = @ _ ;
2018-01-16 13:59:06 +00:00
$ self - > _render_texture ( $ image - > { texture_id } , $ l , $ r , $ b , $ t ) ;
2018-01-17 09:39:05 +00:00
}
2018-01-16 13:59:06 +00:00
sub _render_texture {
my ( $ self , $ tex_id , $ l , $ r , $ b , $ t ) = @ _ ;
2017-02-09 13:56:13 +00:00
glColor4f ( 1 . , 1 . , 1 . , 1 . ) ;
glDisable ( GL_LIGHTING ) ;
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
glEnable ( GL_TEXTURE_2D ) ;
2018-01-16 13:59:06 +00:00
glBindTexture ( GL_TEXTURE_2D , $ tex_id ) ;
2017-02-09 13:56:13 +00:00
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 ) ;
2017-02-07 17:17:12 +00:00
}
2016-12-12 17:02:24 +00:00
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 ) = @ _ ;
2017-03-20 11:05:20 +00:00
return if ( ! $ self - > { layer_height_edit_shader } || ! $ self - > layer_editing_enabled ) ;
2016-12-02 10:14:24 +00:00
2017-06-13 11:42:38 +00:00
# Find the selected volume, over which the layer editing is active.
2016-12-12 17:02:24 +00:00
my $ volume ;
foreach my $ volume_idx ( 0 .. $# { $ self - > volumes } ) {
my $ v = $ self - > volumes - > [ $ volume_idx ] ;
2017-03-13 15:02:17 +00:00
if ( $ v - > selected && $ v - > has_layer_height_texture ) {
2016-12-12 17:02:24 +00:00
$ volume = $ v ;
last ;
}
}
return if ( ! $ volume ) ;
2017-06-13 11:42:38 +00:00
# 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 ;
2016-12-12 17:02:24 +00:00
# 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.
2017-02-09 13:56:13 +00:00
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 ;
2016-12-12 17:02:24 +00:00
2018-04-13 07:01:48 +00:00
my $ print_object = $ self - > { print } - > get_object ( $ object_idx ) ;
my $ z_max = $ print_object - > model_object - > bounding_box - > z_max ;
2017-03-20 11:05:20 +00:00
$ 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 ) ;
2018-04-13 07:01:48 +00:00
$ self - > { layer_height_edit_shader } - > set_uniform ( 'z_cursor' , $ z_max * $ z_cursor_relative ) ;
2017-03-20 11:05:20 +00:00
$ self - > { layer_height_edit_shader } - > set_uniform ( 'z_cursor_band_width' , $ self - > { layer_height_edit_band_width } ) ;
2016-12-12 17:02:24 +00:00
glBindTexture ( GL_TEXTURE_2D , $ self - > { layer_preview_z_texture_id } ) ;
2017-03-13 15:02:17 +00:00
glTexImage2D_c ( GL_TEXTURE_2D , 0 , GL_RGBA8 , $ volume - > layer_height_texture_width , $ volume - > layer_height_texture_height ,
2016-12-12 17:02:24 +00:00
0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ;
2017-03-13 15:02:17 +00:00
glTexImage2D_c ( GL_TEXTURE_2D , 1 , GL_RGBA8 , $ volume - > layer_height_texture_width / 2, $volume->layer_height_texture_height / 2 ,
2016-12-12 17:02:24 +00:00
0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ;
2017-03-13 15:02:17 +00:00
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 ) ;
2016-12-12 17:02:24 +00:00
# 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 ) ;
2018-04-13 07:01:48 +00:00
glVertex3f ( $ bar_right , $ bar_top , $ z_max ) ;
glVertex3f ( $ bar_left , $ bar_top , $ z_max ) ;
2016-12-12 17:02:24 +00:00
glEnd ( ) ;
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2017-03-20 11:05:20 +00:00
$ self - > { layer_height_edit_shader } - > disable ;
2016-12-12 17:02:24 +00:00
2017-02-07 17:17:12 +00:00
# Paint the tooltip.
if ( $ self - > _variable_layer_thickness_load_overlay_image ) {
my $ gap = 10 / $ self - > _zoom ;
2017-02-09 13:56:13 +00:00
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 ) ;
2017-02-07 17:17:12 +00:00
}
2016-12-12 17:02:24 +00:00
# Paint the graph.
2017-02-09 13:56:13 +00:00
#FIXME show some kind of legend.
2016-12-12 17:02:24 +00:00
my $ max_z = unscale ( $ print_object - > size - > z ) ;
2017-02-09 13:56:13 +00:00
my $ profile = $ print_object - > model_object - > layer_height_profile ;
2016-12-12 17:02:24 +00:00
my $ layer_height = $ print_object - > config - > get ( 'layer_height' ) ;
2017-06-05 10:02:26 +00:00
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 ;
2016-12-12 17:02:24 +00:00
# Baseline
glColor3f ( 0 . , 0 . , 0 . ) ;
glBegin ( GL_LINE_STRIP ) ;
2017-04-11 09:16:47 +00:00
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 ) ;
2016-12-12 17:02:24 +00:00
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 ] ;
2017-04-11 09:16:47 +00:00
glVertex3f ( $ bar_left + $ h * ( $ bar_right - $ bar_left ) / $layer_height_max, $bar_bottom + $z * ($bar_top - $bar_bottom) / $ max_z , $ z ) ;
2016-12-12 17:02:24 +00:00
}
glEnd ( ) ;
# Revert the matrices.
glPopMatrix ( ) ;
glEnable ( GL_DEPTH_TEST ) ;
2013-05-17 12:14:33 +00:00
}
2018-01-16 13:59:06 +00:00
sub draw_legend {
my ( $ self ) = @ _ ;
2018-03-09 09:40:42 +00:00
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 )
2018-01-16 13:59:06 +00:00
{
2018-03-09 09:40:42 +00:00
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 ) )
2018-01-16 13:59:06 +00:00
{
2018-03-09 09:40:42 +00:00
glDisable ( GL_DEPTH_TEST ) ;
glPushMatrix ( ) ;
glLoadIdentity ( ) ;
2018-02-06 11:43:25 +00:00
2018-03-09 09:40:42 +00:00
my ( $ cw , $ ch ) = $ self - > GetSizeWH ;
2018-01-16 13:59:06 +00:00
2018-03-09 09:40:42 +00:00
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 ) ;
2018-01-16 13:59:06 +00:00
}
}
}
2018-04-05 10:52:29 +00:00
sub update_volumes_colors_by_extruder {
my ( $ self , $ config ) = @ _ ;
$ self - > volumes - > update_colors_by_extruder ( $ config ) ;
}
2017-02-19 15:04:57 +00:00
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 ) ;
2017-03-20 11:05:20 +00:00
my $ glsl_version = glGetString ( GL_SHADING_LANGUAGE_VERSION ) ;
2017-02-19 15:04:57 +00:00
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 ) {
2017-03-20 11:05:20 +00:00
my $ stat = glpCheckExtension ( $ ext ) ;
$ out . = sprintf ( "%s ${ext}$tag{eol}" , $ stat ? ' ' : '*' ) ;
$ out . = sprintf ( " ${stat}$tag{eol}" ) if ( $ stat && $ stat !~ m | ^ $ ext | ) ;
2017-02-19 15:04:57 +00:00
}
return $ out ;
}
2016-09-15 10:26:53 +00:00
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 ) ;
}
}
2016-09-14 07:38:59 +00:00
2017-03-20 11:05:20 +00:00
sub _vertex_shader_Gouraud {
return << 'VERTEX' ;
#version 110
2018-03-13 13:21:41 +00:00
#define INTENSITY_CORRECTION 0.6
2017-03-20 11:05:20 +00:00
2018-03-09 09:40:42 +00:00
// normalized values for ( - 0.6 /1.31, 0.6/ 1.31 , 1 . / 1.31 )
2018-03-12 08:23:59 +00:00
const vec3 LIGHT_TOP_DIR = vec3 ( - 0.4574957 , 0.4574957 , 0.7624929 ) ;
2017-03-20 11:05:20 +00:00
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
2018-04-17 14:16:25 +00:00
#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SHININESS 20.0
2017-03-20 11:05:20 +00:00
2018-03-09 09:40:42 +00:00
// normalized values for ( 1 . /1.43, 0.2/ 1.43 , 1 . / 1.43 )
2018-03-12 08:23:59 +00:00
const vec3 LIGHT_FRONT_DIR = vec3 ( 0.6985074 , 0.1397015 , 0.6985074 ) ;
2017-03-20 11:05:20 +00:00
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
2018-03-13 13:21:41 +00:00
// #define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
// #define LIGHT_FRONT_SHININESS 5.0
2017-03-20 11:05:20 +00:00
#define INTENSITY_AMBIENT 0.3
2018-03-09 09:40:42 +00:00
const vec3 ZERO = vec3 ( 0.0 , 0.0 , 0.0 ) ;
2017-03-20 11:05:20 +00:00
2018-03-09 09:40:42 +00:00
struct PrintBoxDetection
2017-03-20 11:05:20 +00:00
{
2018-03-09 09:40:42 +00:00
vec3 min ;
vec3 max ;
// xyz contains the offset , if w == 1.0 detection needs to be performed
vec4 volume_origin ;
} ;
uniform PrintBoxDetection print_box ;
2018-03-12 08:23:59 +00:00
// x = tainted , y = specular ;
varying vec2 intensity ;
2017-03-20 11:05:20 +00:00
2018-03-09 09:40:42 +00:00
varying vec3 delta_box_min ;
varying vec3 delta_box_max ;
2017-03-20 11:05:20 +00:00
void main ( )
{
2018-03-12 08:23:59 +00:00
// First transform the normal into camera space and normalize the result .
vec3 normal = normalize ( gl_NormalMatrix * gl_Normal ) ;
2017-03-20 11:05:20 +00:00
// 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 .
2018-03-12 08:23:59 +00:00
float NdotL = max ( dot ( normal , LIGHT_TOP_DIR ) , 0.0 ) ;
2017-03-20 11:05:20 +00:00
2018-03-12 08:23:59 +00:00
intensity . x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE ;
intensity . y = 0.0 ;
2017-03-20 11:05:20 +00:00
if ( NdotL > 0.0 )
2018-04-17 14:16:25 +00:00
intensity . y += LIGHT_TOP_SPECULAR * pow ( max ( dot ( normal , reflect ( - LIGHT_TOP_DIR , normal ) ) , 0.0 ) , LIGHT_TOP_SHININESS ) ;
2017-03-20 11:05:20 +00:00
2018-03-12 08:23:59 +00:00
// Perform the same lighting calculation for the 2 nd light source ( no specular applied ) .
2018-03-09 09:40:42 +00:00
NdotL = max ( dot ( normal , LIGHT_FRONT_DIR ) , 0.0 ) ;
2018-03-12 08:23:59 +00:00
intensity . x += NdotL * LIGHT_FRONT_DIFFUSE ;
2017-03-20 11:05:20 +00:00
2018-03-09 09:40:42 +00:00
// 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 ;
}
2017-03-20 11:05:20 +00:00
gl_Position = ftransform ( ) ;
}
VERTEX
}
sub _fragment_shader_Gouraud {
return << 'FRAGMENT' ;
#version 110
2018-03-12 08:23:59 +00:00
const vec3 ZERO = vec3 ( 0.0 , 0.0 , 0.0 ) ;
// x = tainted , y = specular ;
varying vec2 intensity ;
2018-03-09 09:40:42 +00:00
varying vec3 delta_box_min ;
varying vec3 delta_box_max ;
2017-03-20 11:05:20 +00:00
uniform vec4 uniform_color ;
void main ( )
{
2018-04-12 11:54:26 +00:00
// 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 ) ;
2017-03-20 11:05:20 +00:00
}
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 ) ;
}
2018-03-09 09:40:42 +00:00
2017-03-20 11:05:20 +00:00
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 {
2016-12-02 10:14:24 +00:00
return << 'VERTEX' ;
#version 110
2018-03-13 13:21:41 +00:00
#define INTENSITY_CORRECTION 0.6
2016-12-12 17:02:24 +00:00
2018-03-12 08:23:59 +00:00
const vec3 LIGHT_TOP_DIR = vec3 ( - 0.4574957 , 0.4574957 , 0.7624929 ) ;
2018-03-13 13:21:41 +00:00
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
2018-04-17 14:16:25 +00:00
#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SHININESS 20.0
2016-12-12 17:02:24 +00:00
2018-03-12 08:23:59 +00:00
const vec3 LIGHT_FRONT_DIR = vec3 ( 0.6985074 , 0.1397015 , 0.6985074 ) ;
2018-03-13 13:21:41 +00:00
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
// #define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
// #define LIGHT_FRONT_SHININESS 5.0
2016-12-12 17:02:24 +00:00
2018-03-13 13:21:41 +00:00
#define INTENSITY_AMBIENT 0.3
2016-12-12 17:02:24 +00:00
2018-03-12 08:23:59 +00:00
// x = tainted , y = specular ;
varying vec2 intensity ;
2016-12-02 10:14:24 +00:00
varying float object_z ;
void main ( )
{
2018-03-12 08:23:59 +00:00
// First transform the normal into camera space and normalize the result .
vec3 normal = normalize ( gl_NormalMatrix * gl_Normal ) ;
2016-12-02 10:14:24 +00:00
// 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 .
2018-03-12 08:23:59 +00:00
float NdotL = max ( dot ( normal , LIGHT_TOP_DIR ) , 0.0 ) ;
2016-12-12 17:02:24 +00:00
2018-03-12 08:23:59 +00:00
intensity . x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE ;
intensity . y = 0.0 ;
2016-12-12 17:02:24 +00:00
if ( NdotL > 0.0 )
2018-04-17 14:16:25 +00:00
intensity . y += LIGHT_TOP_SPECULAR * pow ( max ( dot ( normal , reflect ( - LIGHT_TOP_DIR , normal ) ) , 0.0 ) , LIGHT_TOP_SHININESS ) ;
2016-12-12 17:02:24 +00:00
2018-03-12 08:23:59 +00:00
// Perform the same lighting calculation for the 2 nd light source ( no specular )
2018-03-09 09:40:42 +00:00
NdotL = max ( dot ( normal , LIGHT_FRONT_DIR ) , 0.0 ) ;
2016-12-02 10:14:24 +00:00
2018-03-12 08:23:59 +00:00
intensity . x += NdotL * LIGHT_FRONT_DIFFUSE ;
2016-12-12 17:02:24 +00:00
// Scaled to widths of the Z texture .
2018-03-12 08:23:59 +00:00
object_z = gl_Vertex . z ;
2016-12-12 17:02:24 +00:00
gl_Position = ftransform ( ) ;
2016-12-02 10:14:24 +00:00
}
VERTEX
}
2017-03-20 11:05:20 +00:00
sub _fragment_shader_variable_layer_height {
2016-12-02 10:14:24 +00:00
return << 'FRAGMENT' ;
#version 110
2016-12-12 17:02:24 +00:00
2016-12-02 10:14:24 +00:00
#define M_PI 3.1415926535897932384626433832795
2016-12-12 17:02:24 +00:00
// 2 D texture ( 1 D 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 ;
2016-12-02 10:14:24 +00:00
2018-03-12 08:23:59 +00:00
// x = tainted , y = specular ;
varying vec2 intensity ;
varying float object_z ;
2016-12-02 10:14:24 +00:00
void main ( )
{
2016-12-12 17:02:24 +00:00
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 ;
2016-12-21 15:00:41 +00:00
float z_blend = 0.25 * cos ( min ( M_PI , abs ( M_PI * ( object_z - z_cursor ) * 1.8 / z_cursor_band_width ) ) ) + 0.25 ;
2017-02-20 20:54:53 +00:00
// 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 =
2018-03-12 08:23:59 +00:00
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 ) ;
2017-02-20 20:54:53 +00:00
// Mix the final color .
2016-12-12 17:02:24 +00:00
gl_FragColor =
2018-03-12 08:23:59 +00:00
vec4 ( intensity . y , intensity . y , intensity . y , 1.0 ) + intensity . x * mix ( color , vec4 ( 1.0 , 1.0 , 0.0 , 1.0 ) , z_blend ) ;
2016-12-02 10:14:24 +00:00
}
FRAGMENT
}
2016-09-14 07:38:59 +00:00
# The 3D canvas to display objects and tool paths.
2015-01-13 16:25:56 +00:00
package Slic3r::GUI :: 3 DScene ;
use base qw( Slic3r::GUI::3DScene::Base ) ;
2015-01-15 17:49:07 +00:00
use OpenGL qw( :glconstants :gluconstants :glufunctions ) ;
2015-05-17 22:49:16 +00:00
use List::Util qw( first min max ) ;
2015-01-17 22:04:17 +00:00
use Slic3r::Geometry qw( scale unscale epsilon ) ;
2015-01-18 20:31:09 +00:00
use Slic3r::Print::State ':steps' ;
2015-01-13 16:25:56 +00:00
__PACKAGE__ - > mk_accessors ( qw(
2015-01-13 18:07:25 +00:00
color_by
2015-01-13 17:41:23 +00:00
select_by
drag_by
2015-01-13 16:25:56 +00:00
) ) ;
sub new {
my $ class = shift ;
my $ self = $ class - > SUPER:: new ( @ _ ) ;
2015-01-13 18:07:25 +00:00
$ self - > color_by ( 'volume' ) ; # object | volume
$ self - > select_by ( 'object' ) ; # object | volume | instance
2015-01-13 17:41:23 +00:00
$ self - > drag_by ( 'instance' ) ; # object | instance
2015-01-13 16:25:56 +00:00
return $ self ;
}
sub load_object {
2016-12-12 17:02:24 +00:00
my ( $ self , $ model , $ print , $ obj_idx , $ instance_idxs ) = @ _ ;
2015-01-13 16:25:56 +00:00
2017-03-20 11:05:20 +00:00
$ self - > SetCurrent ( $ self - > GetContext ) if $ self - > UseVBOs ;
2015-01-13 16:25:56 +00:00
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 } ] ;
2017-03-20 11:05:20 +00:00
my $ volume_indices = $ self - > volumes - > load_object (
$ model_object , $ obj_idx , $ instance_idxs , $ self - > color_by , $ self - > select_by , $ self - > drag_by ,
$ self - > UseVBOs ) ;
2017-03-13 15:02:17 +00:00
return @ { $ volume_indices } ;
2015-01-15 17:49:07 +00:00
}
2016-09-14 14:31:26 +00:00
# Create 3D thick extrusion lines for a skirt and brim.
# Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes.
2015-05-17 22:49:16 +00:00
sub load_print_toolpaths {
2017-05-24 13:20:20 +00:00
my ( $ self , $ print , $ colors ) = @ _ ;
2016-09-15 10:26:53 +00:00
2017-03-20 11:05:20 +00:00
$ self - > SetCurrent ( $ self - > GetContext ) if $ self - > UseVBOs ;
2017-05-24 13:20:20 +00:00
Slic3r::GUI::_3DScene:: _load_print_toolpaths ( $ print , $ self - > volumes , $ colors , $ self - > UseVBOs )
2017-03-13 15:02:17 +00:00
if ( $ print - > step_done ( STEP_SKIRT ) && $ print - > step_done ( STEP_BRIM ) ) ;
2015-05-17 22:49:16 +00:00
}
2016-09-14 14:31:26 +00:00
# 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.
2015-01-15 19:06:30 +00:00
sub load_print_object_toolpaths {
2017-05-24 13:20:20 +00:00
my ( $ self , $ object , $ colors ) = @ _ ;
2016-09-14 22:06:42 +00:00
2017-03-20 11:05:20 +00:00
$ self - > SetCurrent ( $ self - > GetContext ) if $ self - > UseVBOs ;
2017-05-24 13:20:20 +00:00
Slic3r::GUI::_3DScene:: _load_print_object_toolpaths ( $ object , $ self - > volumes , $ colors , $ self - > UseVBOs ) ;
2015-01-15 19:06:30 +00:00
}
2017-05-25 20:27:53 +00:00
# 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 ) ) ;
}
2018-01-08 12:44:10 +00:00
sub load_gcode_preview {
2018-02-14 19:35:59 +00:00
my ( $ self , $ print , $ gcode_preview_data , $ colors ) = @ _ ;
2018-01-08 12:44:10 +00:00
$ self - > SetCurrent ( $ self - > GetContext ) if $ self - > UseVBOs ;
2018-02-14 19:35:59 +00:00
Slic3r::GUI::_3DScene:: load_gcode_preview ( $ print , $ gcode_preview_data , $ self - > volumes , $ colors , $ self - > UseVBOs ) ;
2018-01-08 12:44:10 +00:00
}
2015-01-18 19:48:54 +00:00
sub set_toolpaths_range {
my ( $ self , $ min_z , $ max_z ) = @ _ ;
2017-03-13 15:02:17 +00:00
$ self - > volumes - > set_range ( $ min_z , $ max_z ) ;
2015-01-15 19:06:30 +00:00
}
2018-02-06 11:43:25 +00:00
sub reset_legend_texture {
Slic3r::GUI::_3DScene:: reset_legend_texture ( ) ;
}
2018-02-22 07:59:47 +00:00
sub get_current_print_zs {
my ( $ self ) = @ _ ;
my $ count = $ self - > volumes - > get_current_print_zs ( ) ;
return $ count ;
}
2013-05-17 12:14:33 +00:00
1 ;