From 5024fc4be7b03d6b5b065d42666d1b26a6cc3c1a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 May 2018 10:47:04 +0200 Subject: [PATCH 001/117] OpenGL to c++ 1st installment - WIP --- lib/Slic3r/GUI/3DScene.pm | 179 +++++++++--- lib/Slic3r/GUI/MainFrame.pm | 13 +- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 6 + lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 7 + lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 3 + xs/CMakeLists.txt | 10 +- xs/lib/Slic3r/XS.pm | 2 + xs/src/slic3r/GUI/3DScene.cpp | 115 +++++++- xs/src/slic3r/GUI/3DScene.hpp | 43 ++- xs/src/slic3r/GUI/GLCanvas3D.cpp | 117 ++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 77 +++++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 263 ++++++++++++++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 86 ++++++ xs/xsp/GUI_3DScene.xsp | 150 +++++++++- 14 files changed, 1015 insertions(+), 56 deletions(-) create mode 100644 xs/src/slic3r/GUI/GLCanvas3D.cpp create mode 100644 xs/src/slic3r/GUI/GLCanvas3D.hpp create mode 100644 xs/src/slic3r/GUI/GLCanvas3DManager.cpp create mode 100644 xs/src/slic3r/GUI/GLCanvas3DManager.hpp diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 1b6adf800e..6cb5f398a5 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -16,7 +16,10 @@ use strict; use warnings; use Wx qw(wxTheApp :timer :bitmap :icon :dialog); -use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER); +#============================================================================================================================== +use Wx::Event qw(EVT_PAINT EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER); +#use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER); +#============================================================================================================================== # must load OpenGL *before* Wx::GLCanvas use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); use base qw(Wx::GLCanvas Class::Accessor); @@ -30,7 +33,8 @@ use Slic3r::Geometry qw(PI); # _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' -__PACKAGE__->mk_accessors( qw(_quat _dirty init +#============================================================================================================================== +__PACKAGE__->mk_accessors( qw(_quat init enable_picking enable_moving use_plain_shader @@ -73,6 +77,50 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init _mouse_dragging ) ); +#__PACKAGE__->mk_accessors( qw(_quat _dirty init +# enable_picking +# enable_moving +# use_plain_shader +# on_viewport_changed +# on_hover +# on_select +# on_double_click +# on_right_click +# on_move +# on_model_update +# volumes +# _sphi _stheta +# cutting_plane_z +# cut_lines_vertices +# bed_shape +# bed_triangles +# bed_grid_lines +# bed_polygon +# background +# origin +# _mouse_pos +# _hover_volume_idx +# +# _drag_volume_idx +# _drag_start_pos +# _drag_volume_center_offset +# _drag_start_xy +# _dragged +# +# _layer_height_edited +# +# _camera_type +# _camera_target +# _camera_distance +# _zoom +# +# _legend_enabled +# _warning_enabled +# _apply_zoom_to_volumes_filter +# _mouse_dragging +# +# ) ); +#============================================================================================================================== use constant TRACKBALLSIZE => 0.8; use constant TURNTABLE_MODE => 1; @@ -130,13 +178,22 @@ sub new { # we request a depth buffer explicitely because it looks like it's not created by # default on Linux, causing transparency issues my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "", $attrib); - if (Wx::wxVERSION >= 3.000003) { - # Wx 3.0.3 contains an ugly hack to support some advanced OpenGL attributes through the attribute list. - # The attribute list is transferred between the wxGLCanvas and wxGLContext constructors using a single static array s_wglContextAttribs. - # Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs. - $self->GetContext(); - } +#============================================================================================================================== +# if (Wx::wxVERSION >= 3.000003) { +# # Wx 3.0.3 contains an ugly hack to support some advanced OpenGL attributes through the attribute list. +# # The attribute list is transferred between the wxGLCanvas and wxGLContext constructors using a single static array s_wglContextAttribs. +# # Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs. +# $self->GetContext(); +# } +#============================================================================================================================== +#============================================================================================================================== + Slic3r::GUI::_3DScene::add_canvas($self, $self->GetContext); +# my $context = $self->GetContext; +# $self->SetCurrent($context); +# Slic3r::GUI::_3DScene::add_canvas($self, $context); +#============================================================================================================================== + $self->{can_multisample} = $can_multisample; $self->background(1); $self->_quat((0, 0, 0, 1)); @@ -171,10 +228,16 @@ sub new { my $dc = Wx::PaintDC->new($self); $self->Render($dc); }); - EVT_SIZE($self, sub { $self->_dirty(1) }); +#======================================================================================================================= +# EVT_SIZE($self, sub { $self->_dirty(1) }); +#======================================================================================================================= EVT_IDLE($self, sub { - return unless $self->_dirty; - return if !$self->IsShownOnScreen; +#============================================================================================================================== + return unless Slic3r::GUI::_3DScene::is_dirty($self); + return unless Slic3r::GUI::_3DScene::is_shown_on_screen($self); +# return unless $self->_dirty; +# return if !$self->IsShownOnScreen; +#============================================================================================================================== $self->Resize( $self->GetSizeWH ); $self->Refresh; }); @@ -237,6 +300,9 @@ sub Destroy { my ($self) = @_; $self->{layer_height_edit_timer}->Stop; $self->DestroyGL; +#============================================================================================================================== + Slic3r::GUI::_3DScene::remove_canvas($self); +#============================================================================================================================== return $self->SUPER::Destroy; } @@ -621,7 +687,10 @@ sub mouse_wheel_event { # ) if 0; $self->on_viewport_changed->() if $self->on_viewport_changed; - $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen; +#============================================================================================================================== + $self->Resize($self->GetSizeWH) if Slic3r::GUI::_3DScene::is_shown_on_screen($self); +# $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen; +#============================================================================================================================== $self->Refresh; } @@ -633,7 +702,10 @@ sub reset_objects { $self->volumes->release_geometry; } $self->volumes->erase; - $self->_dirty(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_dirty($self, 1); +# $self->_dirty(1); +#============================================================================================================================== } # Setup camera to view all objects. @@ -645,7 +717,10 @@ sub set_viewport_from_scene { $self->_camera_target($scene->_camera_target); $self->_zoom($scene->_zoom); $self->_quat($scene->_quat); - $self->_dirty(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_dirty($self, 1); +# $self->_dirty(1); +#============================================================================================================================== } # Set the camera to a default orientation, @@ -777,7 +852,10 @@ sub zoom_to_bounding_box { # center view around bounding box center $self->_camera_target($bb->center); $self->on_viewport_changed->() if $self->on_viewport_changed; - $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen; +#============================================================================================================================== + $self->Resize($self->GetSizeWH) if Slic3r::GUI::_3DScene::is_shown_on_screen($self); +# $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen; +#============================================================================================================================== $self->Refresh; } } @@ -1071,38 +1149,46 @@ sub SetCurrent { sub UseVBOs { my ($self) = @_; - if (! defined ($self->{use_VBOs})) { - my $use_legacy = wxTheApp->{app_config}->get('use_legacy_opengl'); - if ($use_legacy eq '1') { - # Disable OpenGL 2.0 rendering. - $self->{use_VBOs} = 0; - # Don't enable the layer editing tool. - $self->{layer_editing_enabled} = 0; - # 2 means failed - $self->{layer_editing_initialized} = 2; - return 0; - } - # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized - # first when an OpenGL widget is shown for the first time. How ugly. - return 0 if (! $self->init && $^O eq 'linux'); - # Don't use VBOs if anything fails. - $self->{use_VBOs} = 0; - if ($self->GetContext) { - $self->SetCurrent($self->GetContext); - Slic3r::GUI::_3DScene::_glew_init; - my @gl_version = split(/\./, glGetString(GL_VERSION)); - $self->{use_VBOs} = int($gl_version[0]) >= 2; - # print "UseVBOs $self OpenGL major: $gl_version[0], minor: $gl_version[1]. Use VBOs: ", $self->{use_VBOs}, "\n"; - } - } - return $self->{use_VBOs}; +#============================================================================================================================== + return 0 if (! $self->init && $^O eq 'linux'); + return Slic3r::GUI::_3DScene::use_VBOs(); + +# if (! defined ($self->{use_VBOs})) { +# my $use_legacy = wxTheApp->{app_config}->get('use_legacy_opengl'); +# if ($use_legacy eq '1') { +# # Disable OpenGL 2.0 rendering. +# $self->{use_VBOs} = 0; +# # Don't enable the layer editing tool. +# $self->{layer_editing_enabled} = 0; +# # 2 means failed +# $self->{layer_editing_initialized} = 2; +# return 0; +# } +# # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized +# # first when an OpenGL widget is shown for the first time. How ugly. +# return 0 if (! $self->init && $^O eq 'linux'); +# # Don't use VBOs if anything fails. +# $self->{use_VBOs} = 0; +# if ($self->GetContext) { +# $self->SetCurrent($self->GetContext); +# Slic3r::GUI::_3DScene::_glew_init; +# my @gl_version = split(/\./, glGetString(GL_VERSION)); +# $self->{use_VBOs} = int($gl_version[0]) >= 2; +# # print "UseVBOs $self OpenGL major: $gl_version[0], minor: $gl_version[1]. Use VBOs: ", $self->{use_VBOs}, "\n"; +# } +# } +# return $self->{use_VBOs}; +#============================================================================================================================== } sub Resize { my ($self, $x, $y) = @_; return unless $self->GetContext; - $self->_dirty(0); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_dirty($self, 0); +# $self->_dirty(0); +#============================================================================================================================== $self->SetCurrent($self->GetContext); glViewport(0, 0, $x, $y); @@ -1148,13 +1234,17 @@ sub InitGL { return unless $self->GetContext; $self->init(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::init_gl; +#============================================================================================================================== + # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized # first when an OpenGL widget is shown for the first time. How ugly. # In that case the volumes are wainting to be moved to Vertex Buffer Objects # after the OpenGL context is being initialized. $self->volumes->finalize_geometry(1) if ($^O eq 'linux' && $self->UseVBOs); - + $self->zoom_to_bed; glClearColor(0, 0, 0, 1); @@ -1236,7 +1326,10 @@ sub Render { my ($self, $dc) = @_; # prevent calling SetCurrent() when window is not shown yet - return unless $self->IsShownOnScreen; +#============================================================================================================================== + return unless Slic3r::GUI::_3DScene::is_shown_on_screen($self); +# return unless $self->IsShownOnScreen; +#============================================================================================================================== return unless my $context = $self->GetContext; $self->SetCurrent($context); $self->InitGL; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index fbcd34a3f6..d510c87e43 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -28,9 +28,9 @@ our $PRESETS_CHANGED_EVENT = Wx::NewEventType; sub new { my ($class, %params) = @_; - + my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE); - Slic3r::GUI::set_main_frame($self); + Slic3r::GUI::set_main_frame($self); if ($^O eq 'MSWin32') { # Load the icon either from the exe, or from the ico file. my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe'; @@ -39,7 +39,7 @@ sub new { } else { $self->SetIcon(Wx::Icon->new(Slic3r::var("Slic3r_128px.png"), wxBITMAP_TYPE_PNG)); } - + # store input params # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. $self->{no_controller} = $params{no_controller}; @@ -47,7 +47,7 @@ sub new { $self->{loaded} = 0; $self->{lang_ch_event} = $params{lang_ch_event}; $self->{preferences_event} = $params{preferences_event}; - + # initialize tabpanel and menubar $self->_init_tabpanel; $self->_init_menubar; @@ -63,7 +63,7 @@ sub new { $self->SetStatusBar($self->{statusbar}); $self->{loaded} = 1; - + # initialize layout { my $sizer = Wx::BoxSizer->new(wxVERTICAL); @@ -90,6 +90,9 @@ sub new { # Save the slic3r.ini. Usually the ini file is saved from "on idle" callback, # but in rare cases it may not have been called yet. wxTheApp->{app_config}->save; +#============================================================================================================================== + Slic3r::GUI::_3DScene::remove_all_canvases(); +#============================================================================================================================== # propagate event $event->Skip; }); diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 4d55e313a6..f0f50a4f3f 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -144,6 +144,9 @@ sub new { # Note that the window was already closed, so a pending update will not be executed. $self->{already_closed} = 1; $self->EndModal(wxID_OK); +#============================================================================================================================= + $self->{canvas}->Destroy; +#============================================================================================================================= $self->Destroy(); }); @@ -151,6 +154,9 @@ sub new { # Note that the window was already closed, so a pending update will not be executed. $self->{already_closed} = 1; $self->EndModal(wxID_CANCEL); +#============================================================================================================================= + $self->{canvas}->Destroy; +#============================================================================================================================= $self->Destroy(); }); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index a632edeeae..322491f9ec 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -511,6 +511,13 @@ sub CanClose { return ! Slic3r::GUI::catch_error($self); } +#============================================================================================================================= +sub Destroy { + my ($self) = @_; + $self->{canvas}->Destroy if ($self->{canvas}); +} +#============================================================================================================================= + sub PartsChanged { my ($self) = @_; return $self->{parts_changed}; diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index 908d5eff77..d0ee98ee6c 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -36,6 +36,9 @@ sub new { wxTheApp->save_window_pos($self, "object_settings"); $self->EndModal(wxID_OK); +#============================================================================================================================= + $self->{parts}->Destroy; +#============================================================================================================================= $self->Destroy; }); diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 4f44fc7bf7..7d7d371266 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -182,6 +182,12 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/3DScene.hpp ${LIBDIR}/slic3r/GUI/GLShader.cpp ${LIBDIR}/slic3r/GUI/GLShader.hpp + + ${LIBDIR}/slic3r/GUI/GLCanvas3D.hpp + ${LIBDIR}/slic3r/GUI/GLCanvas3D.cpp + ${LIBDIR}/slic3r/GUI/GLCanvas3DManager.hpp + ${LIBDIR}/slic3r/GUI/GLCanvas3DManager.cpp + ${LIBDIR}/slic3r/GUI/Preferences.cpp ${LIBDIR}/slic3r/GUI/Preferences.hpp ${LIBDIR}/slic3r/GUI/Preset.cpp @@ -550,13 +556,13 @@ if (SLIC3R_PRUSACONTROL) set(wxWidgets_UseAlienWx 1) if (wxWidgets_UseAlienWx) set(AlienWx_DEBUG 1) - find_package(AlienWx REQUIRED COMPONENTS base core adv html) + find_package(AlienWx REQUIRED COMPONENTS base core adv html gl) include_directories(${AlienWx_INCLUDE_DIRS}) #add_compile_options(${AlienWx_CXX_FLAGS}) add_definitions(${AlienWx_DEFINITIONS}) set(wxWidgets_LIBRARIES ${AlienWx_LIBRARIES}) else () - find_package(wxWidgets REQUIRED COMPONENTS base core adv html) + find_package(wxWidgets REQUIRED COMPONENTS base core adv html gl) include(${wxWidgets_USE_FILE}) endif () add_definitions(-DSLIC3R_GUI -DSLIC3R_PRUS) diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 06eb041dfb..bd0b698eed 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -12,6 +12,8 @@ our $VERSION = '0.01'; BEGIN { if ($^O eq 'MSWin32') { eval "use Wx"; + eval "use Wx::GLCanvas"; + eval "use Wx::GLContext"; eval "use Wx::Html"; eval "use Wx::Print"; # because of some Wx bug, thread creation fails if we don't have this (looks like Wx::Printout is hard-coded in some thread cleanup code) } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 6b2f5c8309..761485a125 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1473,6 +1473,9 @@ static void point3_to_verts(const Point3& point, double width, double height, GL _3DScene::GCodePreviewVolumeIndex _3DScene::s_gcode_preview_volume_index; _3DScene::LegendTexture _3DScene::s_legend_texture; _3DScene::WarningTexture _3DScene::s_warning_texture; +//################################################################################################################## +GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; +//################################################################################################################## unsigned int _3DScene::TextureBase::finalize() { @@ -1709,11 +1712,117 @@ bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, con return true; } -void _3DScene::_glew_init() -{ - glewInit(); +//################################################################################################################## +void _3DScene::init_gl() +{ + s_canvas_mgr.init_gl(); } +bool _3DScene::use_VBOs() +{ + return s_canvas_mgr.use_VBOs(); +} + +bool _3DScene::add_canvas(wxGLCanvas* canvas, wxGLContext* context) +{ + std::cout << "_3DScene::add_canvas()" << std::endl; + return s_canvas_mgr.add(canvas, context); +} + +bool _3DScene::remove_canvas(wxGLCanvas* canvas) +{ + std::cout << "_3DScene::remove_canvas()" << std::endl; + return s_canvas_mgr.remove(canvas); +} + +void _3DScene::remove_all_canvases() +{ + std::cout << "_3DScene::remove_all_canvases()" << std::endl; + std::cout << "# canvases not yet released: " << s_canvas_mgr.count() << std::endl; + s_canvas_mgr.remove_all(); +} + +bool _3DScene::is_dirty(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_dirty(canvas); +} + +void _3DScene::set_dirty(wxGLCanvas* canvas, bool dirty) +{ + s_canvas_mgr.set_dirty(canvas, dirty); +} + +bool _3DScene::is_shown_on_screen(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_shown_on_screen(canvas); +} + +unsigned int _3DScene::get_camera_type(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_camera_type(canvas); +} + +void _3DScene::set_camera_type(wxGLCanvas* canvas, unsigned int type) +{ + s_canvas_mgr.set_camera_type(canvas, type); +} + +float _3DScene::get_camera_zoom(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_camera_zoom(canvas); +} + +void _3DScene::set_camera_zoom(wxGLCanvas* canvas, float zoom) +{ + s_canvas_mgr.set_camera_zoom(canvas, zoom); +} + +float _3DScene::get_camera_phi(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_camera_phi(canvas); +} + +void _3DScene::set_camera_phi(wxGLCanvas* canvas, float phi) +{ + s_canvas_mgr.set_camera_phi(canvas, phi); +} + +float _3DScene::get_camera_theta(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_camera_theta(canvas); +} + +void _3DScene::set_camera_theta(wxGLCanvas* canvas, float theta) +{ + s_canvas_mgr.set_camera_theta(canvas, theta); +} + +float _3DScene::get_camera_distance(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_camera_distance(canvas); +} + +void _3DScene::set_camera_distance(wxGLCanvas* canvas, float distance) +{ + s_canvas_mgr.set_camera_distance(canvas, distance); +} + +Pointf3 _3DScene::get_camera_target(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_camera_target(canvas); +} + +void _3DScene::set_camera_target(wxGLCanvas* canvas, const Pointf3* target) +{ + s_canvas_mgr.set_camera_target(canvas, target); +} + +//void _3DScene::_glew_init() +//{ +// glewInit(); +//} +//################################################################################################################## + static inline int hex_digit_to_int(const char c) { return diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 8f03e47746..46fbb02fb1 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -6,8 +6,14 @@ #include "../../libslic3r/Line.hpp" #include "../../libslic3r/TriangleMesh.hpp" #include "../../libslic3r/Utils.hpp" +//################################################################################################################## +#include "../../slic3r/GUI/GLCanvas3DManager.hpp" +//################################################################################################################## class wxBitmap; +//################################################################################################################## +class wxWindow; +//################################################################################################################## namespace Slic3r { @@ -523,9 +529,44 @@ class _3DScene static LegendTexture s_legend_texture; static WarningTexture s_warning_texture; +//################################################################################################################## + static GUI::GLCanvas3DManager s_canvas_mgr; +//################################################################################################################## public: - static void _glew_init(); +//################################################################################################################## + static void init_gl(); + static bool use_VBOs(); + + static bool add_canvas(wxGLCanvas* canvas, wxGLContext* context); + static bool remove_canvas(wxGLCanvas* canvas); + static void remove_all_canvases(); + + static bool is_dirty(wxGLCanvas* canvas); + static void set_dirty(wxGLCanvas* canvas, bool dirty); + + static bool is_shown_on_screen(wxGLCanvas* canvas); + + static unsigned int get_camera_type(wxGLCanvas* canvas); + static void set_camera_type(wxGLCanvas* canvas, unsigned int type); + + static float get_camera_zoom(wxGLCanvas* canvas); + static void set_camera_zoom(wxGLCanvas* canvas, float zoom); + + static float get_camera_phi(wxGLCanvas* canvas); + static void set_camera_phi(wxGLCanvas* canvas, float phi); + + static float get_camera_theta(wxGLCanvas* canvas); + static void set_camera_theta(wxGLCanvas* canvas, float theta); + + static float get_camera_distance(wxGLCanvas* canvas); + static void set_camera_distance(wxGLCanvas* canvas, float distance); + + static Pointf3 get_camera_target(wxGLCanvas* canvas); + static void set_camera_target(wxGLCanvas* canvas, const Pointf3* target); + +// static void _glew_init(); +//################################################################################################################## static void load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector& str_tool_colors, bool use_VBOs); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp new file mode 100644 index 0000000000..4074cdf78d --- /dev/null +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -0,0 +1,117 @@ +#include "GLCanvas3D.hpp" + +#include + +#include + +namespace Slic3r { +namespace GUI { + +GLCanvas3D::Camera::Camera() + : type(CT_Ortho) + , zoom(1.0f) + , phi(45.0f) + , theta(45.0f) + , distance(0.0f) + , target(0.0, 0.0, 0.0) + +{ +} + +GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) + : m_canvas(canvas) + , m_context(context) + , m_dirty(true) +{ +} + +void GLCanvas3D::set_current() +{ + if ((m_canvas != nullptr) && (m_context != nullptr)) + m_canvas->SetCurrent(*m_context); +} + +bool GLCanvas3D::is_dirty() const +{ + return m_dirty; +} + +void GLCanvas3D::set_dirty(bool dirty) +{ + m_dirty = dirty; +} + +bool GLCanvas3D::is_shown_on_screen() const +{ + return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; +} + +GLCanvas3D::Camera::EType GLCanvas3D::get_camera_type() const +{ + return m_camera.type; +} + +void GLCanvas3D::set_camera_type(GLCanvas3D::Camera::EType type) +{ + m_camera.type = type; +} + +float GLCanvas3D::get_camera_zoom() const +{ + return m_camera.zoom; +} + +void GLCanvas3D::set_camera_zoom(float zoom) +{ + m_camera.zoom = zoom; +} + +float GLCanvas3D::get_camera_phi() const +{ + return m_camera.phi; +} + +void GLCanvas3D::set_camera_phi(float phi) +{ + m_camera.phi = phi; +} + +float GLCanvas3D::get_camera_theta() const +{ + return m_camera.theta; +} + +void GLCanvas3D::set_camera_theta(float theta) +{ + m_camera.theta = theta; +} + +float GLCanvas3D::get_camera_distance() const +{ + return m_camera.distance; +} + +void GLCanvas3D::set_camera_distance(float distance) +{ + m_camera.distance = distance; +} + +const Pointf3& GLCanvas3D::get_camera_target() const +{ + return m_camera.target; +} + +void GLCanvas3D::set_camera_target(const Pointf3& target) +{ + m_camera.target = target; +} + +void GLCanvas3D::on_size(wxSizeEvent& evt) +{ + std::cout << "GLCanvas3D::on_size: " << (void*)this << std::endl; + + set_dirty(true); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp new file mode 100644 index 0000000000..1548702559 --- /dev/null +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -0,0 +1,77 @@ +#ifndef slic3r_GLCanvas3D_hpp_ +#define slic3r_GLCanvas3D_hpp_ + +#include "../../libslic3r/Point.hpp" + +class wxGLCanvas; +class wxGLContext; +class wxSizeEvent; + +namespace Slic3r { +namespace GUI { + +class GLCanvas3D +{ +public: + struct Camera + { + enum EType : unsigned char + { + CT_Unknown, + CT_Perspective, + CT_Ortho, + CT_Count + }; + + EType type; + float zoom; + float phi; + float theta; + float distance; + Pointf3 target; + + Camera(); + }; + +private: + wxGLCanvas* m_canvas; + wxGLContext* m_context; + Camera m_camera; + + bool m_dirty; + +public: + GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); + + void set_current(); + + bool is_dirty() const; + void set_dirty(bool dirty); + + bool is_shown_on_screen() const; + + Camera::EType get_camera_type() const; + void set_camera_type(Camera::EType type); + + float get_camera_zoom() const; + void set_camera_zoom(float zoom); + + float get_camera_phi() const; + void set_camera_phi(float phi); + + float get_camera_theta() const; + void set_camera_theta(float theta); + + float get_camera_distance() const; + void set_camera_distance(float distance); + + const Pointf3& get_camera_target() const; + void set_camera_target(const Pointf3& target); + + void on_size(wxSizeEvent& evt); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLCanvas3D_hpp_ diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp new file mode 100644 index 0000000000..e27c6b7931 --- /dev/null +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -0,0 +1,263 @@ +#include "GLCanvas3DManager.hpp" +#include "../../slic3r/GUI/GUI.hpp" +#include "../../slic3r/GUI/AppConfig.hpp" + +#include + +#include +#include + +#include + +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +GLCanvas3DManager::GLVersion::GLVersion() + : vn_major(0) + , vn_minor(0) +{ +} + +bool GLCanvas3DManager::GLVersion::detect() +{ + const char* gl_version = (const char*)::glGetString(GL_VERSION); + if (gl_version == nullptr) + return false; + + std::vector tokens; + boost::split(tokens, gl_version, boost::is_any_of(" "), boost::token_compress_on); + + if (tokens.empty()) + return false; + + std::vector numbers; + boost::split(numbers, tokens[0], boost::is_any_of("."), boost::token_compress_on); + + if (numbers.size() > 0) + vn_major = ::atoi(numbers[0].c_str()); + + if (numbers.size() > 1) + vn_minor = ::atoi(numbers[1].c_str()); + + return true; +} + +bool GLCanvas3DManager::GLVersion::is_greater_or_equal_to(unsigned int major, unsigned int minor) const +{ + if (vn_major < major) + return false; + else if (vn_major > major) + return true; + else + return vn_minor >= minor; +} + +GLCanvas3DManager::LayerEditing::LayerEditing() + : allowed(false) +{ +} + +GLCanvas3DManager::GLCanvas3DManager() + : m_gl_initialized(false) + , m_use_legacy_opengl(false) + , m_use_VBOs(false) +{ +} + +bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) +{ + if (_get_canvas(canvas) != m_canvases.end()) + return false; + + GLCanvas3D* canvas3D = new GLCanvas3D(canvas, context); + if (canvas3D == nullptr) + return false; + + canvas->Bind(wxEVT_SIZE, [canvas3D](wxSizeEvent& evt) { canvas3D->on_size(evt); }); + + m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); + + std::cout << "canvas added: " << (void*)canvas << " (" << (void*)canvas3D << ")" << std::endl; + + return true; +} + +bool GLCanvas3DManager::remove(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it == m_canvases.end()) + return false; + + delete it->second; + m_canvases.erase(it); + + std::cout << "canvas removed: " << (void*)canvas << std::endl; + + return true; +} + +void GLCanvas3DManager::remove_all() +{ + for (CanvasesMap::value_type& item : m_canvases) + { + std::cout << "canvas removed: " << (void*)item.second << std::endl; + delete item.second; + } + m_canvases.clear(); +} + +unsigned int GLCanvas3DManager::count() const +{ + return (unsigned int)m_canvases.size(); +} + +void GLCanvas3DManager::init_gl() +{ + if (!m_gl_initialized) + { + std::cout << "GLCanvas3DManager::init_gl()" << std::endl; + + glewInit(); + m_gl_version.detect(); + + const AppConfig* config = GUI::get_app_config(); + m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1"); + m_use_VBOs = !m_use_legacy_opengl && m_gl_version.is_greater_or_equal_to(2, 0); + m_layer_editing.allowed = !m_use_legacy_opengl; + m_gl_initialized = true; + + std::cout << "DETECTED OPENGL: " << m_gl_version.vn_major << "." << m_gl_version.vn_minor << std::endl; + std::cout << "USE VBOS = " << (m_use_VBOs ? "YES" : "NO") << std::endl; + std::cout << "LAYER EDITING ALLOWED = " << (m_layer_editing.allowed ? "YES" : "NO") << std::endl; + } +} + +bool GLCanvas3DManager::use_VBOs() const +{ + return m_use_VBOs; +} + +bool GLCanvas3DManager::layer_editing_allowed() const +{ + return m_layer_editing.allowed; +} + +bool GLCanvas3DManager::is_dirty(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_dirty() : false; +} + +void GLCanvas3DManager::set_dirty(wxGLCanvas* canvas, bool dirty) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_dirty(dirty); +} + +bool GLCanvas3DManager::is_shown_on_screen(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_shown_on_screen() : false; +} + +unsigned int GLCanvas3DManager::get_camera_type(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? (unsigned int)it->second->get_camera_type() : 0; +} + +void GLCanvas3DManager::set_camera_type(wxGLCanvas* canvas, unsigned int type) +{ + if ((type <= (unsigned int)GLCanvas3D::Camera::CT_Unknown) || ((unsigned int)GLCanvas3D::Camera::CT_Count <= type)) + return; + + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_camera_type((GLCanvas3D::Camera::EType)type); +} + +float GLCanvas3DManager::get_camera_zoom(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_camera_zoom() : 1.0f; +} + +void GLCanvas3DManager::set_camera_zoom(wxGLCanvas* canvas, float zoom) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_camera_zoom(zoom); +} + +float GLCanvas3DManager::get_camera_phi(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_camera_phi() : 0.0f; +} + +void GLCanvas3DManager::set_camera_phi(wxGLCanvas* canvas, float phi) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_camera_phi(phi); +} + +float GLCanvas3DManager::get_camera_theta(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_camera_theta() : 0.0f; +} + +void GLCanvas3DManager::set_camera_theta(wxGLCanvas* canvas, float theta) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_camera_theta(theta); +} + +float GLCanvas3DManager::get_camera_distance(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_camera_distance() : 0.0f; +} + +void GLCanvas3DManager::set_camera_distance(wxGLCanvas* canvas, float distance) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_camera_distance(distance); +} + +Pointf3 GLCanvas3DManager::get_camera_target(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_camera_target() : Pointf3(0.0, 0.0, 0.0); +} + +void GLCanvas3DManager::set_camera_target(wxGLCanvas* canvas, const Pointf3* target) +{ + if (target == nullptr) + return; + + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_camera_target(*target); +} + +GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) +{ + return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); +} + +GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) const +{ + return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp new file mode 100644 index 0000000000..3933fc9219 --- /dev/null +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -0,0 +1,86 @@ +#ifndef slic3r_GLCanvas3DManager_hpp_ +#define slic3r_GLCanvas3DManager_hpp_ + +#include "GLCanvas3D.hpp" + +#include + +namespace Slic3r { +namespace GUI { + +class GLCanvas3DManager +{ + struct GLVersion + { + unsigned int vn_major; + unsigned int vn_minor; + + GLVersion(); + bool detect(); + + bool is_greater_or_equal_to(unsigned int major, unsigned int minor) const; + }; + + struct LayerEditing + { + bool allowed; + + LayerEditing(); + }; + + typedef std::map CanvasesMap; + + CanvasesMap m_canvases; + GLVersion m_gl_version; + LayerEditing m_layer_editing; + bool m_gl_initialized; + bool m_use_legacy_opengl; + bool m_use_VBOs; + +public: + GLCanvas3DManager(); + + bool add(wxGLCanvas* canvas, wxGLContext* context); + bool remove(wxGLCanvas* canvas); + + void remove_all(); + + unsigned int count() const; + + void init_gl(); + + bool use_VBOs() const; + bool layer_editing_allowed() const; + + bool is_dirty(wxGLCanvas* canvas) const; + void set_dirty(wxGLCanvas* canvas, bool dirty); + + bool is_shown_on_screen(wxGLCanvas* canvas) const; + + unsigned int get_camera_type(wxGLCanvas* canvas) const; + void set_camera_type(wxGLCanvas* canvas, unsigned int type); + + float get_camera_zoom(wxGLCanvas* canvas) const; + void set_camera_zoom(wxGLCanvas* canvas, float zoom); + + float get_camera_phi(wxGLCanvas* canvas) const; + void set_camera_phi(wxGLCanvas* canvas, float phi); + + float get_camera_theta(wxGLCanvas* canvas) const; + void set_camera_theta(wxGLCanvas* canvas, float theta); + + float get_camera_distance(wxGLCanvas* canvas) const; + void set_camera_distance(wxGLCanvas* canvas, float distance); + + Pointf3 get_camera_target(wxGLCanvas* canvas) const; + void set_camera_target(wxGLCanvas* canvas, const Pointf3* target); + +private: + CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); + CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLCanvas3DManager_hpp_ diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 86d0aeba29..c1bee85e83 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -151,12 +151,158 @@ GLVolumeCollection::arrayref() %package{Slic3r::GUI::_3DScene}; %{ +void +init_gl() + CODE: + _3DScene::init_gl(); + +bool +use_VBOs() + CODE: + RETVAL = _3DScene::use_VBOs(); + OUTPUT: + RETVAL + +bool +add_canvas(canvas, context) + SV *canvas; + SV *context; + CODE: + RETVAL = _3DScene::add_canvas((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (wxGLContext*)wxPli_sv_2_object(aTHX_ context, "Wx::GLContext")); + OUTPUT: + RETVAL + +bool +remove_canvas(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::remove_canvas((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL void -_glew_init() +remove_all_canvases() CODE: - _3DScene::_glew_init(); + _3DScene::remove_all_canvases(); +bool +is_dirty(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_dirty((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_dirty(canvas, dirty) + SV *canvas; + bool dirty; + CODE: + _3DScene::set_dirty((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), dirty); + +bool +is_shown_on_screen(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_shown_on_screen((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +unsigned int +get_camera_type(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_camera_type((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_camera_type(canvas, type) + SV *canvas; + unsigned int type; + CODE: + _3DScene::set_camera_type((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), type); + +float +get_camera_zoom(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_camera_zoom((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_camera_zoom(canvas, zoom) + SV *canvas; + float zoom; + CODE: + _3DScene::set_camera_zoom((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), zoom); + +float +get_camera_phi(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_camera_phi((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_camera_phi(canvas, phi) + SV *canvas; + float phi; + CODE: + _3DScene::set_camera_phi((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), phi); + +float +get_camera_theta(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_camera_theta((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_camera_theta(canvas, theta) + SV *canvas; + float theta; + CODE: + _3DScene::set_camera_theta((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), theta); + +float +get_camera_distance(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_camera_distance((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_camera_distance(canvas, distance) + SV *canvas; + float distance; + CODE: + _3DScene::set_camera_distance((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), distance); + +Clone +get_camera_target(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_camera_target((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_camera_target(canvas, target) + SV *canvas; + Pointf3 *target; + CODE: + _3DScene::set_camera_target((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), target); + + + + + + unsigned int finalize_legend_texture() CODE: From 1fd59144c7243b97f1e277bacb25ab5f080e6dc5 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 14 May 2018 11:31:58 +0200 Subject: [PATCH 002/117] Camera data moved to c++ - WIP --- lib/Slic3r/GUI/3DScene.pm | 193 ++++++++++++++++++------ xs/src/slic3r/GUI/3DScene.cpp | 5 + xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 19 +++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 + xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 6 + xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 3 +- xs/xsp/GUI_3DScene.xsp | 10 +- 8 files changed, 190 insertions(+), 50 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 6cb5f398a5..6a2bec1ca2 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -46,7 +46,6 @@ __PACKAGE__->mk_accessors( qw(_quat init on_move on_model_update volumes - _sphi _stheta cutting_plane_z cut_lines_vertices bed_shape @@ -66,11 +65,6 @@ __PACKAGE__->mk_accessors( qw(_quat init _layer_height_edited - _camera_type - _camera_target - _camera_distance - _zoom - _legend_enabled _warning_enabled _apply_zoom_to_volumes_filter @@ -197,9 +191,15 @@ sub new { $self->{can_multisample} = $can_multisample; $self->background(1); $self->_quat((0, 0, 0, 1)); - $self->_stheta(45); - $self->_sphi(45); - $self->_zoom(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_camera_theta($self, 45.0); + Slic3r::GUI::_3DScene::set_camera_phi($self, 45.0); + Slic3r::GUI::_3DScene::set_camera_zoom($self, 1.0); + +# $self->_stheta(45); +# $self->_sphi(45); +# $self->_zoom(1); +#============================================================================================================================== $self->_legend_enabled(0); $self->_warning_enabled(0); $self->use_plain_shader(0); @@ -210,10 +210,16 @@ sub new { $self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new); # 3D point in model space - $self->_camera_type('ortho'); -# $self->_camera_type('perspective'); - $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); - $self->_camera_distance(0.); +#============================================================================================================================== +# $self->_camera_type('ortho'); +## $self->_camera_type('perspective'); +#============================================================================================================================== +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_camera_target($self, Slic3r::Pointf3->new(0,0,0)); + Slic3r::GUI::_3DScene::set_camera_distance($self, 0.0); +# $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); +# $self->_camera_distance(0.); +#============================================================================================================================== $self->layer_editing_enabled(0); $self->{layer_height_edit_band_width} = 2.; @@ -383,7 +389,11 @@ sub _variable_layer_thickness_bar_rect_screen { sub _variable_layer_thickness_bar_rect_viewport { my ($self) = @_; my ($cw, $ch) = $self->GetSizeWH; - return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom, $cw/(2*$self->_zoom), $ch/(2*$self->_zoom)); +#============================================================================================================================== + my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); + return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$zoom, (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$zoom, $cw/(2*$zoom), $ch/(2*$zoom)); +# 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. @@ -396,7 +406,11 @@ sub _variable_layer_thickness_reset_rect_screen { 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); +#============================================================================================================================== + my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); + return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$zoom, -$ch/(2*$zoom), $cw/(2*$zoom), (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$zoom); +# return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, -$ch/(2*$self->_zoom), $cw/(2*$self->_zoom), (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom); +#============================================================================================================================== } sub _variable_layer_thickness_bar_rect_mouse_inside { @@ -570,10 +584,17 @@ sub mouse_event { my $orig = $self->_drag_start_pos; if (TURNTABLE_MODE) { # Turntable mode is enabled by default. - $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE); - $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #- - $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; - $self->_stheta(0) if $self->_stheta < 0; +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_camera_phi($self, Slic3r::GUI::_3DScene::get_camera_phi($self) + ($pos->x - $orig->x) * TRACKBALLSIZE); + Slic3r::GUI::_3DScene::set_camera_theta($self, Slic3r::GUI::_3DScene::get_camera_theta($self) - ($pos->y - $orig->y) * TRACKBALLSIZE); + Slic3r::GUI::_3DScene::set_camera_theta($self, GIMBALL_LOCK_THETA_MAX) if Slic3r::GUI::_3DScene::get_camera_theta($self) > GIMBALL_LOCK_THETA_MAX; + Slic3r::GUI::_3DScene::set_camera_theta($self, 0) if Slic3r::GUI::_3DScene::get_camera_theta($self) < 0; + +# $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE); +# $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #- +# $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; +# $self->_stheta(0) if $self->_stheta < 0; +#============================================================================================================================== } else { my $size = $self->GetClientSize; my @quat = trackball( @@ -595,7 +616,12 @@ sub mouse_event { # get point in model space at Z = 0 my $cur_pos = $self->mouse_to_3d($e->GetX, $e->GetY, 0); my $orig = $self->mouse_to_3d($self->_drag_start_xy->x, $self->_drag_start_xy->y, 0); - $self->_camera_target->translate(@{$orig->vector_to($cur_pos)->negative}); +#============================================================================================================================== + my $camera_target = Slic3r::GUI::_3DScene::get_camera_target($self); + $camera_target->translate(@{$orig->vector_to($cur_pos)->negative}); + Slic3r::GUI::_3DScene::set_camera_target($self, $camera_target); +# $self->_camera_target->translate(@{$orig->vector_to($cur_pos)->negative}); +#============================================================================================================================== $self->on_viewport_changed->() if $self->on_viewport_changed; $self->Refresh; $self->Update; @@ -662,12 +688,18 @@ sub mouse_wheel_event { my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta(); $zoom = max(min($zoom, 4), -4); $zoom /= 10; - $zoom = $self->_zoom / (1-$zoom); +#============================================================================================================================== + $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self) / (1-$zoom); +# $zoom = $self->_zoom / (1-$zoom); +#============================================================================================================================== # Don't allow to zoom too far outside the scene. my $zoom_min = $self->get_zoom_to_bounding_box_factor($self->max_bounding_box); $zoom_min *= 0.4 if defined $zoom_min; $zoom = $zoom_min if defined $zoom_min && $zoom < $zoom_min; - $self->_zoom($zoom); +#============================================================================================================================== + $zoom = Slic3r::GUI::_3DScene::set_camera_zoom($self, $zoom); +# $self->_zoom($zoom); +#============================================================================================================================== # # In order to zoom around the mouse point we need to translate # # the camera target @@ -712,10 +744,17 @@ sub reset_objects { sub set_viewport_from_scene { my ($self, $scene) = @_; - $self->_sphi($scene->_sphi); - $self->_stheta($scene->_stheta); - $self->_camera_target($scene->_camera_target); - $self->_zoom($scene->_zoom); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_camera_phi($self, Slic3r::GUI::_3DScene::get_camera_phi($scene)); + Slic3r::GUI::_3DScene::set_camera_theta($self, Slic3r::GUI::_3DScene::get_camera_theta($scene)); + Slic3r::GUI::_3DScene::set_camera_target($self, Slic3r::GUI::_3DScene::get_camera_target($scene)); + Slic3r::GUI::_3DScene::set_camera_zoom($self, Slic3r::GUI::_3DScene::get_camera_zoom($scene)); + +# $self->_sphi($scene->_sphi); +# $self->_stheta($scene->_stheta); +# $self->_camera_target($scene->_camera_target); +# $self->_zoom($scene->_zoom); +#============================================================================================================================== $self->_quat($scene->_quat); #============================================================================================================================== Slic3r::GUI::_3DScene::set_dirty($self, 1); @@ -749,11 +788,19 @@ sub select_view { } my $bb = $self->volumes_bounding_box; if (! $bb->empty) { - $self->_sphi($dirvec->[0]); - $self->_stheta($dirvec->[1]); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_camera_phi($self, $dirvec->[0]); + Slic3r::GUI::_3DScene::set_camera_theta($self, $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; + Slic3r::GUI::_3DScene::set_camera_theta($self, GIMBALL_LOCK_THETA_MAX) if Slic3r::GUI::_3DScene::get_camera_theta($self) > GIMBALL_LOCK_THETA_MAX; + Slic3r::GUI::_3DScene::set_camera_theta($self, 0) if Slic3r::GUI::_3DScene::get_camera_theta($self) < 0; + +# $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; @@ -775,22 +822,35 @@ sub get_zoom_to_bounding_box_factor { if (!TURNTABLE_MODE) { # Shift the perspective camera. - my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance); +#============================================================================================================================== + my $camera_pos = Slic3r::Pointf3->new(0,0,-Slic3r::GUI::_3DScene::get_camera_distance($self)); +# 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 +#============================================================================================================================== + glRotatef(-Slic3r::GUI::_3DScene::get_camera_theta($self), 1, 0, 0); # pitch + glRotatef(Slic3r::GUI::_3DScene::get_camera_phi($self), 0, 0, 1); # yaw +# 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); +#============================================================================================================================== + my $camera_pos = Slic3r::Pointf3->new(0,0,-Slic3r::GUI::_3DScene::get_camera_distance($self)); +# 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 }); +#============================================================================================================================== + glTranslatef(@{ Slic3r::GUI::_3DScene::get_camera_target($self)->negative }); +# glTranslatef(@{ $self->_camera_target->negative }); +#============================================================================================================================== # get the view matrix back from opengl my @matrix = glGetFloatv_p(GL_MODELVIEW_MATRIX); @@ -848,9 +908,15 @@ sub zoom_to_bounding_box { # 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); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_camera_zoom($self, $zoom); +# $self->_zoom($zoom); +#============================================================================================================================== # center view around bounding box center - $self->_camera_target($bb->center); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_camera_target($self, $bb->center); +# $self->_camera_target($bb->center); +#============================================================================================================================== $self->on_viewport_changed->() if $self->on_viewport_changed; #============================================================================================================================== $self->Resize($self->GetSizeWH) if Slic3r::GUI::_3DScene::is_shown_on_screen($self); @@ -1193,12 +1259,21 @@ sub Resize { $self->SetCurrent($self->GetContext); glViewport(0, 0, $x, $y); - $x /= $self->_zoom; - $y /= $self->_zoom; +#============================================================================================================================== + my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); + $x /= $zoom; + $y /= $zoom; +# $x /= $self->_zoom; +# $y /= $self->_zoom; +#============================================================================================================================== glMatrixMode(GL_PROJECTION); glLoadIdentity(); - if ($self->_camera_type eq 'ortho') { +#============================================================================================================================== + my $camera_type = Slic3r::GUI::_3DScene::get_camera_type_as_string($self); + if ($camera_type eq 'ortho') { +# if ($self->_camera_type eq 'ortho') { +#============================================================================================================================== #FIXME setting the size of the box 10x larger than necessary # is only a workaround for an incorrectly set camera. # This workaround harms Z-buffer accuracy! @@ -1209,12 +1284,18 @@ sub Resize { -$depth, $depth, ); } else { - die "Invalid camera type: ", $self->_camera_type, "\n" if ($self->_camera_type ne 'perspective'); +#============================================================================================================================== + die "Invalid camera type: ", $camera_type, "\n" if ($camera_type ne 'perspective'); +# 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); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_camera_distance($self, $cam_distance); +# $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); @@ -1344,19 +1425,29 @@ sub Render { if (!TURNTABLE_MODE) { # Shift the perspective camera. - my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance); +#============================================================================================================================== + my $camera_pos = Slic3r::Pointf3->new(0,0,-Slic3r::GUI::_3DScene::get_camera_distance($self)); +# 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 +#============================================================================================================================== + glRotatef(-Slic3r::GUI::_3DScene::get_camera_theta($self), 1, 0, 0); # pitch + glRotatef(Slic3r::GUI::_3DScene::get_camera_phi($self), 0, 0, 1); # yaw +# glRotatef(-$self->_stheta, 1, 0, 0); # pitch +# glRotatef($self->_sphi, 0, 0, 1); # yaw +#============================================================================================================================== } else { my @rotmat = quat_to_rotmatrix($self->quat); glMultMatrixd_p(@rotmat[0..15]); } - glTranslatef(@{ $self->_camera_target->negative }); +#============================================================================================================================== + glTranslatef(@{ Slic3r::GUI::_3DScene::get_camera_target($self)->negative }); +# glTranslatef(@{ $self->_camera_target->negative }); +#============================================================================================================================== # light from above glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0); @@ -1745,8 +1836,14 @@ sub draw_active_object_annotations { # Paint the tooltip. if ($self->_variable_layer_thickness_load_overlay_image) { - my $gap = 10/$self->_zoom; - my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$self->_zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$self->_zoom + $gap, $reset_bottom + $gap); +#============================================================================================================================== + my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); + my $gap = 10/$zoom; + my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$zoom + $gap, $reset_bottom + $gap); + +# my $gap = 10/$self->_zoom; +# my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$self->_zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$self->_zoom + $gap, $reset_bottom + $gap); +#============================================================================================================================== $self->_render_image($self->{layer_preview_annotation}, $l, $r, $t, $b); } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index a6f26b5867..7603c9b843 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1767,6 +1767,11 @@ void _3DScene::set_camera_type(wxGLCanvas* canvas, unsigned int type) s_canvas_mgr.set_camera_type(canvas, type); } +std::string _3DScene::get_camera_type_as_string(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_camera_type_as_string(canvas); +} + float _3DScene::get_camera_zoom(wxGLCanvas* canvas) { return s_canvas_mgr.get_camera_zoom(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 46fbb02fb1..618b8ec348 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -549,6 +549,7 @@ public: static unsigned int get_camera_type(wxGLCanvas* canvas); static void set_camera_type(wxGLCanvas* canvas, unsigned int type); + static std::string get_camera_type_as_string(wxGLCanvas* canvas); static float get_camera_zoom(wxGLCanvas* canvas); static void set_camera_zoom(wxGLCanvas* canvas, float zoom); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 4074cdf78d..9e9f5e45a7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -18,6 +18,20 @@ GLCanvas3D::Camera::Camera() { } +std::string GLCanvas3D::Camera::get_type_as_string() const +{ + switch (type) + { + default: + case CT_Unknown: + return "unknown"; + case CT_Perspective: + return "perspective"; + case CT_Ortho: + return "ortho"; + }; +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -56,6 +70,11 @@ void GLCanvas3D::set_camera_type(GLCanvas3D::Camera::EType type) m_camera.type = type; } +std::string GLCanvas3D::get_camera_type_as_string() const +{ + return m_camera.get_type_as_string(); +} + float GLCanvas3D::get_camera_zoom() const { return m_camera.zoom; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 1548702559..db8aa9d61c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -31,6 +31,8 @@ public: Pointf3 target; Camera(); + + std::string get_type_as_string() const; }; private: @@ -52,6 +54,7 @@ public: Camera::EType get_camera_type() const; void set_camera_type(Camera::EType type); + std::string get_camera_type_as_string() const; float get_camera_zoom() const; void set_camera_zoom(float zoom); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index e27c6b7931..5a565be283 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -181,6 +181,12 @@ void GLCanvas3DManager::set_camera_type(wxGLCanvas* canvas, unsigned int type) it->second->set_camera_type((GLCanvas3D::Camera::EType)type); } +std::string GLCanvas3DManager::get_camera_type_as_string(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_camera_type_as_string() : "unknown"; +} + float GLCanvas3DManager::get_camera_zoom(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 3933fc9219..858c2d9e7b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -59,7 +59,8 @@ public: unsigned int get_camera_type(wxGLCanvas* canvas) const; void set_camera_type(wxGLCanvas* canvas, unsigned int type); - + std::string get_camera_type_as_string(wxGLCanvas* canvas) const; + float get_camera_zoom(wxGLCanvas* canvas) const; void set_camera_zoom(wxGLCanvas* canvas, float zoom); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index c1bee85e83..c7933f0125 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -222,7 +222,15 @@ set_camera_type(canvas, type) unsigned int type; CODE: _3DScene::set_camera_type((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), type); - + +std::string +get_camera_type_as_string(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_camera_type_as_string((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + float get_camera_zoom(canvas) SV *canvas; From a73cb45792c95e52a46fe11e819f8ce15877492b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 14 May 2018 12:08:23 +0200 Subject: [PATCH 003/117] Camera angle clamping moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 9 +-- xs/src/slic3r/GUI/GLCanvas3D.cpp | 108 +++++++++++++++++++++++++------ xs/src/slic3r/GUI/GLCanvas3D.hpp | 34 ++++++++-- 3 files changed, 118 insertions(+), 33 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 6a2bec1ca2..a7de71d4d5 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -139,7 +139,9 @@ use constant MANIPULATION_IDLE => 0; use constant MANIPULATION_DRAGGING => 1; use constant MANIPULATION_LAYER_HEIGHT => 2; -use constant GIMBALL_LOCK_THETA_MAX => 180; +#============================================================================================================================== +#use constant GIMBALL_LOCK_THETA_MAX => 180; +#============================================================================================================================== use constant VARIABLE_LAYER_THICKNESS_BAR_WIDTH => 70; use constant VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT => 22; @@ -587,8 +589,6 @@ sub mouse_event { #============================================================================================================================== Slic3r::GUI::_3DScene::set_camera_phi($self, Slic3r::GUI::_3DScene::get_camera_phi($self) + ($pos->x - $orig->x) * TRACKBALLSIZE); Slic3r::GUI::_3DScene::set_camera_theta($self, Slic3r::GUI::_3DScene::get_camera_theta($self) - ($pos->y - $orig->y) * TRACKBALLSIZE); - Slic3r::GUI::_3DScene::set_camera_theta($self, GIMBALL_LOCK_THETA_MAX) if Slic3r::GUI::_3DScene::get_camera_theta($self) > GIMBALL_LOCK_THETA_MAX; - Slic3r::GUI::_3DScene::set_camera_theta($self, 0) if Slic3r::GUI::_3DScene::get_camera_theta($self) < 0; # $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE); # $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #- @@ -791,9 +791,6 @@ sub select_view { #============================================================================================================================== Slic3r::GUI::_3DScene::set_camera_phi($self, $dirvec->[0]); Slic3r::GUI::_3DScene::set_camera_theta($self, $dirvec->[1]); - # Avoid gimball lock. - Slic3r::GUI::_3DScene::set_camera_theta($self, GIMBALL_LOCK_THETA_MAX) if Slic3r::GUI::_3DScene::get_camera_theta($self) > GIMBALL_LOCK_THETA_MAX; - Slic3r::GUI::_3DScene::set_camera_theta($self, 0) if Slic3r::GUI::_3DScene::get_camera_theta($self) < 0; # $self->_sphi($dirvec->[0]); # $self->_stheta($dirvec->[1]); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 9e9f5e45a7..93186a8ab6 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -4,23 +4,34 @@ #include +static const float GIMBALL_LOCK_THETA_MAX = 180.0f; + namespace Slic3r { namespace GUI { GLCanvas3D::Camera::Camera() - : type(CT_Ortho) - , zoom(1.0f) - , phi(45.0f) - , theta(45.0f) - , distance(0.0f) - , target(0.0, 0.0, 0.0) - + : m_type(CT_Ortho) + , m_zoom(1.0f) + , m_phi(45.0f) + , m_theta(45.0f) + , m_distance(0.0f) + , m_target(0.0, 0.0, 0.0) { } +GLCanvas3D::Camera::EType GLCanvas3D::Camera::get_type() const +{ + return m_type; +} + +void GLCanvas3D::Camera::set_type(GLCanvas3D::Camera::EType type) +{ + m_type = type; +} + std::string GLCanvas3D::Camera::get_type_as_string() const { - switch (type) + switch (m_type) { default: case CT_Unknown: @@ -32,6 +43,63 @@ std::string GLCanvas3D::Camera::get_type_as_string() const }; } +float GLCanvas3D::Camera::get_zoom() const +{ + return m_zoom; +} + +void GLCanvas3D::Camera::set_zoom(float zoom) +{ + m_zoom = zoom; +} + +float GLCanvas3D::Camera::get_phi() const +{ + return m_phi; +} + +void GLCanvas3D::Camera::set_phi(float phi) +{ + m_phi = phi; +} + +float GLCanvas3D::Camera::get_theta() const +{ + return m_theta; +} + +void GLCanvas3D::Camera::set_theta(float theta) +{ + m_theta = theta; + + // clamp angle + if (m_theta > GIMBALL_LOCK_THETA_MAX) + m_theta = GIMBALL_LOCK_THETA_MAX; + + if (m_theta < 0.0f) + m_theta = 0.0f; +} + +float GLCanvas3D::Camera::get_distance() const +{ + return m_distance; +} + +void GLCanvas3D::Camera::set_distance(float distance) +{ + m_distance = distance; +} + +const Pointf3& GLCanvas3D::Camera::get_target() const +{ + return m_target; +} + +void GLCanvas3D::Camera::set_target(const Pointf3& target) +{ + m_target = target; +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -62,12 +130,12 @@ bool GLCanvas3D::is_shown_on_screen() const GLCanvas3D::Camera::EType GLCanvas3D::get_camera_type() const { - return m_camera.type; + return m_camera.get_type(); } void GLCanvas3D::set_camera_type(GLCanvas3D::Camera::EType type) { - m_camera.type = type; + m_camera.set_type(type); } std::string GLCanvas3D::get_camera_type_as_string() const @@ -77,52 +145,52 @@ std::string GLCanvas3D::get_camera_type_as_string() const float GLCanvas3D::get_camera_zoom() const { - return m_camera.zoom; + return m_camera.get_zoom(); } void GLCanvas3D::set_camera_zoom(float zoom) { - m_camera.zoom = zoom; + m_camera.set_zoom(zoom); } float GLCanvas3D::get_camera_phi() const { - return m_camera.phi; + return m_camera.get_phi(); } void GLCanvas3D::set_camera_phi(float phi) { - m_camera.phi = phi; + m_camera.set_phi(phi); } float GLCanvas3D::get_camera_theta() const { - return m_camera.theta; + return m_camera.get_theta(); } void GLCanvas3D::set_camera_theta(float theta) { - m_camera.theta = theta; + m_camera.set_theta(theta); } float GLCanvas3D::get_camera_distance() const { - return m_camera.distance; + return m_camera.get_distance(); } void GLCanvas3D::set_camera_distance(float distance) { - m_camera.distance = distance; + m_camera.set_distance(distance); } const Pointf3& GLCanvas3D::get_camera_target() const { - return m_camera.target; + return m_camera.get_target(); } void GLCanvas3D::set_camera_target(const Pointf3& target) { - m_camera.target = target; + m_camera.set_target(target); } void GLCanvas3D::on_size(wxSizeEvent& evt) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index db8aa9d61c..14c1031dd0 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -13,8 +13,9 @@ namespace GUI { class GLCanvas3D { public: - struct Camera + class Camera { + public: enum EType : unsigned char { CT_Unknown, @@ -23,16 +24,35 @@ public: CT_Count }; - EType type; - float zoom; - float phi; - float theta; - float distance; - Pointf3 target; + private: + EType m_type; + float m_zoom; + float m_phi; + float m_theta; + float m_distance; + Pointf3 m_target; + public: Camera(); + Camera::EType get_type() const; + void set_type(Camera::EType type); std::string get_type_as_string() const; + + float get_zoom() const; + void set_zoom(float zoom); + + float get_phi() const; + void set_phi(float phi); + + float get_theta() const; + void set_theta(float theta); + + float get_distance() const; + void set_distance(float distance); + + const Pointf3& get_target() const; + void set_target(const Pointf3& target); }; private: From 0c1655b884496858971171cac863bae822896b3c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 14 May 2018 14:14:19 +0200 Subject: [PATCH 004/117] 3DScene::Resize() method moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 105 +++++++-------- xs/src/slic3r/GUI/3DScene.cpp | 35 ++++- xs/src/slic3r/GUI/3DScene.hpp | 13 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 165 +++++++++++++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 44 ++++++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 45 ++++++- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 13 +- xs/xsp/GUI_3DScene.xsp | 38 ++++++ 8 files changed, 377 insertions(+), 81 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index a7de71d4d5..60f1199808 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -194,10 +194,6 @@ sub new { $self->background(1); $self->_quat((0, 0, 0, 1)); #============================================================================================================================== - Slic3r::GUI::_3DScene::set_camera_theta($self, 45.0); - Slic3r::GUI::_3DScene::set_camera_phi($self, 45.0); - Slic3r::GUI::_3DScene::set_camera_zoom($self, 1.0); - # $self->_stheta(45); # $self->_sphi(45); # $self->_zoom(1); @@ -210,6 +206,9 @@ sub new { # Collection of GLVolume objects $self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_volumes($self, $self->volumes); +#============================================================================================================================== # 3D point in model space #============================================================================================================================== @@ -217,8 +216,6 @@ sub new { ## $self->_camera_type('perspective'); #============================================================================================================================== #============================================================================================================================== - Slic3r::GUI::_3DScene::set_camera_target($self, Slic3r::Pointf3->new(0,0,0)); - Slic3r::GUI::_3DScene::set_camera_distance($self, 0.0); # $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); # $self->_camera_distance(0.); #============================================================================================================================== @@ -969,9 +966,13 @@ sub bed_bounding_box { sub max_bounding_box { my ($self) = @_; - my $bb = $self->bed_bounding_box; - $bb->merge($self->volumes_bounding_box); - return $bb; +#============================================================================================================================== + return Slic3r::GUI::_3DScene::get_max_bounding_box($self); + +# my $bb = $self->bed_bounding_box; +# $bb->merge($self->volumes_bounding_box); +# return $bb; +#============================================================================================================================== } # Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane @@ -1000,6 +1001,9 @@ sub set_bed_shape { my ($self, $bed_shape) = @_; $self->bed_shape($bed_shape); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_bed_shape($self, $bed_shape); +#============================================================================================================================== # triangulate bed my $expolygon = Slic3r::ExPolygon->new([ map [map scale($_), @$_], @$bed_shape ]); @@ -1246,63 +1250,48 @@ sub UseVBOs { sub Resize { my ($self, $x, $y) = @_; - - return unless $self->GetContext; -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_dirty($self, 0); -# $self->_dirty(0); + #============================================================================================================================== + Slic3r::GUI::_3DScene::resize($self, $x, $y); - $self->SetCurrent($self->GetContext); - glViewport(0, 0, $x, $y); - -#============================================================================================================================== - my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); - $x /= $zoom; - $y /= $zoom; +# return unless $self->GetContext; +# $self->_dirty(0); +# +# $self->SetCurrent($self->GetContext); +# glViewport(0, 0, $x, $y); +# # $x /= $self->_zoom; # $y /= $self->_zoom; -#============================================================================================================================== - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); -#============================================================================================================================== - my $camera_type = Slic3r::GUI::_3DScene::get_camera_type_as_string($self); - if ($camera_type eq 'ortho') { +# +# glMatrixMode(GL_PROJECTION); +# glLoadIdentity(); # if ($self->_camera_type eq 'ortho') { -#============================================================================================================================== - #FIXME setting the size of the box 10x larger than necessary - # is only a workaround for an incorrectly set camera. - # This workaround harms Z-buffer accuracy! -# my $depth = 1.05 * $self->max_bounding_box->radius(); - my $depth = 5.0 * max(@{ $self->max_bounding_box->size }); - glOrtho( - -$x/2, $x/2, -$y/2, $y/2, - -$depth, $depth, - ); - } else { -#============================================================================================================================== - die "Invalid camera type: ", $camera_type, "\n" if ($camera_type ne 'perspective'); +# #FIXME setting the size of the box 10x larger than necessary +# # is only a workaround for an incorrectly set camera. +# # This workaround harms Z-buffer accuracy! +## my $depth = 1.05 * $self->max_bounding_box->radius(); +# my $depth = 5.0 * max(@{ $self->max_bounding_box->size }); +# glOrtho( +# -$x/2, $x/2, -$y/2, $y/2, +# -$depth, $depth, +# ); +# } else { # die "Invalid camera type: ", $self->_camera_type, "\n" if ($self->_camera_type ne 'perspective'); -#============================================================================================================================== - my $bbox_r = $self->max_bounding_box->radius(); - my $fov = PI * 45. / 180.; - my $fov_tan = tan(0.5 * $fov); - my $cam_distance = 0.5 * $bbox_r / $fov_tan; -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_camera_distance($self, $cam_distance); +# my $bbox_r = $self->max_bounding_box->radius(); +# my $fov = PI * 45. / 180.; +# my $fov_tan = tan(0.5 * $fov); +# my $cam_distance = 0.5 * $bbox_r / $fov_tan; # $self->_camera_distance($cam_distance); +# my $nr = $cam_distance - $bbox_r * 1.1; +# my $fr = $cam_distance + $bbox_r * 1.1; +# $nr = 1 if ($nr < 1); +# $fr = $nr + 1 if ($fr < $nr + 1); +# my $h2 = $fov_tan * $nr; +# my $w2 = $h2 * $x / $y; +# glFrustum(-$w2, $w2, -$h2, $h2, $nr, $fr); +# } +# glMatrixMode(GL_MODELVIEW); #============================================================================================================================== - my $nr = $cam_distance - $bbox_r * 1.1; - my $fr = $cam_distance + $bbox_r * 1.1; - $nr = 1 if ($nr < 1); - $fr = $nr + 1 if ($fr < $nr + 1); - my $h2 = $fov_tan * $nr; - my $w2 = $h2 * $x / $y; - glFrustum(-$w2, $w2, -$h2, $h2, $nr, $fr); - } - - glMatrixMode(GL_MODELVIEW); } sub InitGL { diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 7603c9b843..6f485d5114 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1742,6 +1742,36 @@ void _3DScene::remove_all_canvases() s_canvas_mgr.remove_all(); } +void _3DScene::resize(wxGLCanvas* canvas, unsigned int w, unsigned int h) +{ + s_canvas_mgr.resize(canvas, w, h); +} + +bool _3DScene::is_shown_on_screen(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_shown_on_screen(canvas); +} + +GLVolumeCollection* _3DScene::get_volumes(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_volumes(canvas); +} + +void _3DScene::set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes) +{ + s_canvas_mgr.set_volumes(canvas, volumes); +} + +void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) +{ + return s_canvas_mgr.set_bed_shape(canvas, shape); +} + +BoundingBoxf3 _3DScene::get_max_bounding_box(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_max_bounding_box(canvas); +} + bool _3DScene::is_dirty(wxGLCanvas* canvas) { return s_canvas_mgr.is_dirty(canvas); @@ -1752,11 +1782,6 @@ void _3DScene::set_dirty(wxGLCanvas* canvas, bool dirty) s_canvas_mgr.set_dirty(canvas, dirty); } -bool _3DScene::is_shown_on_screen(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_shown_on_screen(canvas); -} - unsigned int _3DScene::get_camera_type(wxGLCanvas* canvas) { return s_canvas_mgr.get_camera_type(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 618b8ec348..0e8cd2bcc1 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -542,11 +542,20 @@ public: static bool remove_canvas(wxGLCanvas* canvas); static void remove_all_canvases(); - static bool is_dirty(wxGLCanvas* canvas); - static void set_dirty(wxGLCanvas* canvas, bool dirty); + static void resize(wxGLCanvas* canvas, unsigned int w, unsigned int h); static bool is_shown_on_screen(wxGLCanvas* canvas); + static GLVolumeCollection* get_volumes(wxGLCanvas* canvas); + static void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); + + static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); + + static BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); + + static bool is_dirty(wxGLCanvas* canvas); + static void set_dirty(wxGLCanvas* canvas, bool dirty); + static unsigned int get_camera_type(wxGLCanvas* canvas); static void set_camera_type(wxGLCanvas* canvas, unsigned int type); static std::string get_camera_type_as_string(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 93186a8ab6..0e539aaefa 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,5 +1,7 @@ #include "GLCanvas3D.hpp" +#include "../../slic3r/GUI/3DScene.hpp" + #include #include @@ -100,10 +102,37 @@ void GLCanvas3D::Camera::set_target(const Pointf3& target) m_target = target; } +const Pointfs& GLCanvas3D::Bed::get_shape() const +{ + return m_shape; +} + +void GLCanvas3D::Bed::set_shape(const Pointfs& shape) +{ + m_shape = shape; + _calc_bounding_box(); +} + +const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const +{ + return m_bounding_box; +} + +void GLCanvas3D::Bed::_calc_bounding_box() +{ + m_bounding_box = BoundingBoxf3(); + for (const Pointf& p : m_shape) + { + m_bounding_box.merge(Pointf3(p.x, p.y, 0.0)); + } +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) + , m_volumes(nullptr) , m_dirty(true) + , m_apply_zoom_to_volumes_filter(false) { } @@ -113,6 +142,94 @@ void GLCanvas3D::set_current() m_canvas->SetCurrent(*m_context); } +bool GLCanvas3D::is_shown_on_screen() const +{ + return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; +} + +void GLCanvas3D::resize(unsigned int w, unsigned int h) +{ + if (m_context == nullptr) + return; + + set_current(); + ::glViewport(0, 0, w, h); + + ::glMatrixMode(GL_PROJECTION); + ::glLoadIdentity(); + + BoundingBoxf3 bbox = max_bounding_box(); + + switch (get_camera_type()) + { + case Camera::CT_Ortho: + { + float w2 = w; + float h2 = h; + float two_zoom = 2.0f * get_camera_zoom(); + if (two_zoom != 0.0f) + { + float inv_two_zoom = 1.0f / two_zoom; + w2 *= inv_two_zoom; + h2 *= inv_two_zoom; + } + + // FIXME: calculate a tighter value for depth will improve z-fighting + Pointf3 bb_size = bbox.size(); + float depth = 5.0f * (float)std::max(bb_size.x, std::max(bb_size.y, bb_size.z)); + ::glOrtho(-w2, w2, -h2, h2, -depth, depth); + + break; + } + case Camera::CT_Perspective: + { + float bbox_r = (float)bbox.radius(); + float fov = PI * 45.0f / 180.0f; + float fov_tan = tan(0.5f * fov); + float cam_distance = 0.5f * bbox_r / fov_tan; + set_camera_distance(cam_distance); + + float nr = cam_distance - bbox_r * 1.1f; + float fr = cam_distance + bbox_r * 1.1f; + if (nr < 1.0f) + nr = 1.0f; + + if (fr < nr + 1.0f) + fr = nr + 1.0f; + + float h2 = fov_tan * nr; + float w2 = h2 * w / h; + ::glFrustum(-w2, w2, -h2, h2, nr, fr); + + break; + } + default: + { + throw std::runtime_error("Invalid camera type."); + break; + } + } + + ::glMatrixMode(GL_MODELVIEW); + + set_dirty(false); +} + +GLVolumeCollection* GLCanvas3D::get_volumes() +{ + return m_volumes; +} + +void GLCanvas3D::set_volumes(GLVolumeCollection* volumes) +{ + m_volumes = volumes; +} + +void GLCanvas3D::set_bed_shape(const Pointfs& shape) +{ + m_bed.set_shape(shape); +} + bool GLCanvas3D::is_dirty() const { return m_dirty; @@ -123,11 +240,6 @@ void GLCanvas3D::set_dirty(bool dirty) m_dirty = dirty; } -bool GLCanvas3D::is_shown_on_screen() const -{ - return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; -} - GLCanvas3D::Camera::EType GLCanvas3D::get_camera_type() const { return m_camera.get_type(); @@ -200,5 +312,48 @@ void GLCanvas3D::on_size(wxSizeEvent& evt) set_dirty(true); } +BoundingBoxf3 GLCanvas3D::bed_bounding_box() const +{ + return m_bed.get_bounding_box(); +} + +BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const +{ + BoundingBoxf3 bb; + if (m_volumes != nullptr) + { + for (const GLVolume* volume : m_volumes->volumes) + { + if (!m_apply_zoom_to_volumes_filter || ((volume != nullptr) && volume->zoom_to_volumes)) + bb.merge(volume->transformed_bounding_box()); + } + } + return bb; +} + +BoundingBoxf3 GLCanvas3D::max_bounding_box() const +{ + BoundingBoxf3 bb = bed_bounding_box(); + bb.merge(volumes_bounding_box()); + return bb; +} + +void GLCanvas3D::_zoom_to_bed() +{ + _zoom_to_bounding_box(bed_bounding_box()); +} + +void GLCanvas3D::_zoom_to_volumes() +{ + m_apply_zoom_to_volumes_filter = true; + _zoom_to_bounding_box(volumes_bounding_box()); + m_apply_zoom_to_volumes_filter = false; +} + +void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) +{ + // >>>>>>>>>>>>>>>>>>>> TODO <<<<<<<<<<<<<<<<<<<<<<<< +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 14c1031dd0..26d0949f09 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -1,13 +1,16 @@ #ifndef slic3r_GLCanvas3D_hpp_ #define slic3r_GLCanvas3D_hpp_ -#include "../../libslic3r/Point.hpp" +#include "../../libslic3r/BoundingBox.hpp" class wxGLCanvas; class wxGLContext; class wxSizeEvent; namespace Slic3r { + +class GLVolumeCollection; + namespace GUI { class GLCanvas3D @@ -55,23 +58,49 @@ public: void set_target(const Pointf3& target); }; + class Bed + { + Pointfs m_shape; + BoundingBoxf3 m_bounding_box; + + public: + const Pointfs& get_shape() const; + void set_shape(const Pointfs& shape); + + const BoundingBoxf3& get_bounding_box() const; + + private: + void _calc_bounding_box(); + }; + private: wxGLCanvas* m_canvas; wxGLContext* m_context; Camera m_camera; + Bed m_bed; + + GLVolumeCollection* m_volumes; bool m_dirty; + bool m_apply_zoom_to_volumes_filter; public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); void set_current(); + bool is_shown_on_screen() const; + + void resize(unsigned int w, unsigned int h); + + GLVolumeCollection* get_volumes(); + void set_volumes(GLVolumeCollection* volumes); + + void set_bed_shape(const Pointfs& shape); + bool is_dirty() const; void set_dirty(bool dirty); - bool is_shown_on_screen() const; - Camera::EType get_camera_type() const; void set_camera_type(Camera::EType type); std::string get_camera_type_as_string() const; @@ -92,6 +121,15 @@ public: void set_camera_target(const Pointf3& target); void on_size(wxSizeEvent& evt); + + BoundingBoxf3 bed_bounding_box() const; + BoundingBoxf3 volumes_bounding_box() const; + BoundingBoxf3 max_bounding_box() const; + +private: + void _zoom_to_bed(); + void _zoom_to_volumes(); + void _zoom_to_bounding_box(const BoundingBoxf3& bbox); }; } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 5a565be283..b9bdde1616 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -146,6 +146,45 @@ bool GLCanvas3DManager::layer_editing_allowed() const return m_layer_editing.allowed; } +bool GLCanvas3DManager::is_shown_on_screen(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_shown_on_screen() : false; +} + +void GLCanvas3DManager::resize(wxGLCanvas* canvas, unsigned int w, unsigned int h) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->resize(w, h); +} + +GLVolumeCollection* GLCanvas3DManager::get_volumes(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_volumes() : nullptr; +} + +void GLCanvas3DManager::set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_volumes(volumes); +} + +void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_bed_shape(shape); +} + +BoundingBoxf3 GLCanvas3DManager::get_max_bounding_box(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->max_bounding_box() : BoundingBoxf3(); +} + bool GLCanvas3DManager::is_dirty(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -159,12 +198,6 @@ void GLCanvas3DManager::set_dirty(wxGLCanvas* canvas, bool dirty) it->second->set_dirty(dirty); } -bool GLCanvas3DManager::is_shown_on_screen(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_shown_on_screen() : false; -} - unsigned int GLCanvas3DManager::get_camera_type(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 858c2d9e7b..c53d6b5d36 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -52,11 +52,20 @@ public: bool use_VBOs() const; bool layer_editing_allowed() const; + bool is_shown_on_screen(wxGLCanvas* canvas) const; + + void resize(wxGLCanvas* canvas, unsigned int w, unsigned int h); + + GLVolumeCollection* get_volumes(wxGLCanvas* canvas); + void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); + + void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); + + BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); + bool is_dirty(wxGLCanvas* canvas) const; void set_dirty(wxGLCanvas* canvas, bool dirty); - bool is_shown_on_screen(wxGLCanvas* canvas) const; - unsigned int get_camera_type(wxGLCanvas* canvas) const; void set_camera_type(wxGLCanvas* canvas, unsigned int type); std::string get_camera_type_as_string(wxGLCanvas* canvas) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index c7933f0125..7e7fbc417b 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -185,6 +185,44 @@ remove_all_canvases() CODE: _3DScene::remove_all_canvases(); +void +resize(canvas, w, h) + SV *canvas; + unsigned int w; + unsigned int h; + CODE: + _3DScene::resize((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), w, h); + +GLVolumeCollection* +get_volumes(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_volumes(canvas, volumes) + SV *canvas; + GLVolumeCollection *volumes; + CODE: + _3DScene::set_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), volumes); + +void +set_bed_shape(canvas, shape) + SV *canvas; + Pointfs shape; + CODE: + _3DScene::set_bed_shape((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), shape); + +Clone +get_max_bounding_box(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_max_bounding_box((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + bool is_dirty(canvas) SV *canvas; From 986630c2dc366bc93629e8a8ae790278ece3636f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 14 May 2018 14:47:13 +0200 Subject: [PATCH 005/117] 3DScene's idle even handler moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 10 +++---- xs/src/slic3r/GUI/GLCanvas3D.cpp | 35 ++++++++++++++++++++----- xs/src/slic3r/GUI/GLCanvas3D.hpp | 7 +++-- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 1 + 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 60f1199808..9cdc51693e 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -236,16 +236,14 @@ sub new { #======================================================================================================================= # EVT_SIZE($self, sub { $self->_dirty(1) }); #======================================================================================================================= - EVT_IDLE($self, sub { #============================================================================================================================== - return unless Slic3r::GUI::_3DScene::is_dirty($self); - return unless Slic3r::GUI::_3DScene::is_shown_on_screen($self); +# EVT_IDLE($self, sub { # return unless $self->_dirty; # return if !$self->IsShownOnScreen; +# $self->Resize( $self->GetSizeWH ); +# $self->Refresh; +# }); #============================================================================================================================== - $self->Resize( $self->GetSizeWH ); - $self->Refresh; - }); EVT_MOUSEWHEEL($self, \&mouse_wheel_event); EVT_MOUSE_EVENTS($self, \&mouse_event); # EVT_KEY_DOWN($self, sub { diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 0e539aaefa..c520279742 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -305,13 +305,6 @@ void GLCanvas3D::set_camera_target(const Pointf3& target) m_camera.set_target(target); } -void GLCanvas3D::on_size(wxSizeEvent& evt) -{ - std::cout << "GLCanvas3D::on_size: " << (void*)this << std::endl; - - set_dirty(true); -} - BoundingBoxf3 GLCanvas3D::bed_bounding_box() const { return m_bed.get_bounding_box(); @@ -338,6 +331,24 @@ BoundingBoxf3 GLCanvas3D::max_bounding_box() const return bb; } +void GLCanvas3D::on_size(wxSizeEvent& evt) +{ + set_dirty(true); +} + +void GLCanvas3D::on_idle(wxIdleEvent& evt) +{ + if (!is_dirty() || !is_shown_on_screen()) + return; + + if (m_canvas != nullptr) + { + std::pair size = _get_canvas_size(); + resize((unsigned int)size.first, (unsigned int)size.second); + m_canvas->Refresh(); + } +} + void GLCanvas3D::_zoom_to_bed() { _zoom_to_bounding_box(bed_bounding_box()); @@ -355,5 +366,15 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) // >>>>>>>>>>>>>>>>>>>> TODO <<<<<<<<<<<<<<<<<<<<<<<< } +std::pair GLCanvas3D::_get_canvas_size() const +{ + std::pair ret(0, 0); + + if (m_canvas != nullptr) + m_canvas->GetSize(&ret.first, &ret.second); + + return ret; +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 26d0949f09..7a8e763f93 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -6,6 +6,7 @@ class wxGLCanvas; class wxGLContext; class wxSizeEvent; +class wxIdleEvent; namespace Slic3r { @@ -120,16 +121,18 @@ public: const Pointf3& get_camera_target() const; void set_camera_target(const Pointf3& target); - void on_size(wxSizeEvent& evt); - BoundingBoxf3 bed_bounding_box() const; BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 max_bounding_box() const; + void on_size(wxSizeEvent& evt); + void on_idle(wxIdleEvent& evt); + private: void _zoom_to_bed(); void _zoom_to_volumes(); void _zoom_to_bounding_box(const BoundingBoxf3& bbox); + std::pair _get_canvas_size() const; }; } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index b9bdde1616..a3036c2ad8 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -78,6 +78,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) return false; canvas->Bind(wxEVT_SIZE, [canvas3D](wxSizeEvent& evt) { canvas3D->on_size(evt); }); + canvas->Bind(wxEVT_IDLE, [canvas3D](wxIdleEvent& evt) { canvas3D->on_idle(evt); }); m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); From f4303ebdb888c322e0298d4f951343e511b44062 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 15 May 2018 09:50:01 +0200 Subject: [PATCH 006/117] 1st attempt of perl callback from c++ for 3DScene --- lib/Slic3r/GUI/3DScene.pm | 16 +-- lib/Slic3r/GUI/Plater.pm | 6 ++ xs/src/libslic3r/BoundingBox.cpp | 10 ++ xs/src/libslic3r/BoundingBox.hpp | 3 + xs/src/slic3r/GUI/3DScene.cpp | 5 + xs/src/slic3r/GUI/3DScene.hpp | 2 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 125 +++++++++++++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 9 ++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 7 ++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 + xs/xsp/GUI_3DScene.xsp | 6 ++ 11 files changed, 181 insertions(+), 10 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 9cdc51693e..86e5d9095d 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -926,13 +926,15 @@ sub zoom_to_bed { } } -sub zoom_to_volume { - my ($self, $volume_idx) = @_; - - my $volume = $self->volumes->[$volume_idx]; - my $bb = $volume->transformed_bounding_box; - $self->zoom_to_bounding_box($bb); -} +#============================================================================================================================== +#sub zoom_to_volume { +# my ($self, $volume_idx) = @_; +# +# my $volume = $self->volumes->[$volume_idx]; +# my $bb = $volume->transformed_bounding_box; +# $self->zoom_to_bounding_box($bb); +#} +#============================================================================================================================== sub zoom_to_volumes { my ($self) = @_; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index eb655ae55a..fb76914f01 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -141,6 +141,9 @@ sub new { $self->{canvas3D}->on_viewport_changed(sub { $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); }); +#============================================================================================================================== + Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); }); +#============================================================================================================================== } # Initialize 2D preview canvas @@ -157,6 +160,9 @@ sub new { $self->{preview3D}->canvas->on_viewport_changed(sub { $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); }); +#============================================================================================================================== + Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); }); +#============================================================================================================================== $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview')); $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; } diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp index 91ba88d84e..e23accbde2 100644 --- a/xs/src/libslic3r/BoundingBox.cpp +++ b/xs/src/libslic3r/BoundingBox.cpp @@ -222,6 +222,16 @@ BoundingBox3Base::center() const } template Pointf3 BoundingBox3Base::center() const; +//######################################################################################################################################33 +template coordf_t +BoundingBox3Base::max_size() const +{ + PointClass s = size(); + return std::max(s.x, std::max(s.y, s.z)); +} +template coordf_t BoundingBox3Base::max_size() const; +//######################################################################################################################################33 + // Align a coordinate to a grid. The coordinate may be negative, // the aligned value will never be bigger than the original one. static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) { diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index 92a2bd451c..7ce24f3a40 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -94,6 +94,9 @@ public: void translate(const Pointf3 &pos) { this->translate(pos.x, pos.y, pos.z); } void offset(coordf_t delta); PointClass center() const; +//######################################################################################################################################33 + coordf_t max_size() const; +//######################################################################################################################################33 bool contains(const PointClass &point) const { return BoundingBoxBase::contains(point) && point.z >= this->min.z && point.z <= this->max.z; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 6f485d5114..79de5e42d1 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1847,6 +1847,11 @@ void _3DScene::set_camera_target(wxGLCanvas* canvas, const Pointf3* target) s_canvas_mgr.set_camera_target(canvas, target); } +void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); +} + //void _3DScene::_glew_init() //{ // glewInit(); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 0e8cd2bcc1..219316c1ba 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -575,6 +575,8 @@ public: static Pointf3 get_camera_target(wxGLCanvas* canvas); static void set_camera_target(wxGLCanvas* canvas, const Pointf3* target); + static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); + // static void _glew_init(); //################################################################################################################## diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index c520279742..706e694d7b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -6,6 +6,7 @@ #include +static const bool TURNTABLE_MODE = true; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; namespace Slic3r { @@ -136,6 +137,11 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) { } +GLCanvas3D::~GLCanvas3D() +{ + _deregister_callbacks(); +} + void GLCanvas3D::set_current() { if ((m_canvas != nullptr) && (m_context != nullptr)) @@ -175,8 +181,7 @@ void GLCanvas3D::resize(unsigned int w, unsigned int h) } // FIXME: calculate a tighter value for depth will improve z-fighting - Pointf3 bb_size = bbox.size(); - float depth = 5.0f * (float)std::max(bb_size.x, std::max(bb_size.y, bb_size.z)); + float depth = 5.0f * (float)bbox.max_size(); ::glOrtho(-w2, w2, -h2, h2, -depth, depth); break; @@ -331,6 +336,12 @@ BoundingBoxf3 GLCanvas3D::max_bounding_box() const return bb; } +void GLCanvas3D::register_on_viewport_changed_callback(void* callback) +{ + if (callback != nullptr) + m_on_viewport_changed_callback.register_callback(callback); +} + void GLCanvas3D::on_size(wxSizeEvent& evt) { set_dirty(true); @@ -363,7 +374,24 @@ void GLCanvas3D::_zoom_to_volumes() void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { - // >>>>>>>>>>>>>>>>>>>> TODO <<<<<<<<<<<<<<<<<<<<<<<< + // Calculate the zoom factor needed to adjust viewport to bounding box. + float zoom = _get_zoom_to_bounding_box_factor(bbox); + if (zoom > 0.0f) + { + set_camera_zoom(zoom); + // center view around bounding box center + set_camera_target(bbox.center()); + + m_on_viewport_changed_callback.call(); + + if (is_shown_on_screen()) + { + std::pair size = _get_canvas_size(); + resize((unsigned int)size.first, (unsigned int)size.second); + if (m_canvas != nullptr) + m_canvas->Refresh(); + } + } } std::pair GLCanvas3D::_get_canvas_size() const @@ -376,5 +404,96 @@ std::pair GLCanvas3D::_get_canvas_size() const return ret; } +float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const +{ + float max_bb_size = bbox.max_size(); + if (max_bb_size == 0.0f) + return -1.0f; + + // 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) + { + // Turntable mode is enabled by default. + ::glRotatef(-get_camera_theta(), 1.0f, 0.0f, 0.0f); // pitch + ::glRotatef(get_camera_phi(), 0.0f, 0.0f, 1.0f); // yaw + } + else + { + // Shift the perspective camera. + Pointf3 camera_pos(0.0, 0.0, -(coordf_t)get_camera_distance()); + ::glTranslatef((float)camera_pos.x, (float)camera_pos.y, (float)camera_pos.z); +// my @rotmat = quat_to_rotmatrix($self->quat); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEMPORARY COMMENTED OUT +// glMultMatrixd_p(@rotmat[0..15]); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEMPORARY COMMENTED OUT + } + + const Pointf3& target = get_camera_target(); + ::glTranslatef(-(float)target.x, -(float)target.y, -(float)target.z); + + // get the view matrix back from opengl + GLfloat matrix[16]; + ::glGetFloatv(GL_MODELVIEW_MATRIX, matrix); + + // camera axes + Pointf3 right((coordf_t)matrix[0], (coordf_t)matrix[4], (coordf_t)matrix[8]); + Pointf3 up((coordf_t)matrix[1], (coordf_t)matrix[5], (coordf_t)matrix[9]); + Pointf3 forward((coordf_t)matrix[2], (coordf_t)matrix[6], (coordf_t)matrix[10]); + + Pointf3 bb_min = bbox.min; + Pointf3 bb_max = bbox.max; + Pointf3 bb_center = bbox.center(); + + // bbox vertices in world space + std::vector vertices; + vertices.reserve(8); + vertices.push_back(bb_min); + vertices.emplace_back(bb_max.x, bb_min.y, bb_min.z); + vertices.emplace_back(bb_max.x, bb_max.y, bb_min.z); + vertices.emplace_back(bb_min.x, bb_max.y, bb_min.z); + vertices.emplace_back(bb_min.x, bb_min.y, bb_max.z); + vertices.emplace_back(bb_max.x, bb_min.y, bb_max.z); + vertices.push_back(bb_max); + vertices.emplace_back(bb_min.x, bb_max.y, bb_max.z); + + coordf_t max_x = 0.0; + coordf_t max_y = 0.0; + + // margin factor to give some empty space around the bbox + coordf_t margin_factor = 1.25; + + for (const Pointf3 v : vertices) + { + // project vertex on the plane perpendicular to camera forward axis + Pointf3 pos(v.x - bb_center.x, v.y - bb_center.y, v.z - bb_center.z); + Pointf3 proj_on_plane = pos - dot(pos, forward) * forward; + + // calculates vertex coordinate along camera xy axes + coordf_t x_on_plane = dot(proj_on_plane, right); + coordf_t y_on_plane = dot(proj_on_plane, up); + + max_x = std::max(max_x, margin_factor * std::abs(x_on_plane)); + max_y = std::max(max_y, margin_factor * std::abs(y_on_plane)); + } + + if ((max_x == 0.0) || (max_y == 0.0)) + return -1.0f; + + max_x *= 2.0; + max_y *= 2.0; + + std::pair cvs_size = _get_canvas_size(); + return (float)std::min((coordf_t)cvs_size.first / max_x, (coordf_t)cvs_size.second / max_y); +} + +void GLCanvas3D::_deregister_callbacks() +{ + m_on_viewport_changed_callback.deregister_callback(); +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 7a8e763f93..9eda704a1d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -2,6 +2,7 @@ #define slic3r_GLCanvas3D_hpp_ #include "../../libslic3r/BoundingBox.hpp" +#include "../../libslic3r/Utils.hpp" class wxGLCanvas; class wxGLContext; @@ -85,8 +86,11 @@ private: bool m_dirty; bool m_apply_zoom_to_volumes_filter; + PerlCallback m_on_viewport_changed_callback; + public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); + ~GLCanvas3D(); void set_current(); @@ -125,6 +129,8 @@ public: BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 max_bounding_box() const; + void register_on_viewport_changed_callback(void* callback); + void on_size(wxSizeEvent& evt); void on_idle(wxIdleEvent& evt); @@ -133,6 +139,9 @@ private: void _zoom_to_volumes(); void _zoom_to_bounding_box(const BoundingBoxf3& bbox); std::pair _get_canvas_size() const; + float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; + + void _deregister_callbacks(); }; } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index a3036c2ad8..90c72f9504 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -289,6 +289,13 @@ void GLCanvas3DManager::set_camera_target(wxGLCanvas* canvas, const Pointf3* tar it->second->set_camera_target(*target); } +void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_viewport_changed_callback(callback); +} + GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index c53d6b5d36..a092fcfa53 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -85,6 +85,8 @@ public: Pointf3 get_camera_target(wxGLCanvas* canvas) const; void set_camera_target(wxGLCanvas* canvas, const Pointf3* target); + void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); + private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 7e7fbc417b..5e13fed652 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -344,6 +344,12 @@ set_camera_target(canvas, target) CODE: _3DScene::set_camera_target((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), target); +void +register_on_viewport_changed_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_viewport_changed_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); From 75f1f832aa01af7cf0595ee3e5feef51d45e5d0b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 15 May 2018 11:07:32 +0200 Subject: [PATCH 007/117] 3DScene bed origin moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 48 ++++++++++++++++++------- xs/src/slic3r/GUI/3DScene.cpp | 11 ++++++ xs/src/slic3r/GUI/3DScene.hpp | 3 ++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 20 +++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 7 ++++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 13 +++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 3 ++ xs/xsp/GUI_3DScene.xsp | 15 ++++++++ 8 files changed, 108 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 67f5866b77..b12f84a94d 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -53,7 +53,6 @@ __PACKAGE__->mk_accessors( qw(_quat init bed_grid_lines bed_polygon background - origin _mouse_pos _hover_volume_idx @@ -997,7 +996,10 @@ sub set_auto_bed_shape { [ $center->x - $max_size, $center->y + $max_size ], #++ ]); # Set the origin for painting of the coordinate system axes. - $self->origin(Slic3r::Pointf->new(@$center[X,Y])); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_bed_origin($self, Slic3r::Pointf->new(@$center[X,Y])); +# $self->origin(Slic3r::Pointf->new(@$center[X,Y])); +#============================================================================================================================== } # Set the bed shape to a single closed 2D polygon (array of two element arrays), @@ -1048,7 +1050,10 @@ sub set_bed_shape { } # Set the origin for painting of the coordinate system axes. - $self->origin(Slic3r::Pointf->new(0,0)); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_bed_origin($self, Slic3r::Pointf->new(0,0)); +# $self->origin(Slic3r::Pointf->new(0,0)); +#============================================================================================================================== $self->bed_polygon(offset_ex([$expolygon->contour], $bed_bb->radius * 1.7, JT_ROUND, scale(0.5))->[0]->contour->clone); } @@ -1548,7 +1553,10 @@ sub Render { # draw axes # disable depth testing so that axes are not covered by ground glDisable(GL_DEPTH_TEST); - my $origin = $self->origin; +#============================================================================================================================== + my $origin = Slic3r::GUI::_3DScene::get_bed_origin($self); +# my $origin = $self->origin; +#============================================================================================================================== my $axis_len = $self->use_plain_shader ? 0.3 * max(@{ $self->bed_bounding_box->size }) : 2 * max(@{ $volumes_bb->size }); glLineWidth(2); glBegin(GL_LINES); @@ -1906,10 +1914,18 @@ sub draw_legend { 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; +#============================================================================================================================== + my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); + my $l = (-0.5 * $cw) / $zoom; + my $t = (0.5 * $ch) / $zoom; + my $r = $l + $tex_w / $zoom; + my $b = $t - $tex_h / $zoom; + +# 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(); @@ -1939,10 +1955,18 @@ sub draw_warning { my ($cw, $ch) = $self->GetSizeWH; - my $l = (-0.5 * $tex_w) / $self->_zoom; - my $t = (-0.5 * $ch + $tex_h) / $self->_zoom; - my $r = $l + $tex_w / $self->_zoom; - my $b = $t - $tex_h / $self->_zoom; +#============================================================================================================================== + my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); + my $l = (-0.5 * $tex_w) / $zoom; + my $t = (-0.5 * $ch + $tex_h) / $zoom; + my $r = $l + $tex_w / $zoom; + my $b = $t - $tex_h / $zoom; + +# 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(); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 98326e95e0..c6effb0a9b 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1767,6 +1767,17 @@ void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) return s_canvas_mgr.set_bed_shape(canvas, shape); } +Pointf _3DScene::get_bed_origin(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_bed_origin(canvas); +} + +void _3DScene::set_bed_origin(wxGLCanvas* canvas, const Pointf* origin) +{ + if (origin != nullptr) + s_canvas_mgr.set_bed_origin(canvas, *origin); +} + BoundingBoxf3 _3DScene::get_bed_bounding_box(wxGLCanvas* canvas) { return s_canvas_mgr.get_bed_bounding_box(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 66f506a7f8..5c603028d7 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -551,6 +551,9 @@ public: static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); + static Pointf get_bed_origin(wxGLCanvas* canvas); + static void set_bed_origin(wxGLCanvas* canvas, const Pointf* origin); + static BoundingBoxf3 get_bed_bounding_box(wxGLCanvas* canvas); static BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); static BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index d358ebeca3..487ffbc75d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -119,6 +119,16 @@ const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const return m_bounding_box; } +const Pointf& GLCanvas3D::Bed::get_origin() const +{ + return m_origin; +} + +void GLCanvas3D::Bed::set_origin(const Pointf& origin) +{ + m_origin = origin; +} + void GLCanvas3D::Bed::_calc_bounding_box() { m_bounding_box = BoundingBoxf3(); @@ -235,6 +245,16 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) m_bed.set_shape(shape); } +const Pointf& GLCanvas3D::get_bed_origin() const +{ + return m_bed.get_origin(); +} + +void GLCanvas3D::set_bed_origin(const Pointf& origin) +{ + m_bed.set_origin(origin); +} + bool GLCanvas3D::is_dirty() const { return m_dirty; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 41c0e0b4af..d324215424 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -64,6 +64,7 @@ public: { Pointfs m_shape; BoundingBoxf3 m_bounding_box; + Pointf m_origin; public: const Pointfs& get_shape() const; @@ -71,6 +72,9 @@ public: const BoundingBoxf3& get_bounding_box() const; + const Pointf& get_origin() const; + void set_origin(const Pointf& origin); + private: void _calc_bounding_box(); }; @@ -103,6 +107,9 @@ public: void set_bed_shape(const Pointfs& shape); + const Pointf& get_bed_origin() const; + void set_bed_origin(const Pointf& origin); + bool is_dirty() const; void set_dirty(bool dirty); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 7c0db99d3c..9bde6210dc 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -180,6 +180,19 @@ void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) it->second->set_bed_shape(shape); } +Pointf GLCanvas3DManager::get_bed_origin(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_bed_origin() : Pointf(); +} + +void GLCanvas3DManager::set_bed_origin(wxGLCanvas* canvas, const Pointf& origin) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_bed_origin(origin); +} + BoundingBoxf3 GLCanvas3DManager::get_bed_bounding_box(wxGLCanvas* canvas) { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index d7e8f0b2f0..4edf97dd6d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -61,6 +61,9 @@ public: void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); + Pointf get_bed_origin(wxGLCanvas* canvas) const; + void set_bed_origin(wxGLCanvas* canvas, const Pointf& origin); + BoundingBoxf3 get_bed_bounding_box(wxGLCanvas* canvas); BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 4d155816d7..ee1a94cc8b 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -215,6 +215,21 @@ set_bed_shape(canvas, shape) CODE: _3DScene::set_bed_shape((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), shape); +Clone +get_bed_origin(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_bed_origin((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_bed_origin(canvas, origin) + SV *canvas; + Pointf *origin + CODE: + _3DScene::set_bed_origin((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), origin); + Clone get_bed_bounding_box(canvas) SV *canvas; From f0d1888ca9e00f2c68d7b325b073553d49b97aba Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 15 May 2018 11:30:11 +0200 Subject: [PATCH 008/117] 3DScene select_view() function moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 55 ++++++++++++------------- xs/src/slic3r/GUI/3DScene.cpp | 5 +++ xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 40 ++++++++++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 7 ++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 1 + xs/xsp/GUI_3DScene.xsp | 7 ++++ 8 files changed, 89 insertions(+), 28 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index b12f84a94d..afe79330de 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -761,41 +761,40 @@ sub set_viewport_from_scene { # zoom to volumes. sub select_view { my ($self, $direction) = @_; - my $dirvec; - if (ref($direction)) { - $dirvec = $direction; - } else { - if ($direction eq 'iso') { - $dirvec = VIEW_DEFAULT; - } elsif ($direction eq 'left') { - $dirvec = VIEW_LEFT; - } elsif ($direction eq 'right') { - $dirvec = VIEW_RIGHT; - } elsif ($direction eq 'top') { - $dirvec = VIEW_TOP; - } elsif ($direction eq 'bottom') { - $dirvec = VIEW_BOTTOM; - } elsif ($direction eq 'front') { - $dirvec = VIEW_FRONT; - } elsif ($direction eq 'rear') { - $dirvec = VIEW_REAR; - } - } - my $bb = $self->volumes_bounding_box; - if (! $bb->empty) { #============================================================================================================================== - Slic3r::GUI::_3DScene::set_camera_phi($self, $dirvec->[0]); - Slic3r::GUI::_3DScene::set_camera_theta($self, $dirvec->[1]); - + Slic3r::GUI::_3DScene::select_view($self, $direction); + +# my $dirvec; +# if (ref($direction)) { +# $dirvec = $direction; +# } else { +# if ($direction eq 'iso') { +# $dirvec = VIEW_DEFAULT; +# } elsif ($direction eq 'left') { +# $dirvec = VIEW_LEFT; +# } elsif ($direction eq 'right') { +# $dirvec = VIEW_RIGHT; +# } elsif ($direction eq 'top') { +# $dirvec = VIEW_TOP; +# } elsif ($direction eq 'bottom') { +# $dirvec = VIEW_BOTTOM; +# } elsif ($direction eq 'front') { +# $dirvec = VIEW_FRONT; +# } elsif ($direction eq 'rear') { +# $dirvec = VIEW_REAR; +# } +# } +# my $bb = $self->volumes_bounding_box; +# if (! $bb->empty) { # $self->_sphi($dirvec->[0]); # $self->_stheta($dirvec->[1]); # # Avoid gimball lock. # $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; # $self->_stheta(0) if $self->_stheta < 0; +# $self->on_viewport_changed->() if $self->on_viewport_changed; +# $self->Refresh; +# } #============================================================================================================================== - $self->on_viewport_changed->() if $self->on_viewport_changed; - $self->Refresh; - } } sub get_zoom_to_bounding_box_factor { diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index c6effb0a9b..ed2b7f8b85 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1878,6 +1878,11 @@ void _3DScene::zoom_to_volumes(wxGLCanvas* canvas) s_canvas_mgr.zoom_to_volumes(canvas); } +void _3DScene::select_view(wxGLCanvas* canvas, const std::string& direction) +{ + s_canvas_mgr.select_view(canvas, direction); +} + void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 5c603028d7..5010d9c2c8 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -582,6 +582,7 @@ public: static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); + static void select_view(wxGLCanvas* canvas, const std::string& direction); static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 487ffbc75d..940a5b79c2 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -9,6 +9,15 @@ static const bool TURNTABLE_MODE = true; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; +// phi / theta angles to orient the camera. +static const float VIEW_DEFAULT[2] = { 45.0f, 45.0f }; +static const float VIEW_LEFT[2] = { 90.0f, 90.0f }; +static const float VIEW_RIGHT[2] = { -90.0f, 90.0f }; +static const float VIEW_TOP[2] = { 0.0f, 0.0f }; +static const float VIEW_BOTTOM[2] = { 0.0f, 180.0f }; +static const float VIEW_FRONT[2] = { 0.0f, 90.0f }; +static const float VIEW_REAR[2] = { 180.0f, 90.0f }; + namespace Slic3r { namespace GUI { @@ -368,6 +377,37 @@ void GLCanvas3D::zoom_to_volumes() m_apply_zoom_to_volumes_filter = false; } +void GLCanvas3D::select_view(const std::string& direction) +{ + const float* dir_vec = nullptr; + + if (direction == "iso") + dir_vec = VIEW_DEFAULT; + else if (direction == "left") + dir_vec = VIEW_LEFT; + else if (direction == "right") + dir_vec = VIEW_RIGHT; + else if (direction == "top") + dir_vec = VIEW_TOP; + else if (direction == "bottom") + dir_vec = VIEW_BOTTOM; + else if (direction == "front") + dir_vec = VIEW_FRONT; + else if (direction == "rear") + dir_vec = VIEW_REAR; + + if ((dir_vec != nullptr) && !empty(volumes_bounding_box())) + { + m_camera.set_phi(dir_vec[0]); + m_camera.set_theta(dir_vec[1]); + + m_on_viewport_changed_callback.call(); + + if (m_canvas != nullptr) + m_canvas->Refresh(); + } +} + void GLCanvas3D::register_on_viewport_changed_callback(void* callback) { if (callback != nullptr) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index d324215424..7568dec39c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -138,6 +138,7 @@ public: void zoom_to_bed(); void zoom_to_volumes(); + void select_view(const std::string& direction); void register_on_viewport_changed_callback(void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 9bde6210dc..f5b76fe60f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -328,6 +328,13 @@ void GLCanvas3DManager::zoom_to_volumes(wxGLCanvas* canvas) it->second->zoom_to_volumes(); } +void GLCanvas3DManager::select_view(wxGLCanvas* canvas, const std::string& direction) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->select_view(direction); +} + void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 4edf97dd6d..c1ce8048c2 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -92,6 +92,7 @@ public: void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); + void select_view(wxGLCanvas* canvas, const std::string& direction); void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index ee1a94cc8b..7211cc513e 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -387,6 +387,13 @@ zoom_to_volumes(canvas) CODE: _3DScene::zoom_to_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); +void +select_view(canvas, direction) + SV *canvas; + std::string direction; + CODE: + _3DScene::select_view((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), direction); + void register_on_viewport_changed_callback(canvas, callback) SV *canvas; From 2b4829a4b9605194076c48778f03f4592c28c04b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 15 May 2018 15:38:25 +0200 Subject: [PATCH 009/117] 3DScene bed variables moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 241 +++++++++++----------- lib/Slic3r/GUI/Plater/3D.pm | 5 +- lib/Slic3r/GUI/Plater/3DPreview.pm | 5 +- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 5 +- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 5 +- xs/src/slic3r/GUI/3DScene.cpp | 30 ++- xs/src/slic3r/GUI/3DScene.hpp | 9 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 197 +++++++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 34 ++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 40 ++-- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 9 +- xs/xsp/GUI_3DScene.xsp | 12 ++ 12 files changed, 425 insertions(+), 167 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index afe79330de..89e46a55db 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -48,10 +48,6 @@ __PACKAGE__->mk_accessors( qw(_quat init volumes cutting_plane_z cut_lines_vertices - bed_shape - bed_triangles - bed_grid_lines - bed_polygon background _mouse_pos _hover_volume_idx @@ -124,14 +120,16 @@ use constant SELECTED_COLOR => [0,1,0,1]; # For mesh selection: Mouse hovers over the object, but object not selected yet - dark green. use constant HOVER_COLOR => [0.4,0.9,0,1]; -# phi / theta angles to orient the camera. -use constant VIEW_DEFAULT => [45.0,45.0]; -use constant VIEW_LEFT => [90.0,90.0]; -use constant VIEW_RIGHT => [-90.0,90.0]; -use constant VIEW_TOP => [0.0,0.0]; -use constant VIEW_BOTTOM => [0.0,180.0]; -use constant VIEW_FRONT => [0.0,90.0]; -use constant VIEW_REAR => [180.0,90.0]; +#============================================================================================================================== +## phi / theta angles to orient the camera. +#use constant VIEW_DEFAULT => [45.0,45.0]; +#use constant VIEW_LEFT => [90.0,90.0]; +#use constant VIEW_RIGHT => [-90.0,90.0]; +#use constant VIEW_TOP => [0.0,0.0]; +#use constant VIEW_BOTTOM => [0.0,180.0]; +#use constant VIEW_FRONT => [0.0,90.0]; +#use constant VIEW_REAR => [180.0,90.0]; +#============================================================================================================================== use constant MANIPULATION_IDLE => 0; use constant MANIPULATION_DRAGGING => 1; @@ -547,17 +545,21 @@ sub mouse_event { $self->mouse_to_3d($e->GetX, $e->GetY, 0), $self->mouse_to_3d($e->GetX, $e->GetY, 1)) ->intersect_plane($self->_drag_start_pos->z); - # Clip the new position, so the object center remains close to the bed. - { - $cur_pos->translate(@{$self->_drag_volume_center_offset}); - my $cur_pos2 = Slic3r::Point->new(scale($cur_pos->x), scale($cur_pos->y)); - if (! $self->bed_polygon->contains_point($cur_pos2)) { - my $ip = $self->bed_polygon->point_projection($cur_pos2); - $cur_pos->set_x(unscale($ip->x)); - $cur_pos->set_y(unscale($ip->y)); - } - $cur_pos->translate(@{$self->_drag_volume_center_offset->negative}); - } +#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +# >>>>>>>>>>>>>>>>>>>>>>>>>> TEMPORARY DISABLED DUE TO bed_polygon REMOVAL <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +# +# # Clip the new position, so the object center remains close to the bed. +# { +# $cur_pos->translate(@{$self->_drag_volume_center_offset}); +# my $cur_pos2 = Slic3r::Point->new(scale($cur_pos->x), scale($cur_pos->y)); +# if (! $self->bed_polygon->contains_point($cur_pos2)) { +# my $ip = $self->bed_polygon->point_projection($cur_pos2); +# $cur_pos->set_x(unscale($ip->x)); +# $cur_pos->set_y(unscale($ip->y)); +# } +# $cur_pos->translate(@{$self->_drag_volume_center_offset->negative}); +# } +#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ # Calculate the translation vector. my $vector = $self->_drag_start_pos->vector_to($cur_pos); # Get the volume being dragged. @@ -980,83 +982,76 @@ sub max_bounding_box { #============================================================================================================================== } -# Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane -# to support the scene objects. -sub set_auto_bed_shape { - my ($self, $bed_shape) = @_; - - # draw a default square bed around object center - my $max_size = max(@{ $self->volumes_bounding_box->size }); - my $center = $self->volumes_bounding_box->center; - $self->set_bed_shape([ - [ $center->x - $max_size, $center->y - $max_size ], #-- - [ $center->x + $max_size, $center->y - $max_size ], #-- - [ $center->x + $max_size, $center->y + $max_size ], #++ - [ $center->x - $max_size, $center->y + $max_size ], #++ - ]); - # Set the origin for painting of the coordinate system axes. #============================================================================================================================== - Slic3r::GUI::_3DScene::set_bed_origin($self, Slic3r::Pointf->new(@$center[X,Y])); +## Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane +## to support the scene objects. +#sub set_auto_bed_shape { +# my ($self, $bed_shape) = @_; +# +# # draw a default square bed around object center +# my $max_size = max(@{ $self->volumes_bounding_box->size }); +# my $center = $self->volumes_bounding_box->center; +# $self->set_bed_shape([ +# [ $center->x - $max_size, $center->y - $max_size ], #-- +# [ $center->x + $max_size, $center->y - $max_size ], #-- +# [ $center->x + $max_size, $center->y + $max_size ], #++ +# [ $center->x - $max_size, $center->y + $max_size ], #++ +# ]); +# # Set the origin for painting of the coordinate system axes. # $self->origin(Slic3r::Pointf->new(@$center[X,Y])); -#============================================================================================================================== -} - -# Set the bed shape to a single closed 2D polygon (array of two element arrays), -# triangulate the bed and store the triangles into $self->bed_triangles, -# fills the $self->bed_grid_lines and sets $self->origin. -# Sets $self->bed_polygon to limit the object placement. -sub set_bed_shape { - my ($self, $bed_shape) = @_; - - $self->bed_shape($bed_shape); -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_bed_shape($self, $bed_shape); -#============================================================================================================================== - - # triangulate bed - my $expolygon = Slic3r::ExPolygon->new([ map [map scale($_), @$_], @$bed_shape ]); - my $bed_bb = $expolygon->bounding_box; - - { - my @points = (); - foreach my $triangle (@{ $expolygon->triangulate }) { - push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$triangle; - } - $self->bed_triangles(OpenGL::Array->new_list(GL_FLOAT, @points)); - } - - { - my @polylines = (); - for (my $x = $bed_bb->x_min; $x <= $bed_bb->x_max; $x += scale 10) { - push @polylines, Slic3r::Polyline->new([$x,$bed_bb->y_min], [$x,$bed_bb->y_max]); - } - for (my $y = $bed_bb->y_min; $y <= $bed_bb->y_max; $y += scale 10) { - push @polylines, Slic3r::Polyline->new([$bed_bb->x_min,$y], [$bed_bb->x_max,$y]); - } - # clip with a slightly grown expolygon because our lines lay on the contours and - # may get erroneously clipped - my @lines = map Slic3r::Line->new(@$_[0,-1]), - @{intersection_pl(\@polylines, [ @{$expolygon->offset(+scaled_epsilon)} ])}; - - # append bed contours - push @lines, map @{$_->lines}, @$expolygon; - - my @points = (); - foreach my $line (@lines) { - push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$line; #)) - } - $self->bed_grid_lines(OpenGL::Array->new_list(GL_FLOAT, @points)); - } - - # Set the origin for painting of the coordinate system axes. -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_bed_origin($self, Slic3r::Pointf->new(0,0)); +#} +# +## Set the bed shape to a single closed 2D polygon (array of two element arrays), +## triangulate the bed and store the triangles into $self->bed_triangles, +## fills the $self->bed_grid_lines and sets $self->origin. +## Sets $self->bed_polygon to limit the object placement. +#sub set_bed_shape { +# my ($self, $bed_shape) = @_; +# +# $self->bed_shape($bed_shape); +# +# # triangulate bed +# my $expolygon = Slic3r::ExPolygon->new([ map [map scale($_), @$_], @$bed_shape ]); +# my $bed_bb = $expolygon->bounding_box; +# +# { +# my @points = (); +# foreach my $triangle (@{ $expolygon->triangulate }) { +# push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$triangle; +# } +# $self->bed_triangles(OpenGL::Array->new_list(GL_FLOAT, @points)); +# } +# +# { +# my @polylines = (); +# for (my $x = $bed_bb->x_min; $x <= $bed_bb->x_max; $x += scale 10) { +# push @polylines, Slic3r::Polyline->new([$x,$bed_bb->y_min], [$x,$bed_bb->y_max]); +# } +# for (my $y = $bed_bb->y_min; $y <= $bed_bb->y_max; $y += scale 10) { +# push @polylines, Slic3r::Polyline->new([$bed_bb->x_min,$y], [$bed_bb->x_max,$y]); +# } +# # clip with a slightly grown expolygon because our lines lay on the contours and +# # may get erroneously clipped +# my @lines = map Slic3r::Line->new(@$_[0,-1]), +# @{intersection_pl(\@polylines, [ @{$expolygon->offset(+scaled_epsilon)} ])}; +# +# # append bed contours +# push @lines, map @{$_->lines}, @$expolygon; +# +# my @points = (); +# foreach my $line (@lines) { +# push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$line; #)) +# } +# $self->bed_grid_lines(OpenGL::Array->new_list(GL_FLOAT, @points)); +# } +# +# # Set the origin for painting of the coordinate system axes. # $self->origin(Slic3r::Pointf->new(0,0)); +# +# $self->bed_polygon(offset_ex([$expolygon->contour], $bed_bb->radius * 1.7, JT_ROUND, scale(0.5))->[0]->contour->clone); +#} #============================================================================================================================== - $self->bed_polygon(offset_ex([$expolygon->contour], $bed_bb->radius * 1.7, JT_ROUND, scale(0.5))->[0]->contour->clone); -} - sub deselect_volumes { my ($self) = @_; $_->set_selected(0) for @{$self->volumes}; @@ -1518,33 +1513,37 @@ sub Render { # draw ground my $ground_z = GROUND_Z; - if ($self->bed_triangles) { - glDisable(GL_DEPTH_TEST); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glEnableClientState(GL_VERTEX_ARRAY); - glColor4f(0.8, 0.6, 0.5, 0.4); - glNormal3d(0,0,1); - glVertexPointer_c(3, GL_FLOAT, 0, $self->bed_triangles->ptr()); - glDrawArrays(GL_TRIANGLES, 0, $self->bed_triangles->elements / 3); - glDisableClientState(GL_VERTEX_ARRAY); - - # we need depth test for grid, otherwise it would disappear when looking - # the object from below - glEnable(GL_DEPTH_TEST); +#============================================================================================================================== + Slic3r::GUI::_3DScene::render($self); - # draw grid - glLineWidth(3); - glColor4f(0.2, 0.2, 0.2, 0.4); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer_c(3, GL_FLOAT, 0, $self->bed_grid_lines->ptr()); - glDrawArrays(GL_LINES, 0, $self->bed_grid_lines->elements / 3); - glDisableClientState(GL_VERTEX_ARRAY); - - glDisable(GL_BLEND); - } +# if ($self->bed_triangles) { +# glDisable(GL_DEPTH_TEST); +# +# glEnable(GL_BLEND); +# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +# +# glEnableClientState(GL_VERTEX_ARRAY); +# glColor4f(0.8, 0.6, 0.5, 0.4); +# glNormal3d(0,0,1); +# glVertexPointer_c(3, GL_FLOAT, 0, $self->bed_triangles->ptr()); +# glDrawArrays(GL_TRIANGLES, 0, $self->bed_triangles->elements / 3); +# glDisableClientState(GL_VERTEX_ARRAY); +# +# # we need depth test for grid, otherwise it would disappear when looking +# # the object from below +# glEnable(GL_DEPTH_TEST); +# +# # draw grid +# glLineWidth(3); +# glColor4f(0.2, 0.2, 0.2, 0.4); +# glEnableClientState(GL_VERTEX_ARRAY); +# glVertexPointer_c(3, GL_FLOAT, 0, $self->bed_grid_lines->ptr()); +# glDrawArrays(GL_LINES, 0, $self->bed_grid_lines->elements / 3); +# glDisableClientState(GL_VERTEX_ARRAY); +# +# glDisable(GL_BLEND); +# } +#============================================================================================================================== my $volumes_bb = $self->volumes_bounding_box; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index c9c9542762..2706df509f 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -255,7 +255,10 @@ sub reload_scene { sub update_bed_size { my ($self) = @_; - $self->set_bed_shape($self->{config}->bed_shape); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_bed_shape($self, $self->{config}->bed_shape); +# $self->set_bed_shape($self->{config}->bed_shape); +#============================================================================================================================== } # Called by the Platter wxNotebook when this page is activated. diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index b746de98f4..88aab7786e 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -474,7 +474,10 @@ sub set_z_idx_high sub set_bed_shape { my ($self, $bed_shape) = @_; - $self->canvas->set_bed_shape($bed_shape); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_bed_shape($self->canvas, $bed_shape); +# $self->canvas->set_bed_shape($bed_shape); +#============================================================================================================================== } sub set_number_extruders { diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index f0f50a4f3f..6d3234ff3f 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -113,7 +113,10 @@ sub new { if ($Slic3r::GUI::have_OpenGL) { $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); $canvas->load_object($self->{model_object}, undef, undef, [0]); - $canvas->set_auto_bed_shape; +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); +# $canvas->set_auto_bed_shape; +#============================================================================================================================== $canvas->SetSize([500,500]); $canvas->SetMinSize($canvas->GetSize); } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 322491f9ec..ee1bf22d10 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -160,7 +160,10 @@ sub new { }); $canvas->load_object($self->{model_object}, undef, undef, [0]); - $canvas->set_auto_bed_shape; +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); +# $canvas->set_auto_bed_shape; +#============================================================================================================================== $canvas->SetSize([500,700]); $canvas->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index ed2b7f8b85..f5193668b4 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1747,6 +1747,16 @@ void _3DScene::resize(wxGLCanvas* canvas, unsigned int w, unsigned int h) s_canvas_mgr.resize(canvas, w, h); } +bool _3DScene::is_dirty(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_dirty(canvas); +} + +void _3DScene::set_dirty(wxGLCanvas* canvas, bool dirty) +{ + s_canvas_mgr.set_dirty(canvas, dirty); +} + bool _3DScene::is_shown_on_screen(wxGLCanvas* canvas) { return s_canvas_mgr.is_shown_on_screen(canvas); @@ -1767,6 +1777,11 @@ void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) return s_canvas_mgr.set_bed_shape(canvas, shape); } +void _3DScene::set_auto_bed_shape(wxGLCanvas* canvas) +{ + return s_canvas_mgr.set_auto_bed_shape(canvas); +} + Pointf _3DScene::get_bed_origin(wxGLCanvas* canvas) { return s_canvas_mgr.get_bed_origin(canvas); @@ -1793,16 +1808,6 @@ BoundingBoxf3 _3DScene::get_max_bounding_box(wxGLCanvas* canvas) return s_canvas_mgr.get_max_bounding_box(canvas); } -bool _3DScene::is_dirty(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_dirty(canvas); -} - -void _3DScene::set_dirty(wxGLCanvas* canvas, bool dirty) -{ - s_canvas_mgr.set_dirty(canvas, dirty); -} - unsigned int _3DScene::get_camera_type(wxGLCanvas* canvas) { return s_canvas_mgr.get_camera_type(canvas); @@ -1883,6 +1888,11 @@ void _3DScene::select_view(wxGLCanvas* canvas, const std::string& direction) s_canvas_mgr.select_view(canvas, direction); } +void _3DScene::render(wxGLCanvas* canvas) +{ + s_canvas_mgr.render(canvas); +} + void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 5010d9c2c8..8d6345f971 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -544,12 +544,16 @@ public: static void resize(wxGLCanvas* canvas, unsigned int w, unsigned int h); + static bool is_dirty(wxGLCanvas* canvas); + static void set_dirty(wxGLCanvas* canvas, bool dirty); + static bool is_shown_on_screen(wxGLCanvas* canvas); static GLVolumeCollection* get_volumes(wxGLCanvas* canvas); static void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); + static void set_auto_bed_shape(wxGLCanvas* canvas); static Pointf get_bed_origin(wxGLCanvas* canvas); static void set_bed_origin(wxGLCanvas* canvas, const Pointf* origin); @@ -558,9 +562,6 @@ public: static BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); static BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); - static bool is_dirty(wxGLCanvas* canvas); - static void set_dirty(wxGLCanvas* canvas, bool dirty); - static unsigned int get_camera_type(wxGLCanvas* canvas); static void set_camera_type(wxGLCanvas* canvas, unsigned int type); static std::string get_camera_type_as_string(wxGLCanvas* canvas); @@ -584,6 +585,8 @@ public: static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); + static void render(wxGLCanvas* canvas); + static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); // static void _glew_init(); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 940a5b79c2..2e4b15af4b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,6 +1,7 @@ #include "GLCanvas3D.hpp" #include "../../slic3r/GUI/3DScene.hpp" +#include "../../libslic3r/ClipperUtils.hpp" #include @@ -8,6 +9,7 @@ static const bool TURNTABLE_MODE = true; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; +static const float GROUND_Z = -0.02f; // phi / theta angles to orient the camera. static const float VIEW_DEFAULT[2] = { 45.0f, 45.0f }; @@ -21,6 +23,65 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f }; namespace Slic3r { namespace GUI { +bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z) +{ + m_data.clear(); + + unsigned int size = 9 * (unsigned int)triangles.size(); + if (size == 0) + return false; + + m_data = std::vector(size, 0.0f); + + unsigned int coord = 0; + for (const Polygon& t : triangles) + { + for (unsigned int v = 0; v < 3; ++v) + { + const Point& p = t.points[v]; + m_data[coord++] = (float)unscale(p.x); + m_data[coord++] = (float)unscale(p.y); + m_data[coord++] = z; + } + } + + return true; +} + +bool GeometryBuffer::set_from_lines(const Lines& lines, float z) +{ + m_data.clear(); + + unsigned int size = 6 * (unsigned int)lines.size(); + if (size == 0) + return false; + + m_data = std::vector(size, 0.0f); + + unsigned int coord = 0; + for (const Line& l : lines) + { + m_data[coord++] = (float)unscale(l.a.x); + m_data[coord++] = (float)unscale(l.a.y); + m_data[coord++] = z; + m_data[coord++] = (float)unscale(l.b.x); + m_data[coord++] = (float)unscale(l.b.y); + m_data[coord++] = z; + } + + return true; +} + +const float* GeometryBuffer::get_data() const +{ + return m_data.data(); +} + +unsigned int GeometryBuffer::get_data_size() const +{ + return (unsigned int)m_data.size(); +} + GLCanvas3D::Camera::Camera() : m_type(CT_Ortho) , m_zoom(1.0f) @@ -120,7 +181,23 @@ const Pointfs& GLCanvas3D::Bed::get_shape() const void GLCanvas3D::Bed::set_shape(const Pointfs& shape) { m_shape = shape; + _calc_bounding_box(); + + ExPolygon poly; + for (const Pointf& p : m_shape) + { + poly.contour.append(Point(scale_(p.x), scale_(p.y))); + } + + _calc_triangles(poly); + + const BoundingBox& bed_bbox = poly.contour.bounding_box(); + _calc_gridlines(poly, bed_bbox); + + m_polygon = offset_ex(poly.contour, bed_bbox.radius() * 1.7, jtRound, scale_(0.5))[0].contour; + + set_origin(Pointf(0.0, 0.0)); } const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const @@ -138,6 +215,43 @@ void GLCanvas3D::Bed::set_origin(const Pointf& origin) m_origin = origin; } +void GLCanvas3D::Bed::render() +{ + unsigned int triangles_vcount = m_triangles.get_data_size() / 3; + if (triangles_vcount > 0) + { + ::glDisable(GL_DEPTH_TEST); + + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ::glEnableClientState(GL_VERTEX_ARRAY); + + ::glColor4f(0.8f, 0.6f, 0.5f, 0.4f); + ::glNormal3d(0.0f, 0.0f, 1.0f); + ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_data()); + ::glDrawArrays(GL_TRIANGLES, 0, triangles_vcount); +// ::glDisableClientState(GL_VERTEX_ARRAY); + + // we need depth test for grid, otherwise it would disappear when looking + // the object from below + glEnable(GL_DEPTH_TEST); + + // draw grid + unsigned int gridlines_vcount = m_gridlines.get_data_size() / 3; + + ::glLineWidth(3.0f); + ::glColor4f(0.2f, 0.2f, 0.2f, 0.4f); +// ::glEnableClientState(GL_VERTEX_ARRAY); + ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_data()); + ::glDrawArrays(GL_LINES, 0, gridlines_vcount); + + ::glDisableClientState(GL_VERTEX_ARRAY); + + ::glDisable(GL_BLEND); + } +} + void GLCanvas3D::Bed::_calc_bounding_box() { m_bounding_box = BoundingBoxf3(); @@ -147,6 +261,44 @@ void GLCanvas3D::Bed::_calc_bounding_box() } } +void GLCanvas3D::Bed::_calc_triangles(const ExPolygon& poly) +{ + Polygons triangles; + poly.triangulate(&triangles); + + if (!m_triangles.set_from_triangles(triangles, GROUND_Z)) + printf("Unable to create bed triangles\n"); +} + +void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) +{ + Polylines axes_lines; + for (coord_t x = bed_bbox.min.x; x <= bed_bbox.max.x; x += scale_(10.0)) + { + Polyline line; + line.append(Point(x, bed_bbox.min.y)); + line.append(Point(x, bed_bbox.max.y)); + axes_lines.push_back(line); + } + for (coord_t y = bed_bbox.min.y; y <= bed_bbox.max.y; y += scale_(10.0)) + { + Polyline line; + line.append(Point(bed_bbox.min.x, y)); + line.append(Point(bed_bbox.max.x, y)); + axes_lines.push_back(line); + } + + // clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped + Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, SCALED_EPSILON))); + + // append bed contours + Lines contour_lines = to_lines(poly); + std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines)); + + if (!m_gridlines.set_from_lines(gridlines, GROUND_Z)) + printf("Unable to create bed grid lines\n"); +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -167,6 +319,16 @@ void GLCanvas3D::set_current() m_canvas->SetCurrent(*m_context); } +bool GLCanvas3D::is_dirty() const +{ + return m_dirty; +} + +void GLCanvas3D::set_dirty(bool dirty) +{ + m_dirty = dirty; +} + bool GLCanvas3D::is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; @@ -254,6 +416,26 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) m_bed.set_shape(shape); } +void GLCanvas3D::set_auto_bed_shape() +{ + // draw a default square bed around object center + const BoundingBoxf3& bbox = volumes_bounding_box(); + coordf_t max_size = bbox.max_size(); + const Pointf3& center = bbox.center(); + + Pointfs bed_shape; + bed_shape.reserve(4); + bed_shape.emplace_back(center.x - max_size, center.y - max_size); + bed_shape.emplace_back(center.x + max_size, center.y - max_size); + bed_shape.emplace_back(center.x + max_size, center.y + max_size); + bed_shape.emplace_back(center.x - max_size, center.y + max_size); + + set_bed_shape(bed_shape); + + // Set the origin for painting of the coordinate system axes. + set_bed_origin(Pointf(center.x, center.y)); +} + const Pointf& GLCanvas3D::get_bed_origin() const { return m_bed.get_origin(); @@ -264,16 +446,6 @@ void GLCanvas3D::set_bed_origin(const Pointf& origin) m_bed.set_origin(origin); } -bool GLCanvas3D::is_dirty() const -{ - return m_dirty; -} - -void GLCanvas3D::set_dirty(bool dirty) -{ - m_dirty = dirty; -} - GLCanvas3D::Camera::EType GLCanvas3D::get_camera_type() const { return m_camera.get_type(); @@ -408,6 +580,11 @@ void GLCanvas3D::select_view(const std::string& direction) } } +void GLCanvas3D::render() +{ + m_bed.render(); +} + void GLCanvas3D::register_on_viewport_changed_callback(void* callback) { if (callback != nullptr) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 7568dec39c..f3b459b968 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -12,9 +12,22 @@ class wxIdleEvent; namespace Slic3r { class GLVolumeCollection; +class ExPolygon; namespace GUI { +class GeometryBuffer +{ + std::vector m_data; + +public: + bool set_from_triangles(const Polygons& triangles, float z); + bool set_from_lines(const Lines& lines, float z); + + const float* get_data() const; + unsigned int get_data_size() const; +}; + class GLCanvas3D { public: @@ -65,6 +78,9 @@ public: Pointfs m_shape; BoundingBoxf3 m_bounding_box; Pointf m_origin; + Polygon m_polygon; + GeometryBuffer m_triangles; + GeometryBuffer m_gridlines; public: const Pointfs& get_shape() const; @@ -75,8 +91,12 @@ public: const Pointf& get_origin() const; void set_origin(const Pointf& origin); + void render(); + private: void _calc_bounding_box(); + void _calc_triangles(const ExPolygon& poly); + void _calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); }; private: @@ -98,6 +118,9 @@ public: void set_current(); + bool is_dirty() const; + void set_dirty(bool dirty); + bool is_shown_on_screen() const; void resize(unsigned int w, unsigned int h); @@ -105,14 +128,17 @@ public: GLVolumeCollection* get_volumes(); void set_volumes(GLVolumeCollection* volumes); + // Set the bed shape to a single closed 2D polygon(array of two element arrays), + // triangulate the bed and store the triangles into m_bed.m_triangles, + // fills the m_bed.m_grid_lines and sets m_bed.m_origin. + // Sets m_bed.m_polygon to limit the object placement. void set_bed_shape(const Pointfs& shape); + // Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane to support the scene objects. + void set_auto_bed_shape(); const Pointf& get_bed_origin() const; void set_bed_origin(const Pointf& origin); - bool is_dirty() const; - void set_dirty(bool dirty); - Camera::EType get_camera_type() const; void set_camera_type(Camera::EType type); std::string get_camera_type_as_string() const; @@ -140,6 +166,8 @@ public: void zoom_to_volumes(); void select_view(const std::string& direction); + void render(); + void register_on_viewport_changed_callback(void* callback); void on_size(wxSizeEvent& evt); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index f5b76fe60f..f4b367c95b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -147,6 +147,19 @@ bool GLCanvas3DManager::layer_editing_allowed() const return m_layer_editing.allowed; } +bool GLCanvas3DManager::is_dirty(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_dirty() : false; +} + +void GLCanvas3DManager::set_dirty(wxGLCanvas* canvas, bool dirty) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_dirty(dirty); +} + bool GLCanvas3DManager::is_shown_on_screen(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -180,6 +193,13 @@ void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) it->second->set_bed_shape(shape); } +void GLCanvas3DManager::set_auto_bed_shape(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_auto_bed_shape(); +} + Pointf GLCanvas3DManager::get_bed_origin(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -211,19 +231,6 @@ BoundingBoxf3 GLCanvas3DManager::get_max_bounding_box(wxGLCanvas* canvas) return (it != m_canvases.end()) ? it->second->max_bounding_box() : BoundingBoxf3(); } -bool GLCanvas3DManager::is_dirty(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_dirty() : false; -} - -void GLCanvas3DManager::set_dirty(wxGLCanvas* canvas, bool dirty) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_dirty(dirty); -} - unsigned int GLCanvas3DManager::get_camera_type(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -335,6 +342,13 @@ void GLCanvas3DManager::select_view(wxGLCanvas* canvas, const std::string& direc it->second->select_view(direction); } +void GLCanvas3DManager::render(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->render(); +} + void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index c1ce8048c2..bf0b9c0668 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -52,6 +52,9 @@ public: bool use_VBOs() const; bool layer_editing_allowed() const; + bool is_dirty(wxGLCanvas* canvas) const; + void set_dirty(wxGLCanvas* canvas, bool dirty); + bool is_shown_on_screen(wxGLCanvas* canvas) const; void resize(wxGLCanvas* canvas, unsigned int w, unsigned int h); @@ -60,6 +63,7 @@ public: void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); + void set_auto_bed_shape(wxGLCanvas* canvas); Pointf get_bed_origin(wxGLCanvas* canvas) const; void set_bed_origin(wxGLCanvas* canvas, const Pointf& origin); @@ -68,9 +72,6 @@ public: BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); - bool is_dirty(wxGLCanvas* canvas) const; - void set_dirty(wxGLCanvas* canvas, bool dirty); - unsigned int get_camera_type(wxGLCanvas* canvas) const; void set_camera_type(wxGLCanvas* canvas, unsigned int type); std::string get_camera_type_as_string(wxGLCanvas* canvas) const; @@ -94,6 +95,8 @@ public: void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); + void render(wxGLCanvas* canvas); + void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); private: diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 7211cc513e..c62c7e9581 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -215,6 +215,12 @@ set_bed_shape(canvas, shape) CODE: _3DScene::set_bed_shape((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), shape); +void +set_auto_bed_shape(canvas) + SV *canvas; + CODE: + _3DScene::set_auto_bed_shape((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + Clone get_bed_origin(canvas) SV *canvas; @@ -393,6 +399,12 @@ select_view(canvas, direction) std::string direction; CODE: _3DScene::select_view((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), direction); + +void +render(canvas) + SV *canvas; + CODE: + _3DScene::render((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); void register_on_viewport_changed_callback(canvas, callback) From 41c51d761446214866c5551c373984e6f77ea2c9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 15 May 2018 16:09:04 +0200 Subject: [PATCH 010/117] 3DScene's char event handler moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 62 +++++++++++++------------ xs/src/slic3r/GUI/GLCanvas3D.cpp | 35 ++++++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 2 + xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 1 + 4 files changed, 70 insertions(+), 30 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 89e46a55db..a654f6554f 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -244,36 +244,38 @@ sub new { #============================================================================================================================== EVT_MOUSEWHEEL($self, \&mouse_wheel_event); EVT_MOUSE_EVENTS($self, \&mouse_event); -# EVT_KEY_DOWN($self, sub { - EVT_CHAR($self, sub { - my ($s, $event) = @_; - if ($event->HasModifiers) { - $event->Skip; - } else { - my $key = $event->GetKeyCode; - if ($key == ord('0')) { - $self->select_view('iso'); - } elsif ($key == ord('1')) { - $self->select_view('top'); - } elsif ($key == ord('2')) { - $self->select_view('bottom'); - } elsif ($key == ord('3')) { - $self->select_view('front'); - } elsif ($key == ord('4')) { - $self->select_view('rear'); - } elsif ($key == ord('5')) { - $self->select_view('left'); - } elsif ($key == ord('6')) { - $self->select_view('right'); - } elsif ($key == ord('z')) { - $self->zoom_to_volumes; - } elsif ($key == ord('b')) { - $self->zoom_to_bed; - } else { - $event->Skip; - } - } - }); +#============================================================================================================================== +## EVT_KEY_DOWN($self, sub { +# EVT_CHAR($self, sub { +# my ($s, $event) = @_; +# if ($event->HasModifiers) { +# $event->Skip; +# } else { +# my $key = $event->GetKeyCode; +# if ($key == ord('0')) { +# $self->select_view('iso'); +# } elsif ($key == ord('1')) { +# $self->select_view('top'); +# } elsif ($key == ord('2')) { +# $self->select_view('bottom'); +# } elsif ($key == ord('3')) { +# $self->select_view('front'); +# } elsif ($key == ord('4')) { +# $self->select_view('rear'); +# } elsif ($key == ord('5')) { +# $self->select_view('left'); +# } elsif ($key == ord('6')) { +# $self->select_view('right'); +# } elsif ($key == ord('z')) { +# $self->zoom_to_volumes; +# } elsif ($key == ord('b')) { +# $self->zoom_to_bed; +# } else { +# $event->Skip; +# } +# } +# }); +#============================================================================================================================== $self->{layer_height_edit_timer_id} = &Wx::NewId(); $self->{layer_height_edit_timer} = Wx::Timer->new($self, $self->{layer_height_edit_timer_id}); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 2e4b15af4b..4a5a97c95e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -609,6 +609,41 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) } } +void GLCanvas3D::on_char(wxKeyEvent& evt) +{ + if (evt.HasModifiers()) + evt.Skip(); + else + { + int keyCode = evt.GetKeyCode(); + switch (keyCode - 48) + { + // numerical input + case 0: { select_view("iso"); break; } + case 1: { select_view("top"); break; } + case 2: { select_view("bottom"); break; } + case 3: { select_view("front"); break; } + case 4: { select_view("rear"); break; } + case 5: { select_view("left"); break; } + case 6: { select_view("right"); break; } + default: + { + // text input + switch (keyCode) + { + // key B/b + case 66: + case 98: { zoom_to_bed(); break; } + // key Z/z + case 90: + case 122: { zoom_to_volumes(); break; } + default: { evt.Skip(); break; } + } + } + } + } +} + void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { // Calculate the zoom factor needed to adjust viewport to bounding box. diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index f3b459b968..507964136e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -8,6 +8,7 @@ class wxGLCanvas; class wxGLContext; class wxSizeEvent; class wxIdleEvent; +class wxKeyEvent; namespace Slic3r { @@ -172,6 +173,7 @@ public: void on_size(wxSizeEvent& evt); void on_idle(wxIdleEvent& evt); + void on_char(wxKeyEvent& evt); private: void _zoom_to_bounding_box(const BoundingBoxf3& bbox); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index f4b367c95b..61867c61ca 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -79,6 +79,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) canvas->Bind(wxEVT_SIZE, [canvas3D](wxSizeEvent& evt) { canvas3D->on_size(evt); }); canvas->Bind(wxEVT_IDLE, [canvas3D](wxIdleEvent& evt) { canvas3D->on_idle(evt); }); + canvas->Bind(wxEVT_CHAR, [canvas3D](wxKeyEvent& evt) { canvas3D->on_char(evt); }); m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); From 1e0a8de5b162bac0e79a8a5b595cd253a045205d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 18 May 2018 11:05:48 +0200 Subject: [PATCH 011/117] 3DScene cutting plane moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 92 +++++++++++++------------ xs/src/slic3r/GUI/3DScene.cpp | 14 +++- xs/src/slic3r/GUI/3DScene.hpp | 5 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 79 +++++++++++++++++++-- xs/src/slic3r/GUI/GLCanvas3D.hpp | 21 +++++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 18 ++++- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 7 +- xs/xsp/GUI_3DScene.xsp | 18 ++++- 8 files changed, 196 insertions(+), 58 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index a654f6554f..e2fdcaacaa 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -46,8 +46,6 @@ __PACKAGE__->mk_accessors( qw(_quat init on_move on_model_update volumes - cutting_plane_z - cut_lines_vertices background _mouse_pos _hover_volume_idx @@ -1071,19 +1069,23 @@ sub select_volume { sub SetCuttingPlane { my ($self, $z, $expolygons) = @_; - $self->cutting_plane_z($z); - - # grow slices in order to display them better - $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1); - - my @verts = (); - foreach my $line (map @{$_->lines}, map @$_, @$expolygons) { - push @verts, ( - unscale($line->a->x), unscale($line->a->y), $z, #)) - unscale($line->b->x), unscale($line->b->y), $z, #)) - ); - } - $self->cut_lines_vertices(OpenGL::Array->new_list(GL_FLOAT, @verts)); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_cutting_plane($self, $z, $expolygons); + +# $self->cutting_plane_z($z); +# +# # grow slices in order to display them better +# $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1); +# +# my @verts = (); +# foreach my $line (map @{$_->lines}, map @$_, @$expolygons) { +# push @verts, ( +# unscale($line->a->x), unscale($line->a->y), $z, #)) +# unscale($line->b->x), unscale($line->b->y), $z, #)) +# ); +# } +# $self->cut_lines_vertices(OpenGL::Array->new_list(GL_FLOAT, @verts)); +#============================================================================================================================== } # Given an axis and angle, compute quaternion. @@ -1516,7 +1518,7 @@ sub Render { # draw ground my $ground_z = GROUND_Z; #============================================================================================================================== - Slic3r::GUI::_3DScene::render($self); + Slic3r::GUI::_3DScene::render_bed($self); # if ($self->bed_triangles) { # glDisable(GL_DEPTH_TEST); @@ -1603,33 +1605,37 @@ sub Render { glEnable(GL_CULL_FACE) if ($self->enable_picking); } - if (defined $self->cutting_plane_z) { - # draw cutting plane - my $plane_z = $self->cutting_plane_z; - my $bb = $volumes_bb; - glDisable(GL_CULL_FACE); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBegin(GL_QUADS); - glColor4f(0.8, 0.8, 0.8, 0.5); - glVertex3f($bb->x_min-20, $bb->y_min-20, $plane_z); - glVertex3f($bb->x_max+20, $bb->y_min-20, $plane_z); - glVertex3f($bb->x_max+20, $bb->y_max+20, $plane_z); - glVertex3f($bb->x_min-20, $bb->y_max+20, $plane_z); - glEnd(); - glEnable(GL_CULL_FACE); - glDisable(GL_BLEND); - - # draw cutting contours - glEnableClientState(GL_VERTEX_ARRAY); - glLineWidth(2); - glColor3f(0, 0, 0); - glVertexPointer_c(3, GL_FLOAT, 0, $self->cut_lines_vertices->ptr()); - glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3); - glVertexPointer_c(3, GL_FLOAT, 0, 0); - glDisableClientState(GL_VERTEX_ARRAY); - } +#============================================================================================================================== + Slic3r::GUI::_3DScene::render_cutting_plane($self); + +# if (defined $self->cutting_plane_z) { +# # draw cutting plane +# my $plane_z = $self->cutting_plane_z; +# my $bb = $volumes_bb; +# glDisable(GL_CULL_FACE); +# glDisable(GL_LIGHTING); +# glEnable(GL_BLEND); +# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +# glBegin(GL_QUADS); +# glColor4f(0.8, 0.8, 0.8, 0.5); +# glVertex3f($bb->x_min-20, $bb->y_min-20, $plane_z); +# glVertex3f($bb->x_max+20, $bb->y_min-20, $plane_z); +# glVertex3f($bb->x_max+20, $bb->y_max+20, $plane_z); +# glVertex3f($bb->x_min-20, $bb->y_max+20, $plane_z); +# glEnd(); +# glEnable(GL_CULL_FACE); +# glDisable(GL_BLEND); +# +# # draw cutting contours +# glEnableClientState(GL_VERTEX_ARRAY); +# glLineWidth(2); +# glColor3f(0, 0, 0); +# glVertexPointer_c(3, GL_FLOAT, 0, $self->cut_lines_vertices->ptr()); +# glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3); +# glVertexPointer_c(3, GL_FLOAT, 0, 0); +# glDisableClientState(GL_VERTEX_ARRAY); +# } +#============================================================================================================================== # draw warning message $self->draw_warning; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index d2e778ac61..9babd8bc61 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1808,6 +1808,11 @@ BoundingBoxf3 _3DScene::get_max_bounding_box(wxGLCanvas* canvas) return s_canvas_mgr.get_max_bounding_box(canvas); } +void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) +{ + return s_canvas_mgr.set_cutting_plane(canvas, z, polygons); +} + unsigned int _3DScene::get_camera_type(wxGLCanvas* canvas) { return s_canvas_mgr.get_camera_type(canvas); @@ -1888,9 +1893,14 @@ void _3DScene::select_view(wxGLCanvas* canvas, const std::string& direction) s_canvas_mgr.select_view(canvas, direction); } -void _3DScene::render(wxGLCanvas* canvas) +void _3DScene::render_bed(wxGLCanvas* canvas) { - s_canvas_mgr.render(canvas); + s_canvas_mgr.render_bed(canvas); +} + +void _3DScene::render_cutting_plane(wxGLCanvas* canvas) +{ + s_canvas_mgr.render_cutting_plane(canvas); } void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 8d6345f971..eb03a53137 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -562,6 +562,8 @@ public: static BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); static BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); + static void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); + static unsigned int get_camera_type(wxGLCanvas* canvas); static void set_camera_type(wxGLCanvas* canvas, unsigned int type); static std::string get_camera_type_as_string(wxGLCanvas* canvas); @@ -585,7 +587,8 @@ public: static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); - static void render(wxGLCanvas* canvas); + static void render_bed(wxGLCanvas* canvas); + static void render_cutting_plane(wxGLCanvas* canvas); static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 4a5a97c95e..9631f7996b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -230,8 +230,7 @@ void GLCanvas3D::Bed::render() ::glColor4f(0.8f, 0.6f, 0.5f, 0.4f); ::glNormal3d(0.0f, 0.0f, 1.0f); ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_data()); - ::glDrawArrays(GL_TRIANGLES, 0, triangles_vcount); -// ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount); // we need depth test for grid, otherwise it would disappear when looking // the object from below @@ -242,9 +241,8 @@ void GLCanvas3D::Bed::render() ::glLineWidth(3.0f); ::glColor4f(0.2f, 0.2f, 0.2f, 0.4f); -// ::glEnableClientState(GL_VERTEX_ARRAY); ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_data()); - ::glDrawArrays(GL_LINES, 0, gridlines_vcount); + ::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount); ::glDisableClientState(GL_VERTEX_ARRAY); @@ -299,6 +297,66 @@ void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& printf("Unable to create bed grid lines\n"); } +GLCanvas3D::CuttingPlane::CuttingPlane() + : m_z(-1.0f) +{ +} + +bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons) +{ + m_z = z; + + // grow slices in order to display them better + ExPolygons expolygons = offset_ex(polygons, scale_(0.1)); + Lines lines = to_lines(expolygons); + return m_lines.set_from_lines(lines, m_z); +} + +void GLCanvas3D::CuttingPlane::render_plane(const BoundingBoxf3& bb) +{ + if (m_z >= 0.0f) + { + ::glDisable(GL_CULL_FACE); + ::glDisable(GL_LIGHTING); + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + float margin = 20.0f; + float min_x = bb.min.x - margin; + float max_x = bb.max.x + margin; + float min_y = bb.min.y - margin; + float max_y = bb.max.y + margin; + + ::glBegin(GL_QUADS); + ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); + ::glVertex3f(min_x, min_y, m_z); + ::glVertex3f(max_x, min_y, m_z); + ::glVertex3f(max_x, max_y, m_z); + ::glVertex3f(min_x, max_y, m_z); + ::glEnd(); + + ::glEnable(GL_CULL_FACE); + ::glDisable(GL_BLEND); + } +} + +void GLCanvas3D::CuttingPlane::render_contour() +{ + ::glEnableClientState(GL_VERTEX_ARRAY); + + if (m_z >= 0.0f) + { + unsigned int lines_vcount = m_lines.get_data_size() / 3; + + ::glLineWidth(2.0f); + ::glColor3f(0.0f, 0.0f, 0.0f); + ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_lines.get_data()); + ::glDrawArrays(GL_LINES, 0, (GLsizei)lines_vcount); + } + + ::glDisableClientState(GL_VERTEX_ARRAY); +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -446,6 +504,11 @@ void GLCanvas3D::set_bed_origin(const Pointf& origin) m_bed.set_origin(origin); } +void GLCanvas3D::set_cutting_plane(float z, const ExPolygons& polygons) +{ + m_cutting_plane.set(z, polygons); +} + GLCanvas3D::Camera::EType GLCanvas3D::get_camera_type() const { return m_camera.get_type(); @@ -580,11 +643,17 @@ void GLCanvas3D::select_view(const std::string& direction) } } -void GLCanvas3D::render() +void GLCanvas3D::render_bed() { m_bed.render(); } +void GLCanvas3D::render_cutting_plane() +{ + m_cutting_plane.render_plane(volumes_bounding_box()); + m_cutting_plane.render_contour(); +} + void GLCanvas3D::register_on_viewport_changed_callback(void* callback) { if (callback != nullptr) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 507964136e..a7e9dbfb00 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -3,6 +3,7 @@ #include "../../libslic3r/BoundingBox.hpp" #include "../../libslic3r/Utils.hpp" +#include "../../libslic3r/ExPolygon.hpp" class wxGLCanvas; class wxGLContext; @@ -100,11 +101,26 @@ public: void _calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); }; + class CuttingPlane + { + float m_z; + GeometryBuffer m_lines; + + public: + CuttingPlane(); + + bool set(float z, const ExPolygons& polygons); + + void render_plane(const BoundingBoxf3& bb); + void render_contour(); + }; + private: wxGLCanvas* m_canvas; wxGLContext* m_context; Camera m_camera; Bed m_bed; + CuttingPlane m_cutting_plane; GLVolumeCollection* m_volumes; @@ -140,6 +156,8 @@ public: const Pointf& get_bed_origin() const; void set_bed_origin(const Pointf& origin); + void set_cutting_plane(float z, const ExPolygons& polygons); + Camera::EType get_camera_type() const; void set_camera_type(Camera::EType type); std::string get_camera_type_as_string() const; @@ -167,7 +185,8 @@ public: void zoom_to_volumes(); void select_view(const std::string& direction); - void render(); + void render_bed(); + void render_cutting_plane(); void register_on_viewport_changed_callback(void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 61867c61ca..911fb2f0ef 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -232,6 +232,13 @@ BoundingBoxf3 GLCanvas3DManager::get_max_bounding_box(wxGLCanvas* canvas) return (it != m_canvases.end()) ? it->second->max_bounding_box() : BoundingBoxf3(); } +void GLCanvas3DManager::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_cutting_plane(z, polygons); +} + unsigned int GLCanvas3DManager::get_camera_type(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -343,11 +350,18 @@ void GLCanvas3DManager::select_view(wxGLCanvas* canvas, const std::string& direc it->second->select_view(direction); } -void GLCanvas3DManager::render(wxGLCanvas* canvas) +void GLCanvas3DManager::render_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); if (it != m_canvases.end()) - it->second->render(); + it->second->render_bed(); +} + +void GLCanvas3DManager::render_cutting_plane(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->render_cutting_plane(); } void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index bf0b9c0668..4d8066597c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -1,7 +1,7 @@ #ifndef slic3r_GLCanvas3DManager_hpp_ #define slic3r_GLCanvas3DManager_hpp_ -#include "GLCanvas3D.hpp" +#include "../../slic3r/GUI/GLCanvas3D.hpp" #include @@ -72,6 +72,8 @@ public: BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); + void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); + unsigned int get_camera_type(wxGLCanvas* canvas) const; void set_camera_type(wxGLCanvas* canvas, unsigned int type); std::string get_camera_type_as_string(wxGLCanvas* canvas) const; @@ -95,7 +97,8 @@ public: void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); - void render(wxGLCanvas* canvas); + void render_bed(wxGLCanvas* canvas); + void render_cutting_plane(wxGLCanvas* canvas); void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index c62c7e9581..2efe8f11b6 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -283,6 +283,14 @@ is_shown_on_screen(canvas) OUTPUT: RETVAL +void +set_cutting_plane(canvas, z, polygons) + SV *canvas; + float z; + ExPolygons polygons; + CODE: + _3DScene::set_cutting_plane((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), z, polygons); + unsigned int get_camera_type(canvas) SV *canvas; @@ -401,10 +409,16 @@ select_view(canvas, direction) _3DScene::select_view((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), direction); void -render(canvas) +render_bed(canvas) SV *canvas; CODE: - _3DScene::render((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + _3DScene::render_bed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + +void +render_cutting_plane(canvas) + SV *canvas; + CODE: + _3DScene::render_cutting_plane((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); void register_on_viewport_changed_callback(canvas, callback) From 5fc8fdee112808b03baf651da17d4c826893336f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 18 May 2018 13:02:47 +0200 Subject: [PATCH 012/117] 3DScene axes moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 53 ++++++------- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 4 + lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 4 + xs/src/slic3r/GUI/3DScene.cpp | 37 ++++++--- xs/src/slic3r/GUI/3DScene.hpp | 10 ++- xs/src/slic3r/GUI/GLCanvas3D.cpp | 93 ++++++++++++++++++----- xs/src/slic3r/GUI/GLCanvas3D.hpp | 30 ++++++-- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 46 +++++++---- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 10 ++- xs/xsp/GUI_3DScene.xsp | 51 +++++++++---- 10 files changed, 244 insertions(+), 94 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index e2fdcaacaa..23b87d6610 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1551,35 +1551,36 @@ sub Render { my $volumes_bb = $self->volumes_bounding_box; - { - # draw axes - # disable depth testing so that axes are not covered by ground - glDisable(GL_DEPTH_TEST); #============================================================================================================================== - my $origin = Slic3r::GUI::_3DScene::get_bed_origin($self); + Slic3r::GUI::_3DScene::render_axes($self); + +# { +# # draw axes +# # disable depth testing so that axes are not covered by ground +# glDisable(GL_DEPTH_TEST); # my $origin = $self->origin; +# my $axis_len = $self->use_plain_shader ? 0.3 * max(@{ $self->bed_bounding_box->size }) : 2 * max(@{ $volumes_bb->size }); +# glLineWidth(2); +# glBegin(GL_LINES); +# # draw line for x axis +# glColor3f(1, 0, 0); +# glVertex3f(@$origin, $ground_z); +# glVertex3f($origin->x + $axis_len, $origin->y, $ground_z); #,, +# # draw line for y axis +# glColor3f(0, 1, 0); +# glVertex3f(@$origin, $ground_z); +# glVertex3f($origin->x, $origin->y + $axis_len, $ground_z); #++ +# glEnd(); +# # draw line for Z axis +# # (re-enable depth test so that axis is correctly shown when objects are behind it) +# glEnable(GL_DEPTH_TEST); +# glBegin(GL_LINES); +# glColor3f(0, 0, 1); +# glVertex3f(@$origin, $ground_z); +# glVertex3f(@$origin, $ground_z+$axis_len); +# glEnd(); +# } #============================================================================================================================== - my $axis_len = $self->use_plain_shader ? 0.3 * max(@{ $self->bed_bounding_box->size }) : 2 * max(@{ $volumes_bb->size }); - glLineWidth(2); - glBegin(GL_LINES); - # draw line for x axis - glColor3f(1, 0, 0); - glVertex3f(@$origin, $ground_z); - glVertex3f($origin->x + $axis_len, $origin->y, $ground_z); #,, - # draw line for y axis - glColor3f(0, 1, 0); - glVertex3f(@$origin, $ground_z); - glVertex3f($origin->x, $origin->y + $axis_len, $ground_z); #++ - glEnd(); - # draw line for Z axis - # (re-enable depth test so that axis is correctly shown when objects are behind it) - glEnable(GL_DEPTH_TEST); - glBegin(GL_LINES); - glColor3f(0, 0, 1); - glVertex3f(@$origin, $ground_z); - glVertex3f(@$origin, $ground_z+$axis_len); - glEnd(); - } glEnable(GL_LIGHTING); diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 6d3234ff3f..80ba4db506 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -9,6 +9,9 @@ use utf8; use Slic3r::Geometry qw(PI X); use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); use Wx::Event qw(EVT_CLOSE EVT_BUTTON); +#============================================================================================================================== +use List::Util qw(max); +#============================================================================================================================== use base 'Wx::Dialog'; sub new { @@ -115,6 +118,7 @@ sub new { $canvas->load_object($self->{model_object}, undef, undef, [0]); #============================================================================================================================== Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); + Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ $canvas->volumes_bounding_box->size })); # $canvas->set_auto_bed_shape; #============================================================================================================================== $canvas->SetSize([500,500]); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index ee1bf22d10..9320e66664 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -10,6 +10,9 @@ use File::Basename qw(basename); use Wx qw(:misc :sizer :treectrl :button :keycode wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxMOD_CONTROL wxTheApp); use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN EVT_KEY_DOWN); +#============================================================================================================================== +use List::Util qw(max); +#============================================================================================================================== use base 'Wx::Panel'; use constant ICON_OBJECT => 0; @@ -162,6 +165,7 @@ sub new { $canvas->load_object($self->{model_object}, undef, undef, [0]); #============================================================================================================================== Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); + Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ $canvas->volumes_bounding_box->size })); # $canvas->set_auto_bed_shape; #============================================================================================================================== $canvas->SetSize([500,700]); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 9babd8bc61..02dfd18aef 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1782,17 +1782,6 @@ void _3DScene::set_auto_bed_shape(wxGLCanvas* canvas) return s_canvas_mgr.set_auto_bed_shape(canvas); } -Pointf _3DScene::get_bed_origin(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_bed_origin(canvas); -} - -void _3DScene::set_bed_origin(wxGLCanvas* canvas, const Pointf* origin) -{ - if (origin != nullptr) - s_canvas_mgr.set_bed_origin(canvas, *origin); -} - BoundingBoxf3 _3DScene::get_bed_bounding_box(wxGLCanvas* canvas) { return s_canvas_mgr.get_bed_bounding_box(canvas); @@ -1808,6 +1797,27 @@ BoundingBoxf3 _3DScene::get_max_bounding_box(wxGLCanvas* canvas) return s_canvas_mgr.get_max_bounding_box(canvas); } +Pointf3 _3DScene::get_axes_origin(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_axes_origin(canvas); +} + +void _3DScene::set_axes_origin(wxGLCanvas* canvas, const Pointf3* origin) +{ + if (origin != nullptr) + s_canvas_mgr.set_axes_origin(canvas, *origin); +} + +float _3DScene::get_axes_length(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_axes_length(canvas); +} + +void _3DScene::set_axes_length(wxGLCanvas* canvas, float length) +{ + s_canvas_mgr.set_axes_length(canvas, length); +} + void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) { return s_canvas_mgr.set_cutting_plane(canvas, z, polygons); @@ -1898,6 +1908,11 @@ void _3DScene::render_bed(wxGLCanvas* canvas) s_canvas_mgr.render_bed(canvas); } +void _3DScene::render_axes(wxGLCanvas* canvas) +{ + s_canvas_mgr.render_axes(canvas); +} + void _3DScene::render_cutting_plane(wxGLCanvas* canvas) { s_canvas_mgr.render_cutting_plane(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index eb03a53137..2ae07d858d 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -555,13 +555,16 @@ public: static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); static void set_auto_bed_shape(wxGLCanvas* canvas); - static Pointf get_bed_origin(wxGLCanvas* canvas); - static void set_bed_origin(wxGLCanvas* canvas, const Pointf* origin); - static BoundingBoxf3 get_bed_bounding_box(wxGLCanvas* canvas); static BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); static BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); + static Pointf3 get_axes_origin(wxGLCanvas* canvas); + static void set_axes_origin(wxGLCanvas* canvas, const Pointf3* origin); + + static float get_axes_length(wxGLCanvas* canvas); + static void set_axes_length(wxGLCanvas* canvas, float length); + static void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); static unsigned int get_camera_type(wxGLCanvas* canvas); @@ -588,6 +591,7 @@ public: static void select_view(wxGLCanvas* canvas, const std::string& direction); static void render_bed(wxGLCanvas* canvas); + static void render_axes(wxGLCanvas* canvas); static void render_cutting_plane(wxGLCanvas* canvas); static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 9631f7996b..de875841cc 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -196,8 +196,6 @@ void GLCanvas3D::Bed::set_shape(const Pointfs& shape) _calc_gridlines(poly, bed_bbox); m_polygon = offset_ex(poly.contour, bed_bbox.radius() * 1.7, jtRound, scale_(0.5))[0].contour; - - set_origin(Pointf(0.0, 0.0)); } const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const @@ -205,16 +203,6 @@ const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const return m_bounding_box; } -const Pointf& GLCanvas3D::Bed::get_origin() const -{ - return m_origin; -} - -void GLCanvas3D::Bed::set_origin(const Pointf& origin) -{ - m_origin = origin; -} - void GLCanvas3D::Bed::render() { unsigned int triangles_vcount = m_triangles.get_data_size() / 3; @@ -297,6 +285,56 @@ void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& printf("Unable to create bed grid lines\n"); } +GLCanvas3D::Axes::Axes() + : m_length(0.0f) +{ +} + +const Pointf3& GLCanvas3D::Axes::get_origin() const +{ + return m_origin; +} + +void GLCanvas3D::Axes::set_origin(const Pointf3& origin) +{ + m_origin = origin; +} + +float GLCanvas3D::Axes::get_length() const +{ + return m_length; +} + +void GLCanvas3D::Axes::set_length(float length) +{ + m_length = length; +} + +void GLCanvas3D::Axes::render() +{ + // disable depth testing so that axes are not covered by ground + ::glDisable(GL_DEPTH_TEST); + ::glLineWidth(2.0f); + ::glBegin(GL_LINES); + // draw line for x axis + ::glColor3f(1.0f, 0.0f, 0.0f); + ::glVertex3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z); + ::glVertex3f((float)m_origin.x + m_length, (float)m_origin.y, (float)m_origin.z); + // draw line for y axis + ::glColor3f(0.0f, 1.0f, 0.0f); + ::glVertex3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z); + ::glVertex3f((float)m_origin.x, (float)m_origin.y + m_length, (float)m_origin.z); + ::glEnd(); + // draw line for Z axis + // (re-enable depth test so that axis is correctly shown when objects are behind it) + ::glEnable(GL_DEPTH_TEST); + ::glBegin(GL_LINES); + ::glColor3f(0.0f, 0.0f, 1.0f); + ::glVertex3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z); + ::glVertex3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z + m_length); + ::glEnd(); +} + GLCanvas3D::CuttingPlane::CuttingPlane() : m_z(-1.0f) { @@ -472,6 +510,10 @@ void GLCanvas3D::set_volumes(GLVolumeCollection* volumes) void GLCanvas3D::set_bed_shape(const Pointfs& shape) { m_bed.set_shape(shape); + + // Set the origin and size for painting of the coordinate system axes. + set_axes_origin(Pointf3(0.0, 0.0, (coordf_t)GROUND_Z)); + set_axes_length(0.3f * (float)bed_bounding_box().max_size()); } void GLCanvas3D::set_auto_bed_shape() @@ -491,17 +533,27 @@ void GLCanvas3D::set_auto_bed_shape() set_bed_shape(bed_shape); // Set the origin for painting of the coordinate system axes. - set_bed_origin(Pointf(center.x, center.y)); + set_axes_origin(Pointf3(center.x, center.y, (coordf_t)GROUND_Z)); } -const Pointf& GLCanvas3D::get_bed_origin() const +const Pointf3& GLCanvas3D::get_axes_origin() const { - return m_bed.get_origin(); + return m_axes.get_origin(); } -void GLCanvas3D::set_bed_origin(const Pointf& origin) +void GLCanvas3D::set_axes_origin(const Pointf3& origin) { - m_bed.set_origin(origin); + m_axes.set_origin(origin); +} + +float GLCanvas3D::get_axes_length() const +{ + return m_axes.get_length(); +} + +void GLCanvas3D::set_axes_length(float length) +{ + return m_axes.set_length(length); } void GLCanvas3D::set_cutting_plane(float z, const ExPolygons& polygons) @@ -645,9 +697,16 @@ void GLCanvas3D::select_view(const std::string& direction) void GLCanvas3D::render_bed() { + ::glDisable(GL_LIGHTING); m_bed.render(); } +void GLCanvas3D::render_axes() +{ + ::glDisable(GL_LIGHTING); + m_axes.render(); +} + void GLCanvas3D::render_cutting_plane() { m_cutting_plane.render_plane(volumes_bounding_box()); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index a7e9dbfb00..201a840bed 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -79,7 +79,6 @@ public: { Pointfs m_shape; BoundingBoxf3 m_bounding_box; - Pointf m_origin; Polygon m_polygon; GeometryBuffer m_triangles; GeometryBuffer m_gridlines; @@ -90,9 +89,6 @@ public: const BoundingBoxf3& get_bounding_box() const; - const Pointf& get_origin() const; - void set_origin(const Pointf& origin); - void render(); private: @@ -101,6 +97,23 @@ public: void _calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); }; + class Axes + { + Pointf3 m_origin; + float m_length; + + public: + Axes(); + + const Pointf3& get_origin() const; + void set_origin(const Pointf3& origin); + + float get_length() const; + void set_length(float length); + + void render(); + }; + class CuttingPlane { float m_z; @@ -120,6 +133,7 @@ private: wxGLContext* m_context; Camera m_camera; Bed m_bed; + Axes m_axes; CuttingPlane m_cutting_plane; GLVolumeCollection* m_volumes; @@ -153,8 +167,11 @@ public: // Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane to support the scene objects. void set_auto_bed_shape(); - const Pointf& get_bed_origin() const; - void set_bed_origin(const Pointf& origin); + const Pointf3& get_axes_origin() const; + void set_axes_origin(const Pointf3& origin); + + float get_axes_length() const; + void set_axes_length(float length); void set_cutting_plane(float z, const ExPolygons& polygons); @@ -186,6 +203,7 @@ public: void select_view(const std::string& direction); void render_bed(); + void render_axes(); void render_cutting_plane(); void register_on_viewport_changed_callback(void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 911fb2f0ef..48241dafb1 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -201,19 +201,6 @@ void GLCanvas3DManager::set_auto_bed_shape(wxGLCanvas* canvas) it->second->set_auto_bed_shape(); } -Pointf GLCanvas3DManager::get_bed_origin(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_bed_origin() : Pointf(); -} - -void GLCanvas3DManager::set_bed_origin(wxGLCanvas* canvas, const Pointf& origin) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_bed_origin(origin); -} - BoundingBoxf3 GLCanvas3DManager::get_bed_bounding_box(wxGLCanvas* canvas) { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -232,6 +219,32 @@ BoundingBoxf3 GLCanvas3DManager::get_max_bounding_box(wxGLCanvas* canvas) return (it != m_canvases.end()) ? it->second->max_bounding_box() : BoundingBoxf3(); } +Pointf3 GLCanvas3DManager::get_axes_origin(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_axes_origin() : Pointf3(); +} + +void GLCanvas3DManager::set_axes_origin(wxGLCanvas* canvas, const Pointf3& origin) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_axes_origin(origin); +} + +float GLCanvas3DManager::get_axes_length(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_axes_length() : 0.0f; +} + +void GLCanvas3DManager::set_axes_length(wxGLCanvas* canvas, float length) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_axes_length(length); +} + void GLCanvas3DManager::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -357,6 +370,13 @@ void GLCanvas3DManager::render_bed(wxGLCanvas* canvas) it->second->render_bed(); } +void GLCanvas3DManager::render_axes(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->render_axes(); +} + void GLCanvas3DManager::render_cutting_plane(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 4d8066597c..4877618f00 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -65,13 +65,16 @@ public: void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); void set_auto_bed_shape(wxGLCanvas* canvas); - Pointf get_bed_origin(wxGLCanvas* canvas) const; - void set_bed_origin(wxGLCanvas* canvas, const Pointf& origin); - BoundingBoxf3 get_bed_bounding_box(wxGLCanvas* canvas); BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); + Pointf3 get_axes_origin(wxGLCanvas* canvas) const; + void set_axes_origin(wxGLCanvas* canvas, const Pointf3& origin); + + float get_axes_length(wxGLCanvas* canvas) const; + void set_axes_length(wxGLCanvas* canvas, float length); + void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); unsigned int get_camera_type(wxGLCanvas* canvas) const; @@ -98,6 +101,7 @@ public: void select_view(wxGLCanvas* canvas, const std::string& direction); void render_bed(wxGLCanvas* canvas); + void render_axes(wxGLCanvas* canvas); void render_cutting_plane(wxGLCanvas* canvas); void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 2efe8f11b6..55679ba16b 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -221,21 +221,6 @@ set_auto_bed_shape(canvas) CODE: _3DScene::set_auto_bed_shape((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); -Clone -get_bed_origin(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_bed_origin((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_bed_origin(canvas, origin) - SV *canvas; - Pointf *origin - CODE: - _3DScene::set_bed_origin((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), origin); - Clone get_bed_bounding_box(canvas) SV *canvas; @@ -283,6 +268,36 @@ is_shown_on_screen(canvas) OUTPUT: RETVAL +Clone +get_axes_origin(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_axes_origin((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_axes_origin(canvas, origin) + SV *canvas; + Pointf3 *origin; + CODE: + _3DScene::set_axes_origin((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), origin); + +float +get_axes_length(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_axes_length((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_axes_length(canvas, length) + SV *canvas; + float length; + CODE: + _3DScene::set_axes_length((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), length); + void set_cutting_plane(canvas, z, polygons) SV *canvas; @@ -414,6 +429,12 @@ render_bed(canvas) CODE: _3DScene::render_bed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); +void +render_axes(canvas) + SV *canvas; + CODE: + _3DScene::render_axes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + void render_cutting_plane(canvas) SV *canvas; From a7fc57a176e1cb8f8f2d3b070563b2dc5a5e1f16 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 18 May 2018 14:08:59 +0200 Subject: [PATCH 013/117] 3DScene reset_object method moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 46 ++++++++++++----------- lib/Slic3r/GUI/Plater/3D.pm | 5 ++- lib/Slic3r/GUI/Plater/3DPreview.pm | 7 +++- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 7 +++- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 15 ++++++-- xs/src/slic3r/GUI/3DScene.cpp | 10 +++++ xs/src/slic3r/GUI/3DScene.hpp | 4 ++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 32 +++++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 17 ++++++++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 13 +++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 4 ++ xs/xsp/GUI_3DScene.xsp | 6 +++ 12 files changed, 135 insertions(+), 31 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 23b87d6610..17e2e63775 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -110,7 +110,9 @@ __PACKAGE__->mk_accessors( qw(_quat init use constant TRACKBALLSIZE => 0.8; use constant TURNTABLE_MODE => 1; -use constant GROUND_Z => -0.02; +#============================================================================================================================== +#use constant GROUND_Z => -0.02; +#============================================================================================================================== # For mesh selection: Not selected - bright yellow. use constant DEFAULT_COLOR => [1,1,0]; # For mesh selection: Selected - bright green. @@ -127,13 +129,11 @@ use constant HOVER_COLOR => [0.4,0.9,0,1]; #use constant VIEW_BOTTOM => [0.0,180.0]; #use constant VIEW_FRONT => [0.0,90.0]; #use constant VIEW_REAR => [180.0,90.0]; -#============================================================================================================================== - -use constant MANIPULATION_IDLE => 0; -use constant MANIPULATION_DRAGGING => 1; -use constant MANIPULATION_LAYER_HEIGHT => 2; - -#============================================================================================================================== +# +#use constant MANIPULATION_IDLE => 0; +#use constant MANIPULATION_DRAGGING => 1; +#use constant MANIPULATION_LAYER_HEIGHT => 2; +# #use constant GIMBALL_LOCK_THETA_MAX => 180; #============================================================================================================================== @@ -223,7 +223,10 @@ sub new { $self->{layer_height_edit_last_z} = 0.; $self->{layer_height_edit_last_action} = 0; - $self->reset_objects; +#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_volumes($self); +# $self->reset_objects; +#============================================================================================================================== EVT_PAINT($self, sub { my $dc = Wx::PaintDC->new($self); @@ -723,19 +726,18 @@ sub mouse_wheel_event { $self->Refresh; } -# Reset selection. -sub reset_objects { - my ($self) = @_; - if ($self->GetContext) { - $self->SetCurrent($self->GetContext); - $self->volumes->release_geometry; - } - $self->volumes->erase; #============================================================================================================================== - Slic3r::GUI::_3DScene::set_dirty($self, 1); +## Reset selection. +#sub reset_objects { +# my ($self) = @_; +# if ($self->GetContext) { +# $self->SetCurrent($self->GetContext); +# $self->volumes->release_geometry; +# } +# $self->volumes->erase; # $self->_dirty(1); +#} #============================================================================================================================== -} # Setup camera to view all objects. sub set_viewport_from_scene { @@ -1515,8 +1517,10 @@ sub Render { # draw ground and axes glDisable(GL_LIGHTING); - # draw ground - my $ground_z = GROUND_Z; +#============================================================================================================================== +# # draw ground +# my $ground_z = GROUND_Z; +#============================================================================================================================== #============================================================================================================================== Slic3r::GUI::_3DScene::render_bed($self); diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 2706df509f..6c3e02e09b 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -197,7 +197,10 @@ sub update_volumes_selection { sub reload_scene { my ($self, $force) = @_; - $self->reset_objects; +#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_volumes($self); +# $self->reset_objects; +#============================================================================================================================== $self->update_bed_size; if (! $self->IsShown && ! $force) { diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 96c28fee5b..0aff64310c 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -277,8 +277,11 @@ sub new { sub reload_print { my ($self, $force) = @_; - - $self->canvas->reset_objects; + +#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_volumes($self->canvas); +# $self->canvas->reset_objects; +#============================================================================================================================== $self->_loaded(0); if (! $self->IsShown && ! $force) { diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 80ba4db506..b379ba8ef7 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -254,8 +254,11 @@ sub _update { for @$expolygon; $expolygon->translate(map Slic3r::Geometry::scale($_), @{ $self->{model_object}->instances->[0]->offset }); } - - $self->{canvas}->reset_objects; + +#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); +# $self->{canvas}->reset_objects; +#============================================================================================================================== $self->{canvas}->load_object($_, undef, undef, [0]) for @objects; $self->{canvas}->SetCuttingPlane( $self->{cut_options}{z}, diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 9320e66664..a2b779a0ab 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -494,7 +494,10 @@ sub _parts_changed { $self->reload_tree; if ($self->{canvas}) { - $self->{canvas}->reset_objects; +#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); +# $self->{canvas}->reset_objects; +#============================================================================================================================== $self->{canvas}->load_object($self->{model_object}); $self->{canvas}->zoom_to_volumes; $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); @@ -539,7 +542,10 @@ sub _update_canvas { my ($self) = @_; if ($self->{canvas}) { - $self->{canvas}->reset_objects; +#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); +# $self->{canvas}->reset_objects; +#============================================================================================================================== $self->{canvas}->load_object($self->{model_object}); # restore selection, if any @@ -572,7 +578,10 @@ sub _update { $self->{parts_changed} = 1; my @objects = (); push @objects, $self->{model_object}; - $self->{canvas}->reset_objects; +#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); +# $self->{canvas}->reset_objects; +#============================================================================================================================== $self->{canvas}->load_object($_, undef, [0]) for @objects; $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); $self->{canvas}->Render; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 02dfd18aef..bb1527f8da 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1772,6 +1772,11 @@ void _3DScene::set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes) s_canvas_mgr.set_volumes(canvas, volumes); } +void _3DScene::reset_volumes(wxGLCanvas* canvas) +{ + s_canvas_mgr.reset_volumes(canvas); +} + void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { return s_canvas_mgr.set_bed_shape(canvas, shape); @@ -1887,6 +1892,11 @@ void _3DScene::set_camera_target(wxGLCanvas* canvas, const Pointf3* target) { s_canvas_mgr.set_camera_target(canvas, target); } + +bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_layers_editing_enabled(canvas); +} void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 2ae07d858d..3f4ecc6376 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -552,6 +552,8 @@ public: static GLVolumeCollection* get_volumes(wxGLCanvas* canvas); static void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); + static void reset_volumes(wxGLCanvas* canvas); + static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); static void set_auto_bed_shape(wxGLCanvas* canvas); @@ -586,6 +588,8 @@ public: static Pointf3 get_camera_target(wxGLCanvas* canvas); static void set_camera_target(wxGLCanvas* canvas, const Pointf3* target); + static bool is_layers_editing_enabled(wxGLCanvas* canvas); + static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index de875841cc..7a47028809 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -395,6 +395,16 @@ void GLCanvas3D::CuttingPlane::render_contour() ::glDisableClientState(GL_VERTEX_ARRAY); } +GLCanvas3D::LayersEditing::LayersEditing() + : m_enabled(false) +{ +} + +bool GLCanvas3D::LayersEditing::is_enabled() const +{ + return m_enabled; +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -409,10 +419,15 @@ GLCanvas3D::~GLCanvas3D() _deregister_callbacks(); } -void GLCanvas3D::set_current() +bool GLCanvas3D::set_current() { if ((m_canvas != nullptr) && (m_context != nullptr)) + { m_canvas->SetCurrent(*m_context); + return true; + } + + return false; } bool GLCanvas3D::is_dirty() const @@ -507,6 +522,16 @@ void GLCanvas3D::set_volumes(GLVolumeCollection* volumes) m_volumes = volumes; } +void GLCanvas3D::reset_volumes() +{ + if (set_current() && (m_volumes != nullptr)) + { + m_volumes->release_geometry(); + m_volumes->clear(); + set_dirty(true); + } +} + void GLCanvas3D::set_bed_shape(const Pointfs& shape) { m_bed.set_shape(shape); @@ -652,6 +677,11 @@ BoundingBoxf3 GLCanvas3D::max_bounding_box() const return bb; } +bool GLCanvas3D::is_layers_editing_enabled() const +{ + return m_layers_editing.is_enabled(); +} + void GLCanvas3D::zoom_to_bed() { _zoom_to_bounding_box(bed_bounding_box()); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 201a840bed..558e6c350e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -128,6 +128,16 @@ public: void render_contour(); }; + class LayersEditing + { + bool m_enabled; + + public: + LayersEditing(); + + bool is_enabled() const; + }; + private: wxGLCanvas* m_canvas; wxGLContext* m_context; @@ -135,6 +145,7 @@ private: Bed m_bed; Axes m_axes; CuttingPlane m_cutting_plane; + LayersEditing m_layers_editing; GLVolumeCollection* m_volumes; @@ -147,7 +158,7 @@ public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); ~GLCanvas3D(); - void set_current(); + bool set_current(); bool is_dirty() const; void set_dirty(bool dirty); @@ -159,6 +170,8 @@ public: GLVolumeCollection* get_volumes(); void set_volumes(GLVolumeCollection* volumes); + void reset_volumes(); + // Set the bed shape to a single closed 2D polygon(array of two element arrays), // triangulate the bed and store the triangles into m_bed.m_triangles, // fills the m_bed.m_grid_lines and sets m_bed.m_origin. @@ -198,6 +211,8 @@ public: BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 max_bounding_box() const; + bool is_layers_editing_enabled() const; + void zoom_to_bed(); void zoom_to_volumes(); void select_view(const std::string& direction); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 48241dafb1..f4cf4235ed 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -187,6 +187,13 @@ void GLCanvas3DManager::set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volu it->second->set_volumes(volumes); } +void GLCanvas3DManager::reset_volumes(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->reset_volumes(); +} + void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -342,6 +349,12 @@ void GLCanvas3DManager::set_camera_target(wxGLCanvas* canvas, const Pointf3* tar it->second->set_camera_target(*target); } +bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_layers_editing_enabled() : false; +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 4877618f00..6abe1bbf72 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -62,6 +62,8 @@ public: GLVolumeCollection* get_volumes(wxGLCanvas* canvas); void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); + void reset_volumes(wxGLCanvas* canvas); + void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); void set_auto_bed_shape(wxGLCanvas* canvas); @@ -96,6 +98,8 @@ public: Pointf3 get_camera_target(wxGLCanvas* canvas) const; void set_camera_target(wxGLCanvas* canvas, const Pointf3* target); + bool is_layers_editing_enabled(wxGLCanvas* canvas) const; + void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 55679ba16b..c55ee8fbdc 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -207,6 +207,12 @@ set_volumes(canvas, volumes) GLVolumeCollection *volumes; CODE: _3DScene::set_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), volumes); + +void +reset_volumes(canvas) + SV *canvas; + CODE: + _3DScene::reset_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); void set_bed_shape(canvas, shape) From 7cff6ef6dba33055179ab30c12016a1ab25eec99 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 May 2018 13:08:02 +0200 Subject: [PATCH 014/117] Shaders loaded from files --- lib/Slic3r/GUI/3DScene.pm | 547 +++++++++++---------- resources/shaders/gouraud.fs | 18 + resources/shaders/gouraud.vs | 70 +++ resources/shaders/variable_layer_height.fs | 40 ++ resources/shaders/variable_layer_height.vs | 46 ++ xs/src/slic3r/GUI/GLShader.cpp | 47 +- xs/src/slic3r/GUI/GLShader.hpp | 6 +- xs/xsp/GUI_3DScene.xsp | 3 +- 8 files changed, 504 insertions(+), 273 deletions(-) create mode 100644 resources/shaders/gouraud.fs create mode 100644 resources/shaders/gouraud.vs create mode 100644 resources/shaders/variable_layer_height.fs create mode 100644 resources/shaders/variable_layer_height.vs diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 17e2e63775..72fdd6d6af 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -321,7 +321,10 @@ sub layer_editing_enabled { $self->SetCurrent($self->GetContext); my $shader = new Slic3r::GUI::_3DScene::GLShader; my $error_message; - if (! $shader->load($self->_fragment_shader_variable_layer_height, $self->_vertex_shader_variable_layer_height)) { +#============================================================================================================================== + if (! $shader->load_from_file("variable_layer_height.fs", "variable_layer_height.vs")) { +# if (! $shader->load_from_text($self->_fragment_shader_variable_layer_height, $self->_vertex_shader_variable_layer_height)) { +#============================================================================================================================== # Compilation or linking of the shaders failed. $error_message = "Cannot compile an OpenGL Shader, therefore the Variable Layer Editing will be disabled.\n\n" . $shader->last_error; @@ -1375,8 +1378,10 @@ sub InitGL { 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)) { +#=================================================================================================================================== + if (! $shader->load_from_file("gouraud.fs", "gouraud.vs")) { +## 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 { @@ -2051,273 +2056,275 @@ sub _report_opengl_state } } -sub _vertex_shader_Gouraud { - return <<'VERTEX'; -#version 110 - -#define INTENSITY_CORRECTION 0.6 - -// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -// normalized values for (1./1.43, 0.2/1.43, 1./1.43) -const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) -//#define LIGHT_FRONT_SHININESS 5.0 - -#define INTENSITY_AMBIENT 0.3 - -const vec3 ZERO = vec3(0.0, 0.0, 0.0); - -struct PrintBoxDetection -{ - vec3 min; - vec3 max; - // xyz contains the offset, if w == 1.0 detection needs to be performed - vec4 volume_origin; -}; - -uniform PrintBoxDetection print_box; - -// x = tainted, y = specular; -varying vec2 intensity; - -varying vec3 delta_box_min; -varying vec3 delta_box_max; - -void main() -{ - // First transform the normal into camera space and normalize the result. - vec3 normal = normalize(gl_NormalMatrix * gl_Normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = 0.0; - - if (NdotL > 0.0) - intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular applied). - NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - - // compute deltas for out of print volume detection (world coordinates) - if (print_box.volume_origin.w == 1.0) - { - vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz; - delta_box_min = v - print_box.min; - delta_box_max = v - print_box.max; - } - else - { - delta_box_min = ZERO; - delta_box_max = ZERO; - } - - gl_Position = ftransform(); -} - -VERTEX -} - -sub _fragment_shader_Gouraud { - return <<'FRAGMENT'; -#version 110 - -const vec3 ZERO = vec3(0.0, 0.0, 0.0); - -// x = tainted, y = specular; -varying vec2 intensity; - -varying vec3 delta_box_min; -varying vec3 delta_box_max; - -uniform vec4 uniform_color; - -void main() -{ - // if the fragment is outside the print volume -> use darker color - vec3 color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(uniform_color.rgb, ZERO, 0.3333) : uniform_color.rgb; - gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + color * intensity.x, uniform_color.a); -} - -FRAGMENT -} - -sub _vertex_shader_Phong { - return <<'VERTEX'; -#version 110 - -varying vec3 normal; -varying vec3 eye; -void main(void) -{ - eye = normalize(vec3(gl_ModelViewMatrix * gl_Vertex)); - normal = normalize(gl_NormalMatrix * gl_Normal); - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; -} -VERTEX -} - -sub _fragment_shader_Phong { - return <<'FRAGMENT'; -#version 110 - -#define INTENSITY_CORRECTION 0.7 - -#define LIGHT_TOP_DIR -0.6/1.31, 0.6/1.31, 1./1.31 -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.5 * INTENSITY_CORRECTION) -//#define LIGHT_TOP_SHININESS 50. -#define LIGHT_TOP_SHININESS 10. - -#define LIGHT_FRONT_DIR 1./1.43, 0.2/1.43, 1./1.43 -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) -#define LIGHT_FRONT_SHININESS 50. - -#define INTENSITY_AMBIENT 0.0 - -varying vec3 normal; -varying vec3 eye; -uniform vec4 uniform_color; -void main() { - - float intensity_specular = 0.; - float intensity_tainted = 0.; - float intensity = max(dot(normal,vec3(LIGHT_TOP_DIR)), 0.0); - // if the vertex is lit compute the specular color - if (intensity > 0.0) { - intensity_tainted = LIGHT_TOP_DIFFUSE * intensity; - // compute the half vector - vec3 h = normalize(vec3(LIGHT_TOP_DIR) + eye); - // compute the specular term into spec - intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(h, normal), 0.0), LIGHT_TOP_SHININESS); - } - intensity = max(dot(normal,vec3(LIGHT_FRONT_DIR)), 0.0); - // if the vertex is lit compute the specular color - if (intensity > 0.0) { - intensity_tainted += LIGHT_FRONT_DIFFUSE * intensity; - // compute the half vector -// vec3 h = normalize(vec3(LIGHT_FRONT_DIR) + eye); - // compute the specular term into spec -// intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(h,normal), 0.0), LIGHT_FRONT_SHININESS); - } - - gl_FragColor = max( - vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted, - INTENSITY_AMBIENT * uniform_color); - gl_FragColor.a = uniform_color.a; -} -FRAGMENT -} - -sub _vertex_shader_variable_layer_height { - return <<'VERTEX'; -#version 110 - -#define INTENSITY_CORRECTION 0.6 - -const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); -#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) -#define LIGHT_TOP_SHININESS 20.0 - -const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); -#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) -//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) -//#define LIGHT_FRONT_SHININESS 5.0 - -#define INTENSITY_AMBIENT 0.3 - -// x = tainted, y = specular; -varying vec2 intensity; - -varying float object_z; - -void main() -{ - // First transform the normal into camera space and normalize the result. - vec3 normal = normalize(gl_NormalMatrix * gl_Normal); - - // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. - // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. - float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); - - intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = 0.0; - - if (NdotL > 0.0) - intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); - - // Perform the same lighting calculation for the 2nd light source (no specular) - NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); - - intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; - - // Scaled to widths of the Z texture. - object_z = gl_Vertex.z; - - gl_Position = ftransform(); -} - -VERTEX -} - -sub _fragment_shader_variable_layer_height { - return <<'FRAGMENT'; -#version 110 - -#define M_PI 3.1415926535897932384626433832795 - -// 2D texture (1D texture split by the rows) of color along the object Z axis. -uniform sampler2D z_texture; -// Scaling from the Z texture rows coordinate to the normalized texture row coordinate. -uniform float z_to_texture_row; -uniform float z_texture_row_to_normalized; -uniform float z_cursor; -uniform float z_cursor_band_width; - -// x = tainted, y = specular; -varying vec2 intensity; - -varying float object_z; - -void main() -{ - float object_z_row = z_to_texture_row * object_z; - // Index of the row in the texture. - float z_texture_row = floor(object_z_row); - // Normalized coordinate from 0. to 1. - float z_texture_col = object_z_row - z_texture_row; - float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25; - // Calculate level of detail from the object Z coordinate. - // This makes the slowly sloping surfaces to be show with high detail (with stripes), - // and the vertical surfaces to be shown with low detail (no stripes) - float z_in_cells = object_z_row * 190.; - // Gradient of Z projected on the screen. - float dx_vtc = dFdx(z_in_cells); - float dy_vtc = dFdy(z_in_cells); - float lod = clamp(0.5 * log2(max(dx_vtc*dx_vtc, dy_vtc*dy_vtc)), 0., 1.); - // Sample the Z texture. Texture coordinates are normalized to <0, 1>. - vec4 color = - mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.), - texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); - - // Mix the final color. - gl_FragColor = - vec4(intensity.y, intensity.y, intensity.y, 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); -} - -FRAGMENT -} +#=================================================================================================================================== +#sub _vertex_shader_Gouraud { +# return <<'VERTEX'; +##version 110 +# +##define INTENSITY_CORRECTION 0.6 +# +#// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +#const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +##define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +##define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +##define LIGHT_TOP_SHININESS 20.0 +# +#// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +#const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +##define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +#//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +#//#define LIGHT_FRONT_SHININESS 5.0 +# +##define INTENSITY_AMBIENT 0.3 +# +#const vec3 ZERO = vec3(0.0, 0.0, 0.0); +# +#struct PrintBoxDetection +#{ +# vec3 min; +# vec3 max; +# // xyz contains the offset, if w == 1.0 detection needs to be performed +# vec4 volume_origin; +#}; +# +#uniform PrintBoxDetection print_box; +# +#// x = tainted, y = specular; +#varying vec2 intensity; +# +#varying vec3 delta_box_min; +#varying vec3 delta_box_max; +# +#void main() +#{ +# // First transform the normal into camera space and normalize the result. +# vec3 normal = normalize(gl_NormalMatrix * gl_Normal); +# +# // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. +# // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. +# float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); +# +# intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; +# intensity.y = 0.0; +# +# if (NdotL > 0.0) +# intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); +# +# // Perform the same lighting calculation for the 2nd light source (no specular applied). +# NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); +# intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; +# +# // compute deltas for out of print volume detection (world coordinates) +# if (print_box.volume_origin.w == 1.0) +# { +# vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz; +# delta_box_min = v - print_box.min; +# delta_box_max = v - print_box.max; +# } +# else +# { +# delta_box_min = ZERO; +# delta_box_max = ZERO; +# } +# +# gl_Position = ftransform(); +#} +# +#VERTEX +#} +# +#sub _fragment_shader_Gouraud { +# return <<'FRAGMENT'; +##version 110 +# +#const vec3 ZERO = vec3(0.0, 0.0, 0.0); +# +#// x = tainted, y = specular; +#varying vec2 intensity; +# +#varying vec3 delta_box_min; +#varying vec3 delta_box_max; +# +#uniform vec4 uniform_color; +# +#void main() +#{ +# // if the fragment is outside the print volume -> use darker color +# vec3 color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(uniform_color.rgb, ZERO, 0.3333) : uniform_color.rgb; +# gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + color * intensity.x, uniform_color.a); +#} +# +#FRAGMENT +#} +# +#sub _vertex_shader_Phong { +# return <<'VERTEX'; +##version 110 +# +#varying vec3 normal; +#varying vec3 eye; +#void main(void) +#{ +# eye = normalize(vec3(gl_ModelViewMatrix * gl_Vertex)); +# normal = normalize(gl_NormalMatrix * gl_Normal); +# gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +#} +#VERTEX +#} +# +#sub _fragment_shader_Phong { +# return <<'FRAGMENT'; +##version 110 +# +##define INTENSITY_CORRECTION 0.7 +# +##define LIGHT_TOP_DIR -0.6/1.31, 0.6/1.31, 1./1.31 +##define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +##define LIGHT_TOP_SPECULAR (0.5 * INTENSITY_CORRECTION) +#//#define LIGHT_TOP_SHININESS 50. +##define LIGHT_TOP_SHININESS 10. +# +##define LIGHT_FRONT_DIR 1./1.43, 0.2/1.43, 1./1.43 +##define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +##define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +##define LIGHT_FRONT_SHININESS 50. +# +##define INTENSITY_AMBIENT 0.0 +# +#varying vec3 normal; +#varying vec3 eye; +#uniform vec4 uniform_color; +#void main() { +# +# float intensity_specular = 0.; +# float intensity_tainted = 0.; +# float intensity = max(dot(normal,vec3(LIGHT_TOP_DIR)), 0.0); +# // if the vertex is lit compute the specular color +# if (intensity > 0.0) { +# intensity_tainted = LIGHT_TOP_DIFFUSE * intensity; +# // compute the half vector +# vec3 h = normalize(vec3(LIGHT_TOP_DIR) + eye); +# // compute the specular term into spec +# intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(h, normal), 0.0), LIGHT_TOP_SHININESS); +# } +# intensity = max(dot(normal,vec3(LIGHT_FRONT_DIR)), 0.0); +# // if the vertex is lit compute the specular color +# if (intensity > 0.0) { +# intensity_tainted += LIGHT_FRONT_DIFFUSE * intensity; +# // compute the half vector +#// vec3 h = normalize(vec3(LIGHT_FRONT_DIR) + eye); +# // compute the specular term into spec +#// intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(h,normal), 0.0), LIGHT_FRONT_SHININESS); +# } +# +# gl_FragColor = max( +# vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted, +# INTENSITY_AMBIENT * uniform_color); +# gl_FragColor.a = uniform_color.a; +#} +#FRAGMENT +#} +# +#sub _vertex_shader_variable_layer_height { +# return <<'VERTEX'; +##version 110 +# +##define INTENSITY_CORRECTION 0.6 +# +#const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +##define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +##define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +##define LIGHT_TOP_SHININESS 20.0 +# +#const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +##define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +#//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +#//#define LIGHT_FRONT_SHININESS 5.0 +# +##define INTENSITY_AMBIENT 0.3 +# +#// x = tainted, y = specular; +#varying vec2 intensity; +# +#varying float object_z; +# +#void main() +#{ +# // First transform the normal into camera space and normalize the result. +# vec3 normal = normalize(gl_NormalMatrix * gl_Normal); +# +# // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. +# // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. +# float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); +# +# intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; +# intensity.y = 0.0; +# +# if (NdotL > 0.0) +# intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); +# +# // Perform the same lighting calculation for the 2nd light source (no specular) +# NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); +# +# intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; +# +# // Scaled to widths of the Z texture. +# object_z = gl_Vertex.z; +# +# gl_Position = ftransform(); +#} +# +#VERTEX +#} +# +#sub _fragment_shader_variable_layer_height { +# return <<'FRAGMENT'; +##version 110 +# +##define M_PI 3.1415926535897932384626433832795 +# +#// 2D texture (1D texture split by the rows) of color along the object Z axis. +#uniform sampler2D z_texture; +#// Scaling from the Z texture rows coordinate to the normalized texture row coordinate. +#uniform float z_to_texture_row; +#uniform float z_texture_row_to_normalized; +#uniform float z_cursor; +#uniform float z_cursor_band_width; +# +#// x = tainted, y = specular; +#varying vec2 intensity; +# +#varying float object_z; +# +#void main() +#{ +# float object_z_row = z_to_texture_row * object_z; +# // Index of the row in the texture. +# float z_texture_row = floor(object_z_row); +# // Normalized coordinate from 0. to 1. +# float z_texture_col = object_z_row - z_texture_row; +# float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25; +# // Calculate level of detail from the object Z coordinate. +# // This makes the slowly sloping surfaces to be show with high detail (with stripes), +# // and the vertical surfaces to be shown with low detail (no stripes) +# float z_in_cells = object_z_row * 190.; +# // Gradient of Z projected on the screen. +# float dx_vtc = dFdx(z_in_cells); +# float dy_vtc = dFdy(z_in_cells); +# float lod = clamp(0.5 * log2(max(dx_vtc*dx_vtc, dy_vtc*dy_vtc)), 0., 1.); +# // Sample the Z texture. Texture coordinates are normalized to <0, 1>. +# vec4 color = +# mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.), +# texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); +# +# // Mix the final color. +# gl_FragColor = +# vec4(intensity.y, intensity.y, intensity.y, 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); +#} +# +#FRAGMENT +#} +#=================================================================================================================================== # The 3D canvas to display objects and tool paths. package Slic3r::GUI::3DScene; diff --git a/resources/shaders/gouraud.fs b/resources/shaders/gouraud.fs new file mode 100644 index 0000000000..9edc8fa769 --- /dev/null +++ b/resources/shaders/gouraud.fs @@ -0,0 +1,18 @@ +#version 110 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +// x = tainted, y = specular; +varying vec2 intensity; + +varying vec3 delta_box_min; +varying vec3 delta_box_max; + +uniform vec4 uniform_color; + +void main() +{ + // if the fragment is outside the print volume -> use darker color + vec3 color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(uniform_color.rgb, ZERO, 0.3333) : uniform_color.rgb; + gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + color * intensity.x, uniform_color.a); +} diff --git a/resources/shaders/gouraud.vs b/resources/shaders/gouraud.vs new file mode 100644 index 0000000000..22ba91a934 --- /dev/null +++ b/resources/shaders/gouraud.vs @@ -0,0 +1,70 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +struct PrintBoxDetection +{ + vec3 min; + vec3 max; + // xyz contains the offset, if w == 1.0 detection needs to be performed + vec4 volume_origin; +}; + +uniform PrintBoxDetection print_box; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying vec3 delta_box_min; +varying vec3 delta_box_max; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(gl_NormalMatrix * gl_Normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = 0.0; + + if (NdotL > 0.0) + intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // compute deltas for out of print volume detection (world coordinates) + if (print_box.volume_origin.w == 1.0) + { + vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz; + delta_box_min = v - print_box.min; + delta_box_max = v - print_box.max; + } + else + { + delta_box_min = ZERO; + delta_box_max = ZERO; + } + + gl_Position = ftransform(); +} diff --git a/resources/shaders/variable_layer_height.fs b/resources/shaders/variable_layer_height.fs new file mode 100644 index 0000000000..f87e6627e4 --- /dev/null +++ b/resources/shaders/variable_layer_height.fs @@ -0,0 +1,40 @@ +#version 110 + +#define M_PI 3.1415926535897932384626433832795 + +// 2D texture (1D texture split by the rows) of color along the object Z axis. +uniform sampler2D z_texture; +// Scaling from the Z texture rows coordinate to the normalized texture row coordinate. +uniform float z_to_texture_row; +uniform float z_texture_row_to_normalized; +uniform float z_cursor; +uniform float z_cursor_band_width; + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float object_z; + +void main() +{ + float object_z_row = z_to_texture_row * object_z; + // Index of the row in the texture. + float z_texture_row = floor(object_z_row); + // Normalized coordinate from 0. to 1. + float z_texture_col = object_z_row - z_texture_row; + float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25; + // Calculate level of detail from the object Z coordinate. + // This makes the slowly sloping surfaces to be show with high detail (with stripes), + // and the vertical surfaces to be shown with low detail (no stripes) + float z_in_cells = object_z_row * 190.; + // Gradient of Z projected on the screen. + float dx_vtc = dFdx(z_in_cells); + float dy_vtc = dFdy(z_in_cells); + float lod = clamp(0.5 * log2(max(dx_vtc * dx_vtc, dy_vtc * dy_vtc)), 0., 1.); + // Sample the Z texture. Texture coordinates are normalized to <0, 1>. + vec4 color = mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.), + texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod); + + // Mix the final color. + gl_FragColor = vec4(intensity.y, intensity.y, intensity.y, 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend); +} diff --git a/resources/shaders/variable_layer_height.vs b/resources/shaders/variable_layer_height.vs new file mode 100644 index 0000000000..2c918c0d49 --- /dev/null +++ b/resources/shaders/variable_layer_height.vs @@ -0,0 +1,46 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +// x = tainted, y = specular; +varying vec2 intensity; + +varying float object_z; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(gl_NormalMatrix * gl_Normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity.y = 0.0; + + if (NdotL > 0.0) + intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular) + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Scaled to widths of the Z texture. + object_z = gl_Vertex.z; + + gl_Position = ftransform(); +} diff --git a/xs/src/slic3r/GUI/GLShader.cpp b/xs/src/slic3r/GUI/GLShader.cpp index ce9a80f059..a888110d61 100644 --- a/xs/src/slic3r/GUI/GLShader.cpp +++ b/xs/src/slic3r/GUI/GLShader.cpp @@ -2,6 +2,11 @@ #include "GLShader.hpp" +//############################################################################################################################################ +#include "../../libslic3r/Utils.hpp" +#include +//############################################################################################################################################ + #include #include #include @@ -22,7 +27,10 @@ inline std::string gl_get_string_safe(GLenum param) return std::string(value ? value : "N/A"); } -bool GLShader::load(const char *fragment_shader, const char *vertex_shader) +//############################################################################################################################################ +bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_shader) +//bool GLShader::load(const char *fragment_shader, const char *vertex_shader) +//############################################################################################################################################ { std::string gl_version = gl_get_string_safe(GL_VERSION); int major = atoi(gl_version.c_str()); @@ -123,6 +131,43 @@ bool GLShader::load(const char *fragment_shader, const char *vertex_shader) return true; } +//############################################################################################################################################ +bool GLShader::load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename) +{ + const std::string& path = resources_dir() + "/shaders/"; + + boost::nowide::ifstream vs(path + std::string(vertex_shader_filename), boost::nowide::ifstream::binary); + if (!vs.good()) + return false; + + vs.seekg(0, vs.end); + int file_length = vs.tellg(); + vs.seekg(0, vs.beg); + std::string vertex_shader(file_length, '\0'); + vs.read(const_cast(vertex_shader.data()), file_length); + if (!vs.good()) + return false; + + vs.close(); + + boost::nowide::ifstream fs(path + std::string(fragment_shader_filename), boost::nowide::ifstream::binary); + if (!fs.good()) + return false; + + fs.seekg(0, fs.end); + file_length = fs.tellg(); + fs.seekg(0, fs.beg); + std::string fragment_shader(file_length, '\0'); + fs.read(const_cast(fragment_shader.data()), file_length); + if (!fs.good()) + return false; + + fs.close(); + + return load_from_text(fragment_shader.c_str(), vertex_shader.c_str()); +} +//############################################################################################################################################ + void GLShader::release() { if (this->shader_program_id) { diff --git a/xs/src/slic3r/GUI/GLShader.hpp b/xs/src/slic3r/GUI/GLShader.hpp index d91463f195..7d52d879df 100644 --- a/xs/src/slic3r/GUI/GLShader.hpp +++ b/xs/src/slic3r/GUI/GLShader.hpp @@ -16,7 +16,11 @@ public: {} ~GLShader(); - bool load(const char *fragment_shader, const char *vertex_shader); +//############################################################################################################################################ + bool load_from_text(const char *fragment_shader, const char *vertex_shader); + bool load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename); +// bool load(const char *fragment_shader, const char *vertex_shader); +//############################################################################################################################################ void release(); int get_attrib_location(const char *name) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index c55ee8fbdc..240e62dbac 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -8,7 +8,8 @@ GLShader(); ~GLShader(); - bool load(const char *fragment_shader, const char *vertex_shader); + bool load_from_text(const char *fragment_shader, const char *vertex_shader); + bool load_from_file(const char *fragment_shader, const char *vertex_shader); void release(); int get_attrib_location(const char *name) const; From 3fdc5e20a7b7455e73dde0033daca20155b63b69 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 May 2018 14:40:09 +0200 Subject: [PATCH 015/117] Warning texture moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 149 ++++++++++++------------ lib/Slic3r/GUI/Plater/3D.pm | 15 ++- xs/src/slic3r/GUI/3DScene.cpp | 15 +++ xs/src/slic3r/GUI/3DScene.hpp | 5 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 64 ++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 6 + xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 21 ++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 5 + xs/xsp/GUI_3DScene.xsp | 26 ++++- 9 files changed, 230 insertions(+), 76 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 72fdd6d6af..e3dd848dbf 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -59,7 +59,6 @@ __PACKAGE__->mk_accessors( qw(_quat init _layer_height_edited _legend_enabled - _warning_enabled _mouse_dragging ) ); @@ -193,7 +192,9 @@ sub new { # $self->_zoom(1); #============================================================================================================================== $self->_legend_enabled(0); - $self->_warning_enabled(0); +#============================================================================================================================== +# $self->_warning_enabled(0); +#============================================================================================================================== $self->use_plain_shader(0); #============================================================================================================================== # $self->_apply_zoom_to_volumes_filter(0); @@ -295,10 +296,12 @@ sub set_legend_enabled { $self->_legend_enabled($value); } -sub set_warning_enabled { - my ($self, $value) = @_; - $self->_warning_enabled($value); -} +#============================================================================================================================== +#sub set_warning_enabled { +# my ($self, $value) = @_; +# $self->_warning_enabled($value); +#} +#============================================================================================================================== sub Destroy { my ($self) = @_; @@ -1617,6 +1620,7 @@ sub Render { #============================================================================================================================== Slic3r::GUI::_3DScene::render_cutting_plane($self); + Slic3r::GUI::_3DScene::render_warning_texture($self); # if (defined $self->cutting_plane_z) { # # draw cutting plane @@ -1645,10 +1649,10 @@ sub Render { # glVertexPointer_c(3, GL_FLOAT, 0, 0); # glDisableClientState(GL_VERTEX_ARRAY); # } +# +# # draw warning message +# $self->draw_warning; #============================================================================================================================== - - # draw warning message - $self->draw_warning; # draw gcode preview legend $self->draw_legend; @@ -1762,32 +1766,34 @@ sub _variable_layer_thickness_load_reset_image { return $self->{layer_preview_reset_image}->{valid}; } -# Paint the tooltip. -sub _render_image { - my ($self, $image, $l, $r, $b, $t) = @_; - $self->_render_texture($image->{texture_id}, $l, $r, $b, $t); -} - -sub _render_texture { - my ($self, $tex_id, $l, $r, $b, $t) = @_; - - glColor4f(1.,1.,1.,1.); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, $tex_id); - glBegin(GL_QUADS); - glTexCoord2d(0.,1.); glVertex3f($l, $b, 0); - glTexCoord2d(1.,1.); glVertex3f($r, $b, 0); - glTexCoord2d(1.,0.); glVertex3f($r, $t, 0); - glTexCoord2d(0.,0.); glVertex3f($l, $t, 0); - glEnd(); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - glEnable(GL_LIGHTING); -} +#============================================================================================================================== +## Paint the tooltip. +#sub _render_image { +# my ($self, $image, $l, $r, $b, $t) = @_; +# $self->_render_texture($image->{texture_id}, $l, $r, $b, $t); +#} +# +#sub _render_texture { +# my ($self, $tex_id, $l, $r, $b, $t) = @_; +# +# glColor4f(1.,1.,1.,1.); +# glDisable(GL_LIGHTING); +# glEnable(GL_BLEND); +# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +# glEnable(GL_TEXTURE_2D); +# glBindTexture(GL_TEXTURE_2D, $tex_id); +# glBegin(GL_QUADS); +# glTexCoord2d(0.,1.); glVertex3f($l, $b, 0); +# glTexCoord2d(1.,1.); glVertex3f($r, $b, 0); +# glTexCoord2d(1.,0.); glVertex3f($r, $t, 0); +# glTexCoord2d(0.,0.); glVertex3f($l, $t, 0); +# glEnd(); +# glBindTexture(GL_TEXTURE_2D, 0); +# glDisable(GL_TEXTURE_2D); +# glDisable(GL_BLEND); +# glEnable(GL_LIGHTING); +#} +#============================================================================================================================== sub draw_active_object_annotations { # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. @@ -1857,16 +1863,20 @@ sub draw_active_object_annotations { my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); my $gap = 10/$zoom; my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$zoom + $gap, $reset_bottom + $gap); + Slic3r::GUI::_3DScene::render_texture($self, $self->{layer_preview_annotation}->{texture_id}, $l, $r, $t, $b); # my $gap = 10/$self->_zoom; # my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$self->_zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$self->_zoom + $gap, $reset_bottom + $gap); +# $self->_render_image($self->{layer_preview_annotation}, $l, $r, $t, $b); #============================================================================================================================== - $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); +#============================================================================================================================== + Slic3r::GUI::_3DScene::render_texture($self, $self->{layer_preview_reset_image}->{texture_id}, $reset_left, $reset_right, $reset_bottom, $reset_top); +# $self->_render_image($self->{layer_preview_reset_image}, $reset_left, $reset_right, $reset_bottom, $reset_top); +#============================================================================================================================== } # Paint the graph. @@ -1936,13 +1946,14 @@ sub draw_legend { my $t = (0.5 * $ch) / $zoom; my $r = $l + $tex_w / $zoom; my $b = $t - $tex_h / $zoom; + Slic3r::GUI::_3DScene::render_texture($self, $tex_id, $l, $r, $b, $t); # 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); #============================================================================================================================== - $self->_render_texture($tex_id, $l, $r, $b, $t); glPopMatrix(); glEnable(GL_DEPTH_TEST); @@ -1950,46 +1961,40 @@ sub draw_legend { } } -sub draw_warning { - my ($self) = @_; - - if (!$self->_warning_enabled) { - return; - } - - # If the warning texture has not been loaded into the GPU, do it now. - my $tex_id = Slic3r::GUI::_3DScene::finalize_warning_texture; - if ($tex_id > 0) - { - my $tex_w = Slic3r::GUI::_3DScene::get_warning_texture_width; - my $tex_h = Slic3r::GUI::_3DScene::get_warning_texture_height; - if (($tex_w > 0) && ($tex_h > 0)) - { - glDisable(GL_DEPTH_TEST); - glPushMatrix(); - glLoadIdentity(); - - my ($cw, $ch) = $self->GetSizeWH; - #============================================================================================================================== - my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); - my $l = (-0.5 * $tex_w) / $zoom; - my $t = (-0.5 * $ch + $tex_h) / $zoom; - my $r = $l + $tex_w / $zoom; - my $b = $t - $tex_h / $zoom; - +#sub draw_warning { +# my ($self) = @_; +# +# if (!$self->_warning_enabled) { +# return; +# } +# +# # If the warning texture has not been loaded into the GPU, do it now. +# my $tex_id = Slic3r::GUI::_3DScene::finalize_warning_texture; +# if ($tex_id > 0) +# { +# my $tex_w = Slic3r::GUI::_3DScene::get_warning_texture_width; +# my $tex_h = Slic3r::GUI::_3DScene::get_warning_texture_height; +# if (($tex_w > 0) && ($tex_h > 0)) +# { +# glDisable(GL_DEPTH_TEST); +# glPushMatrix(); +# glLoadIdentity(); +# +# my ($cw, $ch) = $self->GetSizeWH; +# # my $l = (-0.5 * $tex_w) / $self->_zoom; # my $t = (-0.5 * $ch + $tex_h) / $self->_zoom; # my $r = $l + $tex_w / $self->_zoom; # my $b = $t - $tex_h / $self->_zoom; +# $self->_render_texture($tex_id, $l, $r, $b, $t); +# +# glPopMatrix(); +# glEnable(GL_DEPTH_TEST); +# } +# } +#} #============================================================================================================================== - $self->_render_texture($tex_id, $l, $r, $b, $t); - - glPopMatrix(); - glEnable(GL_DEPTH_TEST); - } - } -} sub update_volumes_colors_by_extruder { my ($self, $config) = @_; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index a678be67a6..ecf841b27e 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -241,17 +241,26 @@ sub reload_scene { if (scalar @{$self->volumes} > 0) { if (!$self->{model}->fits_print_volume($self->{config})) { - $self->set_warning_enabled(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::enable_warning_texture($self, 1); +# $self->set_warning_enabled(1); +#============================================================================================================================== Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume")); $self->on_enable_action_buttons->(0) if ($self->on_enable_action_buttons); } else { - $self->set_warning_enabled(0); +#============================================================================================================================== + Slic3r::GUI::_3DScene::enable_warning_texture($self, 0); +# $self->set_warning_enabled(0); +#============================================================================================================================== $self->volumes->update_outside_state($self->{config}, 1); Slic3r::GUI::_3DScene::reset_warning_texture(); $self->on_enable_action_buttons->(scalar @{$self->{model}->objects} > 0) if ($self->on_enable_action_buttons); } } else { - $self->set_warning_enabled(0); +#============================================================================================================================== + Slic3r::GUI::_3DScene::enable_warning_texture($self, 0); +# $self->set_warning_enabled(0); +#============================================================================================================================== Slic3r::GUI::_3DScene::reset_warning_texture(); } } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index bb1527f8da..be170a2820 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1898,6 +1898,11 @@ bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) return s_canvas_mgr.is_layers_editing_enabled(canvas); } +void _3DScene::enable_warning_texture(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_warning_texture(canvas, enable); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); @@ -1928,6 +1933,16 @@ void _3DScene::render_cutting_plane(wxGLCanvas* canvas) s_canvas_mgr.render_cutting_plane(canvas); } +void _3DScene::render_warning_texture(wxGLCanvas* canvas) +{ + s_canvas_mgr.render_warning_texture(canvas); +} + +void _3DScene::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) +{ + s_canvas_mgr.render_texture(canvas, tex_id, left, right, bottom, top); +} + void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 3f4ecc6376..a984823961 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -590,6 +590,8 @@ public: static bool is_layers_editing_enabled(wxGLCanvas* canvas); + static void enable_warning_texture(wxGLCanvas* canvas, bool enable); + static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); @@ -597,6 +599,9 @@ public: static void render_bed(wxGLCanvas* canvas); static void render_axes(wxGLCanvas* canvas); static void render_cutting_plane(wxGLCanvas* canvas); + static void render_warning_texture(wxGLCanvas* canvas); + + static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 7a47028809..c97a9566c3 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -411,6 +411,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_volumes(nullptr) , m_dirty(true) , m_apply_zoom_to_volumes_filter(false) + , m_warning_texture_enabled(false) { } @@ -682,6 +683,11 @@ bool GLCanvas3D::is_layers_editing_enabled() const return m_layers_editing.is_enabled(); } +void GLCanvas3D::enable_warning_texture(bool enable) +{ + m_warning_texture_enabled = enable; +} + void GLCanvas3D::zoom_to_bed() { _zoom_to_bounding_box(bed_bounding_box()); @@ -743,6 +749,64 @@ void GLCanvas3D::render_cutting_plane() m_cutting_plane.render_contour(); } +void GLCanvas3D::render_warning_texture() +{ + if (!m_warning_texture_enabled) + return; + + // If the warning texture has not been loaded into the GPU, do it now. + unsigned int tex_id = _3DScene::finalize_warning_texture(); + if (tex_id > 0) + { + unsigned int w = _3DScene::get_warning_texture_width(); + unsigned int h = _3DScene::get_warning_texture_height(); + if ((w > 0) && (h > 0)) + { + ::glDisable(GL_DEPTH_TEST); + ::glPushMatrix(); + ::glLoadIdentity(); + + std::pair cnv_size = _get_canvas_size(); + float zoom = get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + float l = (-0.5f * (float)w) * inv_zoom; + float t = (-0.5f * cnv_size.second + (float)h) * inv_zoom; + float r = l + (float)w * inv_zoom; + float b = t - (float)h * inv_zoom; + + render_texture(tex_id, l, r, b, t); + + ::glPopMatrix(); + ::glEnable(GL_DEPTH_TEST); + } + } +} + +void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) +{ + ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + ::glDisable(GL_LIGHTING); + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + ::glEnable(GL_TEXTURE_2D); + + ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); + + ::glBegin(GL_QUADS); + ::glTexCoord2d(0.0f, 1.0f); glVertex3f(left, bottom, 0.0f); + ::glTexCoord2d(1.0f, 1.0f); glVertex3f(right, bottom, 0.0f); + ::glTexCoord2d(1.0f, 0.0f); glVertex3f(right, top, 0.0f); + ::glTexCoord2d(0.0f, 0.0f); glVertex3f(left, top, 0.0f); + ::glEnd(); + + ::glBindTexture(GL_TEXTURE_2D, 0); + + ::glDisable(GL_TEXTURE_2D); + ::glDisable(GL_BLEND); + ::glEnable(GL_LIGHTING); +} + void GLCanvas3D::register_on_viewport_changed_callback(void* callback) { if (callback != nullptr) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 558e6c350e..d7614d4e46 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -151,6 +151,7 @@ private: bool m_dirty; bool m_apply_zoom_to_volumes_filter; + bool m_warning_texture_enabled; PerlCallback m_on_viewport_changed_callback; @@ -213,6 +214,8 @@ public: bool is_layers_editing_enabled() const; + void enable_warning_texture(bool enable); + void zoom_to_bed(); void zoom_to_volumes(); void select_view(const std::string& direction); @@ -220,6 +223,9 @@ public: void render_bed(); void render_axes(); void render_cutting_plane(); + void render_warning_texture(); + + void render_texture(unsigned int tex_id, float left, float right, float bottom, float top); void register_on_viewport_changed_callback(void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index f4cf4235ed..22eff5a01e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -355,6 +355,13 @@ bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_layers_editing_enabled() : false; } +void GLCanvas3DManager::enable_warning_texture(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_warning_texture(enable); +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -397,6 +404,20 @@ void GLCanvas3DManager::render_cutting_plane(wxGLCanvas* canvas) it->second->render_cutting_plane(); } +void GLCanvas3DManager::render_warning_texture(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->render_warning_texture(); +} + +void GLCanvas3DManager::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->render_texture(tex_id, left, right, bottom, top); +} + void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 6abe1bbf72..d0d2bb609d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -100,6 +100,8 @@ public: bool is_layers_editing_enabled(wxGLCanvas* canvas) const; + void enable_warning_texture(wxGLCanvas* canvas, bool enable); + void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); @@ -107,6 +109,9 @@ public: void render_bed(wxGLCanvas* canvas); void render_axes(wxGLCanvas* canvas); void render_cutting_plane(wxGLCanvas* canvas); + void render_warning_texture(wxGLCanvas* canvas); + + void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 240e62dbac..aa4c98d155 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -411,6 +411,13 @@ set_camera_target(canvas, target) CODE: _3DScene::set_camera_target((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), target); +void +enable_warning_texture(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_warning_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void zoom_to_bed(canvas) SV *canvas; @@ -447,7 +454,24 @@ render_cutting_plane(canvas) SV *canvas; CODE: _3DScene::render_cutting_plane((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - + +void +render_warning_texture(canvas) + SV *canvas; + CODE: + _3DScene::render_warning_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + +void +render_texture(canvas, tex_id, left, right, bottom, top) + SV *canvas; + unsigned int tex_id; + float left; + float right; + float bottom; + float top; + CODE: + _3DScene::render_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), tex_id, left, right, bottom, top); + void register_on_viewport_changed_callback(canvas, callback) SV *canvas; From bf7b9eb3e7d5a2fcd74a23f9b7362e76abfb97c1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 May 2018 14:57:43 +0200 Subject: [PATCH 016/117] Legend texture moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 87 +++++++++++-------------- lib/Slic3r/GUI/Plater.pm | 10 ++- xs/src/slic3r/GUI/3DScene.cpp | 10 +++ xs/src/slic3r/GUI/3DScene.hpp | 2 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 40 +++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 + xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 14 ++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 + xs/xsp/GUI_3DScene.xsp | 13 ++++ 9 files changed, 129 insertions(+), 52 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index e3dd848dbf..31982f6a95 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -58,7 +58,6 @@ __PACKAGE__->mk_accessors( qw(_quat init _layer_height_edited - _legend_enabled _mouse_dragging ) ); @@ -190,9 +189,7 @@ sub new { # $self->_stheta(45); # $self->_sphi(45); # $self->_zoom(1); -#============================================================================================================================== - $self->_legend_enabled(0); -#============================================================================================================================== +# $self->_legend_enabled(0); # $self->_warning_enabled(0); #============================================================================================================================== $self->use_plain_shader(0); @@ -291,12 +288,12 @@ sub new { return $self; } -sub set_legend_enabled { - my ($self, $value) = @_; - $self->_legend_enabled($value); -} - #============================================================================================================================== +#sub set_legend_enabled { +# my ($self, $value) = @_; +# $self->_legend_enabled($value); +#} +# #sub set_warning_enabled { # my ($self, $value) = @_; # $self->_warning_enabled($value); @@ -1621,6 +1618,7 @@ sub Render { #============================================================================================================================== Slic3r::GUI::_3DScene::render_cutting_plane($self); Slic3r::GUI::_3DScene::render_warning_texture($self); + Slic3r::GUI::_3DScene::render_legend_texture($self); # if (defined $self->cutting_plane_z) { # # draw cutting plane @@ -1652,11 +1650,11 @@ sub Render { # # # draw warning message # $self->draw_warning; +# +# # draw gcode preview legend +# $self->draw_legend; #============================================================================================================================== - # draw gcode preview legend - $self->draw_legend; - $self->draw_active_object_annotations; $self->SwapBuffers(); @@ -1919,49 +1917,40 @@ sub draw_active_object_annotations { glEnable(GL_DEPTH_TEST); } -sub draw_legend { - my ($self) = @_; - - if (!$self->_legend_enabled) { - return; - } - - # If the legend texture has not been loaded into the GPU, do it now. - my $tex_id = Slic3r::GUI::_3DScene::finalize_legend_texture; - if ($tex_id > 0) - { - my $tex_w = Slic3r::GUI::_3DScene::get_legend_texture_width; - my $tex_h = Slic3r::GUI::_3DScene::get_legend_texture_height; - if (($tex_w > 0) && ($tex_h > 0)) - { - glDisable(GL_DEPTH_TEST); - glPushMatrix(); - glLoadIdentity(); - - my ($cw, $ch) = $self->GetSizeWH; - #============================================================================================================================== - my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); - my $l = (-0.5 * $cw) / $zoom; - my $t = (0.5 * $ch) / $zoom; - my $r = $l + $tex_w / $zoom; - my $b = $t - $tex_h / $zoom; - Slic3r::GUI::_3DScene::render_texture($self, $tex_id, $l, $r, $b, $t); - +#sub draw_legend { +# my ($self) = @_; +# +# if (!$self->_legend_enabled) { +# return; +# } +# +# # If the legend texture has not been loaded into the GPU, do it now. +# my $tex_id = Slic3r::GUI::_3DScene::finalize_legend_texture; +# if ($tex_id > 0) +# { +# my $tex_w = Slic3r::GUI::_3DScene::get_legend_texture_width; +# my $tex_h = Slic3r::GUI::_3DScene::get_legend_texture_height; +# if (($tex_w > 0) && ($tex_h > 0)) +# { +# glDisable(GL_DEPTH_TEST); +# glPushMatrix(); +# glLoadIdentity(); +# +# my ($cw, $ch) = $self->GetSizeWH; +# # my $l = (-0.5 * $cw) / $self->_zoom; # my $t = (0.5 * $ch) / $self->_zoom; # my $r = $l + $tex_w / $self->_zoom; # my $b = $t - $tex_h / $self->_zoom; # $self->_render_texture($tex_id, $l, $r, $b, $t); -#============================================================================================================================== - - glPopMatrix(); - glEnable(GL_DEPTH_TEST); - } - } -} - -#============================================================================================================================== +# +# glPopMatrix(); +# glEnable(GL_DEPTH_TEST); +# } +# } +#} +# #sub draw_warning { # my ($self) = @_; # diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 9f80b1e25a..b136d89a1b 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -177,10 +177,16 @@ sub new { my $preview = $self->{preview_notebook}->GetCurrentPage; if ($preview == $self->{preview3D}) { - $self->{preview3D}->canvas->set_legend_enabled(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1); +# $self->{preview3D}->canvas->set_legend_enabled(1); +#============================================================================================================================== $self->{preview3D}->load_print(1); } else { - $self->{preview3D}->canvas->set_legend_enabled(0); +#============================================================================================================================== + Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 0); +# $self->{preview3D}->canvas->set_legend_enabled(0); +#============================================================================================================================== } $preview->OnActivate if $preview->can('OnActivate'); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index be170a2820..87a3c9aad6 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1903,6 +1903,11 @@ void _3DScene::enable_warning_texture(wxGLCanvas* canvas, bool enable) s_canvas_mgr.enable_warning_texture(canvas, enable); } +void _3DScene::enable_legend_texture(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_legend_texture(canvas, enable); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); @@ -1938,6 +1943,11 @@ void _3DScene::render_warning_texture(wxGLCanvas* canvas) s_canvas_mgr.render_warning_texture(canvas); } +void _3DScene::render_legend_texture(wxGLCanvas* canvas) +{ + s_canvas_mgr.render_legend_texture(canvas); +} + void _3DScene::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) { s_canvas_mgr.render_texture(canvas, tex_id, left, right, bottom, top); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index a984823961..a3d976018d 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -591,6 +591,7 @@ public: static bool is_layers_editing_enabled(wxGLCanvas* canvas); static void enable_warning_texture(wxGLCanvas* canvas, bool enable); + static void enable_legend_texture(wxGLCanvas* canvas, bool enable); static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); @@ -600,6 +601,7 @@ public: static void render_axes(wxGLCanvas* canvas); static void render_cutting_plane(wxGLCanvas* canvas); static void render_warning_texture(wxGLCanvas* canvas); + static void render_legend_texture(wxGLCanvas* canvas); static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index c97a9566c3..fce0459061 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -412,6 +412,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_dirty(true) , m_apply_zoom_to_volumes_filter(false) , m_warning_texture_enabled(false) + , m_legend_texture_enabled(false) { } @@ -688,6 +689,11 @@ void GLCanvas3D::enable_warning_texture(bool enable) m_warning_texture_enabled = enable; } +void GLCanvas3D::enable_legend_texture(bool enable) +{ + m_legend_texture_enabled = enable; +} + void GLCanvas3D::zoom_to_bed() { _zoom_to_bounding_box(bed_bounding_box()); @@ -770,7 +776,7 @@ void GLCanvas3D::render_warning_texture() float zoom = get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float l = (-0.5f * (float)w) * inv_zoom; - float t = (-0.5f * cnv_size.second + (float)h) * inv_zoom; + float t = (-0.5f * (float)cnv_size.second + (float)h) * inv_zoom; float r = l + (float)w * inv_zoom; float b = t - (float)h * inv_zoom; @@ -782,6 +788,38 @@ void GLCanvas3D::render_warning_texture() } } +void GLCanvas3D::render_legend_texture() +{ + if (!m_legend_texture_enabled) + return; + + // If the legend texture has not been loaded into the GPU, do it now. + unsigned int tex_id = _3DScene::finalize_legend_texture(); + if (tex_id > 0) + { + unsigned int w = _3DScene::get_legend_texture_width(); + unsigned int h = _3DScene::get_legend_texture_height(); + if ((w > 0) && (h > 0)) + { + ::glDisable(GL_DEPTH_TEST); + ::glPushMatrix(); + ::glLoadIdentity(); + + std::pair cnv_size = _get_canvas_size(); + float zoom = get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + float l = (-0.5f * (float)cnv_size.first) * inv_zoom; + float t = (0.5f * (float)cnv_size.second) * inv_zoom; + float r = l + (float)w * inv_zoom; + float b = t - (float)h * inv_zoom; + render_texture(tex_id, l, r, b, t); + + ::glPopMatrix(); + ::glEnable(GL_DEPTH_TEST); + } + } +} + void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) { ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index d7614d4e46..e1ce33fad4 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -152,6 +152,7 @@ private: bool m_dirty; bool m_apply_zoom_to_volumes_filter; bool m_warning_texture_enabled; + bool m_legend_texture_enabled; PerlCallback m_on_viewport_changed_callback; @@ -215,6 +216,7 @@ public: bool is_layers_editing_enabled() const; void enable_warning_texture(bool enable); + void enable_legend_texture(bool enable); void zoom_to_bed(); void zoom_to_volumes(); @@ -224,6 +226,7 @@ public: void render_axes(); void render_cutting_plane(); void render_warning_texture(); + void render_legend_texture(); void render_texture(unsigned int tex_id, float left, float right, float bottom, float top); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 22eff5a01e..7647e3525e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -362,6 +362,13 @@ void GLCanvas3DManager::enable_warning_texture(wxGLCanvas* canvas, bool enable) it->second->enable_warning_texture(enable); } +void GLCanvas3DManager::enable_legend_texture(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_legend_texture(enable); +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -411,6 +418,13 @@ void GLCanvas3DManager::render_warning_texture(wxGLCanvas* canvas) it->second->render_warning_texture(); } +void GLCanvas3DManager::render_legend_texture(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->render_legend_texture(); +} + void GLCanvas3DManager::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index d0d2bb609d..d79152f43b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -101,6 +101,7 @@ public: bool is_layers_editing_enabled(wxGLCanvas* canvas) const; void enable_warning_texture(wxGLCanvas* canvas, bool enable); + void enable_legend_texture(wxGLCanvas* canvas, bool enable); void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); @@ -110,6 +111,7 @@ public: void render_axes(wxGLCanvas* canvas); void render_cutting_plane(wxGLCanvas* canvas); void render_warning_texture(wxGLCanvas* canvas); + void render_legend_texture(wxGLCanvas* canvas); void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index aa4c98d155..32500b3eb6 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -418,6 +418,13 @@ enable_warning_texture(canvas, enable) CODE: _3DScene::enable_warning_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); +void +enable_legend_texture(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_legend_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void zoom_to_bed(canvas) SV *canvas; @@ -461,6 +468,12 @@ render_warning_texture(canvas) CODE: _3DScene::render_warning_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); +void +render_legend_texture(canvas) + SV *canvas; + CODE: + _3DScene::render_legend_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + void render_texture(canvas, tex_id, left, right, bottom, top) SV *canvas; From 0f035d0bae758338a46951b26f089b611f6c3bac Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 May 2018 15:24:52 +0200 Subject: [PATCH 017/117] Background rendering moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 69 +++++++++++++------------ xs/src/slic3r/GUI/3DScene.cpp | 5 ++ xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 50 +++++++++++++++--- xs/src/slic3r/GUI/GLCanvas3D.hpp | 8 ++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 7 +++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 1 + xs/xsp/GUI_3DScene.xsp | 6 +++ 8 files changed, 105 insertions(+), 42 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 31982f6a95..dcd8e31106 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -46,7 +46,6 @@ __PACKAGE__->mk_accessors( qw(_quat init on_move on_model_update volumes - background _mouse_pos _hover_volume_idx @@ -183,7 +182,9 @@ sub new { #============================================================================================================================== $self->{can_multisample} = $can_multisample; - $self->background(1); +#============================================================================================================================== +# $self->background(1); +#============================================================================================================================== $self->_quat((0, 0, 0, 1)); #============================================================================================================================== # $self->_stheta(45); @@ -1491,38 +1492,40 @@ sub Render { } } - # draw fixed background - if ($self->background) { - glDisable(GL_LIGHTING); - glPushMatrix(); - glLoadIdentity(); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - # Draws a bluish bottom to top gradient over the complete screen. - glDisable(GL_DEPTH_TEST); - glBegin(GL_QUADS); - glColor3f(0.0,0.0,0.0); - glVertex3f(-1.0,-1.0, 1.0); - glVertex3f( 1.0,-1.0, 1.0); - glColor3f(10/255,98/255,144/255); - glVertex3f( 1.0, 1.0, 1.0); - glVertex3f(-1.0, 1.0, 1.0); - glEnd(); - glPopMatrix(); - glEnable(GL_DEPTH_TEST); - - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glEnable(GL_LIGHTING); - } - - # draw ground and axes - glDisable(GL_LIGHTING); - #============================================================================================================================== + Slic3r::GUI::_3DScene::render_background($self); + +# # draw fixed background +# if ($self->background) { +# glDisable(GL_LIGHTING); +# glPushMatrix(); +# glLoadIdentity(); +# +# glMatrixMode(GL_PROJECTION); +# glPushMatrix(); +# glLoadIdentity(); +# +# # Draws a bluish bottom to top gradient over the complete screen. +# glDisable(GL_DEPTH_TEST); +# glBegin(GL_QUADS); +# glColor3f(0.0,0.0,0.0); +# glVertex3f(-1.0,-1.0, 1.0); +# glVertex3f( 1.0,-1.0, 1.0); +# glColor3f(10/255,98/255,144/255); +# glVertex3f( 1.0, 1.0, 1.0); +# glVertex3f(-1.0, 1.0, 1.0); +# glEnd(); +# glPopMatrix(); +# glEnable(GL_DEPTH_TEST); +# +# glMatrixMode(GL_MODELVIEW); +# glPopMatrix(); +# glEnable(GL_LIGHTING); +# } +# +# # draw ground and axes +# glDisable(GL_LIGHTING); +# # # draw ground # my $ground_z = GROUND_Z; #============================================================================================================================== diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 87a3c9aad6..40e6f18518 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1923,6 +1923,11 @@ void _3DScene::select_view(wxGLCanvas* canvas, const std::string& direction) s_canvas_mgr.select_view(canvas, direction); } +void _3DScene::render_background(wxGLCanvas* canvas) +{ + s_canvas_mgr.render_background(canvas); +} + void _3DScene::render_bed(wxGLCanvas* canvas) { s_canvas_mgr.render_bed(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index a3d976018d..22b8695632 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -597,6 +597,7 @@ public: static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); + static void render_background(wxGLCanvas* canvas); static void render_bed(wxGLCanvas* canvas); static void render_axes(wxGLCanvas* canvas); static void render_cutting_plane(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index fce0459061..f55563f7ae 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -208,6 +208,7 @@ void GLCanvas3D::Bed::render() unsigned int triangles_vcount = m_triangles.get_data_size() / 3; if (triangles_vcount > 0) { + ::glDisable(GL_LIGHTING); ::glDisable(GL_DEPTH_TEST); ::glEnable(GL_BLEND); @@ -312,6 +313,7 @@ void GLCanvas3D::Axes::set_length(float length) void GLCanvas3D::Axes::render() { + ::glDisable(GL_LIGHTING); // disable depth testing so that axes are not covered by ground ::glDisable(GL_DEPTH_TEST); ::glLineWidth(2.0f); @@ -350,12 +352,18 @@ bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons) return m_lines.set_from_lines(lines, m_z); } -void GLCanvas3D::CuttingPlane::render_plane(const BoundingBoxf3& bb) +void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) +{ + ::glDisable(GL_LIGHTING); + _render_plane(bb); + _render_contour(); +} + +void GLCanvas3D::CuttingPlane::_render_plane(const BoundingBoxf3& bb) { if (m_z >= 0.0f) { ::glDisable(GL_CULL_FACE); - ::glDisable(GL_LIGHTING); ::glEnable(GL_BLEND); ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -378,7 +386,7 @@ void GLCanvas3D::CuttingPlane::render_plane(const BoundingBoxf3& bb) } } -void GLCanvas3D::CuttingPlane::render_contour() +void GLCanvas3D::CuttingPlane::_render_contour() { ::glEnableClientState(GL_VERTEX_ARRAY); @@ -737,22 +745,50 @@ void GLCanvas3D::select_view(const std::string& direction) } } +void GLCanvas3D::render_background() +{ + static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f }; + + ::glDisable(GL_LIGHTING); + + ::glPushMatrix(); + ::glLoadIdentity(); + ::glMatrixMode(GL_PROJECTION); + ::glPushMatrix(); + ::glLoadIdentity(); + + // Draws a bluish bottom to top gradient over the complete screen. + ::glDisable(GL_DEPTH_TEST); + + ::glBegin(GL_QUADS); + ::glColor3f(0.0f, 0.0f, 0.0f); + ::glVertex3f(-1.0f, -1.0f, 1.0f); + ::glVertex3f(1.0f, -1.0f, 1.0f); + ::glColor3f(COLOR[0], COLOR[1], COLOR[2]); + ::glVertex3f(1.0f, 1.0f, 1.0f); + ::glVertex3f(-1.0f, 1.0f, 1.0f); + ::glEnd(); + + ::glEnable(GL_DEPTH_TEST); + + ::glPopMatrix(); + ::glMatrixMode(GL_MODELVIEW); + ::glPopMatrix(); +} + void GLCanvas3D::render_bed() { - ::glDisable(GL_LIGHTING); m_bed.render(); } void GLCanvas3D::render_axes() { - ::glDisable(GL_LIGHTING); m_axes.render(); } void GLCanvas3D::render_cutting_plane() { - m_cutting_plane.render_plane(volumes_bounding_box()); - m_cutting_plane.render_contour(); + m_cutting_plane.render(volumes_bounding_box()); } void GLCanvas3D::render_warning_texture() diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index e1ce33fad4..de244511a7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -124,8 +124,11 @@ public: bool set(float z, const ExPolygons& polygons); - void render_plane(const BoundingBoxf3& bb); - void render_contour(); + void render(const BoundingBoxf3& bb); + + private: + void _render_plane(const BoundingBoxf3& bb); + void _render_contour(); }; class LayersEditing @@ -222,6 +225,7 @@ public: void zoom_to_volumes(); void select_view(const std::string& direction); + void render_background(); void render_bed(); void render_axes(); void render_cutting_plane(); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 7647e3525e..4ff07b782d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -390,6 +390,13 @@ void GLCanvas3DManager::select_view(wxGLCanvas* canvas, const std::string& direc it->second->select_view(direction); } +void GLCanvas3DManager::render_background(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->render_background(); +} + void GLCanvas3DManager::render_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index d79152f43b..f7293049ee 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -107,6 +107,7 @@ public: void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); + void render_background(wxGLCanvas* canvas); void render_bed(wxGLCanvas* canvas); void render_axes(wxGLCanvas* canvas); void render_cutting_plane(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 32500b3eb6..6fd23a2f08 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -444,6 +444,12 @@ select_view(canvas, direction) CODE: _3DScene::select_view((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), direction); +void +render_background(canvas) + SV *canvas; + CODE: + _3DScene::render_background((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + void render_bed(canvas) SV *canvas; From ae53c7cb2ede95bdb7eb1b8e52c95b52b309c3eb Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 21 May 2018 15:57:03 +0200 Subject: [PATCH 018/117] Volumes rendering moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 88 ++++++++++++++----------- xs/src/slic3r/GUI/3DScene.cpp | 5 ++ xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 67 +++++++++++++++---- xs/src/slic3r/GUI/GLCanvas3D.hpp | 25 +++---- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 35 ++++++---- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 15 +++-- xs/xsp/GUI_3DScene.xsp | 9 ++- 8 files changed, 159 insertions(+), 86 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index dcd8e31106..d720b40609 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1470,7 +1470,10 @@ sub Render { glDisable(GL_MULTISAMPLE) if ($self->{can_multisample}); glDisable(GL_LIGHTING); glDisable(GL_BLEND); - $self->draw_volumes(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::render_volumes($self, 1); +# $self->draw_volumes(1); +#============================================================================================================================== glPopAttrib(); glFlush(); my $col = [ glReadPixels_p($pos->x, $self->GetSize->GetHeight - $pos->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE) ]; @@ -1598,7 +1601,10 @@ sub Render { # draw objects if (! $self->use_plain_shader) { - $self->draw_volumes; +#============================================================================================================================== + Slic3r::GUI::_3DScene::render_volumes($self, 0); +# $self->draw_volumes; +#============================================================================================================================== } elsif ($self->UseVBOs) { if ($self->enable_picking) { $self->mark_volumes_for_layer_height; @@ -1663,44 +1669,46 @@ sub Render { $self->SwapBuffers(); } -sub draw_volumes { - # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. - my ($self, $fakecolor) = @_; - - # do not cull backfaces to show broken geometry, if any - glDisable(GL_CULL_FACE); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - foreach my $volume_idx (0..$#{$self->volumes}) { - my $volume = $self->volumes->[$volume_idx]; - - if ($fakecolor) { - # Object picking mode. Render the object with a color encoding the object index. - my $r = ($volume_idx & 0x000000FF) >> 0; - my $g = ($volume_idx & 0x0000FF00) >> 8; - my $b = ($volume_idx & 0x00FF0000) >> 16; - glColor4f($r/255.0, $g/255.0, $b/255.0, 1); - } elsif ($volume->selected) { - glColor4f(@{ &SELECTED_COLOR }); - } elsif ($volume->hover) { - glColor4f(@{ &HOVER_COLOR }); - } else { - glColor4f(@{ $volume->color }); - } - - $volume->render; - } - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); -} +#============================================================================================================================== +#sub draw_volumes { +# # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. +# my ($self, $fakecolor) = @_; +# +# # do not cull backfaces to show broken geometry, if any +# glDisable(GL_CULL_FACE); +# +# glEnable(GL_BLEND); +# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +# +# glEnableClientState(GL_VERTEX_ARRAY); +# glEnableClientState(GL_NORMAL_ARRAY); +# +# foreach my $volume_idx (0..$#{$self->volumes}) { +# my $volume = $self->volumes->[$volume_idx]; +# +# if ($fakecolor) { +# # Object picking mode. Render the object with a color encoding the object index. +# my $r = ($volume_idx & 0x000000FF) >> 0; +# my $g = ($volume_idx & 0x0000FF00) >> 8; +# my $b = ($volume_idx & 0x00FF0000) >> 16; +# glColor4f($r/255.0, $g/255.0, $b/255.0, 1); +# } elsif ($volume->selected) { +# glColor4f(@{ &SELECTED_COLOR }); +# } elsif ($volume->hover) { +# glColor4f(@{ &HOVER_COLOR }); +# } else { +# glColor4f(@{ $volume->color }); +# } +# +# $volume->render; +# } +# glDisableClientState(GL_NORMAL_ARRAY); +# glDisableClientState(GL_VERTEX_ARRAY); +# +# glDisable(GL_BLEND); +# glEnable(GL_CULL_FACE); +#} +#============================================================================================================================== sub mark_volumes_for_layer_height { my ($self) = @_; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 40e6f18518..a7f5bd89e5 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1938,6 +1938,11 @@ void _3DScene::render_axes(wxGLCanvas* canvas) s_canvas_mgr.render_axes(canvas); } +void _3DScene::render_volumes(wxGLCanvas* canvas, bool fake_colors) +{ + s_canvas_mgr.render_volumes(canvas, fake_colors); +} + void _3DScene::render_cutting_plane(wxGLCanvas* canvas) { s_canvas_mgr.render_cutting_plane(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 22b8695632..24223b6b64 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -600,6 +600,7 @@ public: static void render_background(wxGLCanvas* canvas); static void render_bed(wxGLCanvas* canvas); static void render_axes(wxGLCanvas* canvas); + static void render_volumes(wxGLCanvas* canvas, bool fake_colors); static void render_cutting_plane(wxGLCanvas* canvas); static void render_warning_texture(wxGLCanvas* canvas); static void render_legend_texture(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index f55563f7ae..80eea86f0d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -203,7 +203,7 @@ const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const return m_bounding_box; } -void GLCanvas3D::Bed::render() +void GLCanvas3D::Bed::render() const { unsigned int triangles_vcount = m_triangles.get_data_size() / 3; if (triangles_vcount > 0) @@ -311,7 +311,7 @@ void GLCanvas3D::Axes::set_length(float length) m_length = length; } -void GLCanvas3D::Axes::render() +void GLCanvas3D::Axes::render() const { ::glDisable(GL_LIGHTING); // disable depth testing so that axes are not covered by ground @@ -352,14 +352,14 @@ bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons) return m_lines.set_from_lines(lines, m_z); } -void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) +void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) const { ::glDisable(GL_LIGHTING); _render_plane(bb); _render_contour(); } -void GLCanvas3D::CuttingPlane::_render_plane(const BoundingBoxf3& bb) +void GLCanvas3D::CuttingPlane::_render_plane(const BoundingBoxf3& bb) const { if (m_z >= 0.0f) { @@ -386,7 +386,7 @@ void GLCanvas3D::CuttingPlane::_render_plane(const BoundingBoxf3& bb) } } -void GLCanvas3D::CuttingPlane::_render_contour() +void GLCanvas3D::CuttingPlane::_render_contour() const { ::glEnableClientState(GL_VERTEX_ARRAY); @@ -745,7 +745,7 @@ void GLCanvas3D::select_view(const std::string& direction) } } -void GLCanvas3D::render_background() +void GLCanvas3D::render_background() const { static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f }; @@ -776,22 +776,65 @@ void GLCanvas3D::render_background() ::glPopMatrix(); } -void GLCanvas3D::render_bed() +void GLCanvas3D::render_bed() const { m_bed.render(); } -void GLCanvas3D::render_axes() +void GLCanvas3D::render_axes() const { m_axes.render(); } -void GLCanvas3D::render_cutting_plane() +void GLCanvas3D::render_volumes(bool fake_colors) const +{ + static const float INV_255 = 1.0f / 255.0f; + + if (m_volumes == nullptr) + return; + + ::glEnable(GL_LIGHTING); + + // do not cull backfaces to show broken geometry, if any + ::glDisable(GL_CULL_FACE); + + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + + unsigned int volume_id = 0; + for (const GLVolume* vol : m_volumes->volumes) + { + if (fake_colors) + { + // Object picking mode. Render the object with a color encoding the object index. + unsigned int r = (volume_id & 0x000000FF) >> 0; + unsigned int g = (volume_id & 0x0000FF00) >> 8; + unsigned int b = (volume_id & 0x00FF0000) >> 16; + ::glColor4f((float)r * INV_255, (float)g * INV_255, (float)b * INV_255, 1.0f); + } + else + ::glColor4f(vol->render_color[0], vol->render_color[1], vol->render_color[2], vol->render_color[3]); + + vol->render(); + ++volume_id; + } + + ::glDisableClientState(GL_NORMAL_ARRAY); + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisable(GL_BLEND); + + ::glEnable(GL_CULL_FACE); +} + +void GLCanvas3D::render_cutting_plane() const { m_cutting_plane.render(volumes_bounding_box()); } -void GLCanvas3D::render_warning_texture() +void GLCanvas3D::render_warning_texture() const { if (!m_warning_texture_enabled) return; @@ -824,7 +867,7 @@ void GLCanvas3D::render_warning_texture() } } -void GLCanvas3D::render_legend_texture() +void GLCanvas3D::render_legend_texture() const { if (!m_legend_texture_enabled) return; @@ -856,7 +899,7 @@ void GLCanvas3D::render_legend_texture() } } -void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) +void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const { ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index de244511a7..29be074903 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -89,7 +89,7 @@ public: const BoundingBoxf3& get_bounding_box() const; - void render(); + void render() const; private: void _calc_bounding_box(); @@ -111,7 +111,7 @@ public: float get_length() const; void set_length(float length); - void render(); + void render() const; }; class CuttingPlane @@ -124,11 +124,11 @@ public: bool set(float z, const ExPolygons& polygons); - void render(const BoundingBoxf3& bb); + void render(const BoundingBoxf3& bb) const; private: - void _render_plane(const BoundingBoxf3& bb); - void _render_contour(); + void _render_plane(const BoundingBoxf3& bb) const; + void _render_contour() const; }; class LayersEditing @@ -225,14 +225,15 @@ public: void zoom_to_volumes(); void select_view(const std::string& direction); - void render_background(); - void render_bed(); - void render_axes(); - void render_cutting_plane(); - void render_warning_texture(); - void render_legend_texture(); + void render_background() const; + void render_bed() const; + void render_axes() const; + void render_volumes(bool fake_colors) const; + void render_cutting_plane() const; + void render_warning_texture() const; + void render_legend_texture() const; - void render_texture(unsigned int tex_id, float left, float right, float bottom, float top); + void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 4ff07b782d..7e8cf913f7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -390,51 +390,58 @@ void GLCanvas3DManager::select_view(wxGLCanvas* canvas, const std::string& direc it->second->select_view(direction); } -void GLCanvas3DManager::render_background(wxGLCanvas* canvas) +void GLCanvas3DManager::render_background(wxGLCanvas* canvas) const { - CanvasesMap::iterator it = _get_canvas(canvas); + CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) it->second->render_background(); } -void GLCanvas3DManager::render_bed(wxGLCanvas* canvas) +void GLCanvas3DManager::render_bed(wxGLCanvas* canvas) const { - CanvasesMap::iterator it = _get_canvas(canvas); + CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) it->second->render_bed(); } -void GLCanvas3DManager::render_axes(wxGLCanvas* canvas) +void GLCanvas3DManager::render_axes(wxGLCanvas* canvas) const { - CanvasesMap::iterator it = _get_canvas(canvas); + CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) it->second->render_axes(); } -void GLCanvas3DManager::render_cutting_plane(wxGLCanvas* canvas) +void GLCanvas3DManager::render_volumes(wxGLCanvas* canvas, bool fake_colors) const { - CanvasesMap::iterator it = _get_canvas(canvas); + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->render_volumes(fake_colors); +} + +void GLCanvas3DManager::render_cutting_plane(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) it->second->render_cutting_plane(); } -void GLCanvas3DManager::render_warning_texture(wxGLCanvas* canvas) +void GLCanvas3DManager::render_warning_texture(wxGLCanvas* canvas) const { - CanvasesMap::iterator it = _get_canvas(canvas); + CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) it->second->render_warning_texture(); } -void GLCanvas3DManager::render_legend_texture(wxGLCanvas* canvas) +void GLCanvas3DManager::render_legend_texture(wxGLCanvas* canvas) const { - CanvasesMap::iterator it = _get_canvas(canvas); + CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) it->second->render_legend_texture(); } -void GLCanvas3DManager::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) +void GLCanvas3DManager::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const { - CanvasesMap::iterator it = _get_canvas(canvas); + CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) it->second->render_texture(tex_id, left, right, bottom, top); } diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index f7293049ee..f2ff8a112f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -107,14 +107,15 @@ public: void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); - void render_background(wxGLCanvas* canvas); - void render_bed(wxGLCanvas* canvas); - void render_axes(wxGLCanvas* canvas); - void render_cutting_plane(wxGLCanvas* canvas); - void render_warning_texture(wxGLCanvas* canvas); - void render_legend_texture(wxGLCanvas* canvas); + void render_background(wxGLCanvas* canvas) const; + void render_bed(wxGLCanvas* canvas) const; + void render_axes(wxGLCanvas* canvas) const; + void render_volumes(wxGLCanvas* canvas, bool fake_colors) const; + void render_cutting_plane(wxGLCanvas* canvas) const; + void render_warning_texture(wxGLCanvas* canvas) const; + void render_legend_texture(wxGLCanvas* canvas) const; - void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); + void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 6fd23a2f08..40332e7ec1 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -461,7 +461,14 @@ render_axes(canvas) SV *canvas; CODE: _3DScene::render_axes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - + +void +render_volumes(canvas, fake_colors) + SV *canvas; + bool fake_colors; + CODE: + _3DScene::render_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), fake_colors); + void render_cutting_plane(canvas) SV *canvas; From 451c58d58fdb2c7270de1fa3e5700bf3d2d9af37 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 22 May 2018 09:02:42 +0200 Subject: [PATCH 019/117] 3DScene's enable_picking variable moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 38 +++++++++++++++++------ lib/Slic3r/GUI/Plater/3D.pm | 5 ++- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 5 ++- xs/src/slic3r/GUI/3DScene.cpp | 10 ++++++ xs/src/slic3r/GUI/3DScene.hpp | 2 ++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 21 +++++++++++-- xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 ++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 13 ++++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 ++ xs/xsp/GUI_3DScene.xsp | 15 +++++++++ 10 files changed, 101 insertions(+), 13 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index d720b40609..fce0231e2c 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -35,7 +35,6 @@ use Slic3r::Geometry qw(PI); # _camera_type: 'perspective' or 'ortho' #============================================================================================================================== __PACKAGE__->mk_accessors( qw(_quat init - enable_picking enable_moving use_plain_shader on_viewport_changed @@ -504,7 +503,10 @@ sub mouse_event { # 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)) { +#============================================================================================================================== + if (Slic3r::GUI::_3DScene::is_picking_enabled($self) && ($volume_idx != -1 || ! $self->layer_editing_enabled)) { +# if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) { +#============================================================================================================================== $self->deselect_volumes; $self->select_volume($volume_idx); @@ -658,7 +660,10 @@ sub mouse_event { $self->_mouse_pos($pos); # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor # hovers over. - if ($self->enable_picking) { +#============================================================================================================================== + if (Slic3r::GUI::_3DScene::is_picking_enabled($self)) { +# if ($self->enable_picking) { +#============================================================================================================================== $self->Update; $self->Refresh; } @@ -1460,8 +1465,11 @@ sub Render { # Head light glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0); - - if ($self->enable_picking && !$self->_mouse_dragging) { + +#============================================================================================================================== + if (Slic3r::GUI::_3DScene::is_picking_enabled($self) && !$self->_mouse_dragging) { +# if ($self->enable_picking && !$self->_mouse_dragging) { +#============================================================================================================================== if (my $pos = $self->_mouse_pos) { # Render the object for picking. # FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing. @@ -1606,7 +1614,10 @@ sub Render { # $self->draw_volumes; #============================================================================================================================== } elsif ($self->UseVBOs) { - if ($self->enable_picking) { +#============================================================================================================================== + if (Slic3r::GUI::_3DScene::is_picking_enabled($self)) { +# if ($self->enable_picking) { +#============================================================================================================================== $self->mark_volumes_for_layer_height; $self->volumes->set_print_box($self->bed_bounding_box->x_min, $self->bed_bounding_box->y_min, 0.0, $self->bed_bounding_box->x_max, $self->bed_bounding_box->y_max, $self->{config}->get('max_print_height')); $self->volumes->update_outside_state($self->{config}, 0); @@ -1616,12 +1627,21 @@ sub Render { $self->{plain_shader}->enable if $self->{plain_shader}; $self->volumes->render_VBOs; $self->{plain_shader}->disable; - glEnable(GL_CULL_FACE) if ($self->enable_picking); +#============================================================================================================================== + glEnable(GL_CULL_FACE) if (Slic3r::GUI::_3DScene::is_picking_enabled($self)); +# glEnable(GL_CULL_FACE) if ($self->enable_picking); +#============================================================================================================================== } else { # do not cull backfaces to show broken geometry, if any - glDisable(GL_CULL_FACE) if ($self->enable_picking); +#============================================================================================================================== + glDisable(GL_CULL_FACE) if (Slic3r::GUI::_3DScene::is_picking_enabled($self)); +# glDisable(GL_CULL_FACE) if ($self->enable_picking); +#============================================================================================================================== $self->volumes->render_legacy; - glEnable(GL_CULL_FACE) if ($self->enable_picking); +#============================================================================================================================== + glEnable(GL_CULL_FACE) if (Slic3r::GUI::_3DScene::is_picking_enabled($self)); +# glEnable(GL_CULL_FACE) if ($self->enable_picking); +#============================================================================================================================== } #============================================================================================================================== diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index ecf841b27e..407a0b6a9e 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -19,7 +19,10 @@ sub new { my ($parent, $objects, $model, $print, $config) = @_; my $self = $class->SUPER::new($parent); - $self->enable_picking(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::enable_picking($self, 1); +# $self->enable_picking(1); +#============================================================================================================================== $self->enable_moving(1); $self->select_by('object'); $self->drag_by('instance'); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index a2b779a0ab..54fdc249e3 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -153,7 +153,10 @@ sub new { my $canvas; if ($Slic3r::GUI::have_OpenGL) { $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); - $canvas->enable_picking(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::enable_picking($canvas, 1); +# $canvas->enable_picking(1); +#============================================================================================================================== $canvas->select_by('volume'); $canvas->on_select(sub { diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index a7f5bd89e5..57c926f7eb 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1898,6 +1898,11 @@ bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) return s_canvas_mgr.is_layers_editing_enabled(canvas); } +bool _3DScene::is_picking_enabled(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_picking_enabled(canvas); +} + void _3DScene::enable_warning_texture(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_warning_texture(canvas, enable); @@ -1908,6 +1913,11 @@ void _3DScene::enable_legend_texture(wxGLCanvas* canvas, bool enable) s_canvas_mgr.enable_legend_texture(canvas, enable); } +void _3DScene::enable_picking(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_picking(canvas, enable); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 24223b6b64..ad8ca75e42 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -589,9 +589,11 @@ public: static void set_camera_target(wxGLCanvas* canvas, const Pointf3* target); static bool is_layers_editing_enabled(wxGLCanvas* canvas); + static bool is_picking_enabled(wxGLCanvas* canvas); static void enable_warning_texture(wxGLCanvas* canvas, bool enable); static void enable_legend_texture(wxGLCanvas* canvas, bool enable); + static void enable_picking(wxGLCanvas* canvas, bool enable); static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 80eea86f0d..4dadf81815 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -421,6 +421,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_apply_zoom_to_volumes_filter(false) , m_warning_texture_enabled(false) , m_legend_texture_enabled(false) + , m_picking_enabled(false) { } @@ -692,6 +693,11 @@ bool GLCanvas3D::is_layers_editing_enabled() const return m_layers_editing.is_enabled(); } +bool GLCanvas3D::is_picking_enabled() const +{ + return m_picking_enabled; +} + void GLCanvas3D::enable_warning_texture(bool enable) { m_warning_texture_enabled = enable; @@ -702,6 +708,11 @@ void GLCanvas3D::enable_legend_texture(bool enable) m_legend_texture_enabled = enable; } +void GLCanvas3D::enable_picking(bool enable) +{ + m_picking_enabled = enable; +} + void GLCanvas3D::zoom_to_bed() { _zoom_to_bounding_box(bed_bounding_box()); @@ -793,7 +804,10 @@ void GLCanvas3D::render_volumes(bool fake_colors) const if (m_volumes == nullptr) return; - ::glEnable(GL_LIGHTING); + if (fake_colors) + ::glDisable(GL_LIGHTING); + else + ::glEnable(GL_LIGHTING); // do not cull backfaces to show broken geometry, if any ::glDisable(GL_CULL_FACE); @@ -805,7 +819,7 @@ void GLCanvas3D::render_volumes(bool fake_colors) const ::glEnableClientState(GL_NORMAL_ARRAY); unsigned int volume_id = 0; - for (const GLVolume* vol : m_volumes->volumes) + for (GLVolume* vol : m_volumes->volumes) { if (fake_colors) { @@ -816,7 +830,10 @@ void GLCanvas3D::render_volumes(bool fake_colors) const ::glColor4f((float)r * INV_255, (float)g * INV_255, (float)b * INV_255, 1.0f); } else + { + vol->set_render_color(); ::glColor4f(vol->render_color[0], vol->render_color[1], vol->render_color[2], vol->render_color[3]); + } vol->render(); ++volume_id; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 29be074903..dcf14e725c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -156,6 +156,7 @@ private: bool m_apply_zoom_to_volumes_filter; bool m_warning_texture_enabled; bool m_legend_texture_enabled; + bool m_picking_enabled; PerlCallback m_on_viewport_changed_callback; @@ -217,9 +218,11 @@ public: BoundingBoxf3 max_bounding_box() const; bool is_layers_editing_enabled() const; + bool is_picking_enabled() const; void enable_warning_texture(bool enable); void enable_legend_texture(bool enable); + void enable_picking(bool enable); void zoom_to_bed(); void zoom_to_volumes(); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 7e8cf913f7..4349ae8458 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -355,6 +355,12 @@ bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_layers_editing_enabled() : false; } +bool GLCanvas3DManager::is_picking_enabled(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_picking_enabled() : false; +} + void GLCanvas3DManager::enable_warning_texture(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -369,6 +375,13 @@ void GLCanvas3DManager::enable_legend_texture(wxGLCanvas* canvas, bool enable) it->second->enable_legend_texture(enable); } +void GLCanvas3DManager::enable_picking(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_picking(enable); +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index f2ff8a112f..46eb872db0 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -99,9 +99,11 @@ public: void set_camera_target(wxGLCanvas* canvas, const Pointf3* target); bool is_layers_editing_enabled(wxGLCanvas* canvas) const; + bool is_picking_enabled(wxGLCanvas* canvas) const; void enable_warning_texture(wxGLCanvas* canvas, bool enable); void enable_legend_texture(wxGLCanvas* canvas, bool enable); + void enable_picking(wxGLCanvas* canvas, bool enable); void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 40332e7ec1..0bb22888b1 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -411,6 +411,14 @@ set_camera_target(canvas, target) CODE: _3DScene::set_camera_target((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), target); +bool +is_picking_enabled(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_picking_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + void enable_warning_texture(canvas, enable) SV *canvas; @@ -425,6 +433,13 @@ enable_legend_texture(canvas, enable) CODE: _3DScene::enable_legend_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); +void +enable_picking(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_picking((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void zoom_to_bed(canvas) SV *canvas; From b4beb7aae974491dbc29f345a1f9094d0cd0f005 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 23 May 2018 09:57:44 +0200 Subject: [PATCH 020/117] 3DScene plain shader moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 48 +++++++------ lib/Slic3r/GUI/Plater.pm | 5 +- lib/Slic3r/GUI/Plater/3DPreview.pm | 5 +- xs/src/slic3r/GUI/3DScene.cpp | 30 ++++++++- xs/src/slic3r/GUI/3DScene.hpp | 9 ++- xs/src/slic3r/GUI/GLCanvas3D.cpp | 89 +++++++++++++++++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 27 ++++++++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 32 +++++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 7 ++ xs/xsp/GUI_3DScene.xsp | 88 +++++++++++++++++------- 10 files changed, 289 insertions(+), 51 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 5f35fb8e3c..3975c12ea2 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -36,7 +36,6 @@ use Slic3r::Geometry qw(PI); #============================================================================================================================== __PACKAGE__->mk_accessors( qw(_quat init enable_moving - use_plain_shader on_viewport_changed on_hover on_select @@ -191,9 +190,7 @@ sub new { # $self->_zoom(1); # $self->_legend_enabled(0); # $self->_warning_enabled(0); -#============================================================================================================================== - $self->use_plain_shader(0); -#============================================================================================================================== +# $self->use_plain_shader(0); # $self->_apply_zoom_to_volumes_filter(0); #============================================================================================================================== $self->_mouse_dragging(0); @@ -1382,28 +1379,30 @@ sub InitGL { glEnable(GL_COLOR_MATERIAL); glEnable(GL_MULTISAMPLE) if ($self->{can_multisample}); - if ($self->UseVBOs) { - my $shader = new Slic3r::GUI::_3DScene::GLShader; #=================================================================================================================================== - if (! $shader->load_from_file("gouraud.fs", "gouraud.vs")) { + Slic3r::GUI::_3DScene::init($self, $self->UseVBOs); +# if ($self->UseVBOs) { +# my $shader = new Slic3r::GUI::_3DScene::GLShader; ## if (! $shader->load($self->_fragment_shader_Phong, $self->_vertex_shader_Phong)) { +# print "Compilaton of path shader failed: \n" . $shader->last_error . "\n"; +# $shader = undef; +# } else { +# $self->{plain_shader} = $shader; +# } +# } #=================================================================================================================================== - print "Compilaton of path shader failed: \n" . $shader->last_error . "\n"; - $shader = undef; - } else { - $self->{plain_shader} = $shader; - } - } } sub DestroyGL { my $self = shift; if ($self->GetContext) { $self->SetCurrent($self->GetContext); - if ($self->{plain_shader}) { - $self->{plain_shader}->release; - delete $self->{plain_shader}; - } +#=================================================================================================================================== +# if ($self->{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}; @@ -1608,7 +1607,10 @@ sub Render { glEnable(GL_LIGHTING); # draw objects - if (! $self->use_plain_shader) { +#=================================================================================================================================== + if (!Slic3r::GUI::_3DScene::is_shader_enabled($self)) { +# if (! $self->use_plain_shader) { +#=================================================================================================================================== #============================================================================================================================== Slic3r::GUI::_3DScene::render_volumes($self, 0); # $self->draw_volumes; @@ -1624,9 +1626,15 @@ sub Render { # do not cull backfaces to show broken geometry, if any glDisable(GL_CULL_FACE); } - $self->{plain_shader}->enable if $self->{plain_shader}; +#============================================================================================================================== + Slic3r::GUI::_3DScene::start_using_shader($self); +# $self->{plain_shader}->enable if $self->{plain_shader}; +#============================================================================================================================== $self->volumes->render_VBOs; - $self->{plain_shader}->disable; +#============================================================================================================================== + Slic3r::GUI::_3DScene::stop_using_shader($self); +# $self->{plain_shader}->disable; +#============================================================================================================================== #============================================================================================================================== glEnable(GL_CULL_FACE) if (Slic3r::GUI::_3DScene::is_picking_enabled($self)); # glEnable(GL_CULL_FACE) if ($self->enable_picking); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 481ffd013b..41bcead29c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -122,7 +122,10 @@ sub new { $self->{canvas3D}->set_on_remove_object(sub { $self->remove() }); $self->{canvas3D}->set_on_instances_moved($on_instances_moved); $self->{canvas3D}->set_on_enable_action_buttons($enable_action_buttons); - $self->{canvas3D}->use_plain_shader(1); +#=================================================================================================================================== + Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); +# $self->{canvas3D}->use_plain_shader(1); +#=================================================================================================================================== $self->{canvas3D}->set_on_wipe_tower_moved(sub { my ($new_pos_3f) = @_; my $cfg = Slic3r::Config->new; diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index eb40775795..390e1b85e0 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -24,7 +24,10 @@ sub new { # init GUI elements my $canvas = Slic3r::GUI::3DScene->new($self); - $canvas->use_plain_shader(1); +#=================================================================================================================================== + Slic3r::GUI::_3DScene::enable_shader($canvas, 1); +# $canvas->use_plain_shader(1); +#=================================================================================================================================== $self->canvas($canvas); my $slider_low = Wx::Slider->new( $self, -1, diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 76353c15d5..38cfde7134 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1752,10 +1752,9 @@ void _3DScene::remove_all_canvases() std::cout << "# canvases not yet released: " << s_canvas_mgr.count() << std::endl; s_canvas_mgr.remove_all(); } - -void _3DScene::resize(wxGLCanvas* canvas, unsigned int w, unsigned int h) +bool _3DScene::init(wxGLCanvas* canvas, bool useVBOs) { - s_canvas_mgr.resize(canvas, w, h); + return s_canvas_mgr.init(canvas, useVBOs); } bool _3DScene::is_dirty(wxGLCanvas* canvas) @@ -1773,6 +1772,11 @@ bool _3DScene::is_shown_on_screen(wxGLCanvas* canvas) return s_canvas_mgr.is_shown_on_screen(canvas); } +void _3DScene::resize(wxGLCanvas* canvas, unsigned int w, unsigned int h) +{ + s_canvas_mgr.resize(canvas, w, h); +} + GLVolumeCollection* _3DScene::get_volumes(wxGLCanvas* canvas) { return s_canvas_mgr.get_volumes(canvas); @@ -1914,6 +1918,11 @@ bool _3DScene::is_picking_enabled(wxGLCanvas* canvas) return s_canvas_mgr.is_picking_enabled(canvas); } +bool _3DScene::is_shader_enabled(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_shader_enabled(canvas); +} + void _3DScene::enable_warning_texture(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_warning_texture(canvas, enable); @@ -1929,6 +1938,11 @@ void _3DScene::enable_picking(wxGLCanvas* canvas, bool enable) s_canvas_mgr.enable_picking(canvas, enable); } +void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_shader(canvas, enable); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); @@ -1944,6 +1958,16 @@ void _3DScene::select_view(wxGLCanvas* canvas, const std::string& direction) s_canvas_mgr.select_view(canvas, direction); } +bool _3DScene::start_using_shader(wxGLCanvas* canvas) +{ + return s_canvas_mgr.start_using_shader(canvas); +} + +void _3DScene::stop_using_shader(wxGLCanvas* canvas) +{ + s_canvas_mgr.stop_using_shader(canvas); +} + void _3DScene::render_background(wxGLCanvas* canvas) { s_canvas_mgr.render_background(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 9c56fbb59a..747bd3d42c 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -544,13 +544,15 @@ public: static bool remove_canvas(wxGLCanvas* canvas); static void remove_all_canvases(); - static void resize(wxGLCanvas* canvas, unsigned int w, unsigned int h); + static bool init(wxGLCanvas* canvas, bool useVBOs); static bool is_dirty(wxGLCanvas* canvas); static void set_dirty(wxGLCanvas* canvas, bool dirty); static bool is_shown_on_screen(wxGLCanvas* canvas); + static void resize(wxGLCanvas* canvas, unsigned int w, unsigned int h); + static GLVolumeCollection* get_volumes(wxGLCanvas* canvas); static void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); @@ -592,14 +594,19 @@ public: static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_picking_enabled(wxGLCanvas* canvas); + static bool is_shader_enabled(wxGLCanvas* canvas); static void enable_warning_texture(wxGLCanvas* canvas, bool enable); static void enable_legend_texture(wxGLCanvas* canvas, bool enable); static void enable_picking(wxGLCanvas* canvas, bool enable); + static void enable_shader(wxGLCanvas* canvas, bool enable); static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); + + static bool start_using_shader(wxGLCanvas* canvas); + static void stop_using_shader(wxGLCanvas* canvas); static void render_background(wxGLCanvas* canvas); static void render_bed(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 4dadf81815..e243db011e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,6 +1,7 @@ #include "GLCanvas3D.hpp" #include "../../slic3r/GUI/3DScene.hpp" +#include "../../slic3r/GUI/GLShader.hpp" #include "../../libslic3r/ClipperUtils.hpp" #include @@ -413,6 +414,65 @@ bool GLCanvas3D::LayersEditing::is_enabled() const return m_enabled; } +GLCanvas3D::Shader::Shader() + : m_enabled(false) + , m_shader(nullptr) +{ +} + +bool GLCanvas3D::Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) +{ + m_shader = new GLShader(); + if (m_shader != nullptr) + { + if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str())) + { + std::cout << "Compilaton of path shader failed:" << std::endl; + std::cout << m_shader->last_error << std::endl; + reset(); + return false; + } + } + + return true; +} + +void GLCanvas3D::Shader::reset() +{ + if (m_shader != nullptr) + { + delete m_shader; + m_shader = nullptr; + } +} + +bool GLCanvas3D::Shader::is_enabled() const +{ + return m_enabled; +} + +void GLCanvas3D::Shader::set_enabled(bool enabled) +{ + m_enabled = enabled; +} + +bool GLCanvas3D::Shader::start() const +{ + if (m_enabled && (m_shader != nullptr)) + { + m_shader->enable(); + return true; + } + else + return false; +} + +void GLCanvas3D::Shader::stop() const +{ + if (m_shader != nullptr) + m_shader->disable(); +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -428,6 +488,15 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) GLCanvas3D::~GLCanvas3D() { _deregister_callbacks(); + m_shader.reset(); +} + +bool GLCanvas3D::init(bool useVBOs) +{ + if (useVBOs && !m_shader.init("gouraud.vs", "gouraud.fs")) + return false; + + return true; } bool GLCanvas3D::set_current() @@ -698,6 +767,11 @@ bool GLCanvas3D::is_picking_enabled() const return m_picking_enabled; } +bool GLCanvas3D::is_shader_enabled() const +{ + return m_shader.is_enabled(); +} + void GLCanvas3D::enable_warning_texture(bool enable) { m_warning_texture_enabled = enable; @@ -713,6 +787,11 @@ void GLCanvas3D::enable_picking(bool enable) m_picking_enabled = enable; } +void GLCanvas3D::enable_shader(bool enable) +{ + m_shader.set_enabled(enable); +} + void GLCanvas3D::zoom_to_bed() { _zoom_to_bounding_box(bed_bounding_box()); @@ -756,6 +835,16 @@ void GLCanvas3D::select_view(const std::string& direction) } } +bool GLCanvas3D::start_using_shader() const +{ + return m_shader.start(); +} + +void GLCanvas3D::stop_using_shader() const +{ + m_shader.stop(); +} + void GLCanvas3D::render_background() const { static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f }; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index dcf14e725c..6a2af8324e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -14,6 +14,7 @@ class wxKeyEvent; namespace Slic3r { class GLVolumeCollection; +class GLShader; class ExPolygon; namespace GUI { @@ -141,6 +142,24 @@ public: bool is_enabled() const; }; + class Shader + { + bool m_enabled; + GLShader* m_shader; + + public: + Shader(); + + bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); + void reset(); + + bool is_enabled() const; + void set_enabled(bool enabled); + + bool start() const; + void stop() const; + }; + private: wxGLCanvas* m_canvas; wxGLContext* m_context; @@ -149,6 +168,7 @@ private: Axes m_axes; CuttingPlane m_cutting_plane; LayersEditing m_layers_editing; + Shader m_shader; GLVolumeCollection* m_volumes; @@ -164,6 +184,8 @@ public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); ~GLCanvas3D(); + bool init(bool useVBOs); + bool set_current(); bool is_dirty() const; @@ -219,15 +241,20 @@ public: bool is_layers_editing_enabled() const; bool is_picking_enabled() const; + bool is_shader_enabled() const; void enable_warning_texture(bool enable); void enable_legend_texture(bool enable); void enable_picking(bool enable); + void enable_shader(bool enable); void zoom_to_bed(); void zoom_to_volumes(); void select_view(const std::string& direction); + bool start_using_shader() const; + void stop_using_shader() const; + void render_background() const; void render_bed() const; void render_axes() const; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 4349ae8458..f612b55304 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -148,6 +148,12 @@ bool GLCanvas3DManager::layer_editing_allowed() const return m_layer_editing.allowed; } +bool GLCanvas3DManager::init(wxGLCanvas* canvas, bool useVBOs) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->init(useVBOs) : false; +} + bool GLCanvas3DManager::is_dirty(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -361,6 +367,12 @@ bool GLCanvas3DManager::is_picking_enabled(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_picking_enabled() : false; } +bool GLCanvas3DManager::is_shader_enabled(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_shader_enabled() : false; +} + void GLCanvas3DManager::enable_warning_texture(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -382,6 +394,13 @@ void GLCanvas3DManager::enable_picking(wxGLCanvas* canvas, bool enable) it->second->enable_picking(enable); } +void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_shader(enable); +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -403,6 +422,19 @@ void GLCanvas3DManager::select_view(wxGLCanvas* canvas, const std::string& direc it->second->select_view(direction); } +bool GLCanvas3DManager::start_using_shader(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->start_using_shader() : false; +} + +void GLCanvas3DManager::stop_using_shader(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->stop_using_shader(); +} + void GLCanvas3DManager::render_background(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 46eb872db0..7c8b82075b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -52,6 +52,8 @@ public: bool use_VBOs() const; bool layer_editing_allowed() const; + bool init(wxGLCanvas* canvas, bool useVBOs); + bool is_dirty(wxGLCanvas* canvas) const; void set_dirty(wxGLCanvas* canvas, bool dirty); @@ -100,15 +102,20 @@ public: bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_picking_enabled(wxGLCanvas* canvas) const; + bool is_shader_enabled(wxGLCanvas* canvas) const; void enable_warning_texture(wxGLCanvas* canvas, bool enable); void enable_legend_texture(wxGLCanvas* canvas, bool enable); void enable_picking(wxGLCanvas* canvas, bool enable); + void enable_shader(wxGLCanvas* canvas, bool enable); void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); + bool start_using_shader(wxGLCanvas* canvas) const; + void stop_using_shader(wxGLCanvas* canvas) const; + void render_background(wxGLCanvas* canvas) const; void render_bed(wxGLCanvas* canvas) const; void render_axes(wxGLCanvas* canvas) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 51317ec5dd..5f80741aad 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -190,6 +190,38 @@ remove_all_canvases() CODE: _3DScene::remove_all_canvases(); +bool +init(canvas, useVBOs) + SV *canvas; + bool useVBOs; + CODE: + RETVAL = _3DScene::init((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), useVBOs); + OUTPUT: + RETVAL + +bool +is_dirty(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_dirty((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_dirty(canvas, dirty) + SV *canvas; + bool dirty; + CODE: + _3DScene::set_dirty((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), dirty); + +bool +is_shown_on_screen(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_shown_on_screen((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + void resize(canvas, w, h) SV *canvas; @@ -256,29 +288,6 @@ get_max_bounding_box(canvas) OUTPUT: RETVAL -bool -is_dirty(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::is_dirty((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_dirty(canvas, dirty) - SV *canvas; - bool dirty; - CODE: - _3DScene::set_dirty((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), dirty); - -bool -is_shown_on_screen(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::is_shown_on_screen((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - Clone get_axes_origin(canvas) SV *canvas; @@ -422,7 +431,15 @@ is_picking_enabled(canvas) RETVAL = _3DScene::is_picking_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); OUTPUT: RETVAL - + +bool +is_shader_enabled(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_shader_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + void enable_warning_texture(canvas, enable) SV *canvas; @@ -443,7 +460,14 @@ enable_picking(canvas, enable) bool enable; CODE: _3DScene::enable_picking((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); - + +void +enable_shader(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void zoom_to_bed(canvas) SV *canvas; @@ -463,6 +487,20 @@ select_view(canvas, direction) CODE: _3DScene::select_view((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), direction); +bool +start_using_shader(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::start_using_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +stop_using_shader(canvas) + SV *canvas; + CODE: + _3DScene::stop_using_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + void render_background(canvas) SV *canvas; From b36243ba10a89fed4259db7a48d6069ed11dc7d7 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 23 May 2018 11:14:49 +0200 Subject: [PATCH 021/117] Objects rendering moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 82 ++++++++++--------------- lib/Slic3r/GUI/Plater/3D.pm | 3 + xs/src/slic3r/GUI/3DScene.cpp | 20 ++++++ xs/src/slic3r/GUI/3DScene.hpp | 6 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 64 +++++++++++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 9 ++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 27 ++++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 6 +- xs/xsp/GUI_3DScene.xsp | 30 ++++++++- 9 files changed, 192 insertions(+), 55 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 3975c12ea2..4c8e790b43 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -282,6 +282,14 @@ sub new { $self->_variable_layer_thickness_action(undef); }); +#============================================================================================================================== + my $on_mark_volumes_for_layer_height = sub { + $self->mark_volumes_for_layer_height; + }; + + Slic3r::GUI::_3DScene::register_on_mark_volumes_for_layer_height($self, $on_mark_volumes_for_layer_height); +#============================================================================================================================== + return $self; } @@ -1504,6 +1512,7 @@ sub Render { #============================================================================================================================== Slic3r::GUI::_3DScene::render_background($self); + Slic3r::GUI::_3DScene::render_bed($self); # # draw fixed background # if ($self->background) { @@ -1538,10 +1547,7 @@ sub Render { # # # draw ground # my $ground_z = GROUND_Z; -#============================================================================================================================== -#============================================================================================================================== - Slic3r::GUI::_3DScene::render_bed($self); - +# # if ($self->bed_triangles) { # glDisable(GL_DEPTH_TEST); # @@ -1575,6 +1581,10 @@ sub Render { #============================================================================================================================== Slic3r::GUI::_3DScene::render_axes($self); + Slic3r::GUI::_3DScene::render_objects($self, $self->UseVBOs); + Slic3r::GUI::_3DScene::render_cutting_plane($self); + Slic3r::GUI::_3DScene::render_warning_texture($self); + Slic3r::GUI::_3DScene::render_legend_texture($self); # { # # draw axes @@ -1602,61 +1612,31 @@ sub Render { # glVertex3f(@$origin, $ground_z+$axis_len); # glEnd(); # } -#============================================================================================================================== - - glEnable(GL_LIGHTING); - - # draw objects -#=================================================================================================================================== - if (!Slic3r::GUI::_3DScene::is_shader_enabled($self)) { +# +# glEnable(GL_LIGHTING); +# +# # draw objects # if (! $self->use_plain_shader) { -#=================================================================================================================================== -#============================================================================================================================== - Slic3r::GUI::_3DScene::render_volumes($self, 0); # $self->draw_volumes; -#============================================================================================================================== - } elsif ($self->UseVBOs) { -#============================================================================================================================== - if (Slic3r::GUI::_3DScene::is_picking_enabled($self)) { +# } elsif ($self->UseVBOs) { # if ($self->enable_picking) { -#============================================================================================================================== - $self->mark_volumes_for_layer_height; - $self->volumes->set_print_box($self->bed_bounding_box->x_min, $self->bed_bounding_box->y_min, 0.0, $self->bed_bounding_box->x_max, $self->bed_bounding_box->y_max, $self->{config}->get('max_print_height')); - $self->volumes->check_outside_state($self->{config}); - # do not cull backfaces to show broken geometry, if any - glDisable(GL_CULL_FACE); - } -#============================================================================================================================== - Slic3r::GUI::_3DScene::start_using_shader($self); +# $self->mark_volumes_for_layer_height; +# $self->volumes->set_print_box($self->bed_bounding_box->x_min, $self->bed_bounding_box->y_min, 0.0, $self->bed_bounding_box->x_max, $self->bed_bounding_box->y_max, $self->{config}->get('max_print_height')); +# $self->volumes->check_outside_state($self->{config}); +# # do not cull backfaces to show broken geometry, if any +# glDisable(GL_CULL_FACE); +# } # $self->{plain_shader}->enable if $self->{plain_shader}; -#============================================================================================================================== - $self->volumes->render_VBOs; -#============================================================================================================================== - Slic3r::GUI::_3DScene::stop_using_shader($self); +# $self->volumes->render_VBOs; # $self->{plain_shader}->disable; -#============================================================================================================================== -#============================================================================================================================== - glEnable(GL_CULL_FACE) if (Slic3r::GUI::_3DScene::is_picking_enabled($self)); # glEnable(GL_CULL_FACE) if ($self->enable_picking); -#============================================================================================================================== - } else { - # do not cull backfaces to show broken geometry, if any -#============================================================================================================================== - glDisable(GL_CULL_FACE) if (Slic3r::GUI::_3DScene::is_picking_enabled($self)); +# } else { +# # do not cull backfaces to show broken geometry, if any # glDisable(GL_CULL_FACE) if ($self->enable_picking); -#============================================================================================================================== - $self->volumes->render_legacy; -#============================================================================================================================== - glEnable(GL_CULL_FACE) if (Slic3r::GUI::_3DScene::is_picking_enabled($self)); +# $self->volumes->render_legacy; # glEnable(GL_CULL_FACE) if ($self->enable_picking); -#============================================================================================================================== - } - -#============================================================================================================================== - Slic3r::GUI::_3DScene::render_cutting_plane($self); - Slic3r::GUI::_3DScene::render_warning_texture($self); - Slic3r::GUI::_3DScene::render_legend_texture($self); - +# } +# # if (defined $self->cutting_plane_z) { # # draw cutting plane # my $plane_z = $self->cutting_plane_z; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index c6d9d1c79e..641c800cb4 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -31,6 +31,9 @@ sub new { $self->{model} = $model; $self->{print} = $print; $self->{config} = $config; +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_config($self, $config); +#============================================================================================================================== $self->{on_select_object} = sub {}; $self->{on_instances_moved} = sub {}; $self->{on_wipe_tower_moved} = sub {}; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 38cfde7134..b3630bbf2d 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1792,6 +1792,16 @@ void _3DScene::reset_volumes(wxGLCanvas* canvas) s_canvas_mgr.reset_volumes(canvas); } +DynamicPrintConfig* _3DScene::get_config(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_config(canvas); +} + +void _3DScene::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) +{ + s_canvas_mgr.set_config(canvas, config); +} + void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { return s_canvas_mgr.set_bed_shape(canvas, shape); @@ -1988,6 +1998,11 @@ void _3DScene::render_volumes(wxGLCanvas* canvas, bool fake_colors) s_canvas_mgr.render_volumes(canvas, fake_colors); } +void _3DScene::render_objects(wxGLCanvas* canvas, bool useVBOs) +{ + s_canvas_mgr.render_objects(canvas, useVBOs); +} + void _3DScene::render_cutting_plane(wxGLCanvas* canvas) { s_canvas_mgr.render_cutting_plane(canvas); @@ -2013,6 +2028,11 @@ void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* c s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); } +void _3DScene::register_on_mark_volumes_for_layer_height(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_mark_volumes_for_layer_height(canvas, callback); +} + //void _3DScene::_glew_init() //{ // glewInit(); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 747bd3d42c..4f513ab4f0 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -555,9 +555,11 @@ public: static GLVolumeCollection* get_volumes(wxGLCanvas* canvas); static void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); - static void reset_volumes(wxGLCanvas* canvas); + static DynamicPrintConfig* get_config(wxGLCanvas* canvas); + static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); + static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); static void set_auto_bed_shape(wxGLCanvas* canvas); @@ -612,6 +614,7 @@ public: static void render_bed(wxGLCanvas* canvas); static void render_axes(wxGLCanvas* canvas); static void render_volumes(wxGLCanvas* canvas, bool fake_colors); + static void render_objects(wxGLCanvas* canvas, bool useVBOs); static void render_cutting_plane(wxGLCanvas* canvas); static void render_warning_texture(wxGLCanvas* canvas); static void render_legend_texture(wxGLCanvas* canvas); @@ -619,6 +622,7 @@ public: static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); + static void register_on_mark_volumes_for_layer_height(wxGLCanvas* canvas, void* callback); // static void _glew_init(); //################################################################################################################## diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index e243db011e..f844ba6473 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -3,6 +3,7 @@ #include "../../slic3r/GUI/3DScene.hpp" #include "../../slic3r/GUI/GLShader.hpp" #include "../../libslic3r/ClipperUtils.hpp" +#include "../../libslic3r/PrintConfig.hpp" #include @@ -477,6 +478,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) , m_volumes(nullptr) + , m_config(nullptr) , m_dirty(true) , m_apply_zoom_to_volumes_filter(false) , m_warning_texture_enabled(false) @@ -612,6 +614,16 @@ void GLCanvas3D::reset_volumes() } } +DynamicPrintConfig* GLCanvas3D::get_config() +{ + return m_config; +} + +void GLCanvas3D::set_config(DynamicPrintConfig* config) +{ + m_config = config; +} + void GLCanvas3D::set_bed_shape(const Pointfs& shape) { m_bed.set_shape(shape); @@ -935,6 +947,51 @@ void GLCanvas3D::render_volumes(bool fake_colors) const ::glEnable(GL_CULL_FACE); } +void GLCanvas3D::render_objects(bool useVBOs) +{ + if (m_volumes == nullptr) + return; + + ::glEnable(GL_LIGHTING); + + if (!is_shader_enabled()) + render_volumes(false); + else if (useVBOs) + { + if (is_picking_enabled()) + { + m_on_mark_volumes_for_layer_height.call(); + + if (m_config != nullptr) + { + const BoundingBoxf3& bed_bb = bed_bounding_box(); + m_volumes->set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height")); + m_volumes->check_outside_state(m_config); + } + // do not cull backfaces to show broken geometry, if any + ::glDisable(GL_CULL_FACE); + } + + start_using_shader(); + m_volumes->render_VBOs(); + stop_using_shader(); + + if (is_picking_enabled()) + ::glEnable(GL_CULL_FACE); + } + else + { + // do not cull backfaces to show broken geometry, if any + if (is_picking_enabled()) + ::glDisable(GL_CULL_FACE); + + m_volumes->render_legacy(); + + if (is_picking_enabled()) + ::glEnable(GL_CULL_FACE); + } +} + void GLCanvas3D::render_cutting_plane() const { m_cutting_plane.render(volumes_bounding_box()); @@ -1036,6 +1093,12 @@ void GLCanvas3D::register_on_viewport_changed_callback(void* callback) m_on_viewport_changed_callback.register_callback(callback); } +void GLCanvas3D::register_on_mark_volumes_for_layer_height(void* callback) +{ + if (callback != nullptr) + m_on_mark_volumes_for_layer_height.register_callback(callback); +} + void GLCanvas3D::on_size(wxSizeEvent& evt) { set_dirty(true); @@ -1210,6 +1273,7 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co void GLCanvas3D::_deregister_callbacks() { m_on_viewport_changed_callback.deregister_callback(); + m_on_mark_volumes_for_layer_height.deregister_callback(); } } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 6a2af8324e..f108f1495c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -14,6 +14,7 @@ class wxKeyEvent; namespace Slic3r { class GLVolumeCollection; +class DynamicPrintConfig; class GLShader; class ExPolygon; @@ -171,6 +172,7 @@ private: Shader m_shader; GLVolumeCollection* m_volumes; + DynamicPrintConfig* m_config; bool m_dirty; bool m_apply_zoom_to_volumes_filter; @@ -179,6 +181,7 @@ private: bool m_picking_enabled; PerlCallback m_on_viewport_changed_callback; + PerlCallback m_on_mark_volumes_for_layer_height; public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); @@ -197,9 +200,11 @@ public: GLVolumeCollection* get_volumes(); void set_volumes(GLVolumeCollection* volumes); - void reset_volumes(); + DynamicPrintConfig* get_config(); + void set_config(DynamicPrintConfig* config); + // Set the bed shape to a single closed 2D polygon(array of two element arrays), // triangulate the bed and store the triangles into m_bed.m_triangles, // fills the m_bed.m_grid_lines and sets m_bed.m_origin. @@ -259,6 +264,7 @@ public: void render_bed() const; void render_axes() const; void render_volumes(bool fake_colors) const; + void render_objects(bool useVBOs); void render_cutting_plane() const; void render_warning_texture() const; void render_legend_texture() const; @@ -266,6 +272,7 @@ public: void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(void* callback); + void register_on_mark_volumes_for_layer_height(void* callback); void on_size(wxSizeEvent& evt); void on_idle(wxIdleEvent& evt); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index f612b55304..2dc41302e0 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -200,6 +200,19 @@ void GLCanvas3DManager::reset_volumes(wxGLCanvas* canvas) it->second->reset_volumes(); } +DynamicPrintConfig* GLCanvas3DManager::get_config(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_config() : nullptr; +} + +void GLCanvas3DManager::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_config(config); +} + void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -463,6 +476,13 @@ void GLCanvas3DManager::render_volumes(wxGLCanvas* canvas, bool fake_colors) con it->second->render_volumes(fake_colors); } +void GLCanvas3DManager::render_objects(wxGLCanvas* canvas, bool useVBOs) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->render_objects(useVBOs); +} + void GLCanvas3DManager::render_cutting_plane(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -498,6 +518,13 @@ void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas it->second->register_on_viewport_changed_callback(callback); } +void GLCanvas3DManager::register_on_mark_volumes_for_layer_height(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_mark_volumes_for_layer_height(callback); +} + GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 7c8b82075b..b2cb238dd5 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -63,9 +63,11 @@ public: GLVolumeCollection* get_volumes(wxGLCanvas* canvas); void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); - void reset_volumes(wxGLCanvas* canvas); + DynamicPrintConfig* get_config(wxGLCanvas* canvas); + void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); + void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); void set_auto_bed_shape(wxGLCanvas* canvas); @@ -120,6 +122,7 @@ public: void render_bed(wxGLCanvas* canvas) const; void render_axes(wxGLCanvas* canvas) const; void render_volumes(wxGLCanvas* canvas, bool fake_colors) const; + void render_objects(wxGLCanvas* canvas, bool useVBOs); void render_cutting_plane(wxGLCanvas* canvas) const; void render_warning_texture(wxGLCanvas* canvas) const; void render_legend_texture(wxGLCanvas* canvas) const; @@ -127,6 +130,7 @@ public: void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); + void register_on_mark_volumes_for_layer_height(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 5f80741aad..e3b1df2db6 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -250,7 +250,22 @@ reset_volumes(canvas) SV *canvas; CODE: _3DScene::reset_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - + +DynamicPrintConfig* +get_config(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_config((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_config(canvas, config) + SV *canvas; + DynamicPrintConfig *config; + CODE: + _3DScene::set_config((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), config); + void set_bed_shape(canvas, shape) SV *canvas; @@ -526,6 +541,13 @@ render_volumes(canvas, fake_colors) CODE: _3DScene::render_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), fake_colors); +void +render_objects(canvas, useVBOs) + SV *canvas; + bool useVBOs; + CODE: + _3DScene::render_objects((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), useVBOs); + void render_cutting_plane(canvas) SV *canvas; @@ -562,6 +584,12 @@ register_on_viewport_changed_callback(canvas, callback) CODE: _3DScene::register_on_viewport_changed_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_mark_volumes_for_layer_height(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_mark_volumes_for_layer_height((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); From 91b9b8aebfa634ee7a6f26284a5b782c72de0fb6 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 23 May 2018 12:49:56 +0200 Subject: [PATCH 022/117] Fixed wrong layer height texture updates when using multiple objects --- lib/Slic3r/GUI/3DScene.pm | 21 +++++++++++++++++++-- xs/xsp/Print.xsp | 3 +++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 33c0e8d37b..fca22bedd6 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -360,6 +360,7 @@ sub _variable_layer_thickness_action { my ($self, $mouse_event, $do_modification) = @_; # A volume is selected. Test, whether hovering over a layer thickness bar. return if $self->{layer_height_edit_last_object_id} == -1; + if (defined($mouse_event)) { my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; $self->{layer_height_edit_last_z} = unscale($self->{print}->get_object($self->{layer_height_edit_last_object_id})->size->z) @@ -374,9 +375,20 @@ sub _variable_layer_thickness_action { $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( + + # searches the id of the first volume of the selected object + my $volume_idx = 0; + for my $i (0..$self->{layer_height_edit_last_object_id} - 1) { + my $obj = $self->{print}->get_object($i); + for my $j (0..$obj->region_volumes_count - 1) { + $volume_idx += scalar @{$obj->get_region_volumes($j)}; + } + } + + $self->volumes->[$volume_idx]->generate_layer_height_texture( $self->{print}->get_object($self->{layer_height_edit_last_object_id}), 1); - $self->Refresh; + + $self->Refresh; # Automatic action on mouse down with the same coordinate. $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS); } @@ -1623,6 +1635,11 @@ sub draw_active_object_annotations { $self->{layer_height_edit_shader}->set_uniform('z_cursor', $z_max * $z_cursor_relative); $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width}); glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); +#============================================================================================================================================= + print "texture id: "; + print $self->{layer_preview_z_texture_id}; + print "\n"; +#============================================================================================================================================= glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index ef9c5345f4..702919514e 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -52,6 +52,9 @@ _constant() int region_count() %code%{ RETVAL = THIS->print()->regions.size(); %}; + int region_volumes_count() + %code%{ RETVAL = THIS->region_volumes.size(); %}; + Ref print(); Ref model_object(); Ref config() From 90c50b281a10d2cc59335eee9a1f3161e39540bd Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 23 May 2018 13:56:54 +0200 Subject: [PATCH 023/117] 3DScene mouse variables moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 28 ++++++++----- xs/src/slic3r/GUI/3DScene.cpp | 28 +++++++++++-- xs/src/slic3r/GUI/3DScene.hpp | 8 +++- xs/src/slic3r/GUI/GLCanvas3D.cpp | 53 +++++++++++++++++++++++-- xs/src/slic3r/GUI/GLCanvas3D.hpp | 26 +++++++++++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 37 +++++++++++++---- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 10 ++++- xs/xsp/GUI_3DScene.xsp | 34 +++++++++++++++- 8 files changed, 192 insertions(+), 32 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 4c8e790b43..d81db3dc9a 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -37,14 +37,12 @@ use Slic3r::Geometry qw(PI); __PACKAGE__->mk_accessors( qw(_quat init enable_moving on_viewport_changed - on_hover on_select on_double_click on_right_click on_move on_model_update volumes - _mouse_pos _hover_volume_idx _drag_volume_idx @@ -54,8 +52,6 @@ __PACKAGE__->mk_accessors( qw(_quat init _dragged _layer_height_edited - - _mouse_dragging ) ); #__PACKAGE__->mk_accessors( qw(_quat _dirty init @@ -192,8 +188,8 @@ sub new { # $self->_warning_enabled(0); # $self->use_plain_shader(0); # $self->_apply_zoom_to_volumes_filter(0); +# $self->_mouse_dragging(0); #============================================================================================================================== - $self->_mouse_dragging(0); # Collection of GLVolume objects $self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new); @@ -287,7 +283,7 @@ sub new { $self->mark_volumes_for_layer_height; }; - Slic3r::GUI::_3DScene::register_on_mark_volumes_for_layer_height($self, $on_mark_volumes_for_layer_height); + Slic3r::GUI::_3DScene::register_on_mark_volumes_for_layer_height_callback($self, $on_mark_volumes_for_layer_height); #============================================================================================================================== return $self; @@ -473,7 +469,10 @@ sub mouse_event { my $pos = Slic3r::Pointf->new($e->GetPositionXY); my $object_idx_selected = $self->{layer_height_edit_last_object_id} = ($self->layer_editing_enabled && $self->{print}) ? $self->_first_selected_object_id_for_variable_layer_height_editing : -1; - $self->_mouse_dragging($e->Dragging); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_mouse_dragging($self, $e->Dragging); +# $self->_mouse_dragging($e->Dragging); +#============================================================================================================================== if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) { # wxMSW needs focus in order to catch mouse wheel events @@ -662,7 +661,10 @@ sub mouse_event { $self->_drag_start_xy(undef); $self->_dragged(undef); } elsif ($e->Moving) { - $self->_mouse_pos($pos); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_mouse_position($self, $pos); +# $self->_mouse_pos($pos); +#============================================================================================================================== # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor # hovers over. #============================================================================================================================== @@ -1474,10 +1476,12 @@ sub Render { glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0); #============================================================================================================================== - if (Slic3r::GUI::_3DScene::is_picking_enabled($self) && !$self->_mouse_dragging) { + if (Slic3r::GUI::_3DScene::is_picking_enabled($self) && !Slic3r::GUI::_3DScene::is_mouse_dragging($self)) { + my $pos = Slic3r::GUI::_3DScene::get_mouse_position($self); + if ($pos) { # if ($self->enable_picking && !$self->_mouse_dragging) { +# if (my $pos = $self->_mouse_pos) { #============================================================================================================================== - if (my $pos = $self->_mouse_pos) { # Render the object for picking. # FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing. # Better to use software ray-casting on a bounding-box hierarchy. @@ -1504,7 +1508,9 @@ sub Render { $_->set_hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes}; } - $self->on_hover->($volume_idx) if $self->on_hover; +#============================================================================================================================== +# $self->on_hover->($volume_idx) if $self->on_hover; +#============================================================================================================================== } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index b3630bbf2d..eee1110667 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1915,7 +1915,8 @@ Pointf3 _3DScene::get_camera_target(wxGLCanvas* canvas) void _3DScene::set_camera_target(wxGLCanvas* canvas, const Pointf3* target) { - s_canvas_mgr.set_camera_target(canvas, target); + if (target != nullptr) + s_canvas_mgr.set_camera_target(canvas, *target); } bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) @@ -1953,6 +1954,27 @@ void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable) s_canvas_mgr.enable_shader(canvas, enable); } +bool _3DScene::is_mouse_dragging(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_mouse_dragging(canvas); +} + +void _3DScene::set_mouse_dragging(wxGLCanvas* canvas, bool dragging) +{ + s_canvas_mgr.set_mouse_dragging(canvas, dragging); +} + +Pointf _3DScene::get_mouse_position(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_mouse_position(canvas); +} + +void _3DScene::set_mouse_position(wxGLCanvas* canvas, const Pointf* position) +{ + if (position != nullptr) + s_canvas_mgr.set_mouse_position(canvas, *position); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); @@ -2028,9 +2050,9 @@ void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* c s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); } -void _3DScene::register_on_mark_volumes_for_layer_height(wxGLCanvas* canvas, void* callback) +void _3DScene::register_on_mark_volumes_for_layer_height_callback(wxGLCanvas* canvas, void* callback) { - s_canvas_mgr.register_on_mark_volumes_for_layer_height(canvas, callback); + s_canvas_mgr.register_on_mark_volumes_for_layer_height_callback(canvas, callback); } //void _3DScene::_glew_init() diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 4f513ab4f0..a34e962867 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -603,6 +603,12 @@ public: static void enable_picking(wxGLCanvas* canvas, bool enable); static void enable_shader(wxGLCanvas* canvas, bool enable); + static bool is_mouse_dragging(wxGLCanvas* canvas); + static void set_mouse_dragging(wxGLCanvas* canvas, bool dragging); + + static Pointf get_mouse_position(wxGLCanvas* canvas); + static void set_mouse_position(wxGLCanvas* canvas, const Pointf* position); + static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); @@ -622,7 +628,7 @@ public: static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); - static void register_on_mark_volumes_for_layer_height(wxGLCanvas* canvas, void* callback); + static void register_on_mark_volumes_for_layer_height_callback(wxGLCanvas* canvas, void* callback); // static void _glew_init(); //################################################################################################################## diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index f844ba6473..65ca546121 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -474,6 +474,31 @@ void GLCanvas3D::Shader::stop() const m_shader->disable(); } +GLCanvas3D::Mouse::Mouse() + : m_dragging(false) +{ +} + +bool GLCanvas3D::Mouse::is_dragging() const +{ + return m_dragging; +} + +void GLCanvas3D::Mouse::set_dragging(bool dragging) +{ + m_dragging = dragging; +} + +const Pointf& GLCanvas3D::Mouse::get_position() const +{ + return m_position; +} + +void GLCanvas3D::Mouse::set_position(const Pointf& position) +{ + m_position = position; +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -804,6 +829,26 @@ void GLCanvas3D::enable_shader(bool enable) m_shader.set_enabled(enable); } +bool GLCanvas3D::is_mouse_dragging() const +{ + return m_mouse.is_dragging(); +} + +void GLCanvas3D::set_mouse_dragging(bool dragging) +{ + m_mouse.set_dragging(dragging); +} + +const Pointf& GLCanvas3D::get_mouse_position() const +{ + return m_mouse.get_position(); +} + +void GLCanvas3D::set_mouse_position(const Pointf& position) +{ + m_mouse.set_position(position); +} + void GLCanvas3D::zoom_to_bed() { _zoom_to_bounding_box(bed_bounding_box()); @@ -960,7 +1005,7 @@ void GLCanvas3D::render_objects(bool useVBOs) { if (is_picking_enabled()) { - m_on_mark_volumes_for_layer_height.call(); + m_on_mark_volumes_for_layer_height_callback.call(); if (m_config != nullptr) { @@ -1093,10 +1138,10 @@ void GLCanvas3D::register_on_viewport_changed_callback(void* callback) m_on_viewport_changed_callback.register_callback(callback); } -void GLCanvas3D::register_on_mark_volumes_for_layer_height(void* callback) +void GLCanvas3D::register_on_mark_volumes_for_layer_height_callback(void* callback) { if (callback != nullptr) - m_on_mark_volumes_for_layer_height.register_callback(callback); + m_on_mark_volumes_for_layer_height_callback.register_callback(callback); } void GLCanvas3D::on_size(wxSizeEvent& evt) @@ -1273,7 +1318,7 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co void GLCanvas3D::_deregister_callbacks() { m_on_viewport_changed_callback.deregister_callback(); - m_on_mark_volumes_for_layer_height.deregister_callback(); + m_on_mark_volumes_for_layer_height_callback.deregister_callback(); } } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index f108f1495c..5198c89c66 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -161,6 +161,21 @@ public: void stop() const; }; + class Mouse + { + bool m_dragging; + Pointf m_position; + + public: + Mouse(); + + bool is_dragging() const; + void set_dragging(bool dragging); + + const Pointf& get_position() const; + void set_position(const Pointf& position); + }; + private: wxGLCanvas* m_canvas; wxGLContext* m_context; @@ -170,6 +185,7 @@ private: CuttingPlane m_cutting_plane; LayersEditing m_layers_editing; Shader m_shader; + Mouse m_mouse; GLVolumeCollection* m_volumes; DynamicPrintConfig* m_config; @@ -181,7 +197,7 @@ private: bool m_picking_enabled; PerlCallback m_on_viewport_changed_callback; - PerlCallback m_on_mark_volumes_for_layer_height; + PerlCallback m_on_mark_volumes_for_layer_height_callback; public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); @@ -253,6 +269,12 @@ public: void enable_picking(bool enable); void enable_shader(bool enable); + bool is_mouse_dragging() const; + void set_mouse_dragging(bool dragging); + + const Pointf& get_mouse_position() const; + void set_mouse_position(const Pointf& position); + void zoom_to_bed(); void zoom_to_volumes(); void select_view(const std::string& direction); @@ -272,7 +294,7 @@ public: void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(void* callback); - void register_on_mark_volumes_for_layer_height(void* callback); + void register_on_mark_volumes_for_layer_height_callback(void* callback); void on_size(wxSizeEvent& evt); void on_idle(wxIdleEvent& evt); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 2dc41302e0..c08d08156e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -358,14 +358,11 @@ Pointf3 GLCanvas3DManager::get_camera_target(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->get_camera_target() : Pointf3(0.0, 0.0, 0.0); } -void GLCanvas3DManager::set_camera_target(wxGLCanvas* canvas, const Pointf3* target) +void GLCanvas3DManager::set_camera_target(wxGLCanvas* canvas, const Pointf3& target) { - if (target == nullptr) - return; - CanvasesMap::iterator it = _get_canvas(canvas); if (it != m_canvases.end()) - it->second->set_camera_target(*target); + it->second->set_camera_target(target); } bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const @@ -414,6 +411,32 @@ void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) it->second->enable_shader(enable); } +bool GLCanvas3DManager::is_mouse_dragging(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_mouse_dragging() : false; +} + +void GLCanvas3DManager::set_mouse_dragging(wxGLCanvas* canvas, bool dragging) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_mouse_dragging(dragging); +} + +Pointf GLCanvas3DManager::get_mouse_position(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_mouse_position() : Pointf(); +} + +void GLCanvas3DManager::set_mouse_position(wxGLCanvas* canvas, const Pointf& position) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_mouse_position(position); +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -518,11 +541,11 @@ void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas it->second->register_on_viewport_changed_callback(callback); } -void GLCanvas3DManager::register_on_mark_volumes_for_layer_height(wxGLCanvas* canvas, void* callback) +void GLCanvas3DManager::register_on_mark_volumes_for_layer_height_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); if (it != m_canvases.end()) - it->second->register_on_mark_volumes_for_layer_height(callback); + it->second->register_on_mark_volumes_for_layer_height_callback(callback); } GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index b2cb238dd5..13cedce8aa 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -100,7 +100,7 @@ public: void set_camera_distance(wxGLCanvas* canvas, float distance); Pointf3 get_camera_target(wxGLCanvas* canvas) const; - void set_camera_target(wxGLCanvas* canvas, const Pointf3* target); + void set_camera_target(wxGLCanvas* canvas, const Pointf3& target); bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_picking_enabled(wxGLCanvas* canvas) const; @@ -111,6 +111,12 @@ public: void enable_picking(wxGLCanvas* canvas, bool enable); void enable_shader(wxGLCanvas* canvas, bool enable); + bool is_mouse_dragging(wxGLCanvas* canvas) const; + void set_mouse_dragging(wxGLCanvas* canvas, bool dragging); + + Pointf get_mouse_position(wxGLCanvas* canvas) const; + void set_mouse_position(wxGLCanvas* canvas, const Pointf& position); + void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); @@ -130,7 +136,7 @@ public: void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); - void register_on_mark_volumes_for_layer_height(wxGLCanvas* canvas, void* callback); + void register_on_mark_volumes_for_layer_height_callback(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index e3b1df2db6..83bf8b7975 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -483,6 +483,36 @@ enable_shader(canvas, enable) CODE: _3DScene::enable_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); +bool +is_mouse_dragging(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_mouse_dragging((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_mouse_dragging(canvas, dragging) + SV *canvas; + bool dragging; + CODE: + _3DScene::set_mouse_dragging((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), dragging); + +Clone +get_mouse_position(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_mouse_position((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_mouse_position(canvas, position) + SV *canvas; + Pointf *position; + CODE: + _3DScene::set_mouse_position((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), position); + void zoom_to_bed(canvas) SV *canvas; @@ -585,11 +615,11 @@ register_on_viewport_changed_callback(canvas, callback) _3DScene::register_on_viewport_changed_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); void -register_on_mark_volumes_for_layer_height(canvas, callback) +register_on_mark_volumes_for_layer_height_callback(canvas, callback) SV *canvas; SV *callback; CODE: - _3DScene::register_on_mark_volumes_for_layer_height((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + _3DScene::register_on_mark_volumes_for_layer_height_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); From 751b41b94b51acb79d46197c335d07d307b188aa Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 23 May 2018 15:35:11 +0200 Subject: [PATCH 024/117] 3DScene picking pass moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 186 ++++++++++++------------ xs/src/slic3r/GUI/3DScene.cpp | 25 ++++ xs/src/slic3r/GUI/3DScene.hpp | 7 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 138 +++++++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 9 ++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 33 +++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 7 + xs/xsp/GUI_3DScene.xsp | 36 +++++ 8 files changed, 338 insertions(+), 103 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index d81db3dc9a..7696e2718b 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -43,7 +43,6 @@ __PACKAGE__->mk_accessors( qw(_quat init on_move on_model_update volumes - _hover_volume_idx _drag_volume_idx _drag_start_pos @@ -170,13 +169,12 @@ sub new { #============================================================================================================================== Slic3r::GUI::_3DScene::add_canvas($self, $self->GetContext); + Slic3r::GUI::_3DScene::allow_multisample($self, $can_multisample); # my $context = $self->GetContext; # $self->SetCurrent($context); # Slic3r::GUI::_3DScene::add_canvas($self, $context); -#============================================================================================================================== - - $self->{can_multisample} = $can_multisample; -#============================================================================================================================== +# +# $self->{can_multisample} = $can_multisample; # $self->background(1); #============================================================================================================================== $self->_quat((0, 0, 0, 1)); @@ -486,7 +484,10 @@ sub mouse_event { } elsif ($e->LeftDown || $e->RightDown) { # If user pressed left or right button we first check whether this happened # on a volume or not. - my $volume_idx = $self->_hover_volume_idx // -1; +#============================================================================================================================== + my $volume_idx = Slic3r::GUI::_3DScene::get_hover_volume_id($self); +# my $volume_idx = $self->_hover_volume_idx // -1; +#============================================================================================================================== $self->_layer_height_edited(0); if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { # A volume is selected and the mouse is hovering over a layer thickness bar. @@ -1342,55 +1343,56 @@ sub InitGL { $self->zoom_to_bed; - glClearColor(0, 0, 0, 1); - glColor3f(1, 0, 0); - glEnable(GL_DEPTH_TEST); - glClearDepth(1.0); - glDepthFunc(GL_LEQUAL); - glEnable(GL_CULL_FACE); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - # Set antialiasing/multisampling - glDisable(GL_LINE_SMOOTH); - glDisable(GL_POLYGON_SMOOTH); - - # See "GL_MULTISAMPLE and GL_ARRAY_BUFFER_ARB messages on failed launch" - # https://github.com/alexrj/Slic3r/issues/4085 - eval { - # Disable the multi sampling by default, so the picking by color will work correctly. - glDisable(GL_MULTISAMPLE); - }; - # Disable multi sampling if the eval failed. - $self->{can_multisample} = 0 if $@; - - # ambient lighting - glLightModelfv_p(GL_LIGHT_MODEL_AMBIENT, 0.3, 0.3, 0.3, 1); - - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_LIGHT1); - - # light from camera - glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0); - glLightfv_p(GL_LIGHT1, GL_SPECULAR, 0.3, 0.3, 0.3, 1); - glLightfv_p(GL_LIGHT1, GL_DIFFUSE, 0.2, 0.2, 0.2, 1); - - # Enables Smooth Color Shading; try GL_FLAT for (lack of) fun. - glShadeModel(GL_SMOOTH); - -# glMaterialfv_p(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, 0.5, 0.3, 0.3, 1); -# glMaterialfv_p(GL_FRONT_AND_BACK, GL_SPECULAR, 1, 1, 1, 1); -# glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50); -# glMaterialfv_p(GL_FRONT_AND_BACK, GL_EMISSION, 0.1, 0, 0, 0.9); - - # A handy trick -- have surface material mirror the color. - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - glEnable(GL_COLOR_MATERIAL); - glEnable(GL_MULTISAMPLE) if ($self->{can_multisample}); - #=================================================================================================================================== Slic3r::GUI::_3DScene::init($self, $self->UseVBOs); + +# glClearColor(0, 0, 0, 1); +# glColor3f(1, 0, 0); +# glEnable(GL_DEPTH_TEST); +# glClearDepth(1.0); +# glDepthFunc(GL_LEQUAL); +# glEnable(GL_CULL_FACE); +# glEnable(GL_BLEND); +# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +# +# # Set antialiasing/multisampling +# glDisable(GL_LINE_SMOOTH); +# glDisable(GL_POLYGON_SMOOTH); +# +# # See "GL_MULTISAMPLE and GL_ARRAY_BUFFER_ARB messages on failed launch" +# # https://github.com/alexrj/Slic3r/issues/4085 +# eval { +# # Disable the multi sampling by default, so the picking by color will work correctly. +# glDisable(GL_MULTISAMPLE); +# }; +# # Disable multi sampling if the eval failed. +# $self->{can_multisample} = 0 if $@; +# +# # ambient lighting +# glLightModelfv_p(GL_LIGHT_MODEL_AMBIENT, 0.3, 0.3, 0.3, 1); +# +# glEnable(GL_LIGHTING); +# glEnable(GL_LIGHT0); +# glEnable(GL_LIGHT1); +# +# # light from camera +# glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0); +# glLightfv_p(GL_LIGHT1, GL_SPECULAR, 0.3, 0.3, 0.3, 1); +# glLightfv_p(GL_LIGHT1, GL_DIFFUSE, 0.2, 0.2, 0.2, 1); +# +# # Enables Smooth Color Shading; try GL_FLAT for (lack of) fun. +# glShadeModel(GL_SMOOTH); +# +## glMaterialfv_p(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, 0.5, 0.3, 0.3, 1); +## glMaterialfv_p(GL_FRONT_AND_BACK, GL_SPECULAR, 1, 1, 1, 1); +## glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50); +## glMaterialfv_p(GL_FRONT_AND_BACK, GL_EMISSION, 0.1, 0, 0, 0.9); +# +# # A handy trick -- have surface material mirror the color. +# glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); +# glEnable(GL_COLOR_MATERIAL); +# glEnable(GL_MULTISAMPLE) if ($self->{can_multisample}); +# # if ($self->UseVBOs) { # my $shader = new Slic3r::GUI::_3DScene::GLShader; ## if (! $shader->load($self->_fragment_shader_Phong, $self->_vertex_shader_Phong)) { @@ -1436,7 +1438,9 @@ sub Render { glClearColor(1, 1, 1, 1); glClearDepth(1); glDepthFunc(GL_LESS); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +#============================================================================================================================== +# glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +#============================================================================================================================== glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -1476,50 +1480,42 @@ sub Render { glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0); #============================================================================================================================== - if (Slic3r::GUI::_3DScene::is_picking_enabled($self) && !Slic3r::GUI::_3DScene::is_mouse_dragging($self)) { - my $pos = Slic3r::GUI::_3DScene::get_mouse_position($self); - if ($pos) { -# if ($self->enable_picking && !$self->_mouse_dragging) { -# if (my $pos = $self->_mouse_pos) { -#============================================================================================================================== - # Render the object for picking. - # FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing. - # Better to use software ray-casting on a bounding-box hierarchy. - glPushAttrib(GL_ENABLE_BIT); - glDisable(GL_MULTISAMPLE) if ($self->{can_multisample}); - glDisable(GL_LIGHTING); - glDisable(GL_BLEND); -#============================================================================================================================== - Slic3r::GUI::_3DScene::render_volumes($self, 1); -# $self->draw_volumes(1); -#============================================================================================================================== - glPopAttrib(); - glFlush(); - my $col = [ glReadPixels_p($pos->x, $self->GetSize->GetHeight - $pos->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE) ]; - my $volume_idx = $col->[0] + $col->[1]*256 + $col->[2]*256*256; - $self->_hover_volume_idx(undef); - $_->set_hover(0) for @{$self->volumes}; - if ($volume_idx <= $#{$self->volumes}) { - $self->_hover_volume_idx($volume_idx); - - $self->volumes->[$volume_idx]->set_hover(1); - my $group_id = $self->volumes->[$volume_idx]->select_group_id; - if ($group_id != -1) { - $_->set_hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes}; - } - -#============================================================================================================================== -# $self->on_hover->($volume_idx) if $self->on_hover; -#============================================================================================================================== - } - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - } - -#============================================================================================================================== + Slic3r::GUI::_3DScene::picking_pass($self); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); Slic3r::GUI::_3DScene::render_background($self); Slic3r::GUI::_3DScene::render_bed($self); +# if ($self->enable_picking && !$self->_mouse_dragging) { +# if (my $pos = $self->_mouse_pos) { +# # Render the object for picking. +# # FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing. +# # Better to use software ray-casting on a bounding-box hierarchy. +# glPushAttrib(GL_ENABLE_BIT); +# glDisable(GL_MULTISAMPLE) if ($self->{can_multisample}); +# glDisable(GL_LIGHTING); +# glDisable(GL_BLEND); +# $self->draw_volumes(1); +# glPopAttrib(); +# glFlush(); +# my $col = [ glReadPixels_p($pos->x, $self->GetSize->GetHeight - $pos->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE) ]; +# my $volume_idx = $col->[0] + $col->[1]*256 + $col->[2]*256*256; +# $self->_hover_volume_idx(undef); +# $_->set_hover(0) for @{$self->volumes}; +# if ($volume_idx <= $#{$self->volumes}) { +# $self->_hover_volume_idx($volume_idx); +# +# $self->volumes->[$volume_idx]->set_hover(1); +# my $group_id = $self->volumes->[$volume_idx]->select_group_id; +# if ($group_id != -1) { +# $_->set_hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes}; +# } +# +# $self->on_hover->($volume_idx) if $self->on_hover; +# } +# glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +# } +# } +# # # draw fixed background # if ($self->background) { # glDisable(GL_LIGHTING); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index eee1110667..a6e46b11a5 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1934,6 +1934,11 @@ bool _3DScene::is_shader_enabled(wxGLCanvas* canvas) return s_canvas_mgr.is_shader_enabled(canvas); } +bool _3DScene::is_multisample_allowed(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_multisample_allowed(canvas); +} + void _3DScene::enable_warning_texture(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_warning_texture(canvas, enable); @@ -1954,6 +1959,11 @@ void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable) s_canvas_mgr.enable_shader(canvas, enable); } +void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow) +{ + s_canvas_mgr.allow_multisample(canvas, allow); +} + bool _3DScene::is_mouse_dragging(wxGLCanvas* canvas) { return s_canvas_mgr.is_mouse_dragging(canvas); @@ -1975,6 +1985,16 @@ void _3DScene::set_mouse_position(wxGLCanvas* canvas, const Pointf* position) s_canvas_mgr.set_mouse_position(canvas, *position); } +int _3DScene::get_hover_volume_id(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_hover_volume_id(canvas); +} + +void _3DScene::set_hover_volume_id(wxGLCanvas* canvas, int id) +{ + s_canvas_mgr.set_hover_volume_id(canvas, id); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); @@ -2000,6 +2020,11 @@ void _3DScene::stop_using_shader(wxGLCanvas* canvas) s_canvas_mgr.stop_using_shader(canvas); } +void _3DScene::picking_pass(wxGLCanvas* canvas) +{ + s_canvas_mgr.picking_pass(canvas); +} + void _3DScene::render_background(wxGLCanvas* canvas) { s_canvas_mgr.render_background(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index a34e962867..03b2421642 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -597,11 +597,13 @@ public: static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_picking_enabled(wxGLCanvas* canvas); static bool is_shader_enabled(wxGLCanvas* canvas); + static bool is_multisample_allowed(wxGLCanvas* canvas); static void enable_warning_texture(wxGLCanvas* canvas, bool enable); static void enable_legend_texture(wxGLCanvas* canvas, bool enable); static void enable_picking(wxGLCanvas* canvas, bool enable); static void enable_shader(wxGLCanvas* canvas, bool enable); + static void allow_multisample(wxGLCanvas* canvas, bool allow); static bool is_mouse_dragging(wxGLCanvas* canvas); static void set_mouse_dragging(wxGLCanvas* canvas, bool dragging); @@ -609,6 +611,9 @@ public: static Pointf get_mouse_position(wxGLCanvas* canvas); static void set_mouse_position(wxGLCanvas* canvas, const Pointf* position); + static int get_hover_volume_id(wxGLCanvas* canvas); + static void set_hover_volume_id(wxGLCanvas* canvas, int id); + static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); @@ -616,6 +621,8 @@ public: static bool start_using_shader(wxGLCanvas* canvas); static void stop_using_shader(wxGLCanvas* canvas); + static void picking_pass(wxGLCanvas* canvas); + static void render_background(wxGLCanvas* canvas); static void render_bed(wxGLCanvas* canvas); static void render_axes(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 65ca546121..da639b144d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -5,6 +5,7 @@ #include "../../libslic3r/ClipperUtils.hpp" #include "../../libslic3r/PrintConfig.hpp" +#include #include #include @@ -506,9 +507,11 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_config(nullptr) , m_dirty(true) , m_apply_zoom_to_volumes_filter(false) + , m_hover_volume_id(-1) , m_warning_texture_enabled(false) , m_legend_texture_enabled(false) , m_picking_enabled(false) + , m_multisample_allowed(false) { } @@ -520,6 +523,45 @@ GLCanvas3D::~GLCanvas3D() bool GLCanvas3D::init(bool useVBOs) { + ::glClearColor(0.0f, 0.0f, 0.0f, 1.0f); +// ::glColor3f(1.0f, 0.0f, 0.0f); + ::glEnable(GL_DEPTH_TEST); + ::glClearDepth(1.0f); + ::glDepthFunc(GL_LEQUAL); + ::glEnable(GL_CULL_FACE); + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Set antialiasing / multisampling + ::glDisable(GL_LINE_SMOOTH); + ::glDisable(GL_POLYGON_SMOOTH); + + // ambient lighting + GLfloat ambient[4] = { 0.3f, 0.3f, 0.3f, 1.0f }; + ::glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); + +// ::glEnable(GL_LIGHTING); + ::glEnable(GL_LIGHT0); + ::glEnable(GL_LIGHT1); + + // light from camera + GLfloat position[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; + ::glLightfv(GL_LIGHT1, GL_POSITION, position); + GLfloat specular[4] = { 0.3f, 0.3f, 0.3f, 1.0f }; + ::glLightfv(GL_LIGHT1, GL_SPECULAR, specular); + GLfloat diffuse[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; + ::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse); + + // Enables Smooth Color Shading; try GL_FLAT for (lack of) fun. + ::glShadeModel(GL_SMOOTH); + + // A handy trick -- have surface material mirror the color. + ::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + ::glEnable(GL_COLOR_MATERIAL); + + if (is_multisample_allowed()) + ::glEnable(GL_MULTISAMPLE); + if (useVBOs && !m_shader.init("gouraud.vs", "gouraud.fs")) return false; @@ -809,6 +851,11 @@ bool GLCanvas3D::is_shader_enabled() const return m_shader.is_enabled(); } +bool GLCanvas3D::is_multisample_allowed() const +{ + return m_multisample_allowed; +} + void GLCanvas3D::enable_warning_texture(bool enable) { m_warning_texture_enabled = enable; @@ -829,6 +876,11 @@ void GLCanvas3D::enable_shader(bool enable) m_shader.set_enabled(enable); } +void GLCanvas3D::allow_multisample(bool allow) +{ + m_multisample_allowed = allow; +} + bool GLCanvas3D::is_mouse_dragging() const { return m_mouse.is_dragging(); @@ -849,6 +901,16 @@ void GLCanvas3D::set_mouse_position(const Pointf& position) m_mouse.set_position(position); } +int GLCanvas3D::get_hover_volume_id() const +{ + return m_hover_volume_id; +} + +void GLCanvas3D::set_hover_volume_id(int id) +{ + m_hover_volume_id = id; +} + void GLCanvas3D::zoom_to_bed() { _zoom_to_bounding_box(bed_bounding_box()); @@ -902,6 +964,66 @@ void GLCanvas3D::stop_using_shader() const m_shader.stop(); } +void GLCanvas3D::picking_pass() +{ + if (is_picking_enabled() && !is_mouse_dragging() && (m_volumes != nullptr)) + { + const Pointf& pos = get_mouse_position(); +// if (pos) { + // Render the object for picking. + // FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing. + // Better to use software ray - casting on a bounding - box hierarchy. + + if (is_multisample_allowed()) + ::glDisable(GL_MULTISAMPLE); + + ::glDisable(GL_LIGHTING); + ::glDisable(GL_BLEND); + + ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + ::glPushAttrib(GL_ENABLE_BIT); + + render_volumes(true); + + ::glPopAttrib(); + + if (is_multisample_allowed()) + ::glEnable(GL_MULTISAMPLE); + +// ::glFlush(); + + const std::pair& cnv_size = _get_canvas_size(); + + GLubyte color[4]; + ::glReadPixels(pos.x, cnv_size.second - pos.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); + int volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256; + + set_hover_volume_id(-1); + + for (GLVolume* vol : m_volumes->volumes) + { + vol->hover = false; + } + + if (volume_id < m_volumes->volumes.size()) + { + set_hover_volume_id(volume_id); + m_volumes->volumes[volume_id]->hover = true; + int group_id = m_volumes->volumes[volume_id]->select_group_id; + if (group_id != -1) + { + for (GLVolume* vol : m_volumes->volumes) + { + if (vol->select_group_id == group_id) + vol->hover = true; + } + } + } + } +// } +} + void GLCanvas3D::render_background() const { static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f }; @@ -1059,7 +1181,7 @@ void GLCanvas3D::render_warning_texture() const ::glPushMatrix(); ::glLoadIdentity(); - std::pair cnv_size = _get_canvas_size(); + const std::pair& cnv_size = _get_canvas_size(); float zoom = get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float l = (-0.5f * (float)w) * inv_zoom; @@ -1092,7 +1214,7 @@ void GLCanvas3D::render_legend_texture() const ::glPushMatrix(); ::glLoadIdentity(); - std::pair cnv_size = _get_canvas_size(); + const std::pair& cnv_size = _get_canvas_size(); float zoom = get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float l = (-0.5f * (float)cnv_size.first) * inv_zoom; @@ -1156,8 +1278,8 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) if (m_canvas != nullptr) { - std::pair size = _get_canvas_size(); - resize((unsigned int)size.first, (unsigned int)size.second); + const std::pair& cnv_size = _get_canvas_size(); + resize((unsigned int)cnv_size.first, (unsigned int)cnv_size.second); m_canvas->Refresh(); } } @@ -1211,8 +1333,8 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) if (is_shown_on_screen()) { - std::pair size = _get_canvas_size(); - resize((unsigned int)size.first, (unsigned int)size.second); + const std::pair& cnv_size = _get_canvas_size(); + resize((unsigned int)cnv_size.first, (unsigned int)cnv_size.second); if (m_canvas != nullptr) m_canvas->Refresh(); } @@ -1311,8 +1433,8 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co max_x *= 2.0; max_y *= 2.0; - std::pair cvs_size = _get_canvas_size(); - return (float)std::min((coordf_t)cvs_size.first / max_x, (coordf_t)cvs_size.second / max_y); + const std::pair& cnv_size = _get_canvas_size(); + return (float)std::min((coordf_t)cnv_size.first / max_x, (coordf_t)cnv_size.second / max_y); } void GLCanvas3D::_deregister_callbacks() diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 5198c89c66..3c0275c9e6 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -192,9 +192,11 @@ private: bool m_dirty; bool m_apply_zoom_to_volumes_filter; + int m_hover_volume_id; bool m_warning_texture_enabled; bool m_legend_texture_enabled; bool m_picking_enabled; + bool m_multisample_allowed; PerlCallback m_on_viewport_changed_callback; PerlCallback m_on_mark_volumes_for_layer_height_callback; @@ -263,11 +265,13 @@ public: bool is_layers_editing_enabled() const; bool is_picking_enabled() const; bool is_shader_enabled() const; + bool is_multisample_allowed() const; void enable_warning_texture(bool enable); void enable_legend_texture(bool enable); void enable_picking(bool enable); void enable_shader(bool enable); + void allow_multisample(bool allow); bool is_mouse_dragging() const; void set_mouse_dragging(bool dragging); @@ -275,6 +279,9 @@ public: const Pointf& get_mouse_position() const; void set_mouse_position(const Pointf& position); + int get_hover_volume_id() const; + void set_hover_volume_id(int id); + void zoom_to_bed(); void zoom_to_volumes(); void select_view(const std::string& direction); @@ -282,6 +289,8 @@ public: bool start_using_shader() const; void stop_using_shader() const; + void picking_pass(); + void render_background() const; void render_bed() const; void render_axes() const; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index c08d08156e..c5a15696d6 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -383,6 +383,12 @@ bool GLCanvas3DManager::is_shader_enabled(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_shader_enabled() : false; } +bool GLCanvas3DManager::is_multisample_allowed(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_multisample_allowed() : false; +} + void GLCanvas3DManager::enable_warning_texture(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -411,6 +417,13 @@ void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) it->second->enable_shader(enable); } +void GLCanvas3DManager::allow_multisample(wxGLCanvas* canvas, bool allow) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->allow_multisample(allow); +} + bool GLCanvas3DManager::is_mouse_dragging(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -437,6 +450,19 @@ void GLCanvas3DManager::set_mouse_position(wxGLCanvas* canvas, const Pointf& pos it->second->set_mouse_position(position); } +int GLCanvas3DManager::get_hover_volume_id(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_hover_volume_id() : -1; +} + +void GLCanvas3DManager::set_hover_volume_id(wxGLCanvas* canvas, int id) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_hover_volume_id(id); +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -471,6 +497,13 @@ void GLCanvas3DManager::stop_using_shader(wxGLCanvas* canvas) const it->second->stop_using_shader(); } +void GLCanvas3DManager::picking_pass(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->picking_pass(); +} + void GLCanvas3DManager::render_background(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 13cedce8aa..e0b2a1d2e2 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -105,11 +105,13 @@ public: bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_picking_enabled(wxGLCanvas* canvas) const; bool is_shader_enabled(wxGLCanvas* canvas) const; + bool is_multisample_allowed(wxGLCanvas* canvas) const; void enable_warning_texture(wxGLCanvas* canvas, bool enable); void enable_legend_texture(wxGLCanvas* canvas, bool enable); void enable_picking(wxGLCanvas* canvas, bool enable); void enable_shader(wxGLCanvas* canvas, bool enable); + void allow_multisample(wxGLCanvas* canvas, bool allow); bool is_mouse_dragging(wxGLCanvas* canvas) const; void set_mouse_dragging(wxGLCanvas* canvas, bool dragging); @@ -117,6 +119,9 @@ public: Pointf get_mouse_position(wxGLCanvas* canvas) const; void set_mouse_position(wxGLCanvas* canvas, const Pointf& position); + int get_hover_volume_id(wxGLCanvas* canvas) const; + void set_hover_volume_id(wxGLCanvas* canvas, int id); + void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); @@ -124,6 +129,8 @@ public: bool start_using_shader(wxGLCanvas* canvas) const; void stop_using_shader(wxGLCanvas* canvas) const; + void picking_pass(wxGLCanvas* canvas); + void render_background(wxGLCanvas* canvas) const; void render_bed(wxGLCanvas* canvas) const; void render_axes(wxGLCanvas* canvas) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 83bf8b7975..a2be3b1df6 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -455,6 +455,14 @@ is_shader_enabled(canvas) OUTPUT: RETVAL +bool +is_multisample_allowed(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_multisample_allowed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + void enable_warning_texture(canvas, enable) SV *canvas; @@ -483,6 +491,13 @@ enable_shader(canvas, enable) CODE: _3DScene::enable_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); +void +allow_multisample(canvas, allow) + SV *canvas; + bool allow; + CODE: + _3DScene::allow_multisample((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), allow); + bool is_mouse_dragging(canvas) SV *canvas; @@ -513,6 +528,21 @@ set_mouse_position(canvas, position) CODE: _3DScene::set_mouse_position((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), position); +int +get_hover_volume_id(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_hover_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_hover_volume_id(canvas, id) + SV *canvas; + int id; + CODE: + _3DScene::set_hover_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id); + void zoom_to_bed(canvas) SV *canvas; @@ -546,6 +576,12 @@ stop_using_shader(canvas) CODE: _3DScene::stop_using_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); +void +picking_pass(canvas) + SV *canvas; + CODE: + _3DScene::picking_pass((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + void render_background(canvas) SV *canvas; From 157a34bcd935fd3b40f26f1e634fb8e06f545b3c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 24 May 2018 09:57:12 +0200 Subject: [PATCH 025/117] AMF I/O - Automatic detection if open file is zip archive or xml format --- xs/src/libslic3r/Format/AMF.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index 83b50ec9e6..2633637569 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -13,6 +13,9 @@ #include #include +//############################################################################################################################################ +#include +//############################################################################################################################################ #include #if 0 @@ -666,10 +669,21 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model) // If bundle is not a null pointer, updates it if the amf file/archive contains config data bool load_amf(const char *path, PresetBundle* bundle, Model *model) { - if (boost::iends_with(path, ".zip.amf")) - return load_amf_archive(path, bundle, model); - else if (boost::iends_with(path, ".amf") || boost::iends_with(path, ".amf.xml")) + if (boost::iends_with(path, ".amf.xml")) + // backward compatibility with older slic3r output return load_amf_file(path, bundle, model); + else if (boost::iends_with(path, ".amf")) + { + boost::nowide::ifstream file(path, boost::nowide::ifstream::binary); + if (!file.good()) + return false; + + std::string zip_mask(2, '\0'); + file.read(const_cast(zip_mask.data()), 2); + file.close(); + + return (zip_mask == "PK") ? load_amf_archive(path, bundle, model) : load_amf_file(path, bundle, model); + } else return false; } From f31c55ceed96cddea7a0e32fb86edac2b6232b85 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 24 May 2018 13:46:17 +0200 Subject: [PATCH 026/117] 3DScene layer editing overlay textures rendering moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 71 +++--- xs/src/slic3r/GUI/3DScene.cpp | 5 + xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 315 +++++++++++++++++++++--- xs/src/slic3r/GUI/GLCanvas3D.hpp | 68 ++++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 7 + xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 1 + xs/xsp/GUI_3DScene.xsp | 6 + 8 files changed, 403 insertions(+), 71 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 7696e2718b..ae91b52472 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1484,6 +1484,11 @@ sub Render { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); Slic3r::GUI::_3DScene::render_background($self); Slic3r::GUI::_3DScene::render_bed($self); + Slic3r::GUI::_3DScene::render_axes($self); + Slic3r::GUI::_3DScene::render_objects($self, $self->UseVBOs); + Slic3r::GUI::_3DScene::render_cutting_plane($self); + Slic3r::GUI::_3DScene::render_warning_texture($self); + Slic3r::GUI::_3DScene::render_legend_texture($self); # if ($self->enable_picking && !$self->_mouse_dragging) { # if (my $pos = $self->_mouse_pos) { @@ -1577,17 +1582,9 @@ sub Render { # # glDisable(GL_BLEND); # } -#============================================================================================================================== - - my $volumes_bb = $self->volumes_bounding_box; - -#============================================================================================================================== - Slic3r::GUI::_3DScene::render_axes($self); - Slic3r::GUI::_3DScene::render_objects($self, $self->UseVBOs); - Slic3r::GUI::_3DScene::render_cutting_plane($self); - Slic3r::GUI::_3DScene::render_warning_texture($self); - Slic3r::GUI::_3DScene::render_legend_texture($self); - +# +# my $volumes_bb = $self->volumes_bounding_box; +# # { # # draw axes # # disable depth testing so that axes are not covered by ground @@ -1771,21 +1768,21 @@ sub _load_image_set_texture { return $params; } -sub _variable_layer_thickness_load_overlay_image { - my ($self) = @_; - $self->{layer_preview_annotation} = $self->_load_image_set_texture('variable_layer_height_tooltip.png') - if (! $self->{layer_preview_annotation}->{loaded}); - return $self->{layer_preview_annotation}->{valid}; -} - -sub _variable_layer_thickness_load_reset_image { - my ($self) = @_; - $self->{layer_preview_reset_image} = $self->_load_image_set_texture('variable_layer_height_reset.png') - if (! $self->{layer_preview_reset_image}->{loaded}); - return $self->{layer_preview_reset_image}->{valid}; -} - #============================================================================================================================== +#sub _variable_layer_thickness_load_overlay_image { +# my ($self) = @_; +# $self->{layer_preview_annotation} = $self->_load_image_set_texture('variable_layer_height_tooltip.png') +# if (! $self->{layer_preview_annotation}->{loaded}); +# return $self->{layer_preview_annotation}->{valid}; +#} +# +#sub _variable_layer_thickness_load_reset_image { +# my ($self) = @_; +# $self->{layer_preview_reset_image} = $self->_load_image_set_texture('variable_layer_height_reset.png') +# if (! $self->{layer_preview_reset_image}->{loaded}); +# return $self->{layer_preview_reset_image}->{valid}; +#} +# ## Paint the tooltip. #sub _render_image { # my ($self, $image, $l, $r, $b, $t) = @_; @@ -1876,27 +1873,21 @@ sub draw_active_object_annotations { glBindTexture(GL_TEXTURE_2D, 0); $self->{layer_height_edit_shader}->disable; - # Paint the tooltip. - if ($self->_variable_layer_thickness_load_overlay_image) { #============================================================================================================================== - my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); - my $gap = 10/$zoom; - my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$zoom + $gap, $reset_bottom + $gap); - Slic3r::GUI::_3DScene::render_texture($self, $self->{layer_preview_annotation}->{texture_id}, $l, $r, $t, $b); - + Slic3r::GUI::_3DScene::render_layer_editing_textures($self); + +# # Paint the tooltip. +# if ($self->_variable_layer_thickness_load_overlay_image) # my $gap = 10/$self->_zoom; # my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$self->_zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$self->_zoom + $gap, $reset_bottom + $gap); # $self->_render_image($self->{layer_preview_annotation}, $l, $r, $t, $b); -#============================================================================================================================== - } - - # Paint the reset button. - if ($self->_variable_layer_thickness_load_reset_image) { -#============================================================================================================================== - Slic3r::GUI::_3DScene::render_texture($self, $self->{layer_preview_reset_image}->{texture_id}, $reset_left, $reset_right, $reset_bottom, $reset_top); +# } +# +# # Paint the reset button. +# if ($self->_variable_layer_thickness_load_reset_image) { # $self->_render_image($self->{layer_preview_reset_image}, $reset_left, $reset_right, $reset_bottom, $reset_top); +# } #============================================================================================================================== - } # Paint the graph. #FIXME show some kind of legend. diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index a6e46b11a5..5c3492edc0 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2065,6 +2065,11 @@ void _3DScene::render_legend_texture(wxGLCanvas* canvas) s_canvas_mgr.render_legend_texture(canvas); } +void _3DScene::render_layer_editing_textures(wxGLCanvas* canvas) +{ + s_canvas_mgr.render_layer_editing_textures(canvas); +} + void _3DScene::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) { s_canvas_mgr.render_texture(canvas, tex_id, left, right, bottom, top); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 03b2421642..cc2d0d5c56 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -631,6 +631,7 @@ public: static void render_cutting_plane(wxGLCanvas* canvas); static void render_warning_texture(wxGLCanvas* canvas); static void render_legend_texture(wxGLCanvas* canvas); + static void render_layer_editing_textures(wxGLCanvas* canvas); static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index da639b144d..a0065348a3 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -6,7 +6,9 @@ #include "../../libslic3r/PrintConfig.hpp" #include + #include +#include #include @@ -23,6 +25,9 @@ static const float VIEW_BOTTOM[2] = { 0.0f, 180.0f }; static const float VIEW_FRONT[2] = { 0.0f, 90.0f }; static const float VIEW_REAR[2] = { 180.0f, 90.0f }; +static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f; +static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; + namespace Slic3r { namespace GUI { @@ -85,6 +90,94 @@ unsigned int GeometryBuffer::get_data_size() const return (unsigned int)m_data.size(); } +Size::Size() + : m_width(0) + , m_height(0) +{ +} + +Size::Size(int width, int height) + : m_width(width) + , m_height(height) +{ +} + +int Size::get_width() const +{ + return m_width; +} + +void Size::set_width(int width) +{ + m_width = width; +} + +int Size::get_height() const +{ + return m_height; +} + +void Size::set_height(int height) +{ + m_height = height; +} + +Rect::Rect() + : m_left(0.0f) + , m_top(0.0f) + , m_right(0.0f) + , m_bottom(0.0f) +{ +} + +Rect::Rect(float left, float top, float right, float bottom) + : m_left(left) + , m_top(top) + , m_right(right) + , m_bottom(bottom) +{ +} + +float Rect::get_left() const +{ + return m_left; +} + +void Rect::set_left(float left) +{ + m_left = left; +} + +float Rect::get_top() const +{ + return m_top; +} + +void Rect::set_top(float top) +{ + m_top = top; +} + +float Rect::get_right() const +{ + return m_right; +} + +void Rect::set_right(float right) +{ + m_right = right; +} + +float Rect::get_bottom() const +{ + return m_bottom; +} + +void Rect::set_bottom(float bottom) +{ + m_bottom = bottom; +} + GLCanvas3D::Camera::Camera() : m_type(CT_Ortho) , m_zoom(1.0f) @@ -406,16 +499,178 @@ void GLCanvas3D::CuttingPlane::_render_contour() const ::glDisableClientState(GL_VERTEX_ARRAY); } +GLCanvas3D::LayersEditing::GLTextureData::GLTextureData() + : id(0) + , width(0) + , height(0) +{ +} + +GLCanvas3D::LayersEditing::GLTextureData::GLTextureData(unsigned int id, int width, int height) + : id(id) + , width(width) + , height(height) +{ +} + GLCanvas3D::LayersEditing::LayersEditing() : m_enabled(false) { } +GLCanvas3D::LayersEditing::~LayersEditing() +{ + if (m_tooltip_texture.id != 0) + { + ::glDeleteTextures(1, &m_tooltip_texture.id); + m_tooltip_texture = GLTextureData(); + } + + if (m_reset_texture.id != 0) + { + ::glDeleteTextures(1, &m_reset_texture.id); + m_reset_texture = GLTextureData(); + } +} + bool GLCanvas3D::LayersEditing::is_enabled() const { return m_enabled; } +void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas) const +{ + const Rect& bar_rect = _get_bar_rect_viewport(canvas); + const Rect& reset_rect = _get_reset_rect_viewport(canvas); + _render_tooltip_texture(canvas, bar_rect, reset_rect); + _render_reset_texture(canvas, reset_rect); +} + +GLCanvas3D::LayersEditing::GLTextureData GLCanvas3D::LayersEditing::_load_texture_from_file(const std::string& filename) const +{ + const std::string& path = resources_dir() + "/icons/"; + + // Load a PNG with an alpha channel. + wxImage image; + if (!image.LoadFile(path + filename, wxBITMAP_TYPE_PNG)) + return GLTextureData(); + + int width = image.GetWidth(); + int height = image.GetHeight(); + int n_pixels = width * height; + + if (n_pixels <= 0) + return GLTextureData(); + + // Get RGB & alpha raw data from wxImage, pack them into an array. + unsigned char* img_rgb = image.GetData(); + if (img_rgb == nullptr) + return GLTextureData(); + + unsigned char* img_alpha = image.GetAlpha(); + + std::vector data(n_pixels * 4, 0); + for (int i = 0; i < n_pixels; ++i) + { + int data_id = i * 4; + int img_id = i * 3; + data[data_id + 0] = img_rgb[img_id + 0]; + data[data_id + 1] = img_rgb[img_id + 1]; + data[data_id + 2] = img_rgb[img_id + 2]; + data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; + } + + // sends data to gpu + GLuint tex_id; + ::glGenTextures(1, &tex_id); + ::glBindTexture(GL_TEXTURE_2D, tex_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(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)width, (GLsizei)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + ::glBindTexture(GL_TEXTURE_2D, 0); + + return GLTextureData((unsigned int)tex_id, width, height); +} + +void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const +{ + if (m_tooltip_texture.id == 0) + { + m_tooltip_texture = _load_texture_from_file("variable_layer_height_tooltip.png"); + if (m_tooltip_texture.id == 0) + return; + } + + float zoom = canvas.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + float gap = 10.0f * inv_zoom; + + float bar_left = bar_rect.get_left(); + float reset_bottom = reset_rect.get_bottom(); + + float l = bar_left - (float)m_tooltip_texture.width * inv_zoom - gap; + float r = bar_left - gap; + float t = reset_bottom + (float)m_tooltip_texture.height * inv_zoom + gap; + float b = reset_bottom + gap; + + canvas.render_texture(m_tooltip_texture.id, l, r, b, t); +} + +void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const +{ + if (m_reset_texture.id == 0) + { + m_reset_texture = _load_texture_from_file("variable_layer_height_reset.png"); + if (m_reset_texture.id == 0) + return; + } + + canvas.render_texture(m_reset_texture.id, reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); +} + +Rect GLCanvas3D::LayersEditing::_get_bar_rect_screen(const GLCanvas3D& canvas) const +{ + const Size& cnv_size = canvas.get_canvas_size(); + float w = (float)cnv_size.get_width(); + float h = (float)cnv_size.get_height(); + + return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0.0f, w, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT); +} + +Rect GLCanvas3D::LayersEditing::_get_reset_rect_screen(const GLCanvas3D& canvas) const +{ + const Size& cnv_size = canvas.get_canvas_size(); + float w = (float)cnv_size.get_width(); + float h = (float)cnv_size.get_height(); + + return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, w, h); +} + +Rect GLCanvas3D::LayersEditing::_get_bar_rect_viewport(const GLCanvas3D& canvas) const +{ + const Size& cnv_size = canvas.get_canvas_size(); + float half_w = 0.5f * (float)cnv_size.get_width(); + float half_h = 0.5f * (float)cnv_size.get_height(); + + float zoom = canvas.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom); +} + +Rect GLCanvas3D::LayersEditing::_get_reset_rect_viewport(const GLCanvas3D& canvas) const +{ + const Size& cnv_size = canvas.get_canvas_size(); + float half_w = 0.5f * (float)cnv_size.get_width(); + float half_h = 0.5f * (float)cnv_size.get_height(); + + float zoom = canvas.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); +} + GLCanvas3D::Shader::Shader() : m_enabled(false) , m_shader(nullptr) @@ -524,7 +779,6 @@ GLCanvas3D::~GLCanvas3D() bool GLCanvas3D::init(bool useVBOs) { ::glClearColor(0.0f, 0.0f, 0.0f, 1.0f); -// ::glColor3f(1.0f, 0.0f, 0.0f); ::glEnable(GL_DEPTH_TEST); ::glClearDepth(1.0f); ::glDepthFunc(GL_LEQUAL); @@ -540,7 +794,6 @@ bool GLCanvas3D::init(bool useVBOs) GLfloat ambient[4] = { 0.3f, 0.3f, 0.3f, 1.0f }; ::glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); -// ::glEnable(GL_LIGHTING); ::glEnable(GL_LIGHT0); ::glEnable(GL_LIGHT1); @@ -968,8 +1221,6 @@ void GLCanvas3D::picking_pass() { if (is_picking_enabled() && !is_mouse_dragging() && (m_volumes != nullptr)) { - const Pointf& pos = get_mouse_position(); -// if (pos) { // Render the object for picking. // FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing. // Better to use software ray - casting on a bounding - box hierarchy. @@ -991,12 +1242,11 @@ void GLCanvas3D::picking_pass() if (is_multisample_allowed()) ::glEnable(GL_MULTISAMPLE); -// ::glFlush(); - - const std::pair& cnv_size = _get_canvas_size(); + const Size& cnv_size = get_canvas_size(); + const Pointf& pos = get_mouse_position(); GLubyte color[4]; - ::glReadPixels(pos.x, cnv_size.second - pos.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); + ::glReadPixels(pos.x, cnv_size.get_height() - pos.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); int volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256; set_hover_volume_id(-1); @@ -1021,7 +1271,6 @@ void GLCanvas3D::picking_pass() } } } -// } } void GLCanvas3D::render_background() const @@ -1181,11 +1430,11 @@ void GLCanvas3D::render_warning_texture() const ::glPushMatrix(); ::glLoadIdentity(); - const std::pair& cnv_size = _get_canvas_size(); + const Size& cnv_size = get_canvas_size(); float zoom = get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float l = (-0.5f * (float)w) * inv_zoom; - float t = (-0.5f * (float)cnv_size.second + (float)h) * inv_zoom; + float t = (-0.5f * (float)cnv_size.get_height() + (float)h) * inv_zoom; float r = l + (float)w * inv_zoom; float b = t - (float)h * inv_zoom; @@ -1214,11 +1463,11 @@ void GLCanvas3D::render_legend_texture() const ::glPushMatrix(); ::glLoadIdentity(); - const std::pair& cnv_size = _get_canvas_size(); + const Size& cnv_size = get_canvas_size(); float zoom = get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - float l = (-0.5f * (float)cnv_size.first) * inv_zoom; - float t = (0.5f * (float)cnv_size.second) * inv_zoom; + float l = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; + float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom; float r = l + (float)w * inv_zoom; float b = t - (float)h * inv_zoom; render_texture(tex_id, l, r, b, t); @@ -1229,6 +1478,11 @@ void GLCanvas3D::render_legend_texture() const } } +void GLCanvas3D::render_layer_editing_textures() const +{ + m_layers_editing.render(*this); +} + void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const { ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); @@ -1278,8 +1532,8 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) if (m_canvas != nullptr) { - const std::pair& cnv_size = _get_canvas_size(); - resize((unsigned int)cnv_size.first, (unsigned int)cnv_size.second); + const Size& cnv_size = get_canvas_size(); + resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); m_canvas->Refresh(); } } @@ -1319,6 +1573,17 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) } } +Size GLCanvas3D::get_canvas_size() const +{ + int w = 0; + int h = 0; + + if (m_canvas != nullptr) + m_canvas->GetSize(&w, &h); + + return Size(w, h); +} + void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { // Calculate the zoom factor needed to adjust viewport to bounding box. @@ -1333,24 +1598,14 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) if (is_shown_on_screen()) { - const std::pair& cnv_size = _get_canvas_size(); - resize((unsigned int)cnv_size.first, (unsigned int)cnv_size.second); + const Size& cnv_size = get_canvas_size(); + resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); if (m_canvas != nullptr) m_canvas->Refresh(); } } } -std::pair GLCanvas3D::_get_canvas_size() const -{ - std::pair ret(0, 0); - - if (m_canvas != nullptr) - m_canvas->GetSize(&ret.first, &ret.second); - - return ret; -} - float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const { float max_bb_size = bbox.max_size(); @@ -1433,8 +1688,8 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co max_x *= 2.0; max_y *= 2.0; - const std::pair& cnv_size = _get_canvas_size(); - return (float)std::min((coordf_t)cnv_size.first / max_x, (coordf_t)cnv_size.second / max_y); + const Size& cnv_size = get_canvas_size(); + return (float)std::min((coordf_t)cnv_size.get_width() / max_x, (coordf_t)cnv_size.get_height() / max_y); } void GLCanvas3D::_deregister_callbacks() diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 3c0275c9e6..7c32ea06c8 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -32,6 +32,46 @@ public: unsigned int get_data_size() const; }; +class Size +{ + int m_width; + int m_height; + +public: + Size(); + Size(int width, int height); + + int get_width() const; + void set_width(int width); + + int get_height() const; + void set_height(int height); +}; + +class Rect +{ + float m_left; + float m_top; + float m_right; + float m_bottom; + +public: + Rect(); + Rect(float left, float top, float right, float bottom); + + float get_left() const; + void set_left(float left); + + float get_top() const; + void set_top(float top); + + float get_right() const; + void set_right(float right); + + float get_bottom() const; + void set_bottom(float bottom); +}; + class GLCanvas3D { public: @@ -135,12 +175,36 @@ public: class LayersEditing { + struct GLTextureData + { + unsigned int id; + int width; + int height; + + GLTextureData(); + GLTextureData(unsigned int id, int width, int height); + }; + bool m_enabled; + mutable GLTextureData m_tooltip_texture; + mutable GLTextureData m_reset_texture; public: LayersEditing(); + ~LayersEditing(); bool is_enabled() const; + + void render(const GLCanvas3D& canvas) const; + + private: + GLTextureData _load_texture_from_file(const std::string& filename) const; + void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; + void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; + Rect _get_bar_rect_screen(const GLCanvas3D& canvas) const; + Rect _get_reset_rect_screen(const GLCanvas3D& canvas) const; + Rect _get_bar_rect_viewport(const GLCanvas3D& canvas) const; + Rect _get_reset_rect_viewport(const GLCanvas3D& canvas) const; }; class Shader @@ -299,6 +363,7 @@ public: void render_cutting_plane() const; void render_warning_texture() const; void render_legend_texture() const; + void render_layer_editing_textures() const; void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; @@ -309,9 +374,10 @@ public: void on_idle(wxIdleEvent& evt); void on_char(wxKeyEvent& evt); + Size get_canvas_size() const; + private: void _zoom_to_bounding_box(const BoundingBoxf3& bbox); - std::pair _get_canvas_size() const; float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; void _deregister_callbacks(); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index c5a15696d6..adcae3ddb8 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -560,6 +560,13 @@ void GLCanvas3DManager::render_legend_texture(wxGLCanvas* canvas) const it->second->render_legend_texture(); } +void GLCanvas3DManager::render_layer_editing_textures(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->render_layer_editing_textures(); +} + void GLCanvas3DManager::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index e0b2a1d2e2..7f64e94b41 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -139,6 +139,7 @@ public: void render_cutting_plane(wxGLCanvas* canvas) const; void render_warning_texture(wxGLCanvas* canvas) const; void render_legend_texture(wxGLCanvas* canvas) const; + void render_layer_editing_textures(wxGLCanvas* canvas) const; void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index a2be3b1df6..306ecf53fb 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -632,6 +632,12 @@ render_legend_texture(canvas) CODE: _3DScene::render_legend_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); +void +render_layer_editing_textures(canvas) + SV *canvas; + CODE: + _3DScene::render_layer_editing_textures((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + void render_texture(canvas, tex_id, left, right, bottom, top) SV *canvas; From 70664122aff580a74aeb818c00780027ab2d5215 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 24 May 2018 15:17:01 +0200 Subject: [PATCH 027/117] 3DScene layer height profile rendering moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 74 ++++++++++++------------- xs/src/slic3r/GUI/3DScene.cpp | 5 +- xs/src/slic3r/GUI/3DScene.hpp | 2 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 63 ++++++++++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 6 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 4 +- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 +- xs/xsp/GUI_3DScene.xsp | 7 ++- 8 files changed, 112 insertions(+), 51 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index ae91b52472..c2f1b8fa8d 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1874,7 +1874,7 @@ sub draw_active_object_annotations { $self->{layer_height_edit_shader}->disable; #============================================================================================================================== - Slic3r::GUI::_3DScene::render_layer_editing_textures($self); + Slic3r::GUI::_3DScene::render_layer_editing_textures($self, $print_object); # # Paint the tooltip. # if ($self->_variable_layer_thickness_load_overlay_image) @@ -1887,43 +1887,43 @@ sub draw_active_object_annotations { # if ($self->_variable_layer_thickness_load_reset_image) { # $self->_render_image($self->{layer_preview_reset_image}, $reset_left, $reset_right, $reset_bottom, $reset_top); # } +# +# # Paint the graph. +# #FIXME show some kind of legend. +# my $max_z = unscale($print_object->size->z); +# my $profile = $print_object->model_object->layer_height_profile; +# my $layer_height = $print_object->config->get('layer_height'); +# my $layer_height_max = 10000000000.; +# { +# # Get a maximum layer height value. +# #FIXME This is a duplicate code of Slicing.cpp. +# my $nozzle_diameters = $print_object->print->config->get('nozzle_diameter'); +# my $layer_heights_min = $print_object->print->config->get('min_layer_height'); +# my $layer_heights_max = $print_object->print->config->get('max_layer_height'); +# for (my $i = 0; $i < scalar(@{$nozzle_diameters}); $i += 1) { +# my $lh_min = ($layer_heights_min->[$i] == 0.) ? 0.07 : max(0.01, $layer_heights_min->[$i]); +# my $lh_max = ($layer_heights_max->[$i] == 0.) ? (0.75 * $nozzle_diameters->[$i]) : $layer_heights_max->[$i]; +# $layer_height_max = min($layer_height_max, max($lh_min, $lh_max)); +# } +# } +# # Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region. +# $layer_height_max *= 1.12; +# # Baseline +# glColor3f(0., 0., 0.); +# glBegin(GL_LINE_STRIP); +# glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / $layer_height_max, $bar_bottom); +# glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / $layer_height_max, $bar_top); +# glEnd(); +# # Curve +# glColor3f(0., 0., 1.); +# glBegin(GL_LINE_STRIP); +# for (my $i = 0; $i < int(@{$profile}); $i += 2) { +# my $z = $profile->[$i]; +# my $h = $profile->[$i+1]; +# glVertex3f($bar_left + $h * ($bar_right - $bar_left) / $layer_height_max, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z); +# } +# glEnd(); #============================================================================================================================== - - # Paint the graph. - #FIXME show some kind of legend. - my $max_z = unscale($print_object->size->z); - my $profile = $print_object->model_object->layer_height_profile; - my $layer_height = $print_object->config->get('layer_height'); - my $layer_height_max = 10000000000.; - { - # Get a maximum layer height value. - #FIXME This is a duplicate code of Slicing.cpp. - my $nozzle_diameters = $print_object->print->config->get('nozzle_diameter'); - my $layer_heights_min = $print_object->print->config->get('min_layer_height'); - my $layer_heights_max = $print_object->print->config->get('max_layer_height'); - for (my $i = 0; $i < scalar(@{$nozzle_diameters}); $i += 1) { - my $lh_min = ($layer_heights_min->[$i] == 0.) ? 0.07 : max(0.01, $layer_heights_min->[$i]); - my $lh_max = ($layer_heights_max->[$i] == 0.) ? (0.75 * $nozzle_diameters->[$i]) : $layer_heights_max->[$i]; - $layer_height_max = min($layer_height_max, max($lh_min, $lh_max)); - } - } - # Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region. - $layer_height_max *= 1.12; - # Baseline - glColor3f(0., 0., 0.); - glBegin(GL_LINE_STRIP); - glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / $layer_height_max, $bar_bottom); - glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / $layer_height_max, $bar_top); - glEnd(); - # Curve - glColor3f(0., 0., 1.); - glBegin(GL_LINE_STRIP); - for (my $i = 0; $i < int(@{$profile}); $i += 2) { - my $z = $profile->[$i]; - my $h = $profile->[$i+1]; - glVertex3f($bar_left + $h * ($bar_right - $bar_left) / $layer_height_max, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z); - } - glEnd(); # Revert the matrices. glPopMatrix(); glEnable(GL_DEPTH_TEST); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 5c3492edc0..0f4b020da7 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2065,9 +2065,10 @@ void _3DScene::render_legend_texture(wxGLCanvas* canvas) s_canvas_mgr.render_legend_texture(canvas); } -void _3DScene::render_layer_editing_textures(wxGLCanvas* canvas) +void _3DScene::render_layer_editing_textures(wxGLCanvas* canvas, const PrintObject* print_object) { - s_canvas_mgr.render_layer_editing_textures(canvas); + if (print_object != nullptr) + s_canvas_mgr.render_layer_editing_textures(canvas, *print_object); } void _3DScene::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index cc2d0d5c56..edbb42bc2a 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -631,7 +631,7 @@ public: static void render_cutting_plane(wxGLCanvas* canvas); static void render_warning_texture(wxGLCanvas* canvas); static void render_legend_texture(wxGLCanvas* canvas); - static void render_layer_editing_textures(wxGLCanvas* canvas); + static void render_layer_editing_textures(wxGLCanvas* canvas, const PrintObject* print_object); static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index a0065348a3..2e0c35e7ce 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -4,6 +4,7 @@ #include "../../slic3r/GUI/GLShader.hpp" #include "../../libslic3r/ClipperUtils.hpp" #include "../../libslic3r/PrintConfig.hpp" +#include "../../libslic3r/Print.hpp" #include @@ -538,12 +539,13 @@ bool GLCanvas3D::LayersEditing::is_enabled() const return m_enabled; } -void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas) const +void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObject& print_object) const { const Rect& bar_rect = _get_bar_rect_viewport(canvas); const Rect& reset_rect = _get_reset_rect_viewport(canvas); _render_tooltip_texture(canvas, bar_rect, reset_rect); _render_reset_texture(canvas, reset_rect); + _render_profile(print_object, bar_rect); } GLCanvas3D::LayersEditing::GLTextureData GLCanvas3D::LayersEditing::_load_texture_from_file(const std::string& filename) const @@ -629,6 +631,61 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, canvas.render_texture(m_reset_texture.id, reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); } +void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, const Rect& bar_rect) const +{ + // FIXME show some kind of legend. + + // Get a maximum layer height value. + // FIXME This is a duplicate code of Slicing.cpp. + double layer_height_max = DBL_MAX; + const PrintConfig& print_config = print_object.print()->config; + const std::vector& nozzle_diameters = dynamic_cast(print_config.option("nozzle_diameter"))->values; + const std::vector& layer_heights_min = dynamic_cast(print_config.option("min_layer_height"))->values; + const std::vector& layer_heights_max = dynamic_cast(print_config.option("max_layer_height"))->values; + for (unsigned int i = 0; i < (unsigned int)nozzle_diameters.size(); ++i) + { + double lh_min = (layer_heights_min[i] == 0.0) ? 0.07 : std::max(0.01, layer_heights_min[i]); + double lh_max = (layer_heights_max[i] == 0.0) ? (0.75 * nozzle_diameters[i]) : layer_heights_max[i]; + layer_height_max = std::min(layer_height_max, std::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; + + coordf_t max_z = unscale(print_object.size.z); + double layer_height = dynamic_cast(print_object.config.option("layer_height"))->value; + float l = bar_rect.get_left(); + float w = bar_rect.get_right() - l; + float b = bar_rect.get_bottom(); + float t = bar_rect.get_top(); + float h = t - b; + float scale_x = w / (float)layer_height_max; + float scale_y = h / (float)max_z; + float x = l + (float)layer_height * scale_x; + + // Baseline + ::glColor3f(0.0f, 0.0f, 0.0f); + ::glBegin(GL_LINE_STRIP); + ::glVertex2f(x, b); + ::glVertex2f(x, t); + ::glEnd(); + + // Curve + const ModelObject* model_object = print_object.model_object(); + if (model_object->layer_height_profile_valid) + { + const std::vector& profile = model_object->layer_height_profile; + + ::glColor3f(0.0f, 0.0f, 1.0f); + ::glBegin(GL_LINE_STRIP); + for (unsigned int i = 0; i < profile.size(); i += 2) + { + ::glVertex2f(l + (float)profile[i + 1] * scale_x, b + (float)profile[i] * scale_y); + } + ::glEnd(); + } +} + Rect GLCanvas3D::LayersEditing::_get_bar_rect_screen(const GLCanvas3D& canvas) const { const Size& cnv_size = canvas.get_canvas_size(); @@ -1478,9 +1535,9 @@ void GLCanvas3D::render_legend_texture() const } } -void GLCanvas3D::render_layer_editing_textures() const +void GLCanvas3D::render_layer_editing_textures(const PrintObject& print_object) const { - m_layers_editing.render(*this); + m_layers_editing.render(*this, print_object); } void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 7c32ea06c8..a15e632fc2 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -17,6 +17,7 @@ class GLVolumeCollection; class DynamicPrintConfig; class GLShader; class ExPolygon; +class PrintObject; namespace GUI { @@ -195,12 +196,13 @@ public: bool is_enabled() const; - void render(const GLCanvas3D& canvas) const; + void render(const GLCanvas3D& canvas, const PrintObject& print_object) const; private: GLTextureData _load_texture_from_file(const std::string& filename) const; void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; + void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; Rect _get_bar_rect_screen(const GLCanvas3D& canvas) const; Rect _get_reset_rect_screen(const GLCanvas3D& canvas) const; Rect _get_bar_rect_viewport(const GLCanvas3D& canvas) const; @@ -363,7 +365,7 @@ public: void render_cutting_plane() const; void render_warning_texture() const; void render_legend_texture() const; - void render_layer_editing_textures() const; + void render_layer_editing_textures(const PrintObject& print_object) const; void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index adcae3ddb8..c28bfb6753 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -560,11 +560,11 @@ void GLCanvas3DManager::render_legend_texture(wxGLCanvas* canvas) const it->second->render_legend_texture(); } -void GLCanvas3DManager::render_layer_editing_textures(wxGLCanvas* canvas) const +void GLCanvas3DManager::render_layer_editing_textures(wxGLCanvas* canvas, const PrintObject& print_object) const { CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) - it->second->render_layer_editing_textures(); + it->second->render_layer_editing_textures(print_object); } void GLCanvas3DManager::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 7f64e94b41..19baaf94fc 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -139,7 +139,7 @@ public: void render_cutting_plane(wxGLCanvas* canvas) const; void render_warning_texture(wxGLCanvas* canvas) const; void render_legend_texture(wxGLCanvas* canvas) const; - void render_layer_editing_textures(wxGLCanvas* canvas) const; + void render_layer_editing_textures(wxGLCanvas* canvas, const PrintObject& print_object) const; void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 306ecf53fb..f87cbda440 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -633,10 +633,11 @@ render_legend_texture(canvas) _3DScene::render_legend_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); void -render_layer_editing_textures(canvas) - SV *canvas; +render_layer_editing_textures(canvas, print_object) + SV *canvas; + PrintObject *print_object; CODE: - _3DScene::render_layer_editing_textures((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + _3DScene::render_layer_editing_textures((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print_object); void render_texture(canvas, tex_id, left, right, bottom, top) From c2e38fc6fe675a6b4f5b33d82c64bc3951487f5d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 24 May 2018 15:22:53 +0200 Subject: [PATCH 028/117] Fixed compile on Linux --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 2e0c35e7ce..942a7e6ef4 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -12,6 +12,7 @@ #include #include +#include static const bool TURNTABLE_MODE = true; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; From f12181750194a0586b721c3e8693c0b8d7005ec6 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 24 May 2018 16:11:34 +0200 Subject: [PATCH 029/117] Removed from Perl all 3DScene methods already moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 144 +++++++++------------- lib/Slic3r/GUI/Plater.pm | 26 +++- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 15 ++- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 13 +- 4 files changed, 99 insertions(+), 99 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index c2f1b8fa8d..d8ee842bda 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -102,15 +102,13 @@ use constant TRACKBALLSIZE => 0.8; use constant TURNTABLE_MODE => 1; #============================================================================================================================== #use constant GROUND_Z => -0.02; -#============================================================================================================================== -# For mesh selection: Not selected - bright yellow. -use constant DEFAULT_COLOR => [1,1,0]; -# For mesh selection: Selected - bright green. -use constant SELECTED_COLOR => [0,1,0,1]; -# For mesh selection: Mouse hovers over the object, but object not selected yet - dark green. -use constant HOVER_COLOR => [0.4,0.9,0,1]; - -#============================================================================================================================== +## For mesh selection: Not selected - bright yellow. +#use constant DEFAULT_COLOR => [1,1,0]; +## For mesh selection: Selected - bright green. +#use constant SELECTED_COLOR => [0,1,0,1]; +## For mesh selection: Mouse hovers over the object, but object not selected yet - dark green. +#use constant HOVER_COLOR => [0.4,0.9,0,1]; +# ## phi / theta angles to orient the camera. #use constant VIEW_DEFAULT => [45.0,45.0]; #use constant VIEW_LEFT => [90.0,90.0]; @@ -710,7 +708,10 @@ sub mouse_wheel_event { # $zoom = $self->_zoom / (1-$zoom); #============================================================================================================================== # Don't allow to zoom too far outside the scene. - my $zoom_min = $self->get_zoom_to_bounding_box_factor($self->max_bounding_box); +#============================================================================================================================== + my $zoom_min = $self->get_zoom_to_bounding_box_factor(Slic3r::GUI::_3DScene::get_max_bounding_box($self)); +# my $zoom_min = $self->get_zoom_to_bounding_box_factor($self->max_bounding_box); +#============================================================================================================================== $zoom_min *= 0.4 if defined $zoom_min; $zoom = $zoom_min if defined $zoom_min && $zoom < $zoom_min; #============================================================================================================================== @@ -737,7 +738,8 @@ sub mouse_wheel_event { $self->on_viewport_changed->() if $self->on_viewport_changed; #============================================================================================================================== - $self->Resize($self->GetSizeWH) if Slic3r::GUI::_3DScene::is_shown_on_screen($self); +#============================================================================================================================== + Slic3r::GUI::_3DScene::resize($self, $self->GetSizeWH) if Slic3r::GUI::_3DScene::is_shown_on_screen($self); # $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen; #============================================================================================================================== $self->Refresh; @@ -778,13 +780,12 @@ sub set_viewport_from_scene { #============================================================================================================================== } -# Set the camera to a default orientation, -# zoom to volumes. -sub select_view { - my ($self, $direction) = @_; #============================================================================================================================== - Slic3r::GUI::_3DScene::select_view($self, $direction); - +## Set the camera to a default orientation, +## zoom to volumes. +#sub select_view { +# my ($self, $direction) = @_; +# # my $dirvec; # if (ref($direction)) { # $dirvec = $direction; @@ -815,8 +816,8 @@ sub select_view { # $self->on_viewport_changed->() if $self->on_viewport_changed; # $self->Refresh; # } +#} #============================================================================================================================== -} sub get_zoom_to_bounding_box_factor { my ($self, $bb) = @_; @@ -927,20 +928,15 @@ sub get_zoom_to_bounding_box_factor { # $self->Refresh; # } #} -#============================================================================================================================== - -sub zoom_to_bed { - my ($self) = @_; - -#============================================================================================================================== - Slic3r::GUI::_3DScene::zoom_to_bed($self); +# +#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) = @_; # @@ -948,60 +944,43 @@ sub zoom_to_bed { # my $bb = $volume->transformed_bounding_box; # $self->zoom_to_bounding_box($bb); #} -#============================================================================================================================== - -sub zoom_to_volumes { - my ($self) = @_; -#============================================================================================================================== - Slic3r::GUI::_3DScene::zoom_to_volumes($self); - +# +#sub zoom_to_volumes { +# my ($self) = @_; +# # $self->_apply_zoom_to_volumes_filter(1); # $self->zoom_to_bounding_box($self->volumes_bounding_box); # $self->_apply_zoom_to_volumes_filter(0); -#============================================================================================================================== -} - -sub volumes_bounding_box { - my ($self) = @_; - -#============================================================================================================================== - return Slic3r::GUI::_3DScene::get_volumes_bounding_box($self); - +#} +# +#sub volumes_bounding_box { +# my ($self) = @_; +# # my $bb = Slic3r::Geometry::BoundingBoxf3->new; # foreach my $v (@{$self->volumes}) { # $bb->merge($v->transformed_bounding_box) if (! $self->_apply_zoom_to_volumes_filter || $v->zoom_to_volumes); # } # return $bb; -#============================================================================================================================== -} - -sub bed_bounding_box { - my ($self) = @_; - -#============================================================================================================================== - return Slic3r::GUI::_3DScene::get_bed_bounding_box($self); - +#} +# +#sub bed_bounding_box { +# my ($self) = @_; +# # my $bb = Slic3r::Geometry::BoundingBoxf3->new; # if ($self->bed_shape) { # $bb->merge_point(Slic3r::Pointf3->new(@$_, 0)) for @{$self->bed_shape}; # } # return $bb; -#============================================================================================================================== -} - -sub max_bounding_box { - my ($self) = @_; - -#============================================================================================================================== - return Slic3r::GUI::_3DScene::get_max_bounding_box($self); - +#} +# +#sub max_bounding_box { +# my ($self) = @_; +# # my $bb = $self->bed_bounding_box; # $bb->merge($self->volumes_bounding_box); # return $bb; -#============================================================================================================================== -} - -#============================================================================================================================== +#} +# ## Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane ## to support the scene objects. #sub set_auto_bed_shape { @@ -1085,12 +1064,10 @@ sub select_volume { if $volume_idx != -1; } -sub SetCuttingPlane { - my ($self, $z, $expolygons) = @_; - #============================================================================================================================== - Slic3r::GUI::_3DScene::set_cutting_plane($self, $z, $expolygons); - +#sub SetCuttingPlane { +# my ($self, $z, $expolygons) = @_; +# # $self->cutting_plane_z($z); # # # grow slices in order to display them better @@ -1104,8 +1081,8 @@ sub SetCuttingPlane { # ); # } # $self->cut_lines_vertices(OpenGL::Array->new_list(GL_FLOAT, @verts)); +#} #============================================================================================================================== -} # Given an axis and angle, compute quaternion. sub axis_to_quat { @@ -1277,12 +1254,10 @@ sub UseVBOs { #============================================================================================================================== } -sub Resize { - my ($self, $x, $y) = @_; - -#============================================================================================================================== - Slic3r::GUI::_3DScene::resize($self, $x, $y); - +#============================================================================================================================== +#sub Resize { +# my ($self, $x, $y) = @_; +# # return unless $self->GetContext; # $self->_dirty(0); # @@ -1320,8 +1295,8 @@ sub Resize { # glFrustum(-$w2, $w2, -$h2, $h2, $nr, $fr); # } # glMatrixMode(GL_MODELVIEW); +#} #============================================================================================================================== -} sub InitGL { my $self = shift; @@ -1341,11 +1316,12 @@ sub InitGL { $self->volumes->finalize_geometry(1) if ($^O eq 'linux' && $self->UseVBOs); - $self->zoom_to_bed; - -#=================================================================================================================================== +#============================================================================================================================== + Slic3r::GUI::_3DScene::zoom_to_bed($self); Slic3r::GUI::_3DScene::init($self, $self->UseVBOs); - + +# $self->zoom_to_bed; +# # glClearColor(0, 0, 0, 1); # glColor3f(1, 0, 0); # glEnable(GL_DEPTH_TEST); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 4581de41bd..e7de0eb9bb 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -385,7 +385,10 @@ sub new { $self->{canvas}->update_bed_size; if ($self->{canvas3D}) { $self->{canvas3D}->update_bed_size; - $self->{canvas3D}->zoom_to_bed; +#============================================================================================================================== + Slic3r::GUI::_3DScene::zoom_to_bed($self->{canvas3D}); +# $self->{canvas3D}->zoom_to_bed; +#============================================================================================================================== } if ($self->{preview3D}) { $self->{preview3D}->set_bed_shape($self->{config}->bed_shape); @@ -840,8 +843,10 @@ sub load_model_objects { $self->update; # zoom to objects - $self->{canvas3D}->zoom_to_volumes - if $self->{canvas3D}; +#============================================================================================================================== + Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; +# $self->{canvas3D}->zoom_to_volumes if $self->{canvas3D}; +#============================================================================================================================== $self->{list}->Update; $self->{list}->Select($obj_idx[-1], 1); @@ -1923,7 +1928,10 @@ sub object_cut_dialog { $self->remove($obj_idx); $self->load_model_objects(grep defined($_), @new_objects); $self->arrange; - $self->{canvas3D}->zoom_to_volumes if $self->{canvas3D}; +#============================================================================================================================== + Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; +# $self->{canvas3D}->zoom_to_volumes if $self->{canvas3D}; +#============================================================================================================================== } } @@ -2202,10 +2210,16 @@ sub select_view { my $idx_page = $self->{preview_notebook}->GetSelection; my $page = ($idx_page == &Wx::wxNOT_FOUND) ? L('3D') : $self->{preview_notebook}->GetPageText($idx_page); if ($page eq L('Preview')) { - $self->{preview3D}->canvas->select_view($direction); +#============================================================================================================================== + Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction); +# $self->{preview3D}->canvas->select_view($direction); +#============================================================================================================================== $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); } else { - $self->{canvas3D}->select_view($direction); +#============================================================================================================================== + Slic3r::GUI::_3DScene::select_view($self->{canvas3D}, $direction); +# $self->{canvas3D}->select_view($direction); +#============================================================================================================================== $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); } } diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index b379ba8ef7..ab53da4a5b 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -118,7 +118,9 @@ sub new { $canvas->load_object($self->{model_object}, undef, undef, [0]); #============================================================================================================================== Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); - Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ $canvas->volumes_bounding_box->size })); + Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); +# Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ $canvas->volumes_bounding_box->size })); + # $canvas->set_auto_bed_shape; #============================================================================================================================== $canvas->SetSize([500,500]); @@ -260,10 +262,13 @@ sub _update { # $self->{canvas}->reset_objects; #============================================================================================================================== $self->{canvas}->load_object($_, undef, undef, [0]) for @objects; - $self->{canvas}->SetCuttingPlane( - $self->{cut_options}{z}, - [@expolygons], - ); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_cutting_plane($self->{canvas}, $self->{cut_options}{z}, [@expolygons]); +# $self->{canvas}->SetCuttingPlane( +# $self->{cut_options}{z}, +# [@expolygons], +# ); +#============================================================================================================================== $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->{config}); $self->{canvas}->Render; } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 54fdc249e3..bab628e231 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -166,9 +166,11 @@ sub new { }); $canvas->load_object($self->{model_object}, undef, undef, [0]); -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); - Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ $canvas->volumes_bounding_box->size })); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); + Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); +# Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ $canvas->volumes_bounding_box->size })); + # $canvas->set_auto_bed_shape; #============================================================================================================================== $canvas->SetSize([500,700]); @@ -502,7 +504,10 @@ sub _parts_changed { # $self->{canvas}->reset_objects; #============================================================================================================================== $self->{canvas}->load_object($self->{model_object}); - $self->{canvas}->zoom_to_volumes; +#============================================================================================================================== + Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas}); +# $self->{canvas}->zoom_to_volumes; +#============================================================================================================================== $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); $self->{canvas}->Render; } From bdbc86167c15545ed91312e28a644f6ae4faa683 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 25 May 2018 09:03:55 +0200 Subject: [PATCH 030/117] 3DScene volume selection methods moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 43 +++++++++++++------------ lib/Slic3r/GUI/Plater.pm | 5 ++- lib/Slic3r/GUI/Plater/3D.pm | 5 ++- xs/src/slic3r/GUI/3DScene.cpp | 10 ++++++ xs/src/slic3r/GUI/3DScene.hpp | 2 ++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 22 +++++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 2 ++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 14 ++++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 ++ xs/xsp/GUI_3DScene.xsp | 13 ++++++++ 10 files changed, 96 insertions(+), 22 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index d8ee842bda..e48d2cf5c7 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -508,16 +508,21 @@ sub mouse_event { # during the scene manipulation. #============================================================================================================================== if (Slic3r::GUI::_3DScene::is_picking_enabled($self) && ($volume_idx != -1 || ! $self->layer_editing_enabled)) { + Slic3r::GUI::_3DScene::deselect_volumes($self); + Slic3r::GUI::_3DScene::select_volume($self, $volume_idx); # if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) { +# $self->deselect_volumes; +# $self->select_volume($volume_idx); #============================================================================================================================== - $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($_) +#============================================================================================================================== + Slic3r::GUI::_3DScene::select_volume($self, $_) +# $self->select_volume($_) +#============================================================================================================================== for grep $self->volumes->[$_]->select_group_id == $group_id, 0..$#{$self->volumes}; } @@ -1048,23 +1053,21 @@ sub get_zoom_to_bounding_box_factor { # # $self->bed_polygon(offset_ex([$expolygon->contour], $bed_bb->radius * 1.7, JT_ROUND, scale(0.5))->[0]->contour->clone); #} -#============================================================================================================================== - -sub deselect_volumes { - my ($self) = @_; - $_->set_selected(0) for @{$self->volumes}; -} - -sub select_volume { - my ($self, $volume_idx) = @_; - - return if ($volume_idx >= scalar(@{$self->volumes})); - - $self->volumes->[$volume_idx]->set_selected(1) - if $volume_idx != -1; -} - -#============================================================================================================================== +# +#sub deselect_volumes { +# my ($self) = @_; +# $_->set_selected(0) for @{$self->volumes}; +#} +# +#sub select_volume { +# my ($self, $volume_idx) = @_; +# +# return if ($volume_idx >= scalar(@{$self->volumes})); +# +# $self->volumes->[$volume_idx]->set_selected(1) +# if $volume_idx != -1; +#} +# #sub SetCuttingPlane { # my ($self, $z, $expolygons) = @_; # diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index e7de0eb9bb..e5e6a73d4c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1854,7 +1854,10 @@ sub list_item_deselected { if ($self->{list}->GetFirstSelected == -1) { $self->select_object(undef); $self->{canvas}->Refresh; - $self->{canvas3D}->deselect_volumes if $self->{canvas3D}; +#============================================================================================================================== + Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}) if $self->{canvas3D}; +# $self->{canvas3D}->deselect_volumes if $self->{canvas3D}; +#============================================================================================================================== $self->{canvas3D}->Render if $self->{canvas3D}; } undef $self->{_lecursor}; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 641c800cb4..5d905c896a 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -195,7 +195,10 @@ sub update_volumes_selection { foreach my $obj_idx (0..$#{$self->{model}->objects}) { if ($self->{objects}[$obj_idx]->selected) { my $volume_idxs = $self->{objects_volumes_idxs}->[$obj_idx]; - $self->select_volume($_) for @{$volume_idxs}; +#============================================================================================================================== + Slic3r::GUI::_3DScene::select_volume($self, $_) for @{$volume_idxs}; +# $self->select_volume($_) for @{$volume_idxs}; +#============================================================================================================================== } } } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 0f4b020da7..affca10835 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1792,6 +1792,16 @@ void _3DScene::reset_volumes(wxGLCanvas* canvas) s_canvas_mgr.reset_volumes(canvas); } +void _3DScene::deselect_volumes(wxGLCanvas* canvas) +{ + s_canvas_mgr.deselect_volumes(canvas); +} + +void _3DScene::select_volume(wxGLCanvas* canvas, unsigned int id) +{ + s_canvas_mgr.select_volume(canvas, id); +} + DynamicPrintConfig* _3DScene::get_config(wxGLCanvas* canvas) { return s_canvas_mgr.get_config(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index edbb42bc2a..e09c226c72 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -556,6 +556,8 @@ public: static GLVolumeCollection* get_volumes(wxGLCanvas* canvas); static void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); static void reset_volumes(wxGLCanvas* canvas); + static void deselect_volumes(wxGLCanvas* canvas); + static void select_volume(wxGLCanvas* canvas, unsigned int id); static DynamicPrintConfig* get_config(wxGLCanvas* canvas); static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 942a7e6ef4..3e93b8a1b3 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -992,6 +992,28 @@ void GLCanvas3D::reset_volumes() } } +void GLCanvas3D::deselect_volumes() +{ + if (m_volumes != nullptr) + { + for (GLVolume* vol : m_volumes->volumes) + { + if (vol != nullptr) + vol->selected = false; + } + } +} + +void GLCanvas3D::select_volume(unsigned int id) +{ + if ((m_volumes != nullptr) && (id < (unsigned int)m_volumes->volumes.size())) + { + GLVolume* vol = m_volumes->volumes[id]; + if (vol != nullptr) + vol->selected = true; + } +} + DynamicPrintConfig* GLCanvas3D::get_config() { return m_config; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index a15e632fc2..2a0a197725 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -285,6 +285,8 @@ public: GLVolumeCollection* get_volumes(); void set_volumes(GLVolumeCollection* volumes); void reset_volumes(); + void deselect_volumes(); + void select_volume(unsigned int id); DynamicPrintConfig* get_config(); void set_config(DynamicPrintConfig* config); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index c28bfb6753..6dd7744c59 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -200,6 +200,20 @@ void GLCanvas3DManager::reset_volumes(wxGLCanvas* canvas) it->second->reset_volumes(); } +void GLCanvas3DManager::deselect_volumes(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->deselect_volumes(); +} + +void GLCanvas3DManager::select_volume(wxGLCanvas* canvas, unsigned int id) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->select_volume(id); +} + DynamicPrintConfig* GLCanvas3DManager::get_config(wxGLCanvas* canvas) { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 19baaf94fc..98baa3070c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -64,6 +64,8 @@ public: GLVolumeCollection* get_volumes(wxGLCanvas* canvas); void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); void reset_volumes(wxGLCanvas* canvas); + void deselect_volumes(wxGLCanvas* canvas); + void select_volume(wxGLCanvas* canvas, unsigned int id); DynamicPrintConfig* get_config(wxGLCanvas* canvas); void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index f87cbda440..c2314e3f05 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -251,6 +251,19 @@ reset_volumes(canvas) CODE: _3DScene::reset_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); +void +deselect_volumes(canvas) + SV *canvas; + CODE: + _3DScene::deselect_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + +void +select_volume(canvas, id) + SV *canvas; + unsigned int id; + CODE: + _3DScene::select_volume((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id); + DynamicPrintConfig* get_config(canvas) SV *canvas; From 455076231b6b45395d628cd7966c1d25768cfef1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 25 May 2018 14:05:08 +0200 Subject: [PATCH 031/117] Layers editing shader moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 167 ++++++++++-------- lib/Slic3r/GUI/Plater.pm | 26 ++- xs/lib/Slic3r/XS.pm | 1 + xs/src/slic3r/GUI/3DScene.cpp | 20 ++- xs/src/slic3r/GUI/3DScene.hpp | 5 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 223 ++++++++++++++++-------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 61 ++++--- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 40 +++-- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 13 +- xs/xsp/GUI_3DScene.xsp | 42 +++-- 10 files changed, 387 insertions(+), 211 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index e48d2cf5c7..7debb898a0 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -197,13 +197,10 @@ sub new { #============================================================================================================================== # $self->_camera_type('ortho'); ## $self->_camera_type('perspective'); -#============================================================================================================================== -#============================================================================================================================== # $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); # $self->_camera_distance(0.); +# $self->layer_editing_enabled(0); #============================================================================================================================== - - $self->layer_editing_enabled(0); $self->{layer_height_edit_band_width} = 2.; $self->{layer_height_edit_strength} = 0.005; $self->{layer_height_edit_last_object_id} = -1; @@ -307,54 +304,53 @@ sub Destroy { return $self->SUPER::Destroy; } -sub layer_editing_enabled { - my ($self, $value) = @_; - if (@_ == 2) { - $self->{layer_editing_enabled} = $value; - if ($value) { - if (! $self->{layer_editing_initialized}) { - # Enabling the layer editing for the first time. This triggers compilation of the necessary OpenGL shaders. - # If compilation fails, a message box is shown with the error codes. - $self->SetCurrent($self->GetContext); - my $shader = new Slic3r::GUI::_3DScene::GLShader; - my $error_message; #============================================================================================================================== - if (! $shader->load_from_file("variable_layer_height.fs", "variable_layer_height.vs")) { +#sub layer_editing_enabled { +# my ($self, $value) = @_; +# if (@_ == 2) { +# $self->{layer_editing_enabled} = $value; +# if ($value) { +# if (! $self->{layer_editing_initialized}) { +# # Enabling the layer editing for the first time. This triggers compilation of the necessary OpenGL shaders. +# # If compilation fails, a message box is shown with the error codes. +# $self->SetCurrent($self->GetContext); +# my $shader = new Slic3r::GUI::_3DScene::GLShader; +# my $error_message; # if (! $shader->load_from_text($self->_fragment_shader_variable_layer_height, $self->_vertex_shader_variable_layer_height)) { +# # Compilation or linking of the shaders failed. +# $error_message = "Cannot compile an OpenGL Shader, therefore the Variable Layer Editing will be disabled.\n\n" +# . $shader->last_error; +# $shader = undef; +# } else { +# $self->{layer_height_edit_shader} = $shader; +# ($self->{layer_preview_z_texture_id}) = glGenTextures_p(1); +# glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); +# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); +# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); +# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); +# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); +# glBindTexture(GL_TEXTURE_2D, 0); +# } +# if (defined($error_message)) { +# # Don't enable the layer editing tool. +# $self->{layer_editing_enabled} = 0; +# # 2 means failed +# $self->{layer_editing_initialized} = 2; +# # Show the error message. +# Wx::MessageBox($error_message, "Slic3r Error", wxOK | wxICON_EXCLAMATION, $self); +# } else { +# $self->{layer_editing_initialized} = 1; +# } +# } elsif ($self->{layer_editing_initialized} == 2) { +# # Initilization failed before. Don't try to initialize and disable layer editing. +# $self->{layer_editing_enabled} = 0; +# } +# } +# } +# return $self->{layer_editing_enabled}; +#} #============================================================================================================================== - # Compilation or linking of the shaders failed. - $error_message = "Cannot compile an OpenGL Shader, therefore the Variable Layer Editing will be disabled.\n\n" - . $shader->last_error; - $shader = undef; - } else { - $self->{layer_height_edit_shader} = $shader; - ($self->{layer_preview_z_texture_id}) = glGenTextures_p(1); - glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - glBindTexture(GL_TEXTURE_2D, 0); - } - if (defined($error_message)) { - # Don't enable the layer editing tool. - $self->{layer_editing_enabled} = 0; - # 2 means failed - $self->{layer_editing_initialized} = 2; - # Show the error message. - Wx::MessageBox($error_message, "Slic3r Error", wxOK | wxICON_EXCLAMATION, $self); - } else { - $self->{layer_editing_initialized} = 1; - } - } elsif ($self->{layer_editing_initialized} == 2) { - # Initilization failed before. Don't try to initialize and disable layer editing. - $self->{layer_editing_enabled} = 0; - } - } - } - return $self->{layer_editing_enabled}; -} sub layer_editing_allowed { my ($self) = @_; @@ -463,7 +459,10 @@ sub mouse_event { my ($self, $e) = @_; my $pos = Slic3r::Pointf->new($e->GetPositionXY); - my $object_idx_selected = $self->{layer_height_edit_last_object_id} = ($self->layer_editing_enabled && $self->{print}) ? $self->_first_selected_object_id_for_variable_layer_height_editing : -1; +#============================================================================================================================== + my $object_idx_selected = $self->{layer_height_edit_last_object_id} = (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $self->{print}) ? $self->_first_selected_object_id_for_variable_layer_height_editing : -1; +# 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; +#============================================================================================================================== #============================================================================================================================== Slic3r::GUI::_3DScene::set_mouse_dragging($self, $e->Dragging); @@ -507,7 +506,9 @@ sub mouse_event { # Don't deselect a volume if layer editing is enabled. We want the object to stay selected # during the scene manipulation. #============================================================================================================================== - if (Slic3r::GUI::_3DScene::is_picking_enabled($self) && ($volume_idx != -1 || ! $self->layer_editing_enabled)) { + +#============================================================================================================================== + if (Slic3r::GUI::_3DScene::is_picking_enabled($self) && ($volume_idx != -1 || ! Slic3r::GUI::_3DScene::is_layers_editing_enabled($self))) { Slic3r::GUI::_3DScene::deselect_volumes($self); Slic3r::GUI::_3DScene::select_volume($self, $volume_idx); # if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) { @@ -690,8 +691,10 @@ sub mouse_wheel_event { # Ignore the wheel events if the middle button is pressed. return; } - - if ($self->layer_editing_enabled && $self->{print}) { +#============================================================================================================================== + if (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $self->{print}) { +# if ($self->layer_editing_enabled && $self->{print}) { +#============================================================================================================================== my $object_idx_selected = $self->_first_selected_object_id_for_variable_layer_height_editing; if ($object_idx_selected != -1) { # A volume is selected. Test, whether hovering over a layer thickness bar. @@ -1393,11 +1396,11 @@ sub DestroyGL { # $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}; +# } #=================================================================================================================================== - if ($self->{layer_height_edit_shader}) { - $self->{layer_height_edit_shader}->release; - delete $self->{layer_height_edit_shader}; - } $self->volumes->release_geometry; } } @@ -1702,10 +1705,19 @@ sub mark_volumes_for_layer_height { 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} && +#============================================================================================================================== + my $shader = Slic3r::GUI::_3DScene::get_layers_editing_shader($self); + + if (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $shader && $volume->selected && $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}); + $volume->set_layer_height_texture_data(Slic3r::GUI::_3DScene::get_layers_editing_z_texture_id($self), $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}); + +# 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(); } @@ -1794,7 +1806,10 @@ sub draw_active_object_annotations { # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. my ($self) = @_; - return if (! $self->{layer_height_edit_shader} || ! $self->layer_editing_enabled); +#============================================================================================================================== + return if (!Slic3r::GUI::_3DScene::is_layers_editing_enabled($self)); +# return if (! $self->{layer_height_edit_shader} || ! $self->layer_editing_enabled); +#============================================================================================================================== # Find the selected volume, over which the layer editing is active. my $volume; @@ -1821,12 +1836,25 @@ sub draw_active_object_annotations { my $print_object = $self->{print}->get_object($object_idx); my $z_max = $print_object->model_object->bounding_box->z_max; - $self->{layer_height_edit_shader}->enable; - $self->{layer_height_edit_shader}->set_uniform('z_to_texture_row', $volume->layer_height_texture_z_to_row_id); - $self->{layer_height_edit_shader}->set_uniform('z_texture_row_to_normalized', 1. / $volume->layer_height_texture_height); - $self->{layer_height_edit_shader}->set_uniform('z_cursor', $z_max * $z_cursor_relative); - $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width}); - glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); +#============================================================================================================================== + my $shader = Slic3r::GUI::_3DScene::get_layers_editing_shader($self); + $shader->enable; + $shader->set_uniform('z_to_texture_row', $volume->layer_height_texture_z_to_row_id); + $shader->set_uniform('z_texture_row_to_normalized', 1. / $volume->layer_height_texture_height); + $shader->set_uniform('z_cursor', $z_max * $z_cursor_relative); + $shader->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width}); + + +# $self->{layer_height_edit_shader}->enable; +# $self->{layer_height_edit_shader}->set_uniform('z_to_texture_row', $volume->layer_height_texture_z_to_row_id); +# $self->{layer_height_edit_shader}->set_uniform('z_texture_row_to_normalized', 1. / $volume->layer_height_texture_height); +# $self->{layer_height_edit_shader}->set_uniform('z_cursor', $z_max * $z_cursor_relative); +# $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width}); +#============================================================================================================================== +#============================================================================================================================== + glBindTexture(GL_TEXTURE_2D, Slic3r::GUI::_3DScene::get_layers_editing_z_texture_id($self)); +# glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); +#============================================================================================================================== glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, @@ -1850,7 +1878,10 @@ sub draw_active_object_annotations { glVertex3f($bar_left, $bar_top, $z_max); glEnd(); glBindTexture(GL_TEXTURE_2D, 0); - $self->{layer_height_edit_shader}->disable; +#============================================================================================================================== + $shader->disable; +# $self->{layer_height_edit_shader}->disable; +#============================================================================================================================== #============================================================================================================================== Slic3r::GUI::_3DScene::render_layer_editing_textures($self, $print_object); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index e5e6a73d4c..3e78b5c95a 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -329,7 +329,10 @@ sub new { EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog }); EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog }); EVT_TOOL($self, TB_LAYER_EDITING, sub { - my $state = $self->{canvas3D}->layer_editing_enabled; +#============================================================================================================================== + my $state = Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D}); +# my $state = $self->{canvas3D}->layer_editing_enabled; +#============================================================================================================================== $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state); $self->on_layer_editing_toggled(! $state); }); @@ -608,8 +611,12 @@ sub _on_select_preset { sub on_layer_editing_toggled { my ($self, $new_state) = @_; - $self->{canvas3D}->layer_editing_enabled($new_state); - if ($new_state && ! $self->{canvas3D}->layer_editing_enabled) { +#============================================================================================================================== + Slic3r::GUI::_3DScene::enable_layers_editing($self->{canvas3D}, $new_state); + if ($new_state && ! Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D})) { +# $self->{canvas3D}->layer_editing_enabled($new_state); +# if ($new_state && ! $self->{canvas3D}->layer_editing_enabled) { +#============================================================================================================================== # Initialization of the OpenGL shaders failed. Disable the tool. if ($self->{htoolbar}) { $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0); @@ -1237,8 +1244,12 @@ sub async_apply_config { my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config); # Just redraw the 3D canvas without reloading the scene. -# $self->{canvas3D}->Refresh if ($invalidated && $self->{canvas3D}->layer_editing_enabled); - $self->{canvas3D}->Refresh if ($self->{canvas3D}->layer_editing_enabled); +#============================================================================================================================== +# $self->{canvas3D}->Refresh if ($invalidated && Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D})); + $self->{canvas3D}->Refresh if Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D}); +## $self->{canvas3D}->Refresh if ($invalidated && $self->{canvas3D}->layer_editing_enabled); +# $self->{canvas3D}->Refresh if ($self->{canvas3D}->layer_editing_enabled); +#============================================================================================================================== # Hide the slicing results if the current slicing status is no more valid. $self->{"print_info_box_show"}->(0) if $invalidated; @@ -1819,7 +1830,10 @@ sub on_config_change { $self->{"btn_layer_editing"}->Disable; $self->{"btn_layer_editing"}->SetValue(0); } - $self->{canvas3D}->layer_editing_enabled(0); +#============================================================================================================================== + Slic3r::GUI::_3DScene::enable_layers_editing($self->{canvas3D}, 0); +# $self->{canvas3D}->layer_editing_enabled(0); +#============================================================================================================================== $self->{canvas3D}->Refresh; $self->{canvas3D}->Update; } elsif ($self->{canvas3D}->layer_editing_allowed) { diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index bd0b698eed..a4847fb45e 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -282,6 +282,7 @@ for my $class (qw( Slic3r::Geometry::BoundingBox Slic3r::Geometry::BoundingBoxf Slic3r::Geometry::BoundingBoxf3 + Slic3r::GUI::_3DScene::GLShader Slic3r::GUI::_3DScene::GLVolume Slic3r::GUI::Preset Slic3r::GUI::PresetCollection diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index affca10835..2969f3e8ad 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1939,16 +1939,16 @@ bool _3DScene::is_picking_enabled(wxGLCanvas* canvas) return s_canvas_mgr.is_picking_enabled(canvas); } -bool _3DScene::is_shader_enabled(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_shader_enabled(canvas); -} - bool _3DScene::is_multisample_allowed(wxGLCanvas* canvas) { return s_canvas_mgr.is_multisample_allowed(canvas); } +void _3DScene::enable_layers_editing(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_layers_editing(canvas, enable); +} + void _3DScene::enable_warning_texture(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_warning_texture(canvas, enable); @@ -2005,6 +2005,16 @@ void _3DScene::set_hover_volume_id(wxGLCanvas* canvas, int id) s_canvas_mgr.set_hover_volume_id(canvas, id); } +unsigned int _3DScene::get_layers_editing_z_texture_id(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_layers_editing_z_texture_id(canvas); +} + +GLShader* _3DScene::get_layers_editing_shader(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_layers_editing_shader(canvas); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index e09c226c72..ab7f1ba7d8 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -598,9 +598,9 @@ public: static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_picking_enabled(wxGLCanvas* canvas); - static bool is_shader_enabled(wxGLCanvas* canvas); static bool is_multisample_allowed(wxGLCanvas* canvas); + static void enable_layers_editing(wxGLCanvas* canvas, bool enable); static void enable_warning_texture(wxGLCanvas* canvas, bool enable); static void enable_legend_texture(wxGLCanvas* canvas, bool enable); static void enable_picking(wxGLCanvas* canvas, bool enable); @@ -616,6 +616,9 @@ public: static int get_hover_volume_id(wxGLCanvas* canvas); static void set_hover_volume_id(wxGLCanvas* canvas, int id); + static unsigned int get_layers_editing_z_texture_id(wxGLCanvas* canvas); + static GLShader* get_layers_editing_shader(wxGLCanvas* canvas); + static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 3e93b8a1b3..31680148e9 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -501,6 +501,73 @@ void GLCanvas3D::CuttingPlane::_render_contour() const ::glDisableClientState(GL_VERTEX_ARRAY); } +GLCanvas3D::Shader::Shader() + : m_shader(nullptr) +{ +} + +GLCanvas3D::Shader::~Shader() +{ + _reset(); +} + +bool GLCanvas3D::Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) +{ + if (is_initialized()) + return true; + + m_shader = new GLShader(); + if (m_shader != nullptr) + { + if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str())) + { + std::cout << "Compilaton of shader failed:" << std::endl; + std::cout << m_shader->last_error << std::endl; + _reset(); + return false; + } + } + + return true; +} + +bool GLCanvas3D::Shader::is_initialized() const +{ + return (m_shader != nullptr); +} + +bool GLCanvas3D::Shader::start_using() const +{ + if (is_initialized()) + { + m_shader->enable(); + return true; + } + else + return false; +} + +void GLCanvas3D::Shader::stop_using() const +{ + if (m_shader != nullptr) + m_shader->disable(); +} + +GLShader* GLCanvas3D::Shader::get_shader() +{ + return m_shader; +} + +void GLCanvas3D::Shader::_reset() +{ + if (m_shader != nullptr) + { + m_shader->release(); + delete m_shader; + m_shader = nullptr; + } +} + GLCanvas3D::LayersEditing::GLTextureData::GLTextureData() : id(0) , width(0) @@ -516,7 +583,9 @@ GLCanvas3D::LayersEditing::GLTextureData::GLTextureData(unsigned int id, int wid } GLCanvas3D::LayersEditing::LayersEditing() - : m_enabled(false) + : m_allowed(false) + , m_enabled(false) + , m_z_texture_id(0) { } @@ -533,6 +602,39 @@ GLCanvas3D::LayersEditing::~LayersEditing() ::glDeleteTextures(1, &m_reset_texture.id); m_reset_texture = GLTextureData(); } + + if (m_z_texture_id != 0) + { + ::glDeleteTextures(1, &m_z_texture_id); + m_z_texture_id = 0; + } +} + +bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) +{ + if (!m_shader.init(vertex_shader_filename, fragment_shader_filename)) + return false; + + ::glGenTextures(1, (GLuint*)&m_z_texture_id); + ::glBindTexture(GL_TEXTURE_2D, m_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); + + return true; +} + +bool GLCanvas3D::LayersEditing::is_allowed() const +{ + return m_allowed; +} + +void GLCanvas3D::LayersEditing::set_allowed(bool allowed) +{ + m_allowed = allowed; } bool GLCanvas3D::LayersEditing::is_enabled() const @@ -540,6 +642,16 @@ bool GLCanvas3D::LayersEditing::is_enabled() const return m_enabled; } +void GLCanvas3D::LayersEditing::set_enabled(bool enabled) +{ + m_enabled = m_allowed && m_shader.is_initialized() && enabled; +} + +unsigned int GLCanvas3D::LayersEditing::get_z_texture_id() const +{ + return m_z_texture_id; +} + void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObject& print_object) const { const Rect& bar_rect = _get_bar_rect_viewport(canvas); @@ -549,6 +661,16 @@ void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObje _render_profile(print_object, bar_rect); } +GLShader* GLCanvas3D::LayersEditing::get_shader() +{ + return m_shader.get_shader(); +} + +bool GLCanvas3D::LayersEditing::_is_initialized() const +{ + return m_shader.is_initialized(); +} + GLCanvas3D::LayersEditing::GLTextureData GLCanvas3D::LayersEditing::_load_texture_from_file(const std::string& filename) const { const std::string& path = resources_dir() + "/icons/"; @@ -729,65 +851,6 @@ Rect GLCanvas3D::LayersEditing::_get_reset_rect_viewport(const GLCanvas3D& canva return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); } -GLCanvas3D::Shader::Shader() - : m_enabled(false) - , m_shader(nullptr) -{ -} - -bool GLCanvas3D::Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) -{ - m_shader = new GLShader(); - if (m_shader != nullptr) - { - if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str())) - { - std::cout << "Compilaton of path shader failed:" << std::endl; - std::cout << m_shader->last_error << std::endl; - reset(); - return false; - } - } - - return true; -} - -void GLCanvas3D::Shader::reset() -{ - if (m_shader != nullptr) - { - delete m_shader; - m_shader = nullptr; - } -} - -bool GLCanvas3D::Shader::is_enabled() const -{ - return m_enabled; -} - -void GLCanvas3D::Shader::set_enabled(bool enabled) -{ - m_enabled = enabled; -} - -bool GLCanvas3D::Shader::start() const -{ - if (m_enabled && (m_shader != nullptr)) - { - m_shader->enable(); - return true; - } - else - return false; -} - -void GLCanvas3D::Shader::stop() const -{ - if (m_shader != nullptr) - m_shader->disable(); -} - GLCanvas3D::Mouse::Mouse() : m_dragging(false) { @@ -824,6 +887,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_warning_texture_enabled(false) , m_legend_texture_enabled(false) , m_picking_enabled(false) + , m_shader_enabled(false) , m_multisample_allowed(false) { } @@ -831,10 +895,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) GLCanvas3D::~GLCanvas3D() { _deregister_callbacks(); - m_shader.reset(); } -bool GLCanvas3D::init(bool useVBOs) +bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) { ::glClearColor(0.0f, 0.0f, 0.0f, 1.0f); ::glEnable(GL_DEPTH_TEST); @@ -876,6 +939,11 @@ bool GLCanvas3D::init(bool useVBOs) if (useVBOs && !m_shader.init("gouraud.vs", "gouraud.fs")) return false; + if (useVBOs && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) + return false; + + m_layers_editing.set_allowed(!use_legacy_opengl); + return true; } @@ -1179,16 +1247,16 @@ bool GLCanvas3D::is_picking_enabled() const return m_picking_enabled; } -bool GLCanvas3D::is_shader_enabled() const -{ - return m_shader.is_enabled(); -} - bool GLCanvas3D::is_multisample_allowed() const { return m_multisample_allowed; } +void GLCanvas3D::enable_layers_editing(bool enable) +{ + m_layers_editing.set_enabled(enable); +} + void GLCanvas3D::enable_warning_texture(bool enable) { m_warning_texture_enabled = enable; @@ -1206,9 +1274,8 @@ void GLCanvas3D::enable_picking(bool enable) void GLCanvas3D::enable_shader(bool enable) { - m_shader.set_enabled(enable); + m_shader_enabled = enable; } - void GLCanvas3D::allow_multisample(bool allow) { m_multisample_allowed = allow; @@ -1289,12 +1356,12 @@ void GLCanvas3D::select_view(const std::string& direction) bool GLCanvas3D::start_using_shader() const { - return m_shader.start(); + return m_shader.start_using(); } void GLCanvas3D::stop_using_shader() const { - m_shader.stop(); + m_shader.stop_using(); } void GLCanvas3D::picking_pass() @@ -1384,6 +1451,16 @@ void GLCanvas3D::render_background() const ::glPopMatrix(); } +unsigned int GLCanvas3D::get_layers_editing_z_texture_id() const +{ + return m_layers_editing.get_z_texture_id(); +} + +GLShader* GLCanvas3D::get_layers_editing_shader() +{ + return m_layers_editing.get_shader(); +} + void GLCanvas3D::render_bed() const { m_bed.render(); @@ -1445,12 +1522,12 @@ void GLCanvas3D::render_volumes(bool fake_colors) const void GLCanvas3D::render_objects(bool useVBOs) { - if (m_volumes == nullptr) + if ((m_volumes == nullptr) || m_volumes->empty()) return; ::glEnable(GL_LIGHTING); - if (!is_shader_enabled()) + if (!m_shader_enabled) render_volumes(false); else if (useVBOs) { diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 2a0a197725..a11780349c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -174,6 +174,27 @@ public: void _render_contour() const; }; + class Shader + { + GLShader* m_shader; + + public: + Shader(); + ~Shader(); + + bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); + + bool is_initialized() const; + + bool start_using() const; + void stop_using() const; + + GLShader* get_shader(); + + private: + void _reset(); + }; + class LayersEditing { struct GLTextureData @@ -186,7 +207,10 @@ public: GLTextureData(unsigned int id, int width, int height); }; + bool m_allowed; bool m_enabled; + Shader m_shader; + unsigned int m_z_texture_id; mutable GLTextureData m_tooltip_texture; mutable GLTextureData m_reset_texture; @@ -194,11 +218,22 @@ public: LayersEditing(); ~LayersEditing(); + bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); + + bool is_allowed() const; + void set_allowed(bool allowed); + bool is_enabled() const; + void set_enabled(bool enabled); + + unsigned int get_z_texture_id() const; void render(const GLCanvas3D& canvas, const PrintObject& print_object) const; + GLShader* get_shader(); + private: + bool _is_initialized() const; GLTextureData _load_texture_from_file(const std::string& filename) const; void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; @@ -209,24 +244,6 @@ public: Rect _get_reset_rect_viewport(const GLCanvas3D& canvas) const; }; - class Shader - { - bool m_enabled; - GLShader* m_shader; - - public: - Shader(); - - bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); - void reset(); - - bool is_enabled() const; - void set_enabled(bool enabled); - - bool start() const; - void stop() const; - }; - class Mouse { bool m_dragging; @@ -262,6 +279,7 @@ private: bool m_warning_texture_enabled; bool m_legend_texture_enabled; bool m_picking_enabled; + bool m_shader_enabled; bool m_multisample_allowed; PerlCallback m_on_viewport_changed_callback; @@ -271,7 +289,7 @@ public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); ~GLCanvas3D(); - bool init(bool useVBOs); + bool init(bool useVBOs, bool use_legacy_opengl); bool set_current(); @@ -332,9 +350,9 @@ public: bool is_layers_editing_enabled() const; bool is_picking_enabled() const; - bool is_shader_enabled() const; bool is_multisample_allowed() const; + void enable_layers_editing(bool enable); void enable_warning_texture(bool enable); void enable_legend_texture(bool enable); void enable_picking(bool enable); @@ -350,6 +368,9 @@ public: int get_hover_volume_id() const; void set_hover_volume_id(int id); + unsigned int get_layers_editing_z_texture_id() const; + GLShader* get_layers_editing_shader(); + void zoom_to_bed(); void zoom_to_volumes(); void select_view(const std::string& direction); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 6dd7744c59..66a66307ee 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -56,11 +56,6 @@ bool GLCanvas3DManager::GLVersion::is_greater_or_equal_to(unsigned int major, un return vn_minor >= minor; } -GLCanvas3DManager::LayerEditing::LayerEditing() - : allowed(false) -{ -} - GLCanvas3DManager::GLCanvas3DManager() : m_gl_initialized(false) , m_use_legacy_opengl(false) @@ -129,12 +124,11 @@ void GLCanvas3DManager::init_gl() const AppConfig* config = GUI::get_app_config(); m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1"); m_use_VBOs = !m_use_legacy_opengl && m_gl_version.is_greater_or_equal_to(2, 0); - m_layer_editing.allowed = !m_use_legacy_opengl; m_gl_initialized = true; std::cout << "DETECTED OPENGL: " << m_gl_version.vn_major << "." << m_gl_version.vn_minor << std::endl; std::cout << "USE VBOS = " << (m_use_VBOs ? "YES" : "NO") << std::endl; - std::cout << "LAYER EDITING ALLOWED = " << (m_layer_editing.allowed ? "YES" : "NO") << std::endl; + std::cout << "LAYER EDITING ALLOWED = " << (!m_use_legacy_opengl ? "YES" : "NO") << std::endl; } } @@ -143,15 +137,10 @@ bool GLCanvas3DManager::use_VBOs() const return m_use_VBOs; } -bool GLCanvas3DManager::layer_editing_allowed() const -{ - return m_layer_editing.allowed; -} - bool GLCanvas3DManager::init(wxGLCanvas* canvas, bool useVBOs) { CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->init(useVBOs) : false; + return (it != m_canvases.end()) ? it->second->init(useVBOs, m_use_legacy_opengl) : false; } bool GLCanvas3DManager::is_dirty(wxGLCanvas* canvas) const @@ -391,18 +380,19 @@ bool GLCanvas3DManager::is_picking_enabled(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_picking_enabled() : false; } -bool GLCanvas3DManager::is_shader_enabled(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_shader_enabled() : false; -} - bool GLCanvas3DManager::is_multisample_allowed(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); return (it != m_canvases.end()) ? it->second->is_multisample_allowed() : false; } +void GLCanvas3DManager::enable_layers_editing(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_layers_editing(enable); +} + void GLCanvas3DManager::enable_warning_texture(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -477,6 +467,18 @@ void GLCanvas3DManager::set_hover_volume_id(wxGLCanvas* canvas, int id) it->second->set_hover_volume_id(id); } +unsigned int GLCanvas3DManager::get_layers_editing_z_texture_id(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_layers_editing_z_texture_id() : 0; +} + +GLShader* GLCanvas3DManager::get_layers_editing_shader(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_layers_editing_shader() : nullptr; +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 98baa3070c..3b3e35c36e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -21,18 +21,10 @@ class GLCanvas3DManager bool is_greater_or_equal_to(unsigned int major, unsigned int minor) const; }; - struct LayerEditing - { - bool allowed; - - LayerEditing(); - }; - typedef std::map CanvasesMap; CanvasesMap m_canvases; GLVersion m_gl_version; - LayerEditing m_layer_editing; bool m_gl_initialized; bool m_use_legacy_opengl; bool m_use_VBOs; @@ -106,9 +98,9 @@ public: bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_picking_enabled(wxGLCanvas* canvas) const; - bool is_shader_enabled(wxGLCanvas* canvas) const; bool is_multisample_allowed(wxGLCanvas* canvas) const; + void enable_layers_editing(wxGLCanvas* canvas, bool enable); void enable_warning_texture(wxGLCanvas* canvas, bool enable); void enable_legend_texture(wxGLCanvas* canvas, bool enable); void enable_picking(wxGLCanvas* canvas, bool enable); @@ -124,6 +116,9 @@ public: int get_hover_volume_id(wxGLCanvas* canvas) const; void set_hover_volume_id(wxGLCanvas* canvas, int id); + unsigned int get_layers_editing_z_texture_id(wxGLCanvas* canvas) const; + GLShader* get_layers_editing_shader(wxGLCanvas* canvas); + void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index c2314e3f05..0f47454dab 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -451,7 +451,15 @@ set_camera_target(canvas, target) Pointf3 *target; CODE: _3DScene::set_camera_target((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), target); - + +bool +is_layers_editing_enabled(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_layers_editing_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + bool is_picking_enabled(canvas) SV *canvas; @@ -460,14 +468,6 @@ is_picking_enabled(canvas) OUTPUT: RETVAL -bool -is_shader_enabled(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::is_shader_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - bool is_multisample_allowed(canvas) SV *canvas; @@ -476,6 +476,13 @@ is_multisample_allowed(canvas) OUTPUT: RETVAL +void +enable_layers_editing(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_layers_editing((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void enable_warning_texture(canvas, enable) SV *canvas; @@ -556,6 +563,22 @@ set_hover_volume_id(canvas, id) CODE: _3DScene::set_hover_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id); +unsigned int +get_layers_editing_z_texture_id(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_layers_editing_z_texture_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +Ref +get_layers_editing_shader(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_layers_editing_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + void zoom_to_bed(canvas) SV *canvas; @@ -679,7 +702,6 @@ register_on_mark_volumes_for_layer_height_callback(canvas, callback) - unsigned int finalize_legend_texture() CODE: From c51ce63b9beb02d4a5f426b15f2ed0902795bb36 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 25 May 2018 15:56:14 +0200 Subject: [PATCH 032/117] 3DScene layer editing overlay completely moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 234 +++++++++++------------- xs/src/libslic3r/Model.cpp | 5 +- xs/src/libslic3r/Model.hpp | 11 +- xs/src/slic3r/GUI/3DScene.cpp | 16 +- xs/src/slic3r/GUI/3DScene.hpp | 6 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 140 +++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 19 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 17 +- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 6 +- xs/xsp/GUI_3DScene.xsp | 23 ++- 10 files changed, 331 insertions(+), 146 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 7debb898a0..b27b7d55fa 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -200,8 +200,8 @@ sub new { # $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); # $self->_camera_distance(0.); # $self->layer_editing_enabled(0); +# $self->{layer_height_edit_band_width} = 2.; #============================================================================================================================== - $self->{layer_height_edit_band_width} = 2.; $self->{layer_height_edit_strength} = 0.005; $self->{layer_height_edit_last_object_id} = -1; $self->{layer_height_edit_last_z} = 0.; @@ -446,7 +446,10 @@ sub _variable_layer_thickness_action { $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}, +#============================================================================================================================== + Slic3r::GUI::_3DScene::get_layers_editing_band_width($self), +# $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); @@ -700,7 +703,10 @@ sub mouse_wheel_event { # 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); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_layers_editing_band_width($self, max(min(Slic3r::GUI::_3DScene::get_layers_editing_band_width($self) * (1 + 0.1 * $e->GetWheelRotation() / $e->GetWheelDelta()), 10.), 1.5)); +# $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; } @@ -1471,6 +1477,7 @@ sub Render { Slic3r::GUI::_3DScene::render_cutting_plane($self); Slic3r::GUI::_3DScene::render_warning_texture($self); Slic3r::GUI::_3DScene::render_legend_texture($self); + Slic3r::GUI::_3DScene::render_layer_editing_overlay($self, $self->{print}); # if ($self->enable_picking && !$self->_mouse_dragging) { # if (my $pos = $self->_mouse_pos) { @@ -1651,10 +1658,10 @@ sub Render { # # # draw gcode preview legend # $self->draw_legend; +# +# $self->draw_active_object_annotations; #============================================================================================================================== - $self->draw_active_object_annotations; - $self->SwapBuffers(); } @@ -1711,7 +1718,7 @@ sub mark_volumes_for_layer_height { if (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $shader && $volume->selected && $volume->has_layer_height_texture && $object_id < $self->{print}->object_count) { $volume->set_layer_height_texture_data(Slic3r::GUI::_3DScene::get_layers_editing_z_texture_id($self), $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}); + $self->{print}->get_object($object_id), $self->_variable_layer_thickness_bar_mouse_cursor_z_relative, Slic3r::GUI::_3DScene::get_layers_editing_band_width($self)); # if ($self->layer_editing_enabled && $volume->selected && $self->{layer_height_edit_shader} && # $volume->has_layer_height_texture && $object_id < $self->{print}->object_count) { @@ -1724,42 +1731,42 @@ sub mark_volumes_for_layer_height { } } -sub _load_image_set_texture { - my ($self, $file_name) = @_; - # Load a PNG with an alpha channel. - my $img = Wx::Image->new; - $img->LoadFile(Slic3r::var($file_name), wxBITMAP_TYPE_PNG); - # Get RGB & alpha raw data from wxImage, interleave them into a Perl array. - my @rgb = unpack 'C*', $img->GetData(); - my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3); - my $n_pixels = int(@alpha); - my @data = (0)x($n_pixels * 4); - for (my $i = 0; $i < $n_pixels; $i += 1) { - $data[$i*4 ] = $rgb[$i*3]; - $data[$i*4+1] = $rgb[$i*3+1]; - $data[$i*4+2] = $rgb[$i*3+2]; - $data[$i*4+3] = $alpha[$i]; - } - # Initialize a raw bitmap data. - my $params = { - loaded => 1, - valid => $n_pixels > 0, - width => $img->GetWidth, - height => $img->GetHeight, - data => OpenGL::Array->new_list(GL_UNSIGNED_BYTE, @data), - texture_id => glGenTextures_p(1) - }; - # Create and initialize a texture with the raw data. - glBindTexture(GL_TEXTURE_2D, $params->{texture_id}); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $params->{width}, $params->{height}, 0, GL_RGBA, GL_UNSIGNED_BYTE, $params->{data}->ptr); - glBindTexture(GL_TEXTURE_2D, 0); - return $params; -} - #============================================================================================================================== +#sub _load_image_set_texture { +# my ($self, $file_name) = @_; +# # Load a PNG with an alpha channel. +# my $img = Wx::Image->new; +# $img->LoadFile(Slic3r::var($file_name), wxBITMAP_TYPE_PNG); +# # Get RGB & alpha raw data from wxImage, interleave them into a Perl array. +# my @rgb = unpack 'C*', $img->GetData(); +# my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3); +# my $n_pixels = int(@alpha); +# my @data = (0)x($n_pixels * 4); +# for (my $i = 0; $i < $n_pixels; $i += 1) { +# $data[$i*4 ] = $rgb[$i*3]; +# $data[$i*4+1] = $rgb[$i*3+1]; +# $data[$i*4+2] = $rgb[$i*3+2]; +# $data[$i*4+3] = $alpha[$i]; +# } +# # Initialize a raw bitmap data. +# my $params = { +# loaded => 1, +# valid => $n_pixels > 0, +# width => $img->GetWidth, +# height => $img->GetHeight, +# data => OpenGL::Array->new_list(GL_UNSIGNED_BYTE, @data), +# texture_id => glGenTextures_p(1) +# }; +# # Create and initialize a texture with the raw data. +# glBindTexture(GL_TEXTURE_2D, $params->{texture_id}); +# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); +# glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $params->{width}, $params->{height}, 0, GL_RGBA, GL_UNSIGNED_BYTE, $params->{data}->ptr); +# glBindTexture(GL_TEXTURE_2D, 0); +# return $params; +#} +# #sub _variable_layer_thickness_load_overlay_image { # my ($self) = @_; # $self->{layer_preview_annotation} = $self->_load_image_set_texture('variable_layer_height_tooltip.png') @@ -1800,92 +1807,69 @@ sub _load_image_set_texture { # glDisable(GL_BLEND); # glEnable(GL_LIGHTING); #} -#============================================================================================================================== - -sub draw_active_object_annotations { - # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. - my ($self) = @_; - -#============================================================================================================================== - return if (!Slic3r::GUI::_3DScene::is_layers_editing_enabled($self)); +# +#sub draw_active_object_annotations { +# # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. +# my ($self) = @_; +# # return if (! $self->{layer_height_edit_shader} || ! $self->layer_editing_enabled); -#============================================================================================================================== - - # Find the selected volume, over which the layer editing is active. - my $volume; - foreach my $volume_idx (0..$#{$self->volumes}) { - my $v = $self->volumes->[$volume_idx]; - if ($v->selected && $v->has_layer_height_texture) { - $volume = $v; - last; - } - } - return if (! $volume); - - # If the active object was not allocated at the Print, go away. This should only be a momentary case between an object addition / deletion - # and an update by Platter::async_apply_config. - my $object_idx = int($volume->select_group_id / 1000000); - return if $object_idx >= $self->{print}->object_count; - - # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth), - # where x, y is the window size divided by $self->_zoom. - my ($bar_left, $bar_bottom, $bar_right, $bar_top) = $self->_variable_layer_thickness_bar_rect_viewport; - my ($reset_left, $reset_bottom, $reset_right, $reset_top) = $self->_variable_layer_thickness_reset_rect_viewport; - my $z_cursor_relative = $self->_variable_layer_thickness_bar_mouse_cursor_z_relative; - - my $print_object = $self->{print}->get_object($object_idx); - my $z_max = $print_object->model_object->bounding_box->z_max; - -#============================================================================================================================== - my $shader = Slic3r::GUI::_3DScene::get_layers_editing_shader($self); - $shader->enable; - $shader->set_uniform('z_to_texture_row', $volume->layer_height_texture_z_to_row_id); - $shader->set_uniform('z_texture_row_to_normalized', 1. / $volume->layer_height_texture_height); - $shader->set_uniform('z_cursor', $z_max * $z_cursor_relative); - $shader->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width}); - - +# +# # Find the selected volume, over which the layer editing is active. +# my $volume; +# foreach my $volume_idx (0..$#{$self->volumes}) { +# my $v = $self->volumes->[$volume_idx]; +# if ($v->selected && $v->has_layer_height_texture) { +# $volume = $v; +# last; +# } +# } +# return if (! $volume); +# +# # If the active object was not allocated at the Print, go away. This should only be a momentary case between an object addition / deletion +# # and an update by Platter::async_apply_config. +# my $object_idx = int($volume->select_group_id / 1000000); +# return if $object_idx >= $self->{print}->object_count; +# +# # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth), +# # where x, y is the window size divided by $self->_zoom. +# my ($bar_left, $bar_bottom, $bar_right, $bar_top) = $self->_variable_layer_thickness_bar_rect_viewport; +# my ($reset_left, $reset_bottom, $reset_right, $reset_top) = $self->_variable_layer_thickness_reset_rect_viewport; +# my $z_cursor_relative = $self->_variable_layer_thickness_bar_mouse_cursor_z_relative; +# +# my $print_object = $self->{print}->get_object($object_idx); +# my $z_max = $print_object->model_object->bounding_box->z_max; +# # $self->{layer_height_edit_shader}->enable; # $self->{layer_height_edit_shader}->set_uniform('z_to_texture_row', $volume->layer_height_texture_z_to_row_id); # $self->{layer_height_edit_shader}->set_uniform('z_texture_row_to_normalized', 1. / $volume->layer_height_texture_height); # $self->{layer_height_edit_shader}->set_uniform('z_cursor', $z_max * $z_cursor_relative); # $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width}); -#============================================================================================================================== -#============================================================================================================================== - glBindTexture(GL_TEXTURE_2D, Slic3r::GUI::_3DScene::get_layers_editing_z_texture_id($self)); # glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); -#============================================================================================================================== - glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, - 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $volume->layer_height_texture_width, $volume->layer_height_texture_height, - GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level0); - glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, - GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level1); - - # Render the color bar. - glDisable(GL_DEPTH_TEST); - # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth), - # where x, y is the window size divided by $self->_zoom. - glPushMatrix(); - glLoadIdentity(); - # Paint the overlay. - glBegin(GL_QUADS); - glVertex3f($bar_left, $bar_bottom, 0); - glVertex3f($bar_right, $bar_bottom, 0); - glVertex3f($bar_right, $bar_top, $z_max); - glVertex3f($bar_left, $bar_top, $z_max); - glEnd(); - glBindTexture(GL_TEXTURE_2D, 0); -#============================================================================================================================== - $shader->disable; +# glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, +# 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); +# glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, +# 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); +# glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $volume->layer_height_texture_width, $volume->layer_height_texture_height, +# GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level0); +# glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, +# GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level1); +# +# # Render the color bar. +# glDisable(GL_DEPTH_TEST); +# # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth), +# # where x, y is the window size divided by $self->_zoom. +# glPushMatrix(); +# glLoadIdentity(); +# # Paint the overlay. +# glBegin(GL_QUADS); +# glVertex3f($bar_left, $bar_bottom, 0); +# glVertex3f($bar_right, $bar_bottom, 0); +# glVertex3f($bar_right, $bar_top, $z_max); +# glVertex3f($bar_left, $bar_top, $z_max); +# glEnd(); +# glBindTexture(GL_TEXTURE_2D, 0); # $self->{layer_height_edit_shader}->disable; -#============================================================================================================================== - -#============================================================================================================================== - Slic3r::GUI::_3DScene::render_layer_editing_textures($self, $print_object); - +# # # Paint the tooltip. # if ($self->_variable_layer_thickness_load_overlay_image) # my $gap = 10/$self->_zoom; @@ -1933,13 +1917,11 @@ sub draw_active_object_annotations { # glVertex3f($bar_left + $h * ($bar_right - $bar_left) / $layer_height_max, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z); # } # glEnd(); -#============================================================================================================================== - # Revert the matrices. - glPopMatrix(); - glEnable(GL_DEPTH_TEST); -} - -#============================================================================================================================== +# # Revert the matrices. +# glPopMatrix(); +# glEnable(GL_DEPTH_TEST); +#} +# #sub draw_legend { # my ($self) = @_; # diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 8ce23b1e54..147353abd6 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -603,7 +603,10 @@ void ModelObject::clear_instances() // Returns the bounding box of the transformed instances. // This bounding box is approximate and not snug. -const BoundingBoxf3& ModelObject::bounding_box() +//======================================================================================================== +const BoundingBoxf3& ModelObject::bounding_box() const +//const BoundingBoxf3& ModelObject::bounding_box() +//======================================================================================================== { if (! m_bounding_box_valid) { BoundingBoxf3 raw_bbox; diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 8b63c3641f..b148ec29d5 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -103,7 +103,10 @@ public: // Returns the bounding box of the transformed instances. // This bounding box is approximate and not snug. // This bounding box is being cached. - const BoundingBoxf3& bounding_box(); +//======================================================================================================== + const BoundingBoxf3& bounding_box() const; +// const BoundingBoxf3& bounding_box(); +//======================================================================================================== void invalidate_bounding_box() { m_bounding_box_valid = false; } // Returns a snug bounding box of the transformed instances. // This bounding box is not being cached. @@ -145,8 +148,10 @@ private: // Parent object, owning this ModelObject. Model *m_model; // Bounding box, cached. - BoundingBoxf3 m_bounding_box; - bool m_bounding_box_valid; +//======================================================================================================== + mutable BoundingBoxf3 m_bounding_box; + mutable bool m_bounding_box_valid; +//======================================================================================================== }; // An object STL, or a modifier volume, over which a different set of parameters shall be applied. diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 2969f3e8ad..87071ce208 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2010,6 +2010,16 @@ unsigned int _3DScene::get_layers_editing_z_texture_id(wxGLCanvas* canvas) return s_canvas_mgr.get_layers_editing_z_texture_id(canvas); } +float _3DScene::get_layers_editing_band_width(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_layers_editing_band_width(canvas); +} + +void _3DScene::set_layers_editing_band_width(wxGLCanvas* canvas, float band_width) +{ + s_canvas_mgr.set_layers_editing_band_width(canvas, band_width); +} + GLShader* _3DScene::get_layers_editing_shader(wxGLCanvas* canvas) { return s_canvas_mgr.get_layers_editing_shader(canvas); @@ -2085,10 +2095,10 @@ void _3DScene::render_legend_texture(wxGLCanvas* canvas) s_canvas_mgr.render_legend_texture(canvas); } -void _3DScene::render_layer_editing_textures(wxGLCanvas* canvas, const PrintObject* print_object) +void _3DScene::render_layer_editing_overlay(wxGLCanvas* canvas, const Print* print) { - if (print_object != nullptr) - s_canvas_mgr.render_layer_editing_textures(canvas, *print_object); + if (print != nullptr) + s_canvas_mgr.render_layer_editing_overlay(canvas, *print); } void _3DScene::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index ab7f1ba7d8..153744987f 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -617,6 +617,10 @@ public: static void set_hover_volume_id(wxGLCanvas* canvas, int id); static unsigned int get_layers_editing_z_texture_id(wxGLCanvas* canvas); + + static float get_layers_editing_band_width(wxGLCanvas* canvas); + static void set_layers_editing_band_width(wxGLCanvas* canvas, float band_width); + static GLShader* get_layers_editing_shader(wxGLCanvas* canvas); static void zoom_to_bed(wxGLCanvas* canvas); @@ -636,7 +640,7 @@ public: static void render_cutting_plane(wxGLCanvas* canvas); static void render_warning_texture(wxGLCanvas* canvas); static void render_legend_texture(wxGLCanvas* canvas); - static void render_layer_editing_textures(wxGLCanvas* canvas, const PrintObject* print_object); + static void render_layer_editing_overlay(wxGLCanvas* canvas, const Print* print); static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 31680148e9..823a4df0ca 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -553,6 +553,12 @@ void GLCanvas3D::Shader::stop_using() const m_shader->disable(); } +void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const +{ + if (m_shader != nullptr) + m_shader->set_uniform(name.c_str(), value); +} + GLShader* GLCanvas3D::Shader::get_shader() { return m_shader; @@ -586,6 +592,7 @@ GLCanvas3D::LayersEditing::LayersEditing() : m_allowed(false) , m_enabled(false) , m_z_texture_id(0) + , m_band_width(2.0f) { } @@ -652,13 +659,40 @@ unsigned int GLCanvas3D::LayersEditing::get_z_texture_id() const return m_z_texture_id; } -void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObject& print_object) const +float GLCanvas3D::LayersEditing::get_band_width() const { + return m_band_width; +} + +void GLCanvas3D::LayersEditing::set_band_width(float band_width) +{ + m_band_width = band_width; +} + +void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const +{ + if (!m_enabled) + return; + const Rect& bar_rect = _get_bar_rect_viewport(canvas); const Rect& reset_rect = _get_reset_rect_viewport(canvas); + + ::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(); + _render_tooltip_texture(canvas, bar_rect, reset_rect); _render_reset_texture(canvas, reset_rect); + _render_active_object_annotations(canvas, volume, print_object, bar_rect); _render_profile(print_object, bar_rect); + + // Revert the matrices. + glPopMatrix(); + + glEnable(GL_DEPTH_TEST); } GLShader* GLCanvas3D::LayersEditing::get_shader() @@ -754,6 +788,45 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, canvas.render_texture(m_reset_texture.id, reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); } +void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const +{ + float max_z = print_object.model_object()->bounding_box().max.z; + + m_shader.start_using(); + + m_shader.set_uniform("z_to_texture_row", (float)volume.layer_height_texture_z_to_row_id()); + m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height()); + m_shader.set_uniform("z_cursor", max_z * _cursor_z_relative(canvas)); + m_shader.set_uniform("z_cursor_band_width", get_band_width()); + + GLsizei w = (GLsizei)volume.layer_height_texture_width(); + GLsizei h = (GLsizei)volume.layer_height_texture_height(); + GLsizei half_w = w / 2; + GLsizei half_h = h / 2; + + ::glBindTexture(GL_TEXTURE_2D, m_z_texture_id); + ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + ::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + ::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level0()); + ::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level1()); + + // Render the color bar + float l = bar_rect.get_left(); + float r = bar_rect.get_right(); + float t = bar_rect.get_top(); + float b = bar_rect.get_bottom(); + + ::glBegin(GL_QUADS); + ::glVertex3f(l, b, 0.0f); + ::glVertex3f(r, b, 0.0f); + ::glVertex3f(r, t, max_z); + ::glVertex3f(l, t, max_z); + ::glEnd(); + ::glBindTexture(GL_TEXTURE_2D, 0); + + m_shader.stop_using(); +} + void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, const Rect& bar_rect) const { // FIXME show some kind of legend. @@ -851,6 +924,21 @@ Rect GLCanvas3D::LayersEditing::_get_reset_rect_viewport(const GLCanvas3D& canva return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); } +float GLCanvas3D::LayersEditing::_cursor_z_relative(const GLCanvas3D& canvas) const +{ + const Point& mouse_pos = canvas.get_local_mouse_position(); + const Rect& bar_rect = _get_bar_rect_screen(canvas); + float x = (float)mouse_pos.x; + float y = (float)mouse_pos.y; + float t = bar_rect.get_top(); + float b = bar_rect.get_bottom(); + return ((bar_rect.get_left() <= x) && (x <= bar_rect.get_right()) && (t <= y) && (y <= b)) ? + // Inside the bar. + (b - y - 1.0f) / (b - t - 1.0f) : + // Outside the bar. + - 1000.0f; +} + GLCanvas3D::Mouse::Mouse() : m_dragging(false) { @@ -1456,6 +1544,16 @@ unsigned int GLCanvas3D::get_layers_editing_z_texture_id() const return m_layers_editing.get_z_texture_id(); } +float GLCanvas3D::get_layers_editing_band_width() const +{ + return m_layers_editing.get_band_width(); +} + +void GLCanvas3D::set_layers_editing_band_width(float band_width) +{ + m_layers_editing.set_band_width(band_width); +} + GLShader* GLCanvas3D::get_layers_editing_shader() { return m_layers_editing.get_shader(); @@ -1635,9 +1733,36 @@ void GLCanvas3D::render_legend_texture() const } } -void GLCanvas3D::render_layer_editing_textures(const PrintObject& print_object) const +void GLCanvas3D::render_layer_editing_overlay(const Print& print) const { - m_layers_editing.render(*this, print_object); + if (m_volumes == nullptr) + return; + + GLVolume* volume = nullptr; + + for (GLVolume* vol : m_volumes->volumes) + { + if ((vol != nullptr) && vol->selected && vol->has_layer_height_texture()) + { + volume = vol; + break; + } + } + + if (volume == nullptr) + return; + + // 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. + int object_idx = int(volume->select_group_id / 1000000); + if ((int)print.objects.size() < object_idx) + return; + + const PrintObject* print_object = print.get_object(object_idx); + if (print_object == nullptr) + return; + + m_layers_editing.render(*this, *print_object, *volume); } void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const @@ -1741,6 +1866,15 @@ Size GLCanvas3D::get_canvas_size() const return Size(w, h); } +Point GLCanvas3D::get_local_mouse_position() const +{ + if (m_canvas == nullptr) + return Point(); + + wxPoint mouse_pos = m_canvas->ScreenToClient(wxGetMousePosition()); + return Point(mouse_pos.x, mouse_pos.x); +} + void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { // Calculate the zoom factor needed to adjust viewport to bounding box. diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index a11780349c..e110fee3b6 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -14,9 +14,11 @@ class wxKeyEvent; namespace Slic3r { class GLVolumeCollection; +class GLVolume; class DynamicPrintConfig; class GLShader; class ExPolygon; +class Print; class PrintObject; namespace GUI { @@ -189,6 +191,8 @@ public: bool start_using() const; void stop_using() const; + void set_uniform(const std::string& name, float value) const; + GLShader* get_shader(); private: @@ -213,6 +217,7 @@ public: unsigned int m_z_texture_id; mutable GLTextureData m_tooltip_texture; mutable GLTextureData m_reset_texture; + float m_band_width; public: LayersEditing(); @@ -228,7 +233,10 @@ public: unsigned int get_z_texture_id() const; - void render(const GLCanvas3D& canvas, const PrintObject& print_object) const; + float get_band_width() const; + void set_band_width(float band_width); + + void render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const; GLShader* get_shader(); @@ -237,11 +245,13 @@ public: GLTextureData _load_texture_from_file(const std::string& filename) const; void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; + void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const; void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; Rect _get_bar_rect_screen(const GLCanvas3D& canvas) const; Rect _get_reset_rect_screen(const GLCanvas3D& canvas) const; Rect _get_bar_rect_viewport(const GLCanvas3D& canvas) const; Rect _get_reset_rect_viewport(const GLCanvas3D& canvas) const; + float _cursor_z_relative(const GLCanvas3D& canvas) const; }; class Mouse @@ -369,6 +379,10 @@ public: void set_hover_volume_id(int id); unsigned int get_layers_editing_z_texture_id() const; + + float get_layers_editing_band_width() const; + void set_layers_editing_band_width(float band_width); + GLShader* get_layers_editing_shader(); void zoom_to_bed(); @@ -388,7 +402,7 @@ public: void render_cutting_plane() const; void render_warning_texture() const; void render_legend_texture() const; - void render_layer_editing_textures(const PrintObject& print_object) const; + void render_layer_editing_overlay(const Print& print) const; void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; @@ -400,6 +414,7 @@ public: void on_char(wxKeyEvent& evt); Size get_canvas_size() const; + Point get_local_mouse_position() const; private: void _zoom_to_bounding_box(const BoundingBoxf3& bbox); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 66a66307ee..730badb873 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -473,6 +473,19 @@ unsigned int GLCanvas3DManager::get_layers_editing_z_texture_id(wxGLCanvas* canv return (it != m_canvases.end()) ? it->second->get_layers_editing_z_texture_id() : 0; } +float GLCanvas3DManager::get_layers_editing_band_width(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_layers_editing_band_width() : 0.0f; +} + +void GLCanvas3DManager::set_layers_editing_band_width(wxGLCanvas* canvas, float band_width) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_layers_editing_band_width(band_width); +} + GLShader* GLCanvas3DManager::get_layers_editing_shader(wxGLCanvas* canvas) { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -576,11 +589,11 @@ void GLCanvas3DManager::render_legend_texture(wxGLCanvas* canvas) const it->second->render_legend_texture(); } -void GLCanvas3DManager::render_layer_editing_textures(wxGLCanvas* canvas, const PrintObject& print_object) const +void GLCanvas3DManager::render_layer_editing_overlay(wxGLCanvas* canvas, const Print& print) const { CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) - it->second->render_layer_editing_textures(print_object); + it->second->render_layer_editing_overlay(print); } void GLCanvas3DManager::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 3b3e35c36e..cab1be2f54 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -117,6 +117,10 @@ public: void set_hover_volume_id(wxGLCanvas* canvas, int id); unsigned int get_layers_editing_z_texture_id(wxGLCanvas* canvas) const; + + float get_layers_editing_band_width(wxGLCanvas* canvas) const; + void set_layers_editing_band_width(wxGLCanvas* canvas, float band_width); + GLShader* get_layers_editing_shader(wxGLCanvas* canvas); void zoom_to_bed(wxGLCanvas* canvas); @@ -136,7 +140,7 @@ public: void render_cutting_plane(wxGLCanvas* canvas) const; void render_warning_texture(wxGLCanvas* canvas) const; void render_legend_texture(wxGLCanvas* canvas) const; - void render_layer_editing_textures(wxGLCanvas* canvas, const PrintObject& print_object) const; + void render_layer_editing_overlay(wxGLCanvas* canvas, const Print& print) const; void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 0f47454dab..53688e8da9 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -571,6 +571,21 @@ get_layers_editing_z_texture_id(canvas) OUTPUT: RETVAL +float +get_layers_editing_band_width(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_layers_editing_band_width((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_layers_editing_band_width(canvas, band_width) + SV *canvas; + float band_width; + CODE: + _3DScene::set_layers_editing_band_width((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), band_width); + Ref get_layers_editing_shader(canvas) SV *canvas; @@ -669,11 +684,11 @@ render_legend_texture(canvas) _3DScene::render_legend_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); void -render_layer_editing_textures(canvas, print_object) - SV *canvas; - PrintObject *print_object; +render_layer_editing_overlay(canvas, print) + SV *canvas; + Print *print; CODE: - _3DScene::render_layer_editing_textures((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print_object); + _3DScene::render_layer_editing_overlay((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print); void render_texture(canvas, tex_id, left, right, bottom, top) From a8311bd1bd75d93565afbb05e2bfaa80fbd317ee Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 25 May 2018 16:28:24 +0200 Subject: [PATCH 033/117] 3DScene layer_editing_allowed method moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 16 ++++++++-------- lib/Slic3r/GUI/Plater.pm | 10 ++++++++-- xs/src/slic3r/GUI/3DScene.cpp | 5 +++++ xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 17 +++++++++++------ xs/src/slic3r/GUI/GLCanvas3D.hpp | 5 +++-- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 6 ++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 1 + xs/xsp/GUI_3DScene.xsp | 8 ++++++++ 9 files changed, 51 insertions(+), 18 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index b27b7d55fa..b2a572c304 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -350,16 +350,16 @@ sub Destroy { # } # return $self->{layer_editing_enabled}; #} +# +#sub layer_editing_allowed { +# my ($self) = @_; +# # Allow layer editing if either the shaders were not initialized yet and we don't know +# # whether it will be possible to initialize them, +# # or if the initialization was done already and it failed. +# return ! (defined($self->{layer_editing_initialized}) && $self->{layer_editing_initialized} == 2); +#} #============================================================================================================================== -sub layer_editing_allowed { - my ($self) = @_; - # Allow layer editing if either the shaders were not initialized yet and we don't know - # whether it will be possible to initialize them, - # or if the initialization was done already and it failed. - return ! (defined($self->{layer_editing_initialized}) && $self->{layer_editing_initialized} == 2); -} - sub _first_selected_object_id_for_variable_layer_height_editing { my ($self) = @_; for my $i (0..$#{$self->volumes}) { diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 3e78b5c95a..f1ce8fe0aa 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1836,7 +1836,10 @@ sub on_config_change { #============================================================================================================================== $self->{canvas3D}->Refresh; $self->{canvas3D}->Update; - } elsif ($self->{canvas3D}->layer_editing_allowed) { +#============================================================================================================================== + } elsif (Slic3r::GUI::_3DScene::is_layers_editing_allowed($self->{canvas3D})) { +# } elsif ($self->{canvas3D}->layer_editing_allowed) { +#============================================================================================================================== # Want to allow the layer editing, but do it only if the OpenGL supports it. if ($self->{htoolbar}) { $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 1); @@ -1997,7 +2000,10 @@ sub object_list_changed { # Enable/disable buttons depending on whether there are any objects on the platter. my $have_objects = @{$self->{objects}} ? 1 : 0; - my $variable_layer_height_allowed = $self->{config}->variable_layer_height && $self->{canvas3D}->layer_editing_allowed; +#============================================================================================================================== + my $variable_layer_height_allowed = $self->{config}->variable_layer_height && Slic3r::GUI::_3DScene::is_layers_editing_allowed($self->{canvas3D}); +# my $variable_layer_height_allowed = $self->{config}->variable_layer_height && $self->{canvas3D}->layer_editing_allowed; +#============================================================================================================================== if ($self->{htoolbar}) { # On OSX or Linux $self->{htoolbar}->EnableTool($_, $have_objects) diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 87071ce208..a9262337b2 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1939,6 +1939,11 @@ bool _3DScene::is_picking_enabled(wxGLCanvas* canvas) return s_canvas_mgr.is_picking_enabled(canvas); } +bool _3DScene::is_layers_editing_allowed(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_layers_editing_allowed(canvas); +} + bool _3DScene::is_multisample_allowed(wxGLCanvas* canvas) { return s_canvas_mgr.is_multisample_allowed(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 153744987f..94ce59a3bb 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -598,6 +598,7 @@ public: static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_picking_enabled(wxGLCanvas* canvas); + static bool is_layers_editing_allowed(wxGLCanvas* canvas); static bool is_multisample_allowed(wxGLCanvas* canvas); static void enable_layers_editing(wxGLCanvas* canvas, bool enable); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 823a4df0ca..95ec9c3423 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -589,7 +589,7 @@ GLCanvas3D::LayersEditing::GLTextureData::GLTextureData(unsigned int id, int wid } GLCanvas3D::LayersEditing::LayersEditing() - : m_allowed(false) + : m_use_legacy_opengl(false) , m_enabled(false) , m_z_texture_id(0) , m_band_width(2.0f) @@ -636,12 +636,12 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, bool GLCanvas3D::LayersEditing::is_allowed() const { - return m_allowed; + return m_use_legacy_opengl && m_shader.is_initialized(); } -void GLCanvas3D::LayersEditing::set_allowed(bool allowed) +void GLCanvas3D::LayersEditing::set_use_legacy_opengl(bool use_legacy_opengl) { - m_allowed = allowed; + m_use_legacy_opengl = use_legacy_opengl; } bool GLCanvas3D::LayersEditing::is_enabled() const @@ -651,7 +651,7 @@ bool GLCanvas3D::LayersEditing::is_enabled() const void GLCanvas3D::LayersEditing::set_enabled(bool enabled) { - m_enabled = m_allowed && m_shader.is_initialized() && enabled; + m_enabled = is_allowed() && enabled; } unsigned int GLCanvas3D::LayersEditing::get_z_texture_id() const @@ -1030,7 +1030,7 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (useVBOs && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) return false; - m_layers_editing.set_allowed(!use_legacy_opengl); + m_layers_editing.set_use_legacy_opengl(!use_legacy_opengl); return true; } @@ -1335,6 +1335,11 @@ bool GLCanvas3D::is_picking_enabled() const return m_picking_enabled; } +bool GLCanvas3D::is_layers_editing_allowed() const +{ + return m_layers_editing.is_allowed(); +} + bool GLCanvas3D::is_multisample_allowed() const { return m_multisample_allowed; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index e110fee3b6..2a78ded074 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -211,7 +211,7 @@ public: GLTextureData(unsigned int id, int width, int height); }; - bool m_allowed; + bool m_use_legacy_opengl; bool m_enabled; Shader m_shader; unsigned int m_z_texture_id; @@ -226,7 +226,7 @@ public: bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); bool is_allowed() const; - void set_allowed(bool allowed); + void set_use_legacy_opengl(bool use_legacy_opengl); bool is_enabled() const; void set_enabled(bool enabled); @@ -360,6 +360,7 @@ public: bool is_layers_editing_enabled() const; bool is_picking_enabled() const; + bool is_layers_editing_allowed() const; bool is_multisample_allowed() const; void enable_layers_editing(bool enable); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 730badb873..2406f0094e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -380,6 +380,12 @@ bool GLCanvas3DManager::is_picking_enabled(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_picking_enabled() : false; } +bool GLCanvas3DManager::is_layers_editing_allowed(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_layers_editing_allowed() : false; +} + bool GLCanvas3DManager::is_multisample_allowed(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index cab1be2f54..14b857920b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -98,6 +98,7 @@ public: bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_picking_enabled(wxGLCanvas* canvas) const; + bool is_layers_editing_allowed(wxGLCanvas* canvas) const; bool is_multisample_allowed(wxGLCanvas* canvas) const; void enable_layers_editing(wxGLCanvas* canvas, bool enable); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 53688e8da9..bbc8fc2f99 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -468,6 +468,14 @@ is_picking_enabled(canvas) OUTPUT: RETVAL +bool +is_layers_editing_allowed(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_layers_editing_allowed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + bool is_multisample_allowed(canvas) SV *canvas; From d5268fdc97adde08bbc5165cfbde4714efc583fa Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 28 May 2018 13:04:01 +0200 Subject: [PATCH 034/117] Removed unneeded debug output --- lib/Slic3r/GUI/3DScene.pm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index fca22bedd6..5fc1e0b18d 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1635,11 +1635,6 @@ sub draw_active_object_annotations { $self->{layer_height_edit_shader}->set_uniform('z_cursor', $z_max * $z_cursor_relative); $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width}); glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); -#============================================================================================================================================= - print "texture id: "; - print $self->{layer_preview_z_texture_id}; - print "\n"; -#============================================================================================================================================= glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, From 951e8528b4352c8f412dfd8b588b4b614ef87ae5 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 28 May 2018 13:43:29 +0200 Subject: [PATCH 035/117] 3DScene layers editing parameters moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 90 +++++++++++------ xs/src/slic3r/GUI/3DScene.cpp | 45 +++++++++ xs/src/slic3r/GUI/3DScene.hpp | 14 +++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 124 ++++++++++++++++++++---- xs/src/slic3r/GUI/GLCanvas3D.hpp | 33 ++++++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 58 +++++++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 14 +++ xs/xsp/GUI_3DScene.xsp | 70 ++++++++++++- xs/xsp/Print.xsp | 3 + 9 files changed, 405 insertions(+), 46 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index b2a572c304..c5626d4bd6 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -201,11 +201,11 @@ sub new { # $self->_camera_distance(0.); # $self->layer_editing_enabled(0); # $self->{layer_height_edit_band_width} = 2.; +# $self->{layer_height_edit_strength} = 0.005; +# $self->{layer_height_edit_last_object_id} = -1; +# $self->{layer_height_edit_last_z} = 0.; +# $self->{layer_height_edit_last_action} = 0; #============================================================================================================================== - $self->{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; #============================================================================================================================== Slic3r::GUI::_3DScene::reset_volumes($self); @@ -267,7 +267,9 @@ sub new { EVT_TIMER($self, $self->{layer_height_edit_timer_id}, sub { my ($self, $event) = @_; return if $self->_layer_height_edited != 1; - return if $self->{layer_height_edit_last_object_id} == -1; +#============================================================================================================================== + return if Slic3r::GUI::_3DScene::get_layers_editing_last_object_id($self) == -1; +#============================================================================================================================== $self->_variable_layer_thickness_action(undef); }); @@ -419,40 +421,73 @@ sub _variable_layer_thickness_reset_rect_mouse_inside { return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; } -sub _variable_layer_thickness_bar_mouse_cursor_z_relative { - my ($self) = @_; - my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); - my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; - return ($mouse_pos->x >= $bar_left && $mouse_pos->x <= $bar_right && $mouse_pos->y >= $bar_top && $mouse_pos->y <= $bar_bottom) ? - # Inside the bar. - ($bar_bottom - $mouse_pos->y - 1.) / ($bar_bottom - $bar_top - 1) : - # Outside the bar. - -1000.; -} +#============================================================================================================================== +#sub _variable_layer_thickness_bar_mouse_cursor_z_relative { +# my ($self) = @_; +# my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); +# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; +# return ($mouse_pos->x >= $bar_left && $mouse_pos->x <= $bar_right && $mouse_pos->y >= $bar_top && $mouse_pos->y <= $bar_bottom) ? +# # Inside the bar. +# ($bar_bottom - $mouse_pos->y - 1.) / ($bar_bottom - $bar_top - 1) : +# # Outside the bar. +# -1000.; +#} +#============================================================================================================================== sub _variable_layer_thickness_action { my ($self, $mouse_event, $do_modification) = @_; +#============================================================================================================================== + my $object_idx_selected = Slic3r::GUI::_3DScene::get_layers_editing_last_object_id($self); # A volume is selected. Test, whether hovering over a layer thickness bar. - return if $self->{layer_height_edit_last_object_id} == -1; + return if ($object_idx_selected == -1); +# return if $self->{layer_height_edit_last_object_id} == -1; +#============================================================================================================================== if (defined($mouse_event)) { my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; - $self->{layer_height_edit_last_z} = unscale($self->{print}->get_object($self->{layer_height_edit_last_object_id})->size->z) - * ($bar_bottom - $mouse_event->GetY - 1.) / ($bar_bottom - $bar_top); - $self->{layer_height_edit_last_action} = $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_layers_editing_last_z($self, unscale($self->{print}->get_object($object_idx_selected)->size->z) + * ($bar_bottom - $mouse_event->GetY - 1.) / ($bar_bottom - $bar_top)); + Slic3r::GUI::_3DScene::set_layers_editing_last_action($self, $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1)); +# $self->{layer_height_edit_last_z} = unscale($self->{print}->get_object($self->{layer_height_edit_last_object_id})->size->z) +# * ($bar_bottom - $mouse_event->GetY - 1.) / ($bar_bottom - $bar_top); +# $self->{layer_height_edit_last_action} = $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1); +#============================================================================================================================== } # Mark the volume as modified, so Print will pick its layer height profile? Where to mark it? # Start a timer to refresh the print? schedule_background_process() ? # The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself. - $self->{print}->get_object($self->{layer_height_edit_last_object_id})->adjust_layer_height_profile( - $self->{layer_height_edit_last_z}, - $self->{layer_height_edit_strength}, #============================================================================================================================== + $self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile( + Slic3r::GUI::_3DScene::get_layers_editing_last_z($self), + Slic3r::GUI::_3DScene::get_layers_editing_strength($self), Slic3r::GUI::_3DScene::get_layers_editing_band_width($self), + Slic3r::GUI::_3DScene::get_layers_editing_last_action($self)); +# $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}); +#============================================================================================================================== + + #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MERGE FROM SCENE_MANIPULATORS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + # searches the id of the first volume of the selected object + my $volume_idx = 0; + for my $i (0..$object_idx_selected - 1) { + my $obj = $self->{print}->get_object($i); + for my $j (0..$obj->region_volumes_count - 1) { + $volume_idx += scalar @{$obj->get_region_volumes($j)}; + } + } + #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MERGE FROM SCENE_MANIPULATORS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +#============================================================================================================================== + #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MERGE FROM SCENE_MANIPULATORS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + $self->volumes->[$volume_idx]->generate_layer_height_texture($self->{print}->get_object($object_idx_selected), 1); +# $self->volumes->[$object_idx_selected]->generate_layer_height_texture($self->{print}->get_object($object_idx_selected), 1); + #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MERGE FROM SCENE_MANIPULATORS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +# $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->{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); @@ -463,7 +498,8 @@ sub mouse_event { my $pos = Slic3r::Pointf->new($e->GetPositionXY); #============================================================================================================================== - my $object_idx_selected = $self->{layer_height_edit_last_object_id} = (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $self->{print}) ? $self->_first_selected_object_id_for_variable_layer_height_editing : -1; + my $object_idx_selected = (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $self->{print}) ? $self->_first_selected_object_id_for_variable_layer_height_editing : -1; + Slic3r::GUI::_3DScene::set_layers_editing_last_object_id($self, $object_idx_selected); # 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; #============================================================================================================================== @@ -1718,7 +1754,7 @@ sub mark_volumes_for_layer_height { if (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $shader && $volume->selected && $volume->has_layer_height_texture && $object_id < $self->{print}->object_count) { $volume->set_layer_height_texture_data(Slic3r::GUI::_3DScene::get_layers_editing_z_texture_id($self), $shader->shader_program_id, - $self->{print}->get_object($object_id), $self->_variable_layer_thickness_bar_mouse_cursor_z_relative, Slic3r::GUI::_3DScene::get_layers_editing_band_width($self)); + $self->{print}->get_object($object_id), Slic3r::GUI::_3DScene::get_layers_editing_cursor_z_relative($self), Slic3r::GUI::_3DScene::get_layers_editing_band_width($self)); # if ($self->layer_editing_enabled && $volume->selected && $self->{layer_height_edit_shader} && # $volume->has_layer_height_texture && $object_id < $self->{print}->object_count) { diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index a9262337b2..46b7a5eca0 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2025,11 +2025,56 @@ void _3DScene::set_layers_editing_band_width(wxGLCanvas* canvas, float band_widt s_canvas_mgr.set_layers_editing_band_width(canvas, band_width); } +float _3DScene::get_layers_editing_strength(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_layers_editing_strength(canvas); +} + +void _3DScene::set_layers_editing_strength(wxGLCanvas* canvas, float strength) +{ + s_canvas_mgr.set_layers_editing_strength(canvas, strength); +} + +int _3DScene::get_layers_editing_last_object_id(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_layers_editing_last_object_id(canvas); +} + +void _3DScene::set_layers_editing_last_object_id(wxGLCanvas* canvas, int id) +{ + s_canvas_mgr.set_layers_editing_last_object_id(canvas, id); +} + +float _3DScene::get_layers_editing_last_z(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_layers_editing_last_z(canvas); +} + +void _3DScene::set_layers_editing_last_z(wxGLCanvas* canvas, float z) +{ + s_canvas_mgr.set_layers_editing_last_z(canvas, z); +} + +unsigned int _3DScene::get_layers_editing_last_action(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_layers_editing_last_action(canvas); +} + +void _3DScene::set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int action) +{ + s_canvas_mgr.set_layers_editing_last_action(canvas, action); +} + GLShader* _3DScene::get_layers_editing_shader(wxGLCanvas* canvas) { return s_canvas_mgr.get_layers_editing_shader(canvas); } +float _3DScene::get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_layers_editing_cursor_z_relative(canvas); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 94ce59a3bb..73835df2fc 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -622,8 +622,22 @@ public: static float get_layers_editing_band_width(wxGLCanvas* canvas); static void set_layers_editing_band_width(wxGLCanvas* canvas, float band_width); + static float get_layers_editing_strength(wxGLCanvas* canvas); + static void set_layers_editing_strength(wxGLCanvas* canvas, float strength); + + static int get_layers_editing_last_object_id(wxGLCanvas* canvas); + static void set_layers_editing_last_object_id(wxGLCanvas* canvas, int id); + + static float get_layers_editing_last_z(wxGLCanvas* canvas); + static void set_layers_editing_last_z(wxGLCanvas* canvas, float z); + + static unsigned int get_layers_editing_last_action(wxGLCanvas* canvas); + static void set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int action); + static GLShader* get_layers_editing_shader(wxGLCanvas* canvas); + static float get_layers_editing_cursor_z_relative(wxGLCanvas* canvas); + static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 95ec9c3423..ef82e7f250 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -593,6 +593,10 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_enabled(false) , m_z_texture_id(0) , m_band_width(2.0f) + , m_strength(0.005f) + , m_last_object_id(-1) + , m_last_z(0.0f) + , m_last_action(0) { } @@ -669,6 +673,46 @@ void GLCanvas3D::LayersEditing::set_band_width(float band_width) m_band_width = band_width; } +float GLCanvas3D::LayersEditing::get_strength() const +{ + return m_strength; +} + +void GLCanvas3D::LayersEditing::set_strength(float strength) +{ + m_strength = strength; +} + +int GLCanvas3D::LayersEditing::get_last_object_id() const +{ + return m_last_object_id; +} + +void GLCanvas3D::LayersEditing::set_last_object_id(int id) +{ + m_last_object_id = id; +} + +float GLCanvas3D::LayersEditing::get_last_z() const +{ + return m_last_z; +} + +void GLCanvas3D::LayersEditing::set_last_z(float z) +{ + m_last_z = z; +} + +unsigned int GLCanvas3D::LayersEditing::get_last_action() const +{ + return m_last_action; +} + +void GLCanvas3D::LayersEditing::set_last_action(unsigned int action) +{ + m_last_action = action; +} + void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const { if (!m_enabled) @@ -700,6 +744,22 @@ GLShader* GLCanvas3D::LayersEditing::get_shader() return m_shader.get_shader(); } +float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) const +{ + const Point& mouse_pos = canvas.get_local_mouse_position(); + const Rect& bar_rect = _get_bar_rect_screen(canvas); + float x = (float)mouse_pos.x; + float y = (float)mouse_pos.y; + float t = bar_rect.get_top(); + float b = bar_rect.get_bottom(); + + return ((bar_rect.get_left() <= x) && (x <= bar_rect.get_right()) && (t <= y) && (y <= b)) ? + // Inside the bar. + (b - y - 1.0f) / (b - t - 1.0f) : + // Outside the bar. + -1000.0f; +} + bool GLCanvas3D::LayersEditing::_is_initialized() const { return m_shader.is_initialized(); @@ -796,7 +856,7 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas m_shader.set_uniform("z_to_texture_row", (float)volume.layer_height_texture_z_to_row_id()); m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height()); - m_shader.set_uniform("z_cursor", max_z * _cursor_z_relative(canvas)); + m_shader.set_uniform("z_cursor", max_z * get_cursor_z_relative(canvas)); m_shader.set_uniform("z_cursor_band_width", get_band_width()); GLsizei w = (GLsizei)volume.layer_height_texture_width(); @@ -924,21 +984,6 @@ Rect GLCanvas3D::LayersEditing::_get_reset_rect_viewport(const GLCanvas3D& canva return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); } -float GLCanvas3D::LayersEditing::_cursor_z_relative(const GLCanvas3D& canvas) const -{ - const Point& mouse_pos = canvas.get_local_mouse_position(); - const Rect& bar_rect = _get_bar_rect_screen(canvas); - float x = (float)mouse_pos.x; - float y = (float)mouse_pos.y; - float t = bar_rect.get_top(); - float b = bar_rect.get_bottom(); - return ((bar_rect.get_left() <= x) && (x <= bar_rect.get_right()) && (t <= y) && (y <= b)) ? - // Inside the bar. - (b - y - 1.0f) / (b - t - 1.0f) : - // Outside the bar. - - 1000.0f; -} - GLCanvas3D::Mouse::Mouse() : m_dragging(false) { @@ -1559,11 +1604,56 @@ void GLCanvas3D::set_layers_editing_band_width(float band_width) m_layers_editing.set_band_width(band_width); } +float GLCanvas3D::get_layers_editing_strength() const +{ + return m_layers_editing.get_strength(); +} + +void GLCanvas3D::set_layers_editing_strength(float strength) +{ + m_layers_editing.set_strength(strength); +} + +int GLCanvas3D::get_layers_editing_last_object_id() const +{ + return m_layers_editing.get_last_object_id(); +} + +void GLCanvas3D::set_layers_editing_last_object_id(int id) +{ + m_layers_editing.set_last_object_id(id); +} + +float GLCanvas3D::get_layers_editing_last_z() const +{ + return m_layers_editing.get_last_z(); +} + +void GLCanvas3D::set_layers_editing_last_z(float z) +{ + m_layers_editing.set_last_z(z); +} + +unsigned int GLCanvas3D::get_layers_editing_last_action() const +{ + return m_layers_editing.get_last_action(); +} + +void GLCanvas3D::set_layers_editing_last_action(unsigned int action) +{ + m_layers_editing.set_last_action(action); +} + GLShader* GLCanvas3D::get_layers_editing_shader() { return m_layers_editing.get_shader(); } +float GLCanvas3D::get_layers_editing_cursor_z_relative(const GLCanvas3D& canvas) const +{ + return m_layers_editing.get_cursor_z_relative(canvas); +} + void GLCanvas3D::render_bed() const { m_bed.render(); @@ -1877,7 +1967,7 @@ Point GLCanvas3D::get_local_mouse_position() const return Point(); wxPoint mouse_pos = m_canvas->ScreenToClient(wxGetMousePosition()); - return Point(mouse_pos.x, mouse_pos.x); + return Point(mouse_pos.x, mouse_pos.y); } void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 2a78ded074..3a83321f59 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -218,6 +218,10 @@ public: mutable GLTextureData m_tooltip_texture; mutable GLTextureData m_reset_texture; float m_band_width; + float m_strength; + int m_last_object_id; + float m_last_z; + unsigned int m_last_action; public: LayersEditing(); @@ -236,10 +240,24 @@ public: float get_band_width() const; void set_band_width(float band_width); + float get_strength() const; + void set_strength(float strength); + + int get_last_object_id() const; + void set_last_object_id(int id); + + float get_last_z() const; + void set_last_z(float z); + + unsigned int get_last_action() const; + void set_last_action(unsigned int action); + void render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const; GLShader* get_shader(); + float get_cursor_z_relative(const GLCanvas3D& canvas) const; + private: bool _is_initialized() const; GLTextureData _load_texture_from_file(const std::string& filename) const; @@ -251,7 +269,6 @@ public: Rect _get_reset_rect_screen(const GLCanvas3D& canvas) const; Rect _get_bar_rect_viewport(const GLCanvas3D& canvas) const; Rect _get_reset_rect_viewport(const GLCanvas3D& canvas) const; - float _cursor_z_relative(const GLCanvas3D& canvas) const; }; class Mouse @@ -384,8 +401,22 @@ public: float get_layers_editing_band_width() const; void set_layers_editing_band_width(float band_width); + float get_layers_editing_strength() const; + void set_layers_editing_strength(float strength); + + int get_layers_editing_last_object_id() const; + void set_layers_editing_last_object_id(int id); + + float get_layers_editing_last_z() const; + void set_layers_editing_last_z(float z); + + unsigned int get_layers_editing_last_action() const; + void set_layers_editing_last_action(unsigned int action); + GLShader* get_layers_editing_shader(); + float get_layers_editing_cursor_z_relative(const GLCanvas3D& canvas) const; + void zoom_to_bed(); void zoom_to_volumes(); void select_view(const std::string& direction); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 2406f0094e..b2a7f5bad5 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -492,12 +492,70 @@ void GLCanvas3DManager::set_layers_editing_band_width(wxGLCanvas* canvas, float it->second->set_layers_editing_band_width(band_width); } +float GLCanvas3DManager::get_layers_editing_strength(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_layers_editing_strength() : 0.0f; +} + +void GLCanvas3DManager::set_layers_editing_strength(wxGLCanvas* canvas, float strength) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_layers_editing_strength(strength); +} + +int GLCanvas3DManager::get_layers_editing_last_object_id(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_layers_editing_last_object_id() : -1; +} + +void GLCanvas3DManager::set_layers_editing_last_object_id(wxGLCanvas* canvas, int id) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_layers_editing_last_object_id(id); +} + +float GLCanvas3DManager::get_layers_editing_last_z(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_layers_editing_last_z() : 0.0f; +} + +void GLCanvas3DManager::set_layers_editing_last_z(wxGLCanvas* canvas, float z) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_layers_editing_last_z(z); +} + +unsigned int GLCanvas3DManager::get_layers_editing_last_action(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_layers_editing_last_action() : 0; +} + +void GLCanvas3DManager::set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int action) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_layers_editing_last_action(action); +} + GLShader* GLCanvas3DManager::get_layers_editing_shader(wxGLCanvas* canvas) { CanvasesMap::const_iterator it = _get_canvas(canvas); return (it != m_canvases.end()) ? it->second->get_layers_editing_shader() : nullptr; } +float GLCanvas3DManager::get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_layers_editing_cursor_z_relative(*it->second) : 0.0f; +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 14b857920b..4a804e9215 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -122,8 +122,22 @@ public: float get_layers_editing_band_width(wxGLCanvas* canvas) const; void set_layers_editing_band_width(wxGLCanvas* canvas, float band_width); + float get_layers_editing_strength(wxGLCanvas* canvas) const; + void set_layers_editing_strength(wxGLCanvas* canvas, float strength); + + int get_layers_editing_last_object_id(wxGLCanvas* canvas) const; + void set_layers_editing_last_object_id(wxGLCanvas* canvas, int id); + + float get_layers_editing_last_z(wxGLCanvas* canvas) const; + void set_layers_editing_last_z(wxGLCanvas* canvas, float z); + + unsigned int get_layers_editing_last_action(wxGLCanvas* canvas) const; + void set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int action); + GLShader* get_layers_editing_shader(wxGLCanvas* canvas); + float get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) const; + void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index bbc8fc2f99..cca3583fb2 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -594,6 +594,66 @@ set_layers_editing_band_width(canvas, band_width) CODE: _3DScene::set_layers_editing_band_width((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), band_width); +float +get_layers_editing_strength(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_layers_editing_strength((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_layers_editing_strength(canvas, strength) + SV *canvas; + float strength; + CODE: + _3DScene::set_layers_editing_strength((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), strength); + +int +get_layers_editing_last_object_id(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_layers_editing_last_object_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_layers_editing_last_object_id(canvas, id) + SV *canvas; + int id; + CODE: + _3DScene::set_layers_editing_last_object_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id); + +float +get_layers_editing_last_z(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_layers_editing_last_z((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_layers_editing_last_z(canvas, z) + SV *canvas; + float z; + CODE: + _3DScene::set_layers_editing_last_z((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), z); + +unsigned int +get_layers_editing_last_action(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_layers_editing_last_action((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_layers_editing_last_action(canvas, action) + SV *canvas; + unsigned int action; + CODE: + _3DScene::set_layers_editing_last_action((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), action); + Ref get_layers_editing_shader(canvas) SV *canvas; @@ -601,7 +661,15 @@ get_layers_editing_shader(canvas) RETVAL = _3DScene::get_layers_editing_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); OUTPUT: RETVAL - + +float +get_layers_editing_cursor_z_relative(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_layers_editing_cursor_z_relative((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + void zoom_to_bed(canvas) SV *canvas; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index ef9c5345f4..f21923b416 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -52,6 +52,9 @@ _constant() int region_count() %code%{ RETVAL = THIS->print()->regions.size(); %}; + int region_volumes_count() + %code%{ RETVAL = THIS->region_volumes.size(); %}; + Ref print(); Ref model_object(); Ref config() From 994222c3171181dd1b695ac4d5582d05e85854de Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 28 May 2018 14:10:02 +0200 Subject: [PATCH 036/117] 3DScene _first_selected_object_id_for_variable_layer_height_editing method moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 31 +++++++++++++------------ xs/src/slic3r/GUI/3DScene.cpp | 5 ++++ xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 30 ++++++++++++++++++++---- xs/src/slic3r/GUI/GLCanvas3D.hpp | 12 ++++++---- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 6 +++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 1 + xs/xsp/GUI_3DScene.xsp | 10 ++++++++ 8 files changed, 71 insertions(+), 25 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index c5626d4bd6..765fde9ab4 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -360,21 +360,21 @@ sub Destroy { # # or if the initialization was done already and it failed. # return ! (defined($self->{layer_editing_initialized}) && $self->{layer_editing_initialized} == 2); #} +# +#sub _first_selected_object_id_for_variable_layer_height_editing { +# my ($self) = @_; +# for my $i (0..$#{$self->volumes}) { +# if ($self->volumes->[$i]->selected) { +# my $object_id = int($self->volumes->[$i]->select_group_id / 1000000); +# # Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy. +# return ($object_id >= $self->{print}->object_count) ? -1 : $object_id +# if $object_id < 10000; +# } +# } +# return -1; +#} #============================================================================================================================== -sub _first_selected_object_id_for_variable_layer_height_editing { - my ($self) = @_; - for my $i (0..$#{$self->volumes}) { - if ($self->volumes->[$i]->selected) { - my $object_id = int($self->volumes->[$i]->select_group_id / 1000000); - # Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy. - return ($object_id >= $self->{print}->object_count) ? -1 : $object_id - if $object_id < 10000; - } - } - return -1; -} - # Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen. sub _variable_layer_thickness_bar_rect_screen { my ($self) = @_; @@ -498,7 +498,7 @@ sub mouse_event { my $pos = Slic3r::Pointf->new($e->GetPositionXY); #============================================================================================================================== - my $object_idx_selected = (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $self->{print}) ? $self->_first_selected_object_id_for_variable_layer_height_editing : -1; + my $object_idx_selected = (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $self->{print}) ? Slic3r::GUI::_3DScene::get_layers_editing_first_selected_object_id($self, $self->{print}->object_count) : -1; Slic3r::GUI::_3DScene::set_layers_editing_last_object_id($self, $object_idx_selected); # 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; #============================================================================================================================== @@ -732,9 +732,10 @@ sub mouse_wheel_event { } #============================================================================================================================== if (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $self->{print}) { + my $object_idx_selected = Slic3r::GUI::_3DScene::get_layers_editing_first_selected_object_id($self, $self->{print}->object_count); # if ($self->layer_editing_enabled && $self->{print}) { +# my $object_idx_selected = $self->_first_selected_object_id_for_variable_layer_height_editing; #============================================================================================================================== - my $object_idx_selected = $self->_first_selected_object_id_for_variable_layer_height_editing; if ($object_idx_selected != -1) { # A volume is selected. Test, whether hovering over a layer thickness bar. if ($self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 46b7a5eca0..41aa1b77d1 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2075,6 +2075,11 @@ float _3DScene::get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) return s_canvas_mgr.get_layers_editing_cursor_z_relative(canvas); } +int _3DScene::get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count) +{ + return s_canvas_mgr.get_layers_editing_first_selected_object_id(canvas, objects_count); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 73835df2fc..38dbfc8043 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -637,6 +637,7 @@ public: static GLShader* get_layers_editing_shader(wxGLCanvas* canvas); static float get_layers_editing_cursor_z_relative(wxGLCanvas* canvas); + static int get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count); static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index ef82e7f250..3b218b88f2 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -744,7 +744,7 @@ GLShader* GLCanvas3D::LayersEditing::get_shader() return m_shader.get_shader(); } -float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) const +float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) { const Point& mouse_pos = canvas.get_local_mouse_position(); const Rect& bar_rect = _get_bar_rect_screen(canvas); @@ -760,6 +760,21 @@ float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) -1000.0f; } +int GLCanvas3D::LayersEditing::get_first_selected_object_id(const GLVolumeCollection& volumes, unsigned int objects_count) +{ + for (const GLVolume* vol : volumes.volumes) + { + if ((vol != nullptr) && vol->selected) + { + int object_id = vol->select_group_id / 1000000; + // Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy. + if (object_id < 10000) + return (object_id >= (int)objects_count) ? -1 : object_id; + } + } + return -1; +} + bool GLCanvas3D::LayersEditing::_is_initialized() const { return m_shader.is_initialized(); @@ -942,7 +957,7 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, } } -Rect GLCanvas3D::LayersEditing::_get_bar_rect_screen(const GLCanvas3D& canvas) const +Rect GLCanvas3D::LayersEditing::_get_bar_rect_screen(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); float w = (float)cnv_size.get_width(); @@ -951,7 +966,7 @@ Rect GLCanvas3D::LayersEditing::_get_bar_rect_screen(const GLCanvas3D& canvas) c return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0.0f, w, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT); } -Rect GLCanvas3D::LayersEditing::_get_reset_rect_screen(const GLCanvas3D& canvas) const +Rect GLCanvas3D::LayersEditing::_get_reset_rect_screen(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); float w = (float)cnv_size.get_width(); @@ -960,7 +975,7 @@ Rect GLCanvas3D::LayersEditing::_get_reset_rect_screen(const GLCanvas3D& canvas) return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, w, h); } -Rect GLCanvas3D::LayersEditing::_get_bar_rect_viewport(const GLCanvas3D& canvas) const +Rect GLCanvas3D::LayersEditing::_get_bar_rect_viewport(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); float half_w = 0.5f * (float)cnv_size.get_width(); @@ -972,7 +987,7 @@ Rect GLCanvas3D::LayersEditing::_get_bar_rect_viewport(const GLCanvas3D& canvas) return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom); } -Rect GLCanvas3D::LayersEditing::_get_reset_rect_viewport(const GLCanvas3D& canvas) const +Rect GLCanvas3D::LayersEditing::_get_reset_rect_viewport(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); float half_w = 0.5f * (float)cnv_size.get_width(); @@ -1654,6 +1669,11 @@ float GLCanvas3D::get_layers_editing_cursor_z_relative(const GLCanvas3D& canvas) return m_layers_editing.get_cursor_z_relative(canvas); } +int GLCanvas3D::get_layers_editing_first_selected_object_id(unsigned int objects_count) const +{ + return (m_volumes != nullptr) ? m_layers_editing.get_first_selected_object_id(*m_volumes, objects_count) : -1; +} + void GLCanvas3D::render_bed() const { m_bed.render(); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 3a83321f59..9b01be0817 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -256,7 +256,8 @@ public: GLShader* get_shader(); - float get_cursor_z_relative(const GLCanvas3D& canvas) const; + static float get_cursor_z_relative(const GLCanvas3D& canvas); + static int get_first_selected_object_id(const GLVolumeCollection& volumes, unsigned int objects_count); private: bool _is_initialized() const; @@ -265,10 +266,10 @@ public: void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const; void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; - Rect _get_bar_rect_screen(const GLCanvas3D& canvas) const; - Rect _get_reset_rect_screen(const GLCanvas3D& canvas) const; - Rect _get_bar_rect_viewport(const GLCanvas3D& canvas) const; - Rect _get_reset_rect_viewport(const GLCanvas3D& canvas) const; + static Rect _get_bar_rect_screen(const GLCanvas3D& canvas); + static Rect _get_reset_rect_screen(const GLCanvas3D& canvas); + static Rect _get_bar_rect_viewport(const GLCanvas3D& canvas); + static Rect _get_reset_rect_viewport(const GLCanvas3D& canvas); }; class Mouse @@ -416,6 +417,7 @@ public: GLShader* get_layers_editing_shader(); float get_layers_editing_cursor_z_relative(const GLCanvas3D& canvas) const; + int get_layers_editing_first_selected_object_id(unsigned int objects_count) const; void zoom_to_bed(); void zoom_to_volumes(); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index b2a7f5bad5..91963a9ad1 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -556,6 +556,12 @@ float GLCanvas3DManager::get_layers_editing_cursor_z_relative(wxGLCanvas* canvas return (it != m_canvases.end()) ? it->second->get_layers_editing_cursor_z_relative(*it->second) : 0.0f; } +int GLCanvas3DManager::get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_layers_editing_first_selected_object_id(objects_count) : 0.; +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 4a804e9215..5166da2e90 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -137,6 +137,7 @@ public: GLShader* get_layers_editing_shader(wxGLCanvas* canvas); float get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) const; + int get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count) const; void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index cca3583fb2..6cc9e0d6a1 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -670,6 +670,16 @@ get_layers_editing_cursor_z_relative(canvas) OUTPUT: RETVAL +int +get_layers_editing_first_selected_object_id(canvas, objects_count) + SV *canvas; + unsigned int objects_count; + CODE: + RETVAL = _3DScene::get_layers_editing_first_selected_object_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), objects_count); + OUTPUT: + RETVAL + + void zoom_to_bed(canvas) SV *canvas; From aacdcd4add5c10a2fcdf8b414ebecece9881b912 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 28 May 2018 14:39:59 +0200 Subject: [PATCH 037/117] 3DScene layers editing mouse containment methods moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 44 +++++---- xs/src/slic3r/GUI/3DScene.cpp | 10 ++ xs/src/slic3r/GUI/3DScene.hpp | 2 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 120 ++++++++++++++---------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 8 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 14 ++- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 + xs/xsp/GUI_3DScene.xsp | 19 ++++ 8 files changed, 151 insertions(+), 68 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 765fde9ab4..8a80eeac72 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -409,19 +409,19 @@ sub _variable_layer_thickness_reset_rect_viewport { #============================================================================================================================== } -sub _variable_layer_thickness_bar_rect_mouse_inside { - my ($self, $mouse_evt) = @_; - my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; - return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; -} - -sub _variable_layer_thickness_reset_rect_mouse_inside { - my ($self, $mouse_evt) = @_; - my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_reset_rect_screen; - return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; -} - #============================================================================================================================== +#sub _variable_layer_thickness_bar_rect_mouse_inside { +# my ($self, $mouse_evt) = @_; +# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; +# return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; +#} +# +#sub _variable_layer_thickness_reset_rect_mouse_inside { +# my ($self, $mouse_evt) = @_; +# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_reset_rect_screen; +# return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; +#} +# #sub _variable_layer_thickness_bar_mouse_cursor_z_relative { # my ($self) = @_; # my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); @@ -513,7 +513,10 @@ sub mouse_event { $self->SetFocus; $self->_drag_start_xy(undef); } elsif ($e->LeftDClick) { - if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { +#============================================================================================================================== + if ($object_idx_selected != -1 && Slic3r::GUI::_3DScene::bar_rect_contains($self, $e->GetX, $e->GetY)) { +# if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { +#============================================================================================================================== } elsif ($self->on_double_click) { $self->on_double_click->(); } @@ -525,12 +528,18 @@ sub mouse_event { # my $volume_idx = $self->_hover_volume_idx // -1; #============================================================================================================================== $self->_layer_height_edited(0); - if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { +#============================================================================================================================== + if ($object_idx_selected != -1 && Slic3r::GUI::_3DScene::bar_rect_contains($self, $e->GetX, $e->GetY)) { +# if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { +#============================================================================================================================== # A volume is selected and the mouse is hovering over a layer thickness bar. # Start editing the layer height. $self->_layer_height_edited(1); $self->_variable_layer_thickness_action($e); - } elsif ($object_idx_selected != -1 && $self->_variable_layer_thickness_reset_rect_mouse_inside($e)) { +#============================================================================================================================== + } elsif ($object_idx_selected != -1 && Slic3r::GUI::_3DScene::reset_rect_contains($self, $e->GetX, $e->GetY)) { +# } elsif ($object_idx_selected != -1 && $self->_variable_layer_thickness_reset_rect_mouse_inside($e)) { +#============================================================================================================================== $self->{print}->get_object($object_idx_selected)->reset_layer_height_profile; # Index 2 means no editing, just wait for mouse up event. $self->_layer_height_edited(2); @@ -738,7 +747,10 @@ sub mouse_wheel_event { #============================================================================================================================== 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)) { +#============================================================================================================================== + if (Slic3r::GUI::_3DScene::bar_rect_contains($self, $e->GetX, $e->GetY)) { +# if ($self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { +#============================================================================================================================== # Adjust the width of the selection. #============================================================================================================================== Slic3r::GUI::_3DScene::set_layers_editing_band_width($self, max(min(Slic3r::GUI::_3DScene::get_layers_editing_band_width($self) * (1 + 0.1 * $e->GetWheelRotation() / $e->GetWheelDelta()), 10.), 1.5)); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 41aa1b77d1..89532677b5 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2080,6 +2080,16 @@ int _3DScene::get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, un return s_canvas_mgr.get_layers_editing_first_selected_object_id(canvas, objects_count); } +bool _3DScene::bar_rect_contains(wxGLCanvas* canvas, float x, float y) +{ + return s_canvas_mgr.bar_rect_contains(canvas, x, y); +} + +bool _3DScene::reset_rect_contains(wxGLCanvas* canvas, float x, float y) +{ + return s_canvas_mgr.reset_rect_contains(canvas, x, y); +} + void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 38dbfc8043..0f39aa5375 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -638,6 +638,8 @@ public: static float get_layers_editing_cursor_z_relative(wxGLCanvas* canvas); static int get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count); + static bool bar_rect_contains(wxGLCanvas* canvas, float x, float y); + static bool reset_rect_contains(wxGLCanvas* canvas, float x, float y); static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 3b218b88f2..3c264f25dd 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -775,58 +775,23 @@ int GLCanvas3D::LayersEditing::get_first_selected_object_id(const GLVolumeCollec return -1; } +bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, float x, float y) +{ + const Rect& rect = _get_bar_rect_screen(canvas); + return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); +} + +bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y) +{ + const Rect& rect = _get_reset_rect_screen(canvas); + return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); +} + bool GLCanvas3D::LayersEditing::_is_initialized() const { return m_shader.is_initialized(); } -GLCanvas3D::LayersEditing::GLTextureData GLCanvas3D::LayersEditing::_load_texture_from_file(const std::string& filename) const -{ - const std::string& path = resources_dir() + "/icons/"; - - // Load a PNG with an alpha channel. - wxImage image; - if (!image.LoadFile(path + filename, wxBITMAP_TYPE_PNG)) - return GLTextureData(); - - int width = image.GetWidth(); - int height = image.GetHeight(); - int n_pixels = width * height; - - if (n_pixels <= 0) - return GLTextureData(); - - // Get RGB & alpha raw data from wxImage, pack them into an array. - unsigned char* img_rgb = image.GetData(); - if (img_rgb == nullptr) - return GLTextureData(); - - unsigned char* img_alpha = image.GetAlpha(); - - std::vector data(n_pixels * 4, 0); - for (int i = 0; i < n_pixels; ++i) - { - int data_id = i * 4; - int img_id = i * 3; - data[data_id + 0] = img_rgb[img_id + 0]; - data[data_id + 1] = img_rgb[img_id + 1]; - data[data_id + 2] = img_rgb[img_id + 2]; - data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; - } - - // sends data to gpu - GLuint tex_id; - ::glGenTextures(1, &tex_id); - ::glBindTexture(GL_TEXTURE_2D, tex_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(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)width, (GLsizei)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); - ::glBindTexture(GL_TEXTURE_2D, 0); - - return GLTextureData((unsigned int)tex_id, width, height); -} - void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const { if (m_tooltip_texture.id == 0) @@ -957,6 +922,53 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, } } +GLCanvas3D::LayersEditing::GLTextureData GLCanvas3D::LayersEditing::_load_texture_from_file(const std::string& filename) +{ + const std::string& path = resources_dir() + "/icons/"; + + // Load a PNG with an alpha channel. + wxImage image; + if (!image.LoadFile(path + filename, wxBITMAP_TYPE_PNG)) + return GLTextureData(); + + int width = image.GetWidth(); + int height = image.GetHeight(); + int n_pixels = width * height; + + if (n_pixels <= 0) + return GLTextureData(); + + // Get RGB & alpha raw data from wxImage, pack them into an array. + unsigned char* img_rgb = image.GetData(); + if (img_rgb == nullptr) + return GLTextureData(); + + unsigned char* img_alpha = image.GetAlpha(); + + std::vector data(n_pixels * 4, 0); + for (int i = 0; i < n_pixels; ++i) + { + int data_id = i * 4; + int img_id = i * 3; + data[data_id + 0] = img_rgb[img_id + 0]; + data[data_id + 1] = img_rgb[img_id + 1]; + data[data_id + 2] = img_rgb[img_id + 2]; + data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; + } + + // sends data to gpu + GLuint tex_id; + ::glGenTextures(1, &tex_id); + ::glBindTexture(GL_TEXTURE_2D, tex_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(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)width, (GLsizei)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + ::glBindTexture(GL_TEXTURE_2D, 0); + + return GLTextureData((unsigned int)tex_id, width, height); +} + Rect GLCanvas3D::LayersEditing::_get_bar_rect_screen(const GLCanvas3D& canvas) { const Size& cnv_size = canvas.get_canvas_size(); @@ -1664,9 +1676,9 @@ GLShader* GLCanvas3D::get_layers_editing_shader() return m_layers_editing.get_shader(); } -float GLCanvas3D::get_layers_editing_cursor_z_relative(const GLCanvas3D& canvas) const +float GLCanvas3D::get_layers_editing_cursor_z_relative() const { - return m_layers_editing.get_cursor_z_relative(canvas); + return m_layers_editing.get_cursor_z_relative(*this); } int GLCanvas3D::get_layers_editing_first_selected_object_id(unsigned int objects_count) const @@ -1674,6 +1686,16 @@ int GLCanvas3D::get_layers_editing_first_selected_object_id(unsigned int objects return (m_volumes != nullptr) ? m_layers_editing.get_first_selected_object_id(*m_volumes, objects_count) : -1; } +bool GLCanvas3D::bar_rect_contains(float x, float y) const +{ + return m_layers_editing.bar_rect_contains(*this, x, y); +} + +bool GLCanvas3D::reset_rect_contains(float x, float y) const +{ + return m_layers_editing.reset_rect_contains(*this, x, y); +} + void GLCanvas3D::render_bed() const { m_bed.render(); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 9b01be0817..9a7c342c43 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -258,14 +258,16 @@ public: static float get_cursor_z_relative(const GLCanvas3D& canvas); static int get_first_selected_object_id(const GLVolumeCollection& volumes, unsigned int objects_count); + static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); + static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y); private: bool _is_initialized() const; - GLTextureData _load_texture_from_file(const std::string& filename) const; void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const; void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; + static GLTextureData _load_texture_from_file(const std::string& filename); static Rect _get_bar_rect_screen(const GLCanvas3D& canvas); static Rect _get_reset_rect_screen(const GLCanvas3D& canvas); static Rect _get_bar_rect_viewport(const GLCanvas3D& canvas); @@ -416,8 +418,10 @@ public: GLShader* get_layers_editing_shader(); - float get_layers_editing_cursor_z_relative(const GLCanvas3D& canvas) const; + float get_layers_editing_cursor_z_relative() const; int get_layers_editing_first_selected_object_id(unsigned int objects_count) const; + bool bar_rect_contains(float x, float y) const; + bool reset_rect_contains(float x, float y) const; void zoom_to_bed(); void zoom_to_volumes(); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 91963a9ad1..27390a14e7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -553,7 +553,7 @@ GLShader* GLCanvas3DManager::get_layers_editing_shader(wxGLCanvas* canvas) float GLCanvas3DManager::get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_cursor_z_relative(*it->second) : 0.0f; + return (it != m_canvases.end()) ? it->second->get_layers_editing_cursor_z_relative() : 0.0f; } int GLCanvas3DManager::get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count) const @@ -562,6 +562,18 @@ int GLCanvas3DManager::get_layers_editing_first_selected_object_id(wxGLCanvas* c return (it != m_canvases.end()) ? it->second->get_layers_editing_first_selected_object_id(objects_count) : 0.; } +bool GLCanvas3DManager::bar_rect_contains(wxGLCanvas* canvas, float x, float y) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->bar_rect_contains(x, y) : false; +} + +bool GLCanvas3DManager::reset_rect_contains(wxGLCanvas* canvas, float x, float y) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->reset_rect_contains(x, y) : false; +} + void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 5166da2e90..27807bd845 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -138,6 +138,8 @@ public: float get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) const; int get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count) const; + bool bar_rect_contains(wxGLCanvas* canvas, float x, float y) const; + bool reset_rect_contains(wxGLCanvas* canvas, float x, float y) const; void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 6cc9e0d6a1..fb0a549fd0 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -679,6 +679,25 @@ get_layers_editing_first_selected_object_id(canvas, objects_count) OUTPUT: RETVAL +bool +bar_rect_contains(canvas, x, y) + SV *canvas; + float x; + float y; + CODE: + RETVAL = _3DScene::bar_rect_contains((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), x, y); + OUTPUT: + RETVAL + +bool +reset_rect_contains(canvas, x, y) + SV *canvas; + float x; + float y; + CODE: + RETVAL = _3DScene::reset_rect_contains((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), x, y); + OUTPUT: + RETVAL void zoom_to_bed(canvas) From db260a669cf642826e3af4956e638c7eea6b71f3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 28 May 2018 15:23:01 +0200 Subject: [PATCH 038/117] 3DScene mouse wheel event moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 129 ++++++++++-------------- lib/Slic3r/GUI/Plater/3D.pm | 1 + xs/src/slic3r/GUI/3DScene.cpp | 10 +- xs/src/slic3r/GUI/3DScene.hpp | 2 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 84 +++++++++++---- xs/src/slic3r/GUI/GLCanvas3D.hpp | 7 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 14 +-- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 +- xs/xsp/GUI_3DScene.xsp | 15 ++- 9 files changed, 148 insertions(+), 116 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 8a80eeac72..435a4892b5 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -226,8 +226,8 @@ sub new { # $self->Resize( $self->GetSizeWH ); # $self->Refresh; # }); +# EVT_MOUSEWHEEL($self, \&mouse_wheel_event); #============================================================================================================================== - EVT_MOUSEWHEEL($self, \&mouse_wheel_event); EVT_MOUSE_EVENTS($self, \&mouse_event); #============================================================================================================================== ## EVT_KEY_DOWN($self, sub { @@ -392,12 +392,14 @@ sub _variable_layer_thickness_bar_rect_viewport { #============================================================================================================================== } -# 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); -} +#============================================================================================================================== +## 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) = @_; @@ -732,83 +734,60 @@ sub mouse_event { } } -sub mouse_wheel_event { - my ($self, $e) = @_; - - if ($e->MiddleIsDown) { - # Ignore the wheel events if the middle button is pressed. - return; - } #============================================================================================================================== - if (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $self->{print}) { - my $object_idx_selected = Slic3r::GUI::_3DScene::get_layers_editing_first_selected_object_id($self, $self->{print}->object_count); +#sub mouse_wheel_event { +# my ($self, $e) = @_; +# +# if ($e->MiddleIsDown) { +# # Ignore the wheel events if the middle button is pressed. +# return; +# } # if ($self->layer_editing_enabled && $self->{print}) { # my $object_idx_selected = $self->_first_selected_object_id_for_variable_layer_height_editing; -#============================================================================================================================== - if ($object_idx_selected != -1) { - # A volume is selected. Test, whether hovering over a layer thickness bar. -#============================================================================================================================== - if (Slic3r::GUI::_3DScene::bar_rect_contains($self, $e->GetX, $e->GetY)) { +# 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. -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_layers_editing_band_width($self, max(min(Slic3r::GUI::_3DScene::get_layers_editing_band_width($self) * (1 + 0.1 * $e->GetWheelRotation() / $e->GetWheelDelta()), 10.), 1.5)); +# # Adjust the width of the selection. # $self->{layer_height_edit_band_width} = max(min($self->{layer_height_edit_band_width} * (1 + 0.1 * $e->GetWheelRotation() / $e->GetWheelDelta()), 10.), 1.5); -#============================================================================================================================== - $self->Refresh; - return; - } - } - } - - # Calculate the zoom delta and apply it to the current zoom factor - my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta(); - $zoom = max(min($zoom, 4), -4); - $zoom /= 10; -#============================================================================================================================== - $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self) / (1-$zoom); +# $self->Refresh; +# return; +# } +# } +# } +# +# # Calculate the zoom delta and apply it to the current zoom factor +# my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta(); +# $zoom = max(min($zoom, 4), -4); +# $zoom /= 10; # $zoom = $self->_zoom / (1-$zoom); -#============================================================================================================================== - # Don't allow to zoom too far outside the scene. -#============================================================================================================================== - my $zoom_min = $self->get_zoom_to_bounding_box_factor(Slic3r::GUI::_3DScene::get_max_bounding_box($self)); +# # Don't allow to zoom too far outside the scene. # my $zoom_min = $self->get_zoom_to_bounding_box_factor($self->max_bounding_box); -#============================================================================================================================== - $zoom_min *= 0.4 if defined $zoom_min; - $zoom = $zoom_min if defined $zoom_min && $zoom < $zoom_min; -#============================================================================================================================== - $zoom = Slic3r::GUI::_3DScene::set_camera_zoom($self, $zoom); +# $zoom_min *= 0.4 if defined $zoom_min; +# $zoom = $zoom_min if defined $zoom_min && $zoom < $zoom_min; # $self->_zoom($zoom); -#============================================================================================================================== - -# # In order to zoom around the mouse point we need to translate -# # the camera target -# my $size = Slic3r::Pointf->new($self->GetSizeWH); -# my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #- -# $self->_camera_target->translate( -# # ($pos - $size/2) represents the vector from the viewport center -# # to the mouse point. By multiplying it by $zoom we get the new, -# # transformed, length of such vector. -# # Since we want that point to stay fixed, we move our camera target -# # in the opposite direction by the delta of the length of such vector -# # ($zoom - 1). We then scale everything by 1/$self->_zoom since -# # $self->_camera_target is expressed in terms of model units. -# -($pos->x - $size->x/2) * ($zoom) / $self->_zoom, -# -($pos->y - $size->y/2) * ($zoom) / $self->_zoom, -# 0, -# ) if 0; - - $self->on_viewport_changed->() if $self->on_viewport_changed; -#============================================================================================================================== -#============================================================================================================================== - Slic3r::GUI::_3DScene::resize($self, $self->GetSizeWH) if Slic3r::GUI::_3DScene::is_shown_on_screen($self); +# +## # In order to zoom around the mouse point we need to translate +## # the camera target +## my $size = Slic3r::Pointf->new($self->GetSizeWH); +## my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #- +## $self->_camera_target->translate( +## # ($pos - $size/2) represents the vector from the viewport center +## # to the mouse point. By multiplying it by $zoom we get the new, +## # transformed, length of such vector. +## # Since we want that point to stay fixed, we move our camera target +## # in the opposite direction by the delta of the length of such vector +## # ($zoom - 1). We then scale everything by 1/$self->_zoom since +## # $self->_camera_target is expressed in terms of model units. +## -($pos->x - $size->x/2) * ($zoom) / $self->_zoom, +## -($pos->y - $size->y/2) * ($zoom) / $self->_zoom, +## 0, +## ) if 0; +# +# $self->on_viewport_changed->() if $self->on_viewport_changed; # $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen; -#============================================================================================================================== - $self->Refresh; -} - -#============================================================================================================================== +# $self->Refresh; +#} +# ## Reset selection. #sub reset_objects { # my ($self) = @_; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 5d905c896a..8e6f2b81f5 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -32,6 +32,7 @@ sub new { $self->{print} = $print; $self->{config} = $config; #============================================================================================================================== + Slic3r::GUI::_3DScene::set_print($self, $print); Slic3r::GUI::_3DScene::set_config($self, $config); #============================================================================================================================== $self->{on_select_object} = sub {}; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 89532677b5..23f0f0194c 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1802,16 +1802,16 @@ void _3DScene::select_volume(wxGLCanvas* canvas, unsigned int id) s_canvas_mgr.select_volume(canvas, id); } -DynamicPrintConfig* _3DScene::get_config(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_config(canvas); -} - void _3DScene::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) { s_canvas_mgr.set_config(canvas, config); } +void _3DScene::set_print(wxGLCanvas* canvas, Print* print) +{ + s_canvas_mgr.set_print(canvas, print); +} + void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { return s_canvas_mgr.set_bed_shape(canvas, shape); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 0f39aa5375..973b5eeb25 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -559,8 +559,8 @@ public: static void deselect_volumes(wxGLCanvas* canvas); static void select_volume(wxGLCanvas* canvas, unsigned int id); - static DynamicPrintConfig* get_config(wxGLCanvas* canvas); static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); + static void set_print(wxGLCanvas* canvas, Print* print); static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); static void set_auto_bed_shape(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 3c264f25dd..cc968755ba 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1041,6 +1041,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_context(context) , m_volumes(nullptr) , m_config(nullptr) + , m_print(nullptr) , m_dirty(true) , m_apply_zoom_to_volumes_filter(false) , m_hover_volume_id(-1) @@ -1242,16 +1243,16 @@ void GLCanvas3D::select_volume(unsigned int id) } } -DynamicPrintConfig* GLCanvas3D::get_config() -{ - return m_config; -} - void GLCanvas3D::set_config(DynamicPrintConfig* config) { m_config = config; } +void GLCanvas3D::set_print(Print* print) +{ + m_print = print; +} + void GLCanvas3D::set_bed_shape(const Pointfs& shape) { m_bed.set_shape(shape); @@ -1946,15 +1947,10 @@ void GLCanvas3D::on_size(wxSizeEvent& evt) void GLCanvas3D::on_idle(wxIdleEvent& evt) { - if (!is_dirty() || !is_shown_on_screen()) + if (!is_dirty()) return; - if (m_canvas != nullptr) - { - const Size& cnv_size = get_canvas_size(); - resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); - m_canvas->Refresh(); - } + _refresh_if_shown_on_screen(); } void GLCanvas3D::on_char(wxKeyEvent& evt) @@ -1992,6 +1988,51 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) } } +void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) +{ + // Ignore the wheel events if the middle button is pressed. + if (evt.MiddleIsDown()) + return; + + // Performs layers editing updates, if enabled + if (is_layers_editing_enabled() && (m_print != nullptr)) + { + int object_idx_selected = get_layers_editing_first_selected_object_id((unsigned int)m_print->objects.size()); + if (object_idx_selected != -1) + { + // A volume is selected. Test, whether hovering over a layer thickness bar. + if (bar_rect_contains((float)evt.GetX(), (float)evt.GetY())) + { + // Adjust the width of the selection. + set_layers_editing_band_width(std::max(std::min(get_layers_editing_band_width() * (1.0f + 0.1f * (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta()), 10.0f), 1.5f)); + if (m_canvas != nullptr) + m_canvas->Refresh(); + + return; + } + } + } + + // Calculate the zoom delta and apply it to the current zoom factor + float zoom = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); + zoom = std::max(std::min(zoom, 4.0f), -4.0f) / 10.0f; + zoom = get_camera_zoom() / (1.0f - zoom); + + // Don't allow to zoom too far outside the scene. + float zoom_min = _get_zoom_to_bounding_box_factor(max_bounding_box()); + if (zoom_min > 0.0f) + { + zoom_min *= 0.4f; + if (zoom < zoom_min) + zoom = zoom_min; + } + + set_camera_zoom(zoom); + m_on_viewport_changed_callback.call(); + + _refresh_if_shown_on_screen(); +} + Size GLCanvas3D::get_canvas_size() const { int w = 0; @@ -2024,13 +2065,7 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) m_on_viewport_changed_callback.call(); - if (is_shown_on_screen()) - { - const Size& cnv_size = get_canvas_size(); - resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); - if (m_canvas != nullptr) - m_canvas->Refresh(); - } + _refresh_if_shown_on_screen(); } } @@ -2126,5 +2161,16 @@ void GLCanvas3D::_deregister_callbacks() m_on_mark_volumes_for_layer_height_callback.deregister_callback(); } +void GLCanvas3D::_refresh_if_shown_on_screen() +{ + if (is_shown_on_screen()) + { + const Size& cnv_size = get_canvas_size(); + resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); + if (m_canvas != nullptr) + m_canvas->Refresh(); + } +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 9a7c342c43..c5aa5a1cdc 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -10,6 +10,7 @@ class wxGLContext; class wxSizeEvent; class wxIdleEvent; class wxKeyEvent; +class wxMouseEvent; namespace Slic3r { @@ -302,6 +303,7 @@ private: GLVolumeCollection* m_volumes; DynamicPrintConfig* m_config; + Print* m_print; bool m_dirty; bool m_apply_zoom_to_volumes_filter; @@ -336,8 +338,8 @@ public: void deselect_volumes(); void select_volume(unsigned int id); - DynamicPrintConfig* get_config(); void set_config(DynamicPrintConfig* config); + void set_print(Print* print); // Set the bed shape to a single closed 2D polygon(array of two element arrays), // triangulate the bed and store the triangles into m_bed.m_triangles, @@ -450,6 +452,7 @@ public: void on_size(wxSizeEvent& evt); void on_idle(wxIdleEvent& evt); void on_char(wxKeyEvent& evt); + void on_mouse_wheel(wxMouseEvent& evt); Size get_canvas_size() const; Point get_local_mouse_position() const; @@ -459,6 +462,8 @@ private: float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; void _deregister_callbacks(); + + void _refresh_if_shown_on_screen(); }; } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 27390a14e7..fea8dab8a9 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -75,6 +75,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) canvas->Bind(wxEVT_SIZE, [canvas3D](wxSizeEvent& evt) { canvas3D->on_size(evt); }); canvas->Bind(wxEVT_IDLE, [canvas3D](wxIdleEvent& evt) { canvas3D->on_idle(evt); }); canvas->Bind(wxEVT_CHAR, [canvas3D](wxKeyEvent& evt) { canvas3D->on_char(evt); }); + canvas->Bind(wxEVT_MOUSEWHEEL, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse_wheel(evt); }); m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); @@ -203,12 +204,6 @@ void GLCanvas3DManager::select_volume(wxGLCanvas* canvas, unsigned int id) it->second->select_volume(id); } -DynamicPrintConfig* GLCanvas3DManager::get_config(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_config() : nullptr; -} - void GLCanvas3DManager::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -216,6 +211,13 @@ void GLCanvas3DManager::set_config(wxGLCanvas* canvas, DynamicPrintConfig* confi it->second->set_config(config); } +void GLCanvas3DManager::set_print(wxGLCanvas* canvas, Print* print) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_print(print); +} + void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 27807bd845..786c095e8f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -59,8 +59,8 @@ public: void deselect_volumes(wxGLCanvas* canvas); void select_volume(wxGLCanvas* canvas, unsigned int id); - DynamicPrintConfig* get_config(wxGLCanvas* canvas); void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); + void set_print(wxGLCanvas* canvas, Print* print); void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); void set_auto_bed_shape(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index fb0a549fd0..8aff20ae3d 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -264,14 +264,6 @@ select_volume(canvas, id) CODE: _3DScene::select_volume((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id); -DynamicPrintConfig* -get_config(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_config((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - void set_config(canvas, config) SV *canvas; @@ -279,6 +271,13 @@ set_config(canvas, config) CODE: _3DScene::set_config((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), config); +void +set_print(canvas, print) + SV *canvas; + Print *print; + CODE: + _3DScene::set_print((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print); + void set_bed_shape(canvas, shape) SV *canvas; From 363a964ebb0cc4247382e8cec53a309449c42510 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 29 May 2018 13:54:34 +0200 Subject: [PATCH 039/117] 3DScene render method partially moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 35 +- xs/src/libslic3r/Utils.hpp | 14 +- xs/src/libslic3r/utils.cpp | 20 +- xs/src/slic3r/GUI/3DScene.cpp | 45 +-- xs/src/slic3r/GUI/3DScene.hpp | 10 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 506 +++++++++++++----------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 22 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 60 +-- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 10 +- xs/xsp/GUI_3DScene.xsp | 54 +-- 10 files changed, 326 insertions(+), 450 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 435a4892b5..327d2976cb 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1482,31 +1482,20 @@ sub Render { my @rotmat = quat_to_rotmatrix($self->quat); glMultMatrixd_p(@rotmat[0..15]); } + #============================================================================================================================== - glTranslatef(@{ Slic3r::GUI::_3DScene::get_camera_target($self)->negative }); + Slic3r::GUI::_3DScene::render($self); + # glTranslatef(@{ $self->_camera_target->negative }); -#============================================================================================================================== - - # light from above - glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0); - glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1); - glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1); - - # Head light - glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0); - -#============================================================================================================================== - Slic3r::GUI::_3DScene::picking_pass($self); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - Slic3r::GUI::_3DScene::render_background($self); - Slic3r::GUI::_3DScene::render_bed($self); - Slic3r::GUI::_3DScene::render_axes($self); - Slic3r::GUI::_3DScene::render_objects($self, $self->UseVBOs); - Slic3r::GUI::_3DScene::render_cutting_plane($self); - Slic3r::GUI::_3DScene::render_warning_texture($self); - Slic3r::GUI::_3DScene::render_legend_texture($self); - Slic3r::GUI::_3DScene::render_layer_editing_overlay($self, $self->{print}); - +# +# # light from above +# glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0); +# glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1); +# glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1); +# +# # Head light +# glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0); +# # if ($self->enable_picking && !$self->_mouse_dragging) { # if (my $pos = $self->_mouse_pos) { # # Render the object for picking. diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index e5157741ec..8e4d654dea 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -91,10 +91,16 @@ public: ~PerlCallback() { this->deregister_callback(); } void register_callback(void *sv); void deregister_callback(); - void call(); - void call(int i); - void call(int i, int j); -// void call(const std::vector &ints); +//############################################################################################################## + void call() const; + void call(int i) const; + void call(int i, int j) const; + // void call(const std::vector &ints); +// void call(); +// void call(int i); +// void call(int i, int j); +//// void call(const std::vector &ints); +//############################################################################################################## private: void *m_callback; }; diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 582488c5a4..c691073a4b 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -184,7 +184,10 @@ void PerlCallback::deregister_callback() } } -void PerlCallback::call() +//############################################################################################################## +void PerlCallback::call() const +//void PerlCallback::call() +//############################################################################################################## { if (! m_callback) return; @@ -198,7 +201,10 @@ void PerlCallback::call() LEAVE; } -void PerlCallback::call(int i) +//############################################################################################################## +void PerlCallback::call(int i) const +//void PerlCallback::call(int i) +//############################################################################################################## { if (! m_callback) return; @@ -213,7 +219,10 @@ void PerlCallback::call(int i) LEAVE; } -void PerlCallback::call(int i, int j) +//############################################################################################################## +void PerlCallback::call(int i, int j) const +//void PerlCallback::call(int i, int j) +//############################################################################################################## { if (! m_callback) return; @@ -230,7 +239,10 @@ void PerlCallback::call(int i, int j) } /* -void PerlCallback::call(const std::vector &ints) +//############################################################################################################## +void PerlCallback::call(const std::vector &ints) const +//void PerlCallback::call(const std::vector &ints) +//############################################################################################################## { if (! m_callback) return; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 23f0f0194c..469c03cbc8 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2115,24 +2115,9 @@ void _3DScene::stop_using_shader(wxGLCanvas* canvas) s_canvas_mgr.stop_using_shader(canvas); } -void _3DScene::picking_pass(wxGLCanvas* canvas) +void _3DScene::render(wxGLCanvas* canvas) { - s_canvas_mgr.picking_pass(canvas); -} - -void _3DScene::render_background(wxGLCanvas* canvas) -{ - s_canvas_mgr.render_background(canvas); -} - -void _3DScene::render_bed(wxGLCanvas* canvas) -{ - s_canvas_mgr.render_bed(canvas); -} - -void _3DScene::render_axes(wxGLCanvas* canvas) -{ - s_canvas_mgr.render_axes(canvas); + s_canvas_mgr.render(canvas); } void _3DScene::render_volumes(wxGLCanvas* canvas, bool fake_colors) @@ -2140,32 +2125,6 @@ void _3DScene::render_volumes(wxGLCanvas* canvas, bool fake_colors) s_canvas_mgr.render_volumes(canvas, fake_colors); } -void _3DScene::render_objects(wxGLCanvas* canvas, bool useVBOs) -{ - s_canvas_mgr.render_objects(canvas, useVBOs); -} - -void _3DScene::render_cutting_plane(wxGLCanvas* canvas) -{ - s_canvas_mgr.render_cutting_plane(canvas); -} - -void _3DScene::render_warning_texture(wxGLCanvas* canvas) -{ - s_canvas_mgr.render_warning_texture(canvas); -} - -void _3DScene::render_legend_texture(wxGLCanvas* canvas) -{ - s_canvas_mgr.render_legend_texture(canvas); -} - -void _3DScene::render_layer_editing_overlay(wxGLCanvas* canvas, const Print* print) -{ - if (print != nullptr) - s_canvas_mgr.render_layer_editing_overlay(canvas, *print); -} - void _3DScene::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) { s_canvas_mgr.render_texture(canvas, tex_id, left, right, bottom, top); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 973b5eeb25..434f7a5b09 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -648,17 +648,9 @@ public: static bool start_using_shader(wxGLCanvas* canvas); static void stop_using_shader(wxGLCanvas* canvas); - static void picking_pass(wxGLCanvas* canvas); + static void render(wxGLCanvas* canvas); - static void render_background(wxGLCanvas* canvas); - static void render_bed(wxGLCanvas* canvas); - static void render_axes(wxGLCanvas* canvas); static void render_volumes(wxGLCanvas* canvas, bool fake_colors); - static void render_objects(wxGLCanvas* canvas, bool useVBOs); - static void render_cutting_plane(wxGLCanvas* canvas); - static void render_warning_texture(wxGLCanvas* canvas); - static void render_legend_texture(wxGLCanvas* canvas); - static void render_layer_editing_overlay(wxGLCanvas* canvas, const Print* print); static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index cc968755ba..406e8939c0 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1530,91 +1530,32 @@ void GLCanvas3D::stop_using_shader() const m_shader.stop_using(); } -void GLCanvas3D::picking_pass() +void GLCanvas3D::render(bool useVBOs) const { - if (is_picking_enabled() && !is_mouse_dragging() && (m_volumes != nullptr)) - { - // 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. + Pointf3 neg_target = get_camera_target().negative(); + ::glTranslatef((GLfloat)neg_target.x, (GLfloat)neg_target.y, (GLfloat)neg_target.z); - if (is_multisample_allowed()) - ::glDisable(GL_MULTISAMPLE); + // light from above + GLfloat position0[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; + ::glLightfv(GL_LIGHT0, GL_POSITION, position0); + GLfloat specular[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; + ::glLightfv(GL_LIGHT0, GL_SPECULAR, specular); + GLfloat diffuse[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; + ::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); - ::glDisable(GL_LIGHTING); - ::glDisable(GL_BLEND); + // Head light + GLfloat position1[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; + ::glLightfv(GL_LIGHT1, GL_POSITION, position1); - ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - ::glPushAttrib(GL_ENABLE_BIT); - - render_volumes(true); - - ::glPopAttrib(); - - if (is_multisample_allowed()) - ::glEnable(GL_MULTISAMPLE); - - const Size& cnv_size = get_canvas_size(); - - const Pointf& pos = get_mouse_position(); - GLubyte color[4]; - ::glReadPixels(pos.x, cnv_size.get_height() - pos.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); - int volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256; - - set_hover_volume_id(-1); - - for (GLVolume* vol : m_volumes->volumes) - { - vol->hover = false; - } - - if (volume_id < m_volumes->volumes.size()) - { - set_hover_volume_id(volume_id); - m_volumes->volumes[volume_id]->hover = true; - int group_id = m_volumes->volumes[volume_id]->select_group_id; - if (group_id != -1) - { - for (GLVolume* vol : m_volumes->volumes) - { - if (vol->select_group_id == group_id) - vol->hover = true; - } - } - } - } -} - -void GLCanvas3D::render_background() const -{ - static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f }; - - ::glDisable(GL_LIGHTING); - - ::glPushMatrix(); - ::glLoadIdentity(); - ::glMatrixMode(GL_PROJECTION); - ::glPushMatrix(); - ::glLoadIdentity(); - - // Draws a bluish bottom to top gradient over the complete screen. - ::glDisable(GL_DEPTH_TEST); - - ::glBegin(GL_QUADS); - ::glColor3f(0.0f, 0.0f, 0.0f); - ::glVertex3f(-1.0f, -1.0f, 1.0f); - ::glVertex3f(1.0f, -1.0f, 1.0f); - ::glColor3f(COLOR[0], COLOR[1], COLOR[2]); - ::glVertex3f(1.0f, 1.0f, 1.0f); - ::glVertex3f(-1.0f, 1.0f, 1.0f); - ::glEnd(); - - ::glEnable(GL_DEPTH_TEST); - - ::glPopMatrix(); - ::glMatrixMode(GL_MODELVIEW); - ::glPopMatrix(); + _picking_pass(); + _render_background(); + _render_bed(); + _render_axes(); + _render_objects(useVBOs); + _render_cutting_plane(); + _render_warning_texture(); + _render_legend_texture(); + _render_layer_editing_overlay(); } unsigned int GLCanvas3D::get_layers_editing_z_texture_id() const @@ -1697,16 +1638,6 @@ bool GLCanvas3D::reset_rect_contains(float x, float y) const return m_layers_editing.reset_rect_contains(*this, x, y); } -void GLCanvas3D::render_bed() const -{ - m_bed.render(); -} - -void GLCanvas3D::render_axes() const -{ - m_axes.render(); -} - void GLCanvas3D::render_volumes(bool fake_colors) const { static const float INV_255 = 1.0f / 255.0f; @@ -1756,153 +1687,6 @@ void GLCanvas3D::render_volumes(bool fake_colors) const ::glEnable(GL_CULL_FACE); } -void GLCanvas3D::render_objects(bool useVBOs) -{ - if ((m_volumes == nullptr) || m_volumes->empty()) - return; - - ::glEnable(GL_LIGHTING); - - if (!m_shader_enabled) - render_volumes(false); - else if (useVBOs) - { - if (is_picking_enabled()) - { - m_on_mark_volumes_for_layer_height_callback.call(); - - if (m_config != nullptr) - { - const BoundingBoxf3& bed_bb = bed_bounding_box(); - m_volumes->set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height")); - m_volumes->check_outside_state(m_config); - } - // do not cull backfaces to show broken geometry, if any - ::glDisable(GL_CULL_FACE); - } - - start_using_shader(); - m_volumes->render_VBOs(); - stop_using_shader(); - - if (is_picking_enabled()) - ::glEnable(GL_CULL_FACE); - } - else - { - // do not cull backfaces to show broken geometry, if any - if (is_picking_enabled()) - ::glDisable(GL_CULL_FACE); - - m_volumes->render_legacy(); - - if (is_picking_enabled()) - ::glEnable(GL_CULL_FACE); - } -} - -void GLCanvas3D::render_cutting_plane() const -{ - m_cutting_plane.render(volumes_bounding_box()); -} - -void GLCanvas3D::render_warning_texture() const -{ - if (!m_warning_texture_enabled) - return; - - // If the warning texture has not been loaded into the GPU, do it now. - unsigned int tex_id = _3DScene::finalize_warning_texture(); - if (tex_id > 0) - { - unsigned int w = _3DScene::get_warning_texture_width(); - unsigned int h = _3DScene::get_warning_texture_height(); - if ((w > 0) && (h > 0)) - { - ::glDisable(GL_DEPTH_TEST); - ::glPushMatrix(); - ::glLoadIdentity(); - - const Size& cnv_size = get_canvas_size(); - float zoom = get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - float l = (-0.5f * (float)w) * inv_zoom; - float t = (-0.5f * (float)cnv_size.get_height() + (float)h) * inv_zoom; - float r = l + (float)w * inv_zoom; - float b = t - (float)h * inv_zoom; - - render_texture(tex_id, l, r, b, t); - - ::glPopMatrix(); - ::glEnable(GL_DEPTH_TEST); - } - } -} - -void GLCanvas3D::render_legend_texture() const -{ - if (!m_legend_texture_enabled) - return; - - // If the legend texture has not been loaded into the GPU, do it now. - unsigned int tex_id = _3DScene::finalize_legend_texture(); - if (tex_id > 0) - { - unsigned int w = _3DScene::get_legend_texture_width(); - unsigned int h = _3DScene::get_legend_texture_height(); - if ((w > 0) && (h > 0)) - { - ::glDisable(GL_DEPTH_TEST); - ::glPushMatrix(); - ::glLoadIdentity(); - - const Size& cnv_size = get_canvas_size(); - float zoom = get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - float l = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; - float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom; - float r = l + (float)w * inv_zoom; - float b = t - (float)h * inv_zoom; - render_texture(tex_id, l, r, b, t); - - ::glPopMatrix(); - ::glEnable(GL_DEPTH_TEST); - } - } -} - -void GLCanvas3D::render_layer_editing_overlay(const Print& print) const -{ - if (m_volumes == nullptr) - return; - - GLVolume* volume = nullptr; - - for (GLVolume* vol : m_volumes->volumes) - { - if ((vol != nullptr) && vol->selected && vol->has_layer_height_texture()) - { - volume = vol; - break; - } - } - - if (volume == nullptr) - return; - - // 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. - int object_idx = int(volume->select_group_id / 1000000); - if ((int)print.objects.size() < object_idx) - return; - - const PrintObject* print_object = print.get_object(object_idx); - if (print_object == nullptr) - return; - - m_layers_editing.render(*this, *print_object, *volume); -} - void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const { ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); @@ -2172,5 +1956,251 @@ void GLCanvas3D::_refresh_if_shown_on_screen() } } +void GLCanvas3D::_picking_pass() const +{ + if (is_picking_enabled() && !is_mouse_dragging() && (m_volumes != nullptr)) + { + // 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. + + if (is_multisample_allowed()) + ::glDisable(GL_MULTISAMPLE); + + ::glDisable(GL_LIGHTING); + ::glDisable(GL_BLEND); + + ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + ::glPushAttrib(GL_ENABLE_BIT); + + render_volumes(true); + + ::glPopAttrib(); + + if (is_multisample_allowed()) + ::glEnable(GL_MULTISAMPLE); + + const Size& cnv_size = get_canvas_size(); + + const Pointf& pos = get_mouse_position(); + GLubyte color[4]; + ::glReadPixels(pos.x, cnv_size.get_height() - pos.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); + int volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256; + + m_hover_volume_id = -1; + + for (GLVolume* vol : m_volumes->volumes) + { + vol->hover = false; + } + + if (volume_id < m_volumes->volumes.size()) + { + m_hover_volume_id = volume_id; + m_volumes->volumes[volume_id]->hover = true; + int group_id = m_volumes->volumes[volume_id]->select_group_id; + if (group_id != -1) + { + for (GLVolume* vol : m_volumes->volumes) + { + if (vol->select_group_id == group_id) + vol->hover = true; + } + } + } + } +} + +void GLCanvas3D::_render_background() const +{ + ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f }; + + ::glDisable(GL_LIGHTING); + + ::glPushMatrix(); + ::glLoadIdentity(); + ::glMatrixMode(GL_PROJECTION); + ::glPushMatrix(); + ::glLoadIdentity(); + + // Draws a bluish bottom to top gradient over the complete screen. + ::glDisable(GL_DEPTH_TEST); + + ::glBegin(GL_QUADS); + ::glColor3f(0.0f, 0.0f, 0.0f); + ::glVertex3f(-1.0f, -1.0f, 1.0f); + ::glVertex3f(1.0f, -1.0f, 1.0f); + ::glColor3f(COLOR[0], COLOR[1], COLOR[2]); + ::glVertex3f(1.0f, 1.0f, 1.0f); + ::glVertex3f(-1.0f, 1.0f, 1.0f); + ::glEnd(); + + ::glEnable(GL_DEPTH_TEST); + + ::glPopMatrix(); + ::glMatrixMode(GL_MODELVIEW); + ::glPopMatrix(); +} + +void GLCanvas3D::_render_bed() const +{ + m_bed.render(); +} + +void GLCanvas3D::_render_axes() const +{ + m_axes.render(); +} + +void GLCanvas3D::_render_objects(bool useVBOs) const +{ + if ((m_volumes == nullptr) || m_volumes->empty()) + return; + + ::glEnable(GL_LIGHTING); + + if (!m_shader_enabled) + render_volumes(false); + else if (useVBOs) + { + if (is_picking_enabled()) + { + m_on_mark_volumes_for_layer_height_callback.call(); + + if (m_config != nullptr) + { + const BoundingBoxf3& bed_bb = bed_bounding_box(); + m_volumes->set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height")); + m_volumes->check_outside_state(m_config); + } + // do not cull backfaces to show broken geometry, if any + ::glDisable(GL_CULL_FACE); + } + + start_using_shader(); + m_volumes->render_VBOs(); + stop_using_shader(); + + if (is_picking_enabled()) + ::glEnable(GL_CULL_FACE); + } + else + { + // do not cull backfaces to show broken geometry, if any + if (is_picking_enabled()) + ::glDisable(GL_CULL_FACE); + + m_volumes->render_legacy(); + + if (is_picking_enabled()) + ::glEnable(GL_CULL_FACE); + } +} + +void GLCanvas3D::_render_cutting_plane() const +{ + m_cutting_plane.render(volumes_bounding_box()); +} + +void GLCanvas3D::_render_warning_texture() const +{ + if (!m_warning_texture_enabled) + return; + + // If the warning texture has not been loaded into the GPU, do it now. + unsigned int tex_id = _3DScene::finalize_warning_texture(); + if (tex_id > 0) + { + unsigned int w = _3DScene::get_warning_texture_width(); + unsigned int h = _3DScene::get_warning_texture_height(); + if ((w > 0) && (h > 0)) + { + ::glDisable(GL_DEPTH_TEST); + ::glPushMatrix(); + ::glLoadIdentity(); + + const Size& cnv_size = get_canvas_size(); + float zoom = get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + float l = (-0.5f * (float)w) * inv_zoom; + float t = (-0.5f * (float)cnv_size.get_height() + (float)h) * inv_zoom; + float r = l + (float)w * inv_zoom; + float b = t - (float)h * inv_zoom; + + render_texture(tex_id, l, r, b, t); + + ::glPopMatrix(); + ::glEnable(GL_DEPTH_TEST); + } + } +} + +void GLCanvas3D::_render_legend_texture() const +{ + if (!m_legend_texture_enabled) + return; + + // If the legend texture has not been loaded into the GPU, do it now. + unsigned int tex_id = _3DScene::finalize_legend_texture(); + if (tex_id > 0) + { + unsigned int w = _3DScene::get_legend_texture_width(); + unsigned int h = _3DScene::get_legend_texture_height(); + if ((w > 0) && (h > 0)) + { + ::glDisable(GL_DEPTH_TEST); + ::glPushMatrix(); + ::glLoadIdentity(); + + const Size& cnv_size = get_canvas_size(); + float zoom = get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + float l = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; + float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom; + float r = l + (float)w * inv_zoom; + float b = t - (float)h * inv_zoom; + render_texture(tex_id, l, r, b, t); + + ::glPopMatrix(); + ::glEnable(GL_DEPTH_TEST); + } + } +} + +void GLCanvas3D::_render_layer_editing_overlay() const +{ + if ((m_volumes == nullptr) && (m_print == nullptr)) + return; + + GLVolume* volume = nullptr; + + for (GLVolume* vol : m_volumes->volumes) + { + if ((vol != nullptr) && vol->selected && vol->has_layer_height_texture()) + { + volume = vol; + break; + } + } + + if (volume == nullptr) + return; + + // 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. + int object_idx = int(volume->select_group_id / 1000000); + if ((int)m_print->objects.size() < object_idx) + return; + + const PrintObject* print_object = m_print->get_object(object_idx); + if (print_object == nullptr) + return; + + m_layers_editing.render(*this, *print_object, *volume); +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index c5aa5a1cdc..d5065986c7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -307,7 +307,7 @@ private: bool m_dirty; bool m_apply_zoom_to_volumes_filter; - int m_hover_volume_id; + mutable int m_hover_volume_id; bool m_warning_texture_enabled; bool m_legend_texture_enabled; bool m_picking_enabled; @@ -432,17 +432,9 @@ public: bool start_using_shader() const; void stop_using_shader() const; - void picking_pass(); + void render(bool useVBOs) const; - void render_background() const; - void render_bed() const; - void render_axes() const; void render_volumes(bool fake_colors) const; - void render_objects(bool useVBOs); - void render_cutting_plane() const; - void render_warning_texture() const; - void render_legend_texture() const; - void render_layer_editing_overlay(const Print& print) const; void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; @@ -464,6 +456,16 @@ private: void _deregister_callbacks(); void _refresh_if_shown_on_screen(); + + void _picking_pass() const; + void _render_background() const; + void _render_bed() const; + void _render_axes() const; + void _render_objects(bool useVBOs) const; + void _render_cutting_plane() const; + void _render_warning_texture() const; + void _render_legend_texture() const; + void _render_layer_editing_overlay() const; }; } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index fea8dab8a9..9d2e3733e6 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -610,32 +610,11 @@ void GLCanvas3DManager::stop_using_shader(wxGLCanvas* canvas) const it->second->stop_using_shader(); } -void GLCanvas3DManager::picking_pass(wxGLCanvas* canvas) +void GLCanvas3DManager::render(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) - it->second->picking_pass(); -} - -void GLCanvas3DManager::render_background(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render_background(); -} - -void GLCanvas3DManager::render_bed(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render_bed(); -} - -void GLCanvas3DManager::render_axes(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render_axes(); + it->second->render(m_use_VBOs); } void GLCanvas3DManager::render_volumes(wxGLCanvas* canvas, bool fake_colors) const @@ -645,41 +624,6 @@ void GLCanvas3DManager::render_volumes(wxGLCanvas* canvas, bool fake_colors) con it->second->render_volumes(fake_colors); } -void GLCanvas3DManager::render_objects(wxGLCanvas* canvas, bool useVBOs) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render_objects(useVBOs); -} - -void GLCanvas3DManager::render_cutting_plane(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render_cutting_plane(); -} - -void GLCanvas3DManager::render_warning_texture(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render_warning_texture(); -} - -void GLCanvas3DManager::render_legend_texture(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render_legend_texture(); -} - -void GLCanvas3DManager::render_layer_editing_overlay(wxGLCanvas* canvas, const Print& print) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render_layer_editing_overlay(print); -} - void GLCanvas3DManager::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 786c095e8f..dcb10e63b2 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -148,17 +148,9 @@ public: bool start_using_shader(wxGLCanvas* canvas) const; void stop_using_shader(wxGLCanvas* canvas) const; - void picking_pass(wxGLCanvas* canvas); + void render(wxGLCanvas* canvas) const; - void render_background(wxGLCanvas* canvas) const; - void render_bed(wxGLCanvas* canvas) const; - void render_axes(wxGLCanvas* canvas) const; void render_volumes(wxGLCanvas* canvas, bool fake_colors) const; - void render_objects(wxGLCanvas* canvas, bool useVBOs); - void render_cutting_plane(wxGLCanvas* canvas) const; - void render_warning_texture(wxGLCanvas* canvas) const; - void render_legend_texture(wxGLCanvas* canvas) const; - void render_layer_editing_overlay(wxGLCanvas* canvas, const Print& print) const; void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 8aff20ae3d..fc57ce89b1 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -732,28 +732,10 @@ stop_using_shader(canvas) _3DScene::stop_using_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); void -picking_pass(canvas) +render(canvas) SV *canvas; CODE: - _3DScene::picking_pass((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - -void -render_background(canvas) - SV *canvas; - CODE: - _3DScene::render_background((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - -void -render_bed(canvas) - SV *canvas; - CODE: - _3DScene::render_bed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - -void -render_axes(canvas) - SV *canvas; - CODE: - _3DScene::render_axes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + _3DScene::render((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); void render_volumes(canvas, fake_colors) @@ -762,38 +744,6 @@ render_volumes(canvas, fake_colors) CODE: _3DScene::render_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), fake_colors); -void -render_objects(canvas, useVBOs) - SV *canvas; - bool useVBOs; - CODE: - _3DScene::render_objects((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), useVBOs); - -void -render_cutting_plane(canvas) - SV *canvas; - CODE: - _3DScene::render_cutting_plane((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - -void -render_warning_texture(canvas) - SV *canvas; - CODE: - _3DScene::render_warning_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - -void -render_legend_texture(canvas) - SV *canvas; - CODE: - _3DScene::render_legend_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - -void -render_layer_editing_overlay(canvas, print) - SV *canvas; - Print *print; - CODE: - _3DScene::render_layer_editing_overlay((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print); - void render_texture(canvas, tex_id, left, right, bottom, top) SV *canvas; From c3b1eca2c73ce5c206ebe3813e4e9a339bbf2f11 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 29 May 2018 14:09:02 +0200 Subject: [PATCH 040/117] Fixed a crash --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 406e8939c0..1c8d87d2c7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -2172,7 +2172,7 @@ void GLCanvas3D::_render_legend_texture() const void GLCanvas3D::_render_layer_editing_overlay() const { - if ((m_volumes == nullptr) && (m_print == nullptr)) + if ((m_volumes == nullptr) || (m_print == nullptr)) return; GLVolume* volume = nullptr; From 5ee5465f94dc3f0adc67009e0991f510d1a836df Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 29 May 2018 14:34:45 +0200 Subject: [PATCH 041/117] 3DScene mark_volumes_for_layer_height method moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 49 ++++++++----------------- xs/src/slic3r/GUI/3DScene.cpp | 7 +--- xs/src/slic3r/GUI/3DScene.hpp | 3 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 41 +++++++++++++++------ xs/src/slic3r/GUI/GLCanvas3D.hpp | 9 ++--- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 9 +---- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 3 +- xs/xsp/GUI_3DScene.xsp | 7 ---- 8 files changed, 53 insertions(+), 75 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 327d2976cb..0fd70cd916 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -273,14 +273,6 @@ sub new { $self->_variable_layer_thickness_action(undef); }); -#============================================================================================================================== - my $on_mark_volumes_for_layer_height = sub { - $self->mark_volumes_for_layer_height; - }; - - Slic3r::GUI::_3DScene::register_on_mark_volumes_for_layer_height_callback($self, $on_mark_volumes_for_layer_height); -#============================================================================================================================== - return $self; } @@ -1677,9 +1669,9 @@ sub Render { # $self->draw_legend; # # $self->draw_active_object_annotations; +# +# $self->SwapBuffers(); #============================================================================================================================== - - $self->SwapBuffers(); } #============================================================================================================================== @@ -1721,34 +1713,23 @@ sub Render { # glDisable(GL_BLEND); # glEnable(GL_CULL_FACE); #} -#============================================================================================================================== - -sub mark_volumes_for_layer_height { - my ($self) = @_; - - foreach my $volume_idx (0..$#{$self->volumes}) { - my $volume = $self->volumes->[$volume_idx]; - my $object_id = int($volume->select_group_id / 1000000); -#============================================================================================================================== - my $shader = Slic3r::GUI::_3DScene::get_layers_editing_shader($self); - - if (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $shader && $volume->selected && - $volume->has_layer_height_texture && $object_id < $self->{print}->object_count) { - $volume->set_layer_height_texture_data(Slic3r::GUI::_3DScene::get_layers_editing_z_texture_id($self), $shader->shader_program_id, - $self->{print}->get_object($object_id), Slic3r::GUI::_3DScene::get_layers_editing_cursor_z_relative($self), Slic3r::GUI::_3DScene::get_layers_editing_band_width($self)); - +# +#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(); - } - } -} - -#============================================================================================================================== +# } else { +# $volume->reset_layer_height_texture_data(); +# } +# } +#} +# #sub _load_image_set_texture { # my ($self, $file_name) = @_; # # Load a PNG with an alpha channel. diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 469c03cbc8..79ddd6acd7 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2065,7 +2065,7 @@ void _3DScene::set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int a s_canvas_mgr.set_layers_editing_last_action(canvas, action); } -GLShader* _3DScene::get_layers_editing_shader(wxGLCanvas* canvas) +const GLShader* _3DScene::get_layers_editing_shader(wxGLCanvas* canvas) { return s_canvas_mgr.get_layers_editing_shader(canvas); } @@ -2135,11 +2135,6 @@ void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* c s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); } -void _3DScene::register_on_mark_volumes_for_layer_height_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_mark_volumes_for_layer_height_callback(canvas, callback); -} - //void _3DScene::_glew_init() //{ // glewInit(); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 434f7a5b09..ab62723b74 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -634,7 +634,7 @@ public: static unsigned int get_layers_editing_last_action(wxGLCanvas* canvas); static void set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int action); - static GLShader* get_layers_editing_shader(wxGLCanvas* canvas); + static const GLShader* get_layers_editing_shader(wxGLCanvas* canvas); static float get_layers_editing_cursor_z_relative(wxGLCanvas* canvas); static int get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count); @@ -655,7 +655,6 @@ public: static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); - static void register_on_mark_volumes_for_layer_height_callback(wxGLCanvas* canvas, void* callback); // static void _glew_init(); //################################################################################################################## diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 1c8d87d2c7..9487c3fccd 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -559,7 +559,7 @@ void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const m_shader->set_uniform(name.c_str(), value); } -GLShader* GLCanvas3D::Shader::get_shader() +const GLShader* GLCanvas3D::Shader::get_shader() const { return m_shader; } @@ -739,7 +739,7 @@ void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObje glEnable(GL_DEPTH_TEST); } -GLShader* GLCanvas3D::LayersEditing::get_shader() +const GLShader* GLCanvas3D::LayersEditing::get_shader() const { return m_shader.get_shader(); } @@ -1532,6 +1532,9 @@ void GLCanvas3D::stop_using_shader() const void GLCanvas3D::render(bool useVBOs) const { + if (m_canvas == nullptr) + return; + Pointf3 neg_target = get_camera_target().negative(); ::glTranslatef((GLfloat)neg_target.x, (GLfloat)neg_target.y, (GLfloat)neg_target.z); @@ -1556,6 +1559,8 @@ void GLCanvas3D::render(bool useVBOs) const _render_warning_texture(); _render_legend_texture(); _render_layer_editing_overlay(); + + m_canvas->SwapBuffers(); } unsigned int GLCanvas3D::get_layers_editing_z_texture_id() const @@ -1613,7 +1618,7 @@ void GLCanvas3D::set_layers_editing_last_action(unsigned int action) m_layers_editing.set_last_action(action); } -GLShader* GLCanvas3D::get_layers_editing_shader() +const GLShader* GLCanvas3D::get_layers_editing_shader() const { return m_layers_editing.get_shader(); } @@ -1718,12 +1723,6 @@ void GLCanvas3D::register_on_viewport_changed_callback(void* callback) m_on_viewport_changed_callback.register_callback(callback); } -void GLCanvas3D::register_on_mark_volumes_for_layer_height_callback(void* callback) -{ - if (callback != nullptr) - m_on_mark_volumes_for_layer_height_callback.register_callback(callback); -} - void GLCanvas3D::on_size(wxSizeEvent& evt) { set_dirty(true); @@ -1942,7 +1941,27 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co void GLCanvas3D::_deregister_callbacks() { m_on_viewport_changed_callback.deregister_callback(); - m_on_mark_volumes_for_layer_height_callback.deregister_callback(); +} + +void GLCanvas3D::_mark_volumes_for_layer_height() const +{ + if ((m_volumes == nullptr) || (m_print == nullptr)) + return; + + for (GLVolume* vol : m_volumes->volumes) + { + int object_id = int(vol->select_group_id / 1000000); + const GLShader* shader = get_layers_editing_shader(); + + if (is_layers_editing_enabled() && (shader != nullptr) && vol->selected && + vol->has_layer_height_texture() && (object_id < (int)m_print->objects.size())) + { + vol->set_layer_height_texture_data(get_layers_editing_z_texture_id(), shader->shader_program_id, + m_print->get_object(object_id), get_layers_editing_cursor_z_relative(), get_layers_editing_band_width()); + } + else + vol->reset_layer_height_texture_data(); + } } void GLCanvas3D::_refresh_if_shown_on_screen() @@ -2068,7 +2087,7 @@ void GLCanvas3D::_render_objects(bool useVBOs) const { if (is_picking_enabled()) { - m_on_mark_volumes_for_layer_height_callback.call(); + _mark_volumes_for_layer_height(); if (m_config != nullptr) { diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index d5065986c7..b0985e71e6 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -194,7 +194,7 @@ public: void set_uniform(const std::string& name, float value) const; - GLShader* get_shader(); + const GLShader* get_shader() const; private: void _reset(); @@ -255,7 +255,7 @@ public: void render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const; - GLShader* get_shader(); + const GLShader* get_shader() const; static float get_cursor_z_relative(const GLCanvas3D& canvas); static int get_first_selected_object_id(const GLVolumeCollection& volumes, unsigned int objects_count); @@ -315,7 +315,6 @@ private: bool m_multisample_allowed; PerlCallback m_on_viewport_changed_callback; - PerlCallback m_on_mark_volumes_for_layer_height_callback; public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); @@ -418,7 +417,7 @@ public: unsigned int get_layers_editing_last_action() const; void set_layers_editing_last_action(unsigned int action); - GLShader* get_layers_editing_shader(); + const GLShader* get_layers_editing_shader() const; float get_layers_editing_cursor_z_relative() const; int get_layers_editing_first_selected_object_id(unsigned int objects_count) const; @@ -439,7 +438,6 @@ public: void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(void* callback); - void register_on_mark_volumes_for_layer_height_callback(void* callback); void on_size(wxSizeEvent& evt); void on_idle(wxIdleEvent& evt); @@ -455,6 +453,7 @@ private: void _deregister_callbacks(); + void _mark_volumes_for_layer_height() const; void _refresh_if_shown_on_screen(); void _picking_pass() const; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 9d2e3733e6..0cce911579 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -546,7 +546,7 @@ void GLCanvas3DManager::set_layers_editing_last_action(wxGLCanvas* canvas, unsig it->second->set_layers_editing_last_action(action); } -GLShader* GLCanvas3DManager::get_layers_editing_shader(wxGLCanvas* canvas) +const GLShader* GLCanvas3DManager::get_layers_editing_shader(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); return (it != m_canvases.end()) ? it->second->get_layers_editing_shader() : nullptr; @@ -638,13 +638,6 @@ void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas it->second->register_on_viewport_changed_callback(callback); } -void GLCanvas3DManager::register_on_mark_volumes_for_layer_height_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_mark_volumes_for_layer_height_callback(callback); -} - GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index dcb10e63b2..a3aeab9577 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -134,7 +134,7 @@ public: unsigned int get_layers_editing_last_action(wxGLCanvas* canvas) const; void set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int action); - GLShader* get_layers_editing_shader(wxGLCanvas* canvas); + const GLShader* get_layers_editing_shader(wxGLCanvas* canvas) const; float get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) const; int get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count) const; @@ -155,7 +155,6 @@ public: void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); - void register_on_mark_volumes_for_layer_height_callback(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index fc57ce89b1..2ca1e614e3 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -762,13 +762,6 @@ register_on_viewport_changed_callback(canvas, callback) CODE: _3DScene::register_on_viewport_changed_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); -void -register_on_mark_volumes_for_layer_height_callback(canvas, callback) - SV *canvas; - SV *callback; - CODE: - _3DScene::register_on_mark_volumes_for_layer_height_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); - unsigned int From df14a3c3991525ad850af4b0f5aa1c159765e9c2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 29 May 2018 15:07:06 +0200 Subject: [PATCH 042/117] 3DScene update_volumes_colors_by_extruder method moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 10 +++++----- lib/Slic3r/GUI/Plater/3D.pm | 7 +++++-- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 7 +++++-- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 22 ++++++++++++++++------ xs/src/slic3r/GUI/3DScene.cpp | 5 +++++ xs/src/slic3r/GUI/3DScene.hpp | 6 +++--- xs/src/slic3r/GUI/GLCanvas3D.cpp | 8 ++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 4 ++-- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 7 +++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 4 ++-- xs/xsp/GUI_3DScene.xsp | 6 ++++++ 11 files changed, 64 insertions(+), 22 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 0fd70cd916..991371182e 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1985,13 +1985,13 @@ sub Render { # } # } #} +# +#sub update_volumes_colors_by_extruder { +# my ($self, $config) = @_; +# $self->volumes->update_colors_by_extruder($config); +#} #============================================================================================================================== -sub update_volumes_colors_by_extruder { - my ($self, $config) = @_; - $self->volumes->update_colors_by_extruder($config); -} - sub opengl_info { my ($self, %params) = @_; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 8e6f2b81f5..a8db8ccbf1 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -244,8 +244,11 @@ sub reload_scene { $self->{model}->bounding_box->z_max, $self->{config}->wipe_tower_rotation_angle, $self->UseVBOs); } } - - $self->update_volumes_colors_by_extruder($self->{config}); + +#============================================================================================================================== + Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self); +# $self->update_volumes_colors_by_extruder($self->{config}); +#============================================================================================================================== # checks for geometry outside the print volume to render it accordingly if (scalar @{$self->volumes} > 0) diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index ab53da4a5b..bbb3543bb7 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -119,12 +119,14 @@ sub new { #============================================================================================================================== Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); -# Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ $canvas->volumes_bounding_box->size })); # $canvas->set_auto_bed_shape; #============================================================================================================================== $canvas->SetSize([500,500]); $canvas->SetMinSize($canvas->GetSize); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->{config}); +#============================================================================================================================== } $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); @@ -264,12 +266,13 @@ sub _update { $self->{canvas}->load_object($_, undef, undef, [0]) for @objects; #============================================================================================================================== Slic3r::GUI::_3DScene::set_cutting_plane($self->{canvas}, $self->{cut_options}{z}, [@expolygons]); + Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); # $self->{canvas}->SetCuttingPlane( # $self->{cut_options}{z}, # [@expolygons], # ); +# $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->{config}); #============================================================================================================================== - $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->{config}); $self->{canvas}->Render; } } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index bab628e231..c2e7be5f26 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -169,12 +169,15 @@ sub new { #============================================================================================================================== Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); -# Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ $canvas->volumes_bounding_box->size })); # $canvas->set_auto_bed_shape; #============================================================================================================================== $canvas->SetSize([500,700]); - $canvas->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->GetParent->GetParent->{config}); + Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($canvas); +# $canvas->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); +#============================================================================================================================== } $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); @@ -506,9 +509,10 @@ sub _parts_changed { $self->{canvas}->load_object($self->{model_object}); #============================================================================================================================== Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas}); + Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); # $self->{canvas}->zoom_to_volumes; +# $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); #============================================================================================================================== - $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); $self->{canvas}->Render; } } @@ -562,8 +566,11 @@ sub _update_canvas { $self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1); } } - - $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); + +#============================================================================================================================== + Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); +# $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); +#============================================================================================================================== $self->{canvas}->Render; } } @@ -591,7 +598,10 @@ sub _update { # $self->{canvas}->reset_objects; #============================================================================================================================== $self->{canvas}->load_object($_, undef, [0]) for @objects; - $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); +#============================================================================================================================== + Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); +# $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); +#============================================================================================================================== $self->{canvas}->Render; } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 79ddd6acd7..a7b7d6e34e 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2105,6 +2105,11 @@ void _3DScene::select_view(wxGLCanvas* canvas, const std::string& direction) s_canvas_mgr.select_view(canvas, direction); } +void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas) +{ + s_canvas_mgr.update_volumes_colors_by_extruder(canvas); +} + bool _3DScene::start_using_shader(wxGLCanvas* canvas) { return s_canvas_mgr.start_using_shader(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index ab62723b74..69f48e0922 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -644,14 +644,14 @@ public: static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); - + + static void update_volumes_colors_by_extruder(wxGLCanvas* canvas); + static bool start_using_shader(wxGLCanvas* canvas); static void stop_using_shader(wxGLCanvas* canvas); static void render(wxGLCanvas* canvas); - static void render_volumes(wxGLCanvas* canvas, bool fake_colors); - static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 9487c3fccd..28f105d3ce 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1520,6 +1520,14 @@ void GLCanvas3D::select_view(const std::string& direction) } } +void GLCanvas3D::update_volumes_colors_by_extruder() +{ + if ((m_volumes == nullptr) || (m_config == nullptr)) + return; + + m_volumes->update_colors_by_extruder(m_config); +} + bool GLCanvas3D::start_using_shader() const { return m_shader.start_using(); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index b0985e71e6..620f688585 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -428,13 +428,13 @@ public: void zoom_to_volumes(); void select_view(const std::string& direction); + void update_volumes_colors_by_extruder(); + bool start_using_shader() const; void stop_using_shader() const; void render(bool useVBOs) const; - void render_volumes(bool fake_colors) const; - void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 0cce911579..27aaa31889 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -597,6 +597,13 @@ void GLCanvas3DManager::select_view(wxGLCanvas* canvas, const std::string& direc it->second->select_view(direction); } +void GLCanvas3DManager::update_volumes_colors_by_extruder(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->update_volumes_colors_by_extruder(); +} + bool GLCanvas3DManager::start_using_shader(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index a3aeab9577..b3580ec63c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -145,13 +145,13 @@ public: void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); + void update_volumes_colors_by_extruder(wxGLCanvas* canvas); + bool start_using_shader(wxGLCanvas* canvas) const; void stop_using_shader(wxGLCanvas* canvas) const; void render(wxGLCanvas* canvas) const; - void render_volumes(wxGLCanvas* canvas, bool fake_colors) const; - void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 2ca1e614e3..7e1e80d4c7 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -717,6 +717,12 @@ select_view(canvas, direction) CODE: _3DScene::select_view((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), direction); +void +update_volumes_colors_by_extruder(canvas) + SV *canvas; + CODE: + _3DScene::update_volumes_colors_by_extruder((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + bool start_using_shader(canvas) SV *canvas; From 2f773a89df2f8838bb006cf37f5e62cdff38a3b3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 29 May 2018 15:36:09 +0200 Subject: [PATCH 043/117] 3DScene set_viewport_from_scene method moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 199 ++++++++++-------------- lib/Slic3r/GUI/Plater.pm | 20 ++- xs/src/slic3r/GUI/3DScene.cpp | 5 + xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 9 ++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 11 ++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 1 + xs/xsp/GUI_3DScene.xsp | 7 + 9 files changed, 135 insertions(+), 119 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 991371182e..a6ec2e6920 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -790,31 +790,19 @@ sub mouse_event { # $self->volumes->erase; # $self->_dirty(1); #} -#============================================================================================================================== - -# Setup camera to view all objects. -sub set_viewport_from_scene { - my ($self, $scene) = @_; - -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_camera_phi($self, Slic3r::GUI::_3DScene::get_camera_phi($scene)); - Slic3r::GUI::_3DScene::set_camera_theta($self, Slic3r::GUI::_3DScene::get_camera_theta($scene)); - Slic3r::GUI::_3DScene::set_camera_target($self, Slic3r::GUI::_3DScene::get_camera_target($scene)); - Slic3r::GUI::_3DScene::set_camera_zoom($self, Slic3r::GUI::_3DScene::get_camera_zoom($scene)); - +# +## Setup camera to view all objects. +#sub set_viewport_from_scene { +# my ($self, $scene) = @_; +# # $self->_sphi($scene->_sphi); # $self->_stheta($scene->_stheta); # $self->_camera_target($scene->_camera_target); # $self->_zoom($scene->_zoom); -#============================================================================================================================== - $self->_quat($scene->_quat); -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_dirty($self, 1); +# $self->_quat($scene->_quat); # $self->_dirty(1); -#============================================================================================================================== -} - -#============================================================================================================================== +#} +# ## Set the camera to a default orientation, ## zoom to volumes. #sub select_view { @@ -851,104 +839,89 @@ sub set_viewport_from_scene { # $self->Refresh; # } #} -#============================================================================================================================== - -sub get_zoom_to_bounding_box_factor { - my ($self, $bb) = @_; - my $max_bb_size = max(@{ $bb->size }); - return undef if ($max_bb_size == 0); - - # project the bbox vertices on a plane perpendicular to the camera forward axis - # then calculates the vertices coordinate on this plane along the camera xy axes - - # we need the view matrix, we let opengl calculate it (same as done in render sub) - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - if (!TURNTABLE_MODE) { - # Shift the perspective camera. -#============================================================================================================================== - my $camera_pos = Slic3r::Pointf3->new(0,0,-Slic3r::GUI::_3DScene::get_camera_distance($self)); +# +#sub get_zoom_to_bounding_box_factor { +# my ($self, $bb) = @_; +# my $max_bb_size = max(@{ $bb->size }); +# return undef if ($max_bb_size == 0); +# +# # project the bbox vertices on a plane perpendicular to the camera forward axis +# # then calculates the vertices coordinate on this plane along the camera xy axes +# +# # we need the view matrix, we let opengl calculate it (same as done in render sub) +# glMatrixMode(GL_MODELVIEW); +# glLoadIdentity(); +# +# if (!TURNTABLE_MODE) { +# # Shift the perspective camera. # my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance); -#============================================================================================================================== - glTranslatef(@$camera_pos); - } - - if (TURNTABLE_MODE) { - # Turntable mode is enabled by default. -#============================================================================================================================== - glRotatef(-Slic3r::GUI::_3DScene::get_camera_theta($self), 1, 0, 0); # pitch - glRotatef(Slic3r::GUI::_3DScene::get_camera_phi($self), 0, 0, 1); # yaw +# 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,-Slic3r::GUI::_3DScene::get_camera_distance($self)); +# } 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(@{ Slic3r::GUI::_3DScene::get_camera_target($self)->negative }); +# glTranslatef(@$camera_pos); +# my @rotmat = quat_to_rotmatrix($self->quat); +# glMultMatrixd_p(@rotmat[0..15]); +# } # glTranslatef(@{ $self->_camera_target->negative }); -#============================================================================================================================== - - # get the view matrix back from opengl - my @matrix = glGetFloatv_p(GL_MODELVIEW_MATRIX); - - # camera axes - my $right = Slic3r::Pointf3->new($matrix[0], $matrix[4], $matrix[8]); - my $up = Slic3r::Pointf3->new($matrix[1], $matrix[5], $matrix[9]); - my $forward = Slic3r::Pointf3->new($matrix[2], $matrix[6], $matrix[10]); - - my $bb_min = $bb->min_point(); - my $bb_max = $bb->max_point(); - my $bb_center = $bb->center(); - - # bbox vertices in world space - my @vertices = (); - push(@vertices, $bb_min); - push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_min->y(), $bb_min->z())); - push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_max->y(), $bb_min->z())); - push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_max->y(), $bb_min->z())); - push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_min->y(), $bb_max->z())); - push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_min->y(), $bb_max->z())); - push(@vertices, $bb_max); - push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_max->y(), $bb_max->z())); - - my $max_x = 0.0; - my $max_y = 0.0; - - # margin factor to give some empty space around the bbox - my $margin_factor = 1.25; - - foreach my $v (@vertices) { - # project vertex on the plane perpendicular to camera forward axis - my $pos = Slic3r::Pointf3->new($v->x() - $bb_center->x(), $v->y() - $bb_center->y(), $v->z() - $bb_center->z()); - my $proj_on_normal = $pos->x() * $forward->x() + $pos->y() * $forward->y() + $pos->z() * $forward->z(); - my $proj_on_plane = Slic3r::Pointf3->new($pos->x() - $proj_on_normal * $forward->x(), $pos->y() - $proj_on_normal * $forward->y(), $pos->z() - $proj_on_normal * $forward->z()); - - # calculates vertex coordinate along camera xy axes - my $x_on_plane = $proj_on_plane->x() * $right->x() + $proj_on_plane->y() * $right->y() + $proj_on_plane->z() * $right->z(); - my $y_on_plane = $proj_on_plane->x() * $up->x() + $proj_on_plane->y() * $up->y() + $proj_on_plane->z() * $up->z(); - - $max_x = max($max_x, $margin_factor * 2 * abs($x_on_plane)); - $max_y = max($max_y, $margin_factor * 2 * abs($y_on_plane)); - } - - return undef if (($max_x == 0) || ($max_y == 0)); - - my ($cw, $ch) = $self->GetSizeWH; - my $min_ratio = min($cw / $max_x, $ch / $max_y); - - return $min_ratio; -} - -#============================================================================================================================== +# +# # get the view matrix back from opengl +# my @matrix = glGetFloatv_p(GL_MODELVIEW_MATRIX); +# +# # camera axes +# my $right = Slic3r::Pointf3->new($matrix[0], $matrix[4], $matrix[8]); +# my $up = Slic3r::Pointf3->new($matrix[1], $matrix[5], $matrix[9]); +# my $forward = Slic3r::Pointf3->new($matrix[2], $matrix[6], $matrix[10]); +# +# my $bb_min = $bb->min_point(); +# my $bb_max = $bb->max_point(); +# my $bb_center = $bb->center(); +# +# # bbox vertices in world space +# my @vertices = (); +# push(@vertices, $bb_min); +# push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_min->y(), $bb_min->z())); +# push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_max->y(), $bb_min->z())); +# push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_max->y(), $bb_min->z())); +# push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_min->y(), $bb_max->z())); +# push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_min->y(), $bb_max->z())); +# push(@vertices, $bb_max); +# push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_max->y(), $bb_max->z())); +# +# my $max_x = 0.0; +# my $max_y = 0.0; +# +# # margin factor to give some empty space around the bbox +# my $margin_factor = 1.25; +# +# foreach my $v (@vertices) { +# # project vertex on the plane perpendicular to camera forward axis +# my $pos = Slic3r::Pointf3->new($v->x() - $bb_center->x(), $v->y() - $bb_center->y(), $v->z() - $bb_center->z()); +# my $proj_on_normal = $pos->x() * $forward->x() + $pos->y() * $forward->y() + $pos->z() * $forward->z(); +# my $proj_on_plane = Slic3r::Pointf3->new($pos->x() - $proj_on_normal * $forward->x(), $pos->y() - $proj_on_normal * $forward->y(), $pos->z() - $proj_on_normal * $forward->z()); +# +# # calculates vertex coordinate along camera xy axes +# my $x_on_plane = $proj_on_plane->x() * $right->x() + $proj_on_plane->y() * $right->y() + $proj_on_plane->z() * $right->z(); +# my $y_on_plane = $proj_on_plane->x() * $up->x() + $proj_on_plane->y() * $up->y() + $proj_on_plane->z() * $up->z(); +# +# $max_x = max($max_x, $margin_factor * 2 * abs($x_on_plane)); +# $max_y = max($max_y, $margin_factor * 2 * abs($y_on_plane)); +# } +# +# return undef if (($max_x == 0) || ($max_y == 0)); +# +# my ($cw, $ch) = $self->GetSizeWH; +# my $min_ratio = min($cw / $max_x, $ch / $max_y); +# +# return $min_ratio; +#} +# #sub zoom_to_bounding_box { # my ($self, $bb) = @_; # # Calculate the zoom factor needed to adjust viewport to bounding box. diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index f1ce8fe0aa..3d1f75221e 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -142,10 +142,13 @@ sub new { } }); $self->{canvas3D}->on_viewport_changed(sub { - $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); +# $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); +#============================================================================================================================== }); #============================================================================================================================== - Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); }); + Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); #============================================================================================================================== } @@ -161,10 +164,13 @@ sub new { if ($Slic3r::GUI::have_OpenGL) { $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); $self->{preview3D}->canvas->on_viewport_changed(sub { - $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); +# $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); +#============================================================================================================================== }); #============================================================================================================================== - Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); }); + Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); }); #============================================================================================================================== $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview')); $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; @@ -2235,15 +2241,17 @@ sub select_view { if ($page eq L('Preview')) { #============================================================================================================================== Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction); + Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); # $self->{preview3D}->canvas->select_view($direction); +# $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); #============================================================================================================================== - $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); } else { #============================================================================================================================== Slic3r::GUI::_3DScene::select_view($self->{canvas3D}, $direction); + Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); # $self->{canvas3D}->select_view($direction); +# $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); #============================================================================================================================== - $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); } } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index a7b7d6e34e..80102cc502 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2105,6 +2105,11 @@ void _3DScene::select_view(wxGLCanvas* canvas, const std::string& direction) s_canvas_mgr.select_view(canvas, direction); } +void _3DScene::set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other) +{ + s_canvas_mgr.set_viewport_from_scene(canvas, other); +} + void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas) { s_canvas_mgr.update_volumes_colors_by_extruder(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 69f48e0922..45fdbbb411 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -644,6 +644,7 @@ public: static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); + static void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other); static void update_volumes_colors_by_extruder(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 28f105d3ce..4946ae807f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1520,6 +1520,15 @@ void GLCanvas3D::select_view(const std::string& direction) } } +void GLCanvas3D::set_viewport_from_scene(const GLCanvas3D& other) +{ + set_camera_phi(other.get_camera_phi()); + set_camera_theta(other.get_camera_theta()); + set_camera_target(other.get_camera_target()); + set_camera_zoom(other.get_camera_zoom()); + set_dirty(true); +} + void GLCanvas3D::update_volumes_colors_by_extruder() { if ((m_volumes == nullptr) || (m_config == nullptr)) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 620f688585..30d219cff5 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -427,6 +427,7 @@ public: void zoom_to_bed(); void zoom_to_volumes(); void select_view(const std::string& direction); + void set_viewport_from_scene(const GLCanvas3D& other); void update_volumes_colors_by_extruder(); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 27aaa31889..16398b6d64 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -597,6 +597,17 @@ void GLCanvas3DManager::select_view(wxGLCanvas* canvas, const std::string& direc it->second->select_view(direction); } +void GLCanvas3DManager::set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + { + CanvasesMap::iterator other_it = _get_canvas(other); + if (other_it != m_canvases.end()) + it->second->set_viewport_from_scene(*other_it->second); + } +} + void GLCanvas3DManager::update_volumes_colors_by_extruder(wxGLCanvas* canvas) { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index b3580ec63c..301650990e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -144,6 +144,7 @@ public: void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); + void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other); void update_volumes_colors_by_extruder(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 7e1e80d4c7..b155c0cb00 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -717,6 +717,13 @@ select_view(canvas, direction) CODE: _3DScene::select_view((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), direction); +void +set_viewport_from_scene(canvas, other) + SV *canvas; + SV *other; + CODE: + _3DScene::set_viewport_from_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (wxGLCanvas*)wxPli_sv_2_object(aTHX_ other, "Wx::GLCanvas")); + void update_volumes_colors_by_extruder(canvas) SV *canvas; From 30a3b2179be6bb9cfe43e66ba94b535431331338 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 30 May 2018 15:18:45 +0200 Subject: [PATCH 044/117] 3DScene timer and _variable_layer_thickness_action method moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 131 +++++++-------- xs/src/libslic3r/Print.hpp | 4 + xs/src/libslic3r/PrintObject.cpp | 13 ++ xs/src/slic3r/GUI/3DScene.cpp | 25 +++ xs/src/slic3r/GUI/3DScene.hpp | 7 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 213 ++++++++++++++++++------ xs/src/slic3r/GUI/GLCanvas3D.hpp | 35 +++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 36 ++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 7 + xs/xsp/GUI_3DScene.xsp | 36 ++++ xs/xsp/Print.xsp | 18 +- 11 files changed, 389 insertions(+), 136 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index a6ec2e6920..319a155351 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -49,9 +49,6 @@ __PACKAGE__->mk_accessors( qw(_quat init _drag_volume_center_offset _drag_start_xy _dragged - - _layer_height_edited - ) ); #__PACKAGE__->mk_accessors( qw(_quat _dirty init # enable_picking @@ -260,19 +257,16 @@ sub new { # } # } # }); +# +# $self->{layer_height_edit_timer_id} = &Wx::NewId(); +# $self->{layer_height_edit_timer} = Wx::Timer->new($self, $self->{layer_height_edit_timer_id}); +# EVT_TIMER($self, $self->{layer_height_edit_timer_id}, sub { +# my ($self, $event) = @_; +# return if $self->_layer_height_edited != 1; +# $self->_variable_layer_thickness_action(undef); +# }); #============================================================================================================================== - $self->{layer_height_edit_timer_id} = &Wx::NewId(); - $self->{layer_height_edit_timer} = Wx::Timer->new($self, $self->{layer_height_edit_timer_id}); - EVT_TIMER($self, $self->{layer_height_edit_timer_id}, sub { - my ($self, $event) = @_; - return if $self->_layer_height_edited != 1; -#============================================================================================================================== - return if Slic3r::GUI::_3DScene::get_layers_editing_last_object_id($self) == -1; -#============================================================================================================================== - $self->_variable_layer_thickness_action(undef); - }); - return $self; } @@ -290,7 +284,10 @@ sub new { sub Destroy { my ($self) = @_; - $self->{layer_height_edit_timer}->Stop; +#============================================================================================================================== + Slic3r::GUI::_3DScene::stop_timer($self); +# $self->{layer_height_edit_timer}->Stop; +#============================================================================================================================== $self->DestroyGL; #============================================================================================================================== Slic3r::GUI::_3DScene::remove_canvas($self); @@ -426,66 +423,33 @@ sub _variable_layer_thickness_reset_rect_viewport { # # Outside the bar. # -1000.; #} -#============================================================================================================================== - -sub _variable_layer_thickness_action { - my ($self, $mouse_event, $do_modification) = @_; -#============================================================================================================================== - my $object_idx_selected = Slic3r::GUI::_3DScene::get_layers_editing_last_object_id($self); - # A volume is selected. Test, whether hovering over a layer thickness bar. - return if ($object_idx_selected == -1); +# +#sub _variable_layer_thickness_action { +# my ($self, $mouse_event, $do_modification) = @_; +# # A volume is selected. Test, whether hovering over a layer thickness bar. # return if $self->{layer_height_edit_last_object_id} == -1; -#============================================================================================================================== - if (defined($mouse_event)) { - my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_layers_editing_last_z($self, unscale($self->{print}->get_object($object_idx_selected)->size->z) - * ($bar_bottom - $mouse_event->GetY - 1.) / ($bar_bottom - $bar_top)); - Slic3r::GUI::_3DScene::set_layers_editing_last_action($self, $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1)); +# if (defined($mouse_event)) { +# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; # $self->{layer_height_edit_last_z} = unscale($self->{print}->get_object($self->{layer_height_edit_last_object_id})->size->z) # * ($bar_bottom - $mouse_event->GetY - 1.) / ($bar_bottom - $bar_top); # $self->{layer_height_edit_last_action} = $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1); -#============================================================================================================================== - } - # Mark the volume as modified, so Print will pick its layer height profile? Where to mark it? - # Start a timer to refresh the print? schedule_background_process() ? - # The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself. -#============================================================================================================================== - $self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile( - Slic3r::GUI::_3DScene::get_layers_editing_last_z($self), - Slic3r::GUI::_3DScene::get_layers_editing_strength($self), - Slic3r::GUI::_3DScene::get_layers_editing_band_width($self), - Slic3r::GUI::_3DScene::get_layers_editing_last_action($self)); +# } +# # 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}); -#============================================================================================================================== - - #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MERGE FROM SCENE_MANIPULATORS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - # searches the id of the first volume of the selected object - my $volume_idx = 0; - for my $i (0..$object_idx_selected - 1) { - my $obj = $self->{print}->get_object($i); - for my $j (0..$obj->region_volumes_count - 1) { - $volume_idx += scalar @{$obj->get_region_volumes($j)}; - } - } - #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MERGE FROM SCENE_MANIPULATORS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - -#============================================================================================================================== - #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MERGE FROM SCENE_MANIPULATORS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - $self->volumes->[$volume_idx]->generate_layer_height_texture($self->{print}->get_object($object_idx_selected), 1); -# $self->volumes->[$object_idx_selected]->generate_layer_height_texture($self->{print}->get_object($object_idx_selected), 1); - #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MERGE FROM SCENE_MANIPULATORS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +# # $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); +#} #============================================================================================================================== - $self->Refresh; - # Automatic action on mouse down with the same coordinate. - $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS); -} sub mouse_event { my ($self, $e) = @_; @@ -519,24 +483,32 @@ sub mouse_event { # on a volume or not. #============================================================================================================================== my $volume_idx = Slic3r::GUI::_3DScene::get_hover_volume_id($self); + Slic3r::GUI::_3DScene::set_layers_editing_state($self, 0); # my $volume_idx = $self->_hover_volume_idx // -1; +# $self->_layer_height_edited(0); #============================================================================================================================== - $self->_layer_height_edited(0); #============================================================================================================================== if ($object_idx_selected != -1 && Slic3r::GUI::_3DScene::bar_rect_contains($self, $e->GetX, $e->GetY)) { # if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { #============================================================================================================================== # A volume is selected and the mouse is hovering over a layer thickness bar. # Start editing the layer height. - $self->_layer_height_edited(1); - $self->_variable_layer_thickness_action($e); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_layers_editing_state($self, 1); + Slic3r::GUI::_3DScene::perform_layer_editing_action($self, $e->GetY, $e->ShiftDown, $e->RightIsDown); +# $self->_layer_height_edited(1); +# $self->_variable_layer_thickness_action($e); +#============================================================================================================================== #============================================================================================================================== } elsif ($object_idx_selected != -1 && Slic3r::GUI::_3DScene::reset_rect_contains($self, $e->GetX, $e->GetY)) { # } elsif ($object_idx_selected != -1 && $self->_variable_layer_thickness_reset_rect_mouse_inside($e)) { #============================================================================================================================== $self->{print}->get_object($object_idx_selected)->reset_layer_height_profile; # Index 2 means no editing, just wait for mouse up event. - $self->_layer_height_edited(2); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_layers_editing_state($self, 2); +# $self->_layer_height_edited(2); +#============================================================================================================================== $self->Refresh; $self->Update; } else { @@ -599,7 +571,10 @@ sub mouse_event { } } } - } elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) { +#============================================================================================================================== + } elsif ($e->Dragging && $e->LeftIsDown && (Slic3r::GUI::_3DScene::get_layers_editing_state($self) == 0) && defined($self->_drag_volume_idx)) { +# } elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) { +#============================================================================================================================== # Get new position at the same Z of the initial click point. my $cur_pos = Slic3r::Linef3->new( $self->mouse_to_3d($e->GetX, $e->GetY, 0), @@ -635,8 +610,13 @@ sub mouse_event { $self->Refresh; $self->Update; } elsif ($e->Dragging) { - if ($self->_layer_height_edited && $object_idx_selected != -1) { - $self->_variable_layer_thickness_action($e) if ($self->_layer_height_edited == 1); +#============================================================================================================================== + if ((Slic3r::GUI::_3DScene::get_layers_editing_state($self) > 0) && ($object_idx_selected != -1)) { + Slic3r::GUI::_3DScene::perform_layer_editing_action($self, $e->GetY, $e->ShiftDown, $e->RightIsDown) if (Slic3r::GUI::_3DScene::get_layers_editing_state($self) == 1); + +# if ($self->_layer_height_edited && $object_idx_selected != -1) { +# $self->_variable_layer_thickness_action($e) if ($self->_layer_height_edited == 1); +#============================================================================================================================== } elsif ($e->LeftIsDown) { # if dragging over blank area with left button, rotate if (defined $self->_drag_start_pos) { @@ -686,9 +666,14 @@ sub mouse_event { $self->_drag_start_xy($pos); } } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) { - if ($self->_layer_height_edited) { - $self->_layer_height_edited(undef); - $self->{layer_height_edit_timer}->Stop; +#============================================================================================================================== + if (Slic3r::GUI::_3DScene::get_layers_editing_state($self) > 0) { + Slic3r::GUI::_3DScene::set_layers_editing_state($self, 0); + Slic3r::GUI::_3DScene::stop_timer($self); +# 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) { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index c56e64c6c4..8b6b3773f8 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -184,6 +184,10 @@ public: void reset_layer_height_profile(); +//############################################################################################################################################ + void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action); +//############################################################################################################################################ + // Collect the slicing parameters, to be used by variable layer thickness algorithm, // by the interactive layer height editor and by the printing process itself. // The slicing parameters are dependent on various configuration values diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index b0341db16e..9d0fe03fbd 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -4,6 +4,9 @@ #include "Geometry.hpp" #include "SupportMaterial.hpp" #include "Surface.hpp" +//############################################################################################################################################ +#include "Slicing.hpp" +//############################################################################################################################################ #include #include @@ -1961,4 +1964,14 @@ void PrintObject::reset_layer_height_profile() this->model_object()->layer_height_profile_valid = false; } +//############################################################################################################################################ +void PrintObject::adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) +{ + update_layer_height_profile(_model_object->layer_height_profile); + Slic3r::adjust_layer_height_profile(slicing_parameters(), _model_object->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); + _model_object->layer_height_profile_valid = true; + layer_height_profile_valid = false; +} +//############################################################################################################################################ + } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 80102cc502..cb87a43d26 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2015,6 +2015,16 @@ unsigned int _3DScene::get_layers_editing_z_texture_id(wxGLCanvas* canvas) return s_canvas_mgr.get_layers_editing_z_texture_id(canvas); } +unsigned int _3DScene::get_layers_editing_state(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_layers_editing_state(canvas); +} + +void _3DScene::set_layers_editing_state(wxGLCanvas* canvas, unsigned int state) +{ + s_canvas_mgr.set_layers_editing_state(canvas, state); +} + float _3DScene::get_layers_editing_band_width(wxGLCanvas* canvas) { return s_canvas_mgr.get_layers_editing_band_width(canvas); @@ -2140,6 +2150,21 @@ void _3DScene::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float lef s_canvas_mgr.render_texture(canvas, tex_id, left, right, bottom, top); } +void _3DScene::start_timer(wxGLCanvas* canvas) +{ + s_canvas_mgr.start_timer(canvas); +} + +void _3DScene::stop_timer(wxGLCanvas* canvas) +{ + s_canvas_mgr.stop_timer(canvas); +} + +void _3DScene::perform_layer_editing_action(wxGLCanvas* canvas, int y, bool shift_down, bool right_down) +{ + s_canvas_mgr.perform_layer_editing_action(canvas, y, shift_down, right_down); +} + void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 45fdbbb411..22a5e8d618 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -619,6 +619,9 @@ public: static unsigned int get_layers_editing_z_texture_id(wxGLCanvas* canvas); + static unsigned int get_layers_editing_state(wxGLCanvas* canvas); + static void set_layers_editing_state(wxGLCanvas* canvas, unsigned int state); + static float get_layers_editing_band_width(wxGLCanvas* canvas); static void set_layers_editing_band_width(wxGLCanvas* canvas, float band_width); @@ -655,6 +658,10 @@ public: static void render_volumes(wxGLCanvas* canvas, bool fake_colors); static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); + static void start_timer(wxGLCanvas* canvas); + static void stop_timer(wxGLCanvas* canvas); + static void perform_layer_editing_action(wxGLCanvas* canvas, int y, bool shift_down, bool right_down); + static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); // static void _glew_init(); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 4946ae807f..c9b31a94c1 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -589,7 +590,8 @@ GLCanvas3D::LayersEditing::GLTextureData::GLTextureData(unsigned int id, int wid } GLCanvas3D::LayersEditing::LayersEditing() - : m_use_legacy_opengl(false) + : m_state(Unknown) + , m_use_legacy_opengl(false) , m_enabled(false) , m_z_texture_id(0) , m_band_width(2.0f) @@ -638,6 +640,16 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, return true; } +GLCanvas3D::LayersEditing::EState GLCanvas3D::LayersEditing::get_state() const +{ + return m_state; +} + +void GLCanvas3D::LayersEditing::set_state(GLCanvas3D::LayersEditing::EState state) +{ + m_state = state; +} + bool GLCanvas3D::LayersEditing::is_allowed() const { return m_use_legacy_opengl && m_shader.is_initialized(); @@ -718,8 +730,8 @@ void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObje if (!m_enabled) return; - const Rect& bar_rect = _get_bar_rect_viewport(canvas); - const Rect& reset_rect = _get_reset_rect_viewport(canvas); + const Rect& bar_rect = get_bar_rect_viewport(canvas); + const Rect& reset_rect = get_reset_rect_viewport(canvas); ::glDisable(GL_DEPTH_TEST); @@ -747,13 +759,13 @@ const GLShader* GLCanvas3D::LayersEditing::get_shader() const float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) { const Point& mouse_pos = canvas.get_local_mouse_position(); - const Rect& bar_rect = _get_bar_rect_screen(canvas); + const Rect& rect = get_bar_rect_screen(canvas); float x = (float)mouse_pos.x; float y = (float)mouse_pos.y; - float t = bar_rect.get_top(); - float b = bar_rect.get_bottom(); + float t = rect.get_top(); + float b = rect.get_bottom(); - return ((bar_rect.get_left() <= x) && (x <= bar_rect.get_right()) && (t <= y) && (y <= b)) ? + return ((rect.get_left() <= x) && (x <= rect.get_right()) && (t <= y) && (y <= b)) ? // Inside the bar. (b - y - 1.0f) / (b - t - 1.0f) : // Outside the bar. @@ -777,16 +789,59 @@ int GLCanvas3D::LayersEditing::get_first_selected_object_id(const GLVolumeCollec bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, float x, float y) { - const Rect& rect = _get_bar_rect_screen(canvas); + const Rect& rect = get_bar_rect_screen(canvas); return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); } bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y) { - const Rect& rect = _get_reset_rect_screen(canvas); + const Rect& rect = get_reset_rect_screen(canvas); return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); } +Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) +{ + const Size& cnv_size = canvas.get_canvas_size(); + float w = (float)cnv_size.get_width(); + float h = (float)cnv_size.get_height(); + + return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0.0f, w, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT); +} + +Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) +{ + const Size& cnv_size = canvas.get_canvas_size(); + float w = (float)cnv_size.get_width(); + float h = (float)cnv_size.get_height(); + + return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, w, h); +} + +Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) +{ + const Size& cnv_size = canvas.get_canvas_size(); + float half_w = 0.5f * (float)cnv_size.get_width(); + float half_h = 0.5f * (float)cnv_size.get_height(); + + float zoom = canvas.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom); +} + +Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas) +{ + const Size& cnv_size = canvas.get_canvas_size(); + float half_w = 0.5f * (float)cnv_size.get_width(); + float half_h = 0.5f * (float)cnv_size.get_height(); + + float zoom = canvas.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); +} + + bool GLCanvas3D::LayersEditing::_is_initialized() const { return m_shader.is_initialized(); @@ -969,48 +1024,6 @@ GLCanvas3D::LayersEditing::GLTextureData GLCanvas3D::LayersEditing::_load_textur return GLTextureData((unsigned int)tex_id, width, height); } -Rect GLCanvas3D::LayersEditing::_get_bar_rect_screen(const GLCanvas3D& canvas) -{ - const Size& cnv_size = canvas.get_canvas_size(); - float w = (float)cnv_size.get_width(); - float h = (float)cnv_size.get_height(); - - return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0.0f, w, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT); -} - -Rect GLCanvas3D::LayersEditing::_get_reset_rect_screen(const GLCanvas3D& canvas) -{ - const Size& cnv_size = canvas.get_canvas_size(); - float w = (float)cnv_size.get_width(); - float h = (float)cnv_size.get_height(); - - return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, w, h); -} - -Rect GLCanvas3D::LayersEditing::_get_bar_rect_viewport(const GLCanvas3D& canvas) -{ - const Size& cnv_size = canvas.get_canvas_size(); - float half_w = 0.5f * (float)cnv_size.get_width(); - float half_h = 0.5f * (float)cnv_size.get_height(); - - float zoom = canvas.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom); -} - -Rect GLCanvas3D::LayersEditing::_get_reset_rect_viewport(const GLCanvas3D& canvas) -{ - const Size& cnv_size = canvas.get_canvas_size(); - float half_w = 0.5f * (float)cnv_size.get_width(); - float half_h = 0.5f * (float)cnv_size.get_height(); - - float zoom = canvas.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); -} - GLCanvas3D::Mouse::Mouse() : m_dragging(false) { @@ -1039,6 +1052,7 @@ void GLCanvas3D::Mouse::set_position(const Pointf& position) GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) + , m_timer(nullptr) , m_volumes(nullptr) , m_config(nullptr) , m_print(nullptr) @@ -1051,10 +1065,18 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_shader_enabled(false) , m_multisample_allowed(false) { + if (m_canvas != nullptr) + m_timer = new wxTimer(m_canvas); } GLCanvas3D::~GLCanvas3D() { + if (m_timer != nullptr) + { + delete m_timer; + m_timer = nullptr; + } + _deregister_callbacks(); } @@ -1585,6 +1607,17 @@ unsigned int GLCanvas3D::get_layers_editing_z_texture_id() const return m_layers_editing.get_z_texture_id(); } +unsigned int GLCanvas3D::get_layers_editing_state() const +{ + return (unsigned int)m_layers_editing.get_state(); +} + +void GLCanvas3D::set_layers_editing_state(unsigned int state) +{ + if (state < (unsigned int)LayersEditing::Num_States) + m_layers_editing.set_state((LayersEditing::EState)state); +} + float GLCanvas3D::get_layers_editing_band_width() const { return m_layers_editing.get_band_width(); @@ -1833,6 +1866,14 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) _refresh_if_shown_on_screen(); } +void GLCanvas3D::on_timer(wxTimerEvent& evt) +{ + if (get_layers_editing_state() != 1) + return; + + _perform_layer_editing_action(); +} + Size GLCanvas3D::get_canvas_size() const { int w = 0; @@ -1853,6 +1894,28 @@ Point GLCanvas3D::get_local_mouse_position() const return Point(mouse_pos.x, mouse_pos.y); } +void GLCanvas3D::start_timer() +{ + if (m_timer != nullptr) + m_timer->Start(100, wxTIMER_CONTINUOUS); +} + +void GLCanvas3D::stop_timer() +{ + if (m_timer != nullptr) + m_timer->Stop(); +} + +void GLCanvas3D::perform_layer_editing_action(int y, bool shift_down, bool right_down) +{ + wxMouseEvent evt; + evt.m_y = (wxCoord)y; + evt.SetShiftDown(shift_down); + evt.SetRightDown(right_down); + + _perform_layer_editing_action(&evt); +} + void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { // Calculate the zoom factor needed to adjust viewport to bounding box. @@ -2238,5 +2301,53 @@ void GLCanvas3D::_render_layer_editing_overlay() const m_layers_editing.render(*this, *print_object, *volume); } +void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) +{ + int object_idx_selected = get_layers_editing_last_object_id(); + if (object_idx_selected == -1) + return; + + if ((m_volumes == nullptr) || (m_print == nullptr)) + return; + + PrintObject* selected_obj = m_print->get_object(object_idx_selected); + if (selected_obj == nullptr) + return; + + // A volume is selected. Test, whether hovering over a layer thickness bar. + if (evt != nullptr) + { + const Rect& rect = LayersEditing::get_bar_rect_screen(*this); + float b = rect.get_bottom(); + set_layers_editing_last_z(unscale(selected_obj->size.z) * (b - evt->GetY() - 1.0f) / (b - rect.get_top())); + set_layers_editing_last_action(evt->ShiftDown() ? (evt->RightIsDown() ? 3 : 2) : (evt->RightIsDown() ? 0 : 1)); + } + + // Mark the volume as modified, so Print will pick its layer height profile ? Where to mark it ? + // Start a timer to refresh the print ? schedule_background_process() ? + // The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself. + selected_obj->adjust_layer_height_profile(get_layers_editing_last_z(), get_layers_editing_strength(), get_layers_editing_band_width(), get_layers_editing_last_action()); + + // searches the id of the first volume of the selected object + int volume_idx = 0; + for (int i = 0; i < object_idx_selected; ++i) + { + PrintObject* obj = m_print->get_object(i); + if (obj != nullptr) + { + for (int j = 0; j < (int)obj->region_volumes.size(); ++j) + { + volume_idx += (int)obj->region_volumes[j].size(); + } + } + } + + m_volumes->volumes[volume_idx]->generate_layer_height_texture(selected_obj, 1); + _refresh_if_shown_on_screen(); + + // Automatic action on mouse down with the same coordinate. + start_timer(); +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 30d219cff5..bbb5880f1c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -7,10 +7,12 @@ class wxGLCanvas; class wxGLContext; +class wxTimer; class wxSizeEvent; class wxIdleEvent; class wxKeyEvent; class wxMouseEvent; +class wxTimerEvent; namespace Slic3r { @@ -202,6 +204,16 @@ public: class LayersEditing { + public: + enum EState : unsigned char + { + Unknown, + Editing, + Completed, + Num_States + }; + + private: struct GLTextureData { unsigned int id; @@ -212,6 +224,7 @@ public: GLTextureData(unsigned int id, int width, int height); }; + EState m_state; bool m_use_legacy_opengl; bool m_enabled; Shader m_shader; @@ -230,6 +243,9 @@ public: bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); + EState get_state() const; + void set_state(EState state); + bool is_allowed() const; void set_use_legacy_opengl(bool use_legacy_opengl); @@ -261,6 +277,10 @@ public: static int get_first_selected_object_id(const GLVolumeCollection& volumes, unsigned int objects_count); static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y); + static Rect get_bar_rect_screen(const GLCanvas3D& canvas); + static Rect get_reset_rect_screen(const GLCanvas3D& canvas); + static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); + static Rect get_reset_rect_viewport(const GLCanvas3D& canvas); private: bool _is_initialized() const; @@ -269,10 +289,6 @@ public: void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const; void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; static GLTextureData _load_texture_from_file(const std::string& filename); - static Rect _get_bar_rect_screen(const GLCanvas3D& canvas); - static Rect _get_reset_rect_screen(const GLCanvas3D& canvas); - static Rect _get_bar_rect_viewport(const GLCanvas3D& canvas); - static Rect _get_reset_rect_viewport(const GLCanvas3D& canvas); }; class Mouse @@ -293,6 +309,7 @@ public: private: wxGLCanvas* m_canvas; wxGLContext* m_context; + wxTimer* m_timer; Camera m_camera; Bed m_bed; Axes m_axes; @@ -402,6 +419,9 @@ public: unsigned int get_layers_editing_z_texture_id() const; + unsigned int get_layers_editing_state() const; + void set_layers_editing_state(unsigned int state); + float get_layers_editing_band_width() const; void set_layers_editing_band_width(float band_width); @@ -444,10 +464,15 @@ public: void on_idle(wxIdleEvent& evt); void on_char(wxKeyEvent& evt); void on_mouse_wheel(wxMouseEvent& evt); + void on_timer(wxTimerEvent& evt); Size get_canvas_size() const; Point get_local_mouse_position() const; + void start_timer(); + void stop_timer(); + void perform_layer_editing_action(int y, bool shift_down, bool right_down); + private: void _zoom_to_bounding_box(const BoundingBoxf3& bbox); float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; @@ -466,6 +491,8 @@ private: void _render_warning_texture() const; void _render_legend_texture() const; void _render_layer_editing_overlay() const; + + void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); }; } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 16398b6d64..2d692cc273 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -76,6 +77,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) canvas->Bind(wxEVT_IDLE, [canvas3D](wxIdleEvent& evt) { canvas3D->on_idle(evt); }); canvas->Bind(wxEVT_CHAR, [canvas3D](wxKeyEvent& evt) { canvas3D->on_char(evt); }); canvas->Bind(wxEVT_MOUSEWHEEL, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse_wheel(evt); }); + canvas->Bind(wxEVT_TIMER, [canvas3D](wxTimerEvent& evt) { canvas3D->on_timer(evt); }); m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); @@ -481,6 +483,19 @@ unsigned int GLCanvas3DManager::get_layers_editing_z_texture_id(wxGLCanvas* canv return (it != m_canvases.end()) ? it->second->get_layers_editing_z_texture_id() : 0; } +unsigned int GLCanvas3DManager::get_layers_editing_state(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_layers_editing_state() : 0; +} + +void GLCanvas3DManager::set_layers_editing_state(wxGLCanvas* canvas, unsigned int state) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_layers_editing_state(state); +} + float GLCanvas3DManager::get_layers_editing_band_width(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -649,6 +664,27 @@ void GLCanvas3DManager::render_texture(wxGLCanvas* canvas, unsigned int tex_id, it->second->render_texture(tex_id, left, right, bottom, top); } +void GLCanvas3DManager::start_timer(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->start_timer(); +} + +void GLCanvas3DManager::stop_timer(wxGLCanvas* canvas) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->stop_timer(); +} + +void GLCanvas3DManager::perform_layer_editing_action(wxGLCanvas* canvas, int y, bool shift_down, bool right_down) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->perform_layer_editing_action(y, shift_down, right_down); +} + void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 301650990e..1cb4c316e0 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -119,6 +119,9 @@ public: unsigned int get_layers_editing_z_texture_id(wxGLCanvas* canvas) const; + unsigned int get_layers_editing_state(wxGLCanvas* canvas) const; + void set_layers_editing_state(wxGLCanvas* canvas, unsigned int state); + float get_layers_editing_band_width(wxGLCanvas* canvas) const; void set_layers_editing_band_width(wxGLCanvas* canvas, float band_width); @@ -155,6 +158,10 @@ public: void render_volumes(wxGLCanvas* canvas, bool fake_colors) const; void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; + void start_timer(wxGLCanvas* canvas); + void stop_timer(wxGLCanvas* canvas); + void perform_layer_editing_action(wxGLCanvas* canvas, int y, bool shift_down, bool right_down); + void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); private: diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index b155c0cb00..0ffe3677bb 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -578,6 +578,21 @@ get_layers_editing_z_texture_id(canvas) OUTPUT: RETVAL +unsigned int +get_layers_editing_state(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::get_layers_editing_state((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + +void +set_layers_editing_state(canvas, state) + SV *canvas; + unsigned int state; + CODE: + _3DScene::set_layers_editing_state((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), state); + float get_layers_editing_band_width(canvas) SV *canvas; @@ -768,6 +783,27 @@ render_texture(canvas, tex_id, left, right, bottom, top) CODE: _3DScene::render_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), tex_id, left, right, bottom, top); +void +start_timer(canvas) + SV *canvas; + CODE: + _3DScene::start_timer((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + +void +stop_timer(canvas) + SV *canvas; + CODE: + _3DScene::stop_timer((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + +void +perform_layer_editing_action(canvas, y, shift_down, right_down) + SV *canvas; + int y; + bool shift_down; + bool right_down; + CODE: + _3DScene::perform_layer_editing_action((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), y, shift_down, right_down); + void register_on_viewport_changed_callback(canvas, callback) SV *canvas; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index f21923b416..60b589336d 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -122,14 +122,16 @@ _constant() RETVAL.push_back(slicing_params.layer_height); %}; - void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) - %code%{ - THIS->update_layer_height_profile(THIS->model_object()->layer_height_profile); - adjust_layer_height_profile( - THIS->slicing_parameters(), THIS->model_object()->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); - THIS->model_object()->layer_height_profile_valid = true; - THIS->layer_height_profile_valid = false; - %}; +//################################################################################################################################################################### +// void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) +// %code%{ +// THIS->update_layer_height_profile(THIS->model_object()->layer_height_profile); +// adjust_layer_height_profile( +// THIS->slicing_parameters(), THIS->model_object()->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); +// THIS->model_object()->layer_height_profile_valid = true; +// THIS->layer_height_profile_valid = false; +// %}; +//################################################################################################################################################################### void reset_layer_height_profile(); From cf8e7475cad6d8027ad7c872827c22ccdf0a69d4 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 31 May 2018 08:44:39 +0200 Subject: [PATCH 045/117] Removed unused methods from 3DScene --- lib/Slic3r/GUI/3DScene.pm | 115 ++++++++++++------------------- xs/src/slic3r/GUI/GLCanvas3D.cpp | 68 ++++++++---------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 1 + 3 files changed, 74 insertions(+), 110 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 319a155351..83cf5b45dd 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -188,6 +188,7 @@ sub new { $self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new); #============================================================================================================================== Slic3r::GUI::_3DScene::set_volumes($self, $self->volumes); + Slic3r::GUI::_3DScene::reset_volumes($self); #============================================================================================================================== # 3D point in model space @@ -202,10 +203,7 @@ sub new { # $self->{layer_height_edit_last_object_id} = -1; # $self->{layer_height_edit_last_z} = 0.; # $self->{layer_height_edit_last_action} = 0; -#============================================================================================================================== - -#============================================================================================================================== - Slic3r::GUI::_3DScene::reset_volumes($self); +# # $self->reset_objects; #============================================================================================================================== @@ -215,8 +213,6 @@ sub new { }); #======================================================================================================================= # EVT_SIZE($self, sub { $self->_dirty(1) }); -#======================================================================================================================= -#============================================================================================================================== # EVT_IDLE($self, sub { # return unless $self->_dirty; # return if !$self->IsShownOnScreen; @@ -362,45 +358,33 @@ sub Destroy { # } # return -1; #} -#============================================================================================================================== - -# Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen. -sub _variable_layer_thickness_bar_rect_screen { - my ($self) = @_; - my ($cw, $ch) = $self->GetSizeWH; - return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0, $cw, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT); -} - -sub _variable_layer_thickness_bar_rect_viewport { - my ($self) = @_; - my ($cw, $ch) = $self->GetSizeWH; -#============================================================================================================================== - my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); - return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$zoom, (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$zoom, $cw/(2*$zoom), $ch/(2*$zoom)); +# +## Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen. +#sub _variable_layer_thickness_bar_rect_screen { +# my ($self) = @_; +# my ($cw, $ch) = $self->GetSizeWH; +# return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0, $cw, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT); +#} +# +#sub _variable_layer_thickness_bar_rect_viewport { +# my ($self) = @_; +# my ($cw, $ch) = $self->GetSizeWH; # return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom, $cw/(2*$self->_zoom), $ch/(2*$self->_zoom)); -#============================================================================================================================== -} - -#============================================================================================================================== +#} +# ## Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen. #sub _variable_layer_thickness_reset_rect_screen { # my ($self) = @_; # my ($cw, $ch) = $self->GetSizeWH; # return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, $cw, $ch); #} -#============================================================================================================================== - -sub _variable_layer_thickness_reset_rect_viewport { - my ($self) = @_; - my ($cw, $ch) = $self->GetSizeWH; -#============================================================================================================================== - my $zoom = Slic3r::GUI::_3DScene::get_camera_zoom($self); - return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$zoom, -$ch/(2*$zoom), $cw/(2*$zoom), (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$zoom); +# +#sub _variable_layer_thickness_reset_rect_viewport { +# my ($self) = @_; +# my ($cw, $ch) = $self->GetSizeWH; # return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, -$ch/(2*$self->_zoom), $cw/(2*$self->_zoom), (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom); -#============================================================================================================================== -} - -#============================================================================================================================== +#} +# #sub _variable_layer_thickness_bar_rect_mouse_inside { # my ($self, $mouse_evt) = @_; # my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; @@ -1400,42 +1384,33 @@ sub Render { return unless my $context = $self->GetContext; $self->SetCurrent($context); $self->InitGL; - - glClearColor(1, 1, 1, 1); - glClearDepth(1); - glDepthFunc(GL_LESS); -#============================================================================================================================== -# glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -#============================================================================================================================== - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - if (!TURNTABLE_MODE) { - # Shift the perspective camera. -#============================================================================================================================== - my $camera_pos = Slic3r::Pointf3->new(0,0,-Slic3r::GUI::_3DScene::get_camera_distance($self)); -# my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance); -#============================================================================================================================== - glTranslatef(@$camera_pos); - } - - if (TURNTABLE_MODE) { - # Turntable mode is enabled by default. -#============================================================================================================================== - glRotatef(-Slic3r::GUI::_3DScene::get_camera_theta($self), 1, 0, 0); # pitch - glRotatef(Slic3r::GUI::_3DScene::get_camera_phi($self), 0, 0, 1); # yaw -# 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]); - } - + #============================================================================================================================== Slic3r::GUI::_3DScene::render($self); +# glClearColor(1, 1, 1, 1); +# glClearDepth(1); +# glDepthFunc(GL_LESS); +# glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +# +# glMatrixMode(GL_MODELVIEW); +# glLoadIdentity(); +# +# if (!TURNTABLE_MODE) { +# # Shift the perspective camera. +# my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance); +# glTranslatef(@$camera_pos); +# } +# +# if (TURNTABLE_MODE) { +# # Turntable mode is enabled by default. +# glRotatef(-$self->_stheta, 1, 0, 0); # pitch +# glRotatef($self->_sphi, 0, 0, 1); # yaw +# } else { +# my @rotmat = quat_to_rotmatrix($self->quat); +# glMultMatrixd_p(@rotmat[0..15]); +# } +# # glTranslatef(@{ $self->_camera_target->negative }); # # # light from above diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index c9b31a94c1..4e2355c893 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -15,7 +15,6 @@ #include #include -static const bool TURNTABLE_MODE = true; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; static const float GROUND_Z = -0.02f; @@ -1082,10 +1081,12 @@ GLCanvas3D::~GLCanvas3D() bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) { - ::glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - ::glEnable(GL_DEPTH_TEST); + ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + ::glClearDepth(1.0f); - ::glDepthFunc(GL_LEQUAL); + ::glDepthFunc(GL_LESS); + + ::glEnable(GL_DEPTH_TEST); ::glEnable(GL_CULL_FACE); ::glEnable(GL_BLEND); ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1109,6 +1110,14 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) GLfloat diffuse[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; ::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse); + // light from above + GLfloat position1[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; + ::glLightfv(GL_LIGHT0, GL_POSITION, position1); + GLfloat specular1[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; + ::glLightfv(GL_LIGHT0, GL_SPECULAR, specular1); + GLfloat diffuse1[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; + ::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse1); + // Enables Smooth Color Shading; try GL_FLAT for (lack of) fun. ::glShadeModel(GL_SMOOTH); @@ -1574,21 +1583,7 @@ void GLCanvas3D::render(bool useVBOs) const if (m_canvas == nullptr) return; - Pointf3 neg_target = get_camera_target().negative(); - ::glTranslatef((GLfloat)neg_target.x, (GLfloat)neg_target.y, (GLfloat)neg_target.z); - - // light from above - GLfloat position0[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; - ::glLightfv(GL_LIGHT0, GL_POSITION, position0); - GLfloat specular[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - ::glLightfv(GL_LIGHT0, GL_SPECULAR, specular); - GLfloat diffuse[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; - ::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); - - // Head light - GLfloat position1[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; - ::glLightfv(GL_LIGHT1, GL_POSITION, position1); - + _camera_tranform(); _picking_pass(); _render_background(); _render_bed(); @@ -1941,27 +1936,8 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co // 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) - { - // Turntable mode is enabled by default. - ::glRotatef(-get_camera_theta(), 1.0f, 0.0f, 0.0f); // pitch - ::glRotatef(get_camera_phi(), 0.0f, 0.0f, 1.0f); // yaw - } - else - { - // Shift the perspective camera. - Pointf3 camera_pos(0.0, 0.0, -(coordf_t)get_camera_distance()); - ::glTranslatef((float)camera_pos.x, (float)camera_pos.y, (float)camera_pos.z); -// my @rotmat = quat_to_rotmatrix($self->quat); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEMPORARY COMMENTED OUT -// glMultMatrixd_p(@rotmat[0..15]); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEMPORARY COMMENTED OUT - } - - const Pointf3& target = get_camera_target(); - ::glTranslatef(-(float)target.x, -(float)target.y, -(float)target.z); + // we need the view matrix, we let opengl calculate it (same as done in render()) + _camera_tranform(); // get the view matrix back from opengl GLfloat matrix[16]; @@ -2055,6 +2031,18 @@ void GLCanvas3D::_refresh_if_shown_on_screen() } } +void GLCanvas3D::_camera_tranform() const +{ + ::glMatrixMode(GL_MODELVIEW); + ::glLoadIdentity(); + + ::glRotatef(-get_camera_theta(), 1.0f, 0.0f, 0.0f); // pitch + ::glRotatef(get_camera_phi(), 0.0f, 0.0f, 1.0f); // yaw + + Pointf3 neg_target = get_camera_target().negative(); + ::glTranslatef((GLfloat)neg_target.x, (GLfloat)neg_target.y, (GLfloat)neg_target.z); +} + void GLCanvas3D::_picking_pass() const { if (is_picking_enabled() && !is_mouse_dragging() && (m_volumes != nullptr)) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index bbb5880f1c..22f561cbde 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -482,6 +482,7 @@ private: void _mark_volumes_for_layer_height() const; void _refresh_if_shown_on_screen(); + void _camera_tranform() const; void _picking_pass() const; void _render_background() const; void _render_bed() const; From 276533e236fed185744b9fc71331bd576c951b31 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 31 May 2018 13:51:50 +0200 Subject: [PATCH 046/117] 3DScene mouse event handler partially moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 421 ++++++++++-------------- lib/Slic3r/GUI/Plater.pm | 20 +- lib/Slic3r/GUI/Plater/2D.pm | 5 +- lib/Slic3r/GUI/Plater/3D.pm | 26 +- xs/src/slic3r/GUI/3DScene.cpp | 25 ++ xs/src/slic3r/GUI/3DScene.hpp | 5 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 329 +++++++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 38 +++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 46 +++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 5 + xs/xsp/GUI_3DScene.xsp | 39 ++- 11 files changed, 693 insertions(+), 266 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 83cf5b45dd..4715b6ecfe 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -35,11 +35,8 @@ use Slic3r::Geometry qw(PI); # _camera_type: 'perspective' or 'ortho' #============================================================================================================================== __PACKAGE__->mk_accessors( qw(_quat init - enable_moving on_viewport_changed on_select - on_double_click - on_right_click on_move on_model_update volumes @@ -220,9 +217,7 @@ sub new { # $self->Refresh; # }); # EVT_MOUSEWHEEL($self, \&mouse_wheel_event); -#============================================================================================================================== - EVT_MOUSE_EVENTS($self, \&mouse_event); -#============================================================================================================================== +# EVT_MOUSE_EVENTS($self, \&mouse_event); ## EVT_KEY_DOWN($self, sub { # EVT_CHAR($self, sub { # my ($s, $event) = @_; @@ -433,139 +428,97 @@ sub Destroy { # # Automatic action on mouse down with the same coordinate. # $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS); #} -#============================================================================================================================== - -sub mouse_event { - my ($self, $e) = @_; - - my $pos = Slic3r::Pointf->new($e->GetPositionXY); -#============================================================================================================================== - my $object_idx_selected = (Slic3r::GUI::_3DScene::is_layers_editing_enabled($self) && $self->{print}) ? Slic3r::GUI::_3DScene::get_layers_editing_first_selected_object_id($self, $self->{print}->object_count) : -1; - Slic3r::GUI::_3DScene::set_layers_editing_last_object_id($self, $object_idx_selected); +# +#sub mouse_event { +# my ($self, $e) = @_; +# +# my $pos = Slic3r::Pointf->new($e->GetPositionXY); # my $object_idx_selected = $self->{layer_height_edit_last_object_id} = ($self->layer_editing_enabled && $self->{print}) ? $self->_first_selected_object_id_for_variable_layer_height_editing : -1; -#============================================================================================================================== - -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_mouse_dragging($self, $e->Dragging); +# # $self->_mouse_dragging($e->Dragging); -#============================================================================================================================== - - if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) { - # wxMSW needs focus in order to catch mouse wheel events - $self->SetFocus; - $self->_drag_start_xy(undef); - } elsif ($e->LeftDClick) { -#============================================================================================================================== - if ($object_idx_selected != -1 && Slic3r::GUI::_3DScene::bar_rect_contains($self, $e->GetX, $e->GetY)) { +# +# if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) { +# # wxMSW needs focus in order to catch mouse wheel events +# $self->SetFocus; +# $self->_drag_start_xy(undef); +# } elsif ($e->LeftDClick) { # if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { -#============================================================================================================================== - } elsif ($self->on_double_click) { - $self->on_double_click->(); - } - } elsif ($e->LeftDown || $e->RightDown) { - # If user pressed left or right button we first check whether this happened - # on a volume or not. -#============================================================================================================================== - my $volume_idx = Slic3r::GUI::_3DScene::get_hover_volume_id($self); - Slic3r::GUI::_3DScene::set_layers_editing_state($self, 0); +# } elsif ($self->on_double_click) { +# $self->on_double_click->(); +# } +# } elsif ($e->LeftDown || $e->RightDown) { +# # If user pressed left or right button we first check whether this happened +# # on a volume or not. # my $volume_idx = $self->_hover_volume_idx // -1; # $self->_layer_height_edited(0); -#============================================================================================================================== -#============================================================================================================================== - if ($object_idx_selected != -1 && Slic3r::GUI::_3DScene::bar_rect_contains($self, $e->GetX, $e->GetY)) { # 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. -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_layers_editing_state($self, 1); - Slic3r::GUI::_3DScene::perform_layer_editing_action($self, $e->GetY, $e->ShiftDown, $e->RightIsDown); +# # A volume is selected and the mouse is hovering over a layer thickness bar. +# # Start editing the layer height. # $self->_layer_height_edited(1); # $self->_variable_layer_thickness_action($e); -#============================================================================================================================== -#============================================================================================================================== - } elsif ($object_idx_selected != -1 && Slic3r::GUI::_3DScene::reset_rect_contains($self, $e->GetX, $e->GetY)) { # } elsif ($object_idx_selected != -1 && $self->_variable_layer_thickness_reset_rect_mouse_inside($e)) { -#============================================================================================================================== - $self->{print}->get_object($object_idx_selected)->reset_layer_height_profile; - # Index 2 means no editing, just wait for mouse up event. -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_layers_editing_state($self, 2); +# $self->{print}->get_object($object_idx_selected)->reset_layer_height_profile; +# # Index 2 means no editing, just wait for mouse up event. # $self->_layer_height_edited(2); -#============================================================================================================================== - $self->Refresh; - $self->Update; - } else { - # The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate $pos->x,y, - # an converts the screen space coordinate to unscaled object space. - my $pos3d = ($volume_idx == -1) ? undef : $self->mouse_to_3d(@$pos); - - # Select volume in this 3D canvas. - # Don't deselect a volume if layer editing is enabled. We want the object to stay selected - # during the scene manipulation. -#============================================================================================================================== - -#============================================================================================================================== - if (Slic3r::GUI::_3DScene::is_picking_enabled($self) && ($volume_idx != -1 || ! Slic3r::GUI::_3DScene::is_layers_editing_enabled($self))) { - Slic3r::GUI::_3DScene::deselect_volumes($self); - Slic3r::GUI::_3DScene::select_volume($self, $volume_idx); +# $self->Refresh; +# $self->Update; +# } else { +# # The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate $pos->x,y, +# # an converts the screen space coordinate to unscaled object space. +# my $pos3d = ($volume_idx == -1) ? undef : $self->mouse_to_3d(@$pos); +# +# # Select volume in this 3D canvas. +# # Don't deselect a volume if layer editing is enabled. We want the object to stay selected +# # during the scene manipulation. +# # if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) { # $self->deselect_volumes; # $self->select_volume($volume_idx); -#============================================================================================================================== - - if ($volume_idx != -1) { - my $group_id = $self->volumes->[$volume_idx]->select_group_id; - my @volumes; - if ($group_id != -1) { -#============================================================================================================================== - Slic3r::GUI::_3DScene::select_volume($self, $_) +# +# if ($volume_idx != -1) { +# my $group_id = $self->volumes->[$volume_idx]->select_group_id; +# my @volumes; +# if ($group_id != -1) { # $self->select_volume($_) -#============================================================================================================================== - for grep $self->volumes->[$_]->select_group_id == $group_id, - 0..$#{$self->volumes}; - } - } - - $self->Refresh; - $self->Update; - } - - # propagate event through callback - $self->on_select->($volume_idx) - if $self->on_select; - - if ($volume_idx != -1) { - if ($e->LeftDown && $self->enable_moving) { - # Only accept the initial position, if it is inside the volume bounding box. - my $volume_bbox = $self->volumes->[$volume_idx]->transformed_bounding_box; - $volume_bbox->offset(1.); - if ($volume_bbox->contains_point($pos3d)) { - # The dragging operation is initiated. - $self->_drag_volume_idx($volume_idx); - $self->_drag_start_pos($pos3d); - # Remember the shift to to the object center. The object center will later be used - # to limit the object placement close to the bed. - $self->_drag_volume_center_offset($pos3d->vector_to($volume_bbox->center)); - } - } elsif ($e->RightDown) { - # if right clicking on volume, propagate event through callback - $self->on_right_click->($e->GetPosition) - if $self->on_right_click; - } - } - } -#============================================================================================================================== - } elsif ($e->Dragging && $e->LeftIsDown && (Slic3r::GUI::_3DScene::get_layers_editing_state($self) == 0) && defined($self->_drag_volume_idx)) { +# for grep $self->volumes->[$_]->select_group_id == $group_id, +# 0..$#{$self->volumes}; +# } +# } +# +# $self->Refresh; +# $self->Update; +# } +# +# # propagate event through callback +# $self->on_select->($volume_idx) +# if $self->on_select; +# +# if ($volume_idx != -1) { +# if ($e->LeftDown && $self->enable_moving) { +# # Only accept the initial position, if it is inside the volume bounding box. +# my $volume_bbox = $self->volumes->[$volume_idx]->transformed_bounding_box; +# $volume_bbox->offset(1.); +# if ($volume_bbox->contains_point($pos3d)) { +# # The dragging operation is initiated. +# $self->_drag_volume_idx($volume_idx); +# $self->_drag_start_pos($pos3d); +# # Remember the shift to to the object center. The object center will later be used +# # to limit the object placement close to the bed. +# $self->_drag_volume_center_offset($pos3d->vector_to($volume_bbox->center)); +# } +# } elsif ($e->RightDown) { +# # if right clicking on volume, propagate event through callback +# $self->on_right_click->($e->GetPosition) +# if $self->on_right_click; +# } +# } +# } # } elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) { -#============================================================================================================================== - # Get new position at the same Z of the initial click point. - my $cur_pos = Slic3r::Linef3->new( - $self->mouse_to_3d($e->GetX, $e->GetY, 0), - $self->mouse_to_3d($e->GetX, $e->GetY, 1)) - ->intersect_plane($self->_drag_start_pos->z); -#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ -# >>>>>>>>>>>>>>>>>>>>>>>>>> TEMPORARY DISABLED DUE TO bed_polygon REMOVAL <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +# # Get new position at the same Z of the initial click point. +# my $cur_pos = Slic3r::Linef3->new( +# $self->mouse_to_3d($e->GetX, $e->GetY, 0), +# $self->mouse_to_3d($e->GetX, $e->GetY, 1)) +# ->intersect_plane($self->_drag_start_pos->z); # # # Clip the new position, so the object center remains close to the bed. # { @@ -578,124 +531,96 @@ sub mouse_event { # } # $cur_pos->translate(@{$self->_drag_volume_center_offset->negative}); # } -#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ - # Calculate the translation vector. - my $vector = $self->_drag_start_pos->vector_to($cur_pos); - # Get the volume being dragged. - my $volume = $self->volumes->[$self->_drag_volume_idx]; - # Get all volumes belonging to the same group, if any. - my @volumes = ($volume->drag_group_id == -1) ? - ($volume) : - grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes}; - # Apply new temporary volume origin and ignore Z. - $_->translate($vector->x, $vector->y, 0) for @volumes; - $self->_drag_start_pos($cur_pos); - $self->_dragged(1); - $self->Refresh; - $self->Update; - } elsif ($e->Dragging) { -#============================================================================================================================== - if ((Slic3r::GUI::_3DScene::get_layers_editing_state($self) > 0) && ($object_idx_selected != -1)) { - Slic3r::GUI::_3DScene::perform_layer_editing_action($self, $e->GetY, $e->ShiftDown, $e->RightIsDown) if (Slic3r::GUI::_3DScene::get_layers_editing_state($self) == 1); - +# # Calculate the translation vector. +# my $vector = $self->_drag_start_pos->vector_to($cur_pos); +# # Get the volume being dragged. +# my $volume = $self->volumes->[$self->_drag_volume_idx]; +# # Get all volumes belonging to the same group, if any. +# my @volumes = ($volume->drag_group_id == -1) ? +# ($volume) : +# grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes}; +# # Apply new temporary volume origin and ignore Z. +# $_->translate($vector->x, $vector->y, 0) for @volumes; +# $self->_drag_start_pos($cur_pos); +# $self->_dragged(1); +# $self->Refresh; +# $self->Update; +# } elsif ($e->Dragging) { # if ($self->_layer_height_edited && $object_idx_selected != -1) { # $self->_variable_layer_thickness_action($e) if ($self->_layer_height_edited == 1); -#============================================================================================================================== - } elsif ($e->LeftIsDown) { - # if dragging over blank area with left button, rotate - if (defined $self->_drag_start_pos) { - my $orig = $self->_drag_start_pos; - if (TURNTABLE_MODE) { - # Turntable mode is enabled by default. -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_camera_phi($self, Slic3r::GUI::_3DScene::get_camera_phi($self) + ($pos->x - $orig->x) * TRACKBALLSIZE); - Slic3r::GUI::_3DScene::set_camera_theta($self, Slic3r::GUI::_3DScene::get_camera_theta($self) - ($pos->y - $orig->y) * TRACKBALLSIZE); - +# } elsif ($e->LeftIsDown) { +# # if dragging over blank area with left button, rotate +# if (defined $self->_drag_start_pos) { +# my $orig = $self->_drag_start_pos; +# if (TURNTABLE_MODE) { +# # Turntable mode is enabled by default. # $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE); # $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #- # $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; # $self->_stheta(0) if $self->_stheta < 0; -#============================================================================================================================== - } else { - my $size = $self->GetClientSize; - my @quat = trackball( - $orig->x / ($size->width / 2) - 1, - 1 - $orig->y / ($size->height / 2), #/ - $pos->x / ($size->width / 2) - 1, - 1 - $pos->y / ($size->height / 2), #/ - ); - $self->_quat(mulquats($self->_quat, \@quat)); - } - $self->on_viewport_changed->() if $self->on_viewport_changed; - $self->Refresh; - $self->Update; - } - $self->_drag_start_pos($pos); - } elsif ($e->MiddleIsDown || $e->RightIsDown) { - # If dragging over blank area with right button, pan. - if (defined $self->_drag_start_xy) { - # get point in model space at Z = 0 - my $cur_pos = $self->mouse_to_3d($e->GetX, $e->GetY, 0); - my $orig = $self->mouse_to_3d($self->_drag_start_xy->x, $self->_drag_start_xy->y, 0); -#============================================================================================================================== - my $camera_target = Slic3r::GUI::_3DScene::get_camera_target($self); - $camera_target->translate(@{$orig->vector_to($cur_pos)->negative}); - Slic3r::GUI::_3DScene::set_camera_target($self, $camera_target); +# } else { +# my $size = $self->GetClientSize; +# my @quat = trackball( +# $orig->x / ($size->width / 2) - 1, +# 1 - $orig->y / ($size->height / 2), #/ +# $pos->x / ($size->width / 2) - 1, +# 1 - $pos->y / ($size->height / 2), #/ +# ); +# $self->_quat(mulquats($self->_quat, \@quat)); +# } +# $self->on_viewport_changed->() if $self->on_viewport_changed; +# $self->Refresh; +# $self->Update; +# } +# $self->_drag_start_pos($pos); +# } elsif ($e->MiddleIsDown || $e->RightIsDown) { +# # If dragging over blank area with right button, pan. +# if (defined $self->_drag_start_xy) { +# # get point in model space at Z = 0 +# my $cur_pos = $self->mouse_to_3d($e->GetX, $e->GetY, 0); +# my $orig = $self->mouse_to_3d($self->_drag_start_xy->x, $self->_drag_start_xy->y, 0); # $self->_camera_target->translate(@{$orig->vector_to($cur_pos)->negative}); -#============================================================================================================================== - $self->on_viewport_changed->() if $self->on_viewport_changed; - $self->Refresh; - $self->Update; - } - $self->_drag_start_xy($pos); - } - } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) { -#============================================================================================================================== - if (Slic3r::GUI::_3DScene::get_layers_editing_state($self) > 0) { - Slic3r::GUI::_3DScene::set_layers_editing_state($self, 0); - Slic3r::GUI::_3DScene::stop_timer($self); +# $self->on_viewport_changed->() if $self->on_viewport_changed; +# $self->Refresh; +# $self->Update; +# } +# $self->_drag_start_xy($pos); +# } +# } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) { # if ($self->_layer_height_edited) { # $self->_layer_height_edited(undef); # $self->{layer_height_edit_timer}->Stop; -#============================================================================================================================== - $self->on_model_update->() - if ($object_idx_selected != -1 && $self->on_model_update); - } elsif ($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) { - # get all volumes belonging to the same group, if any - my @volume_idxs; - my $group_id = $self->volumes->[$self->_drag_volume_idx]->drag_group_id; - if ($group_id == -1) { - @volume_idxs = ($self->_drag_volume_idx); - } else { - @volume_idxs = grep $self->volumes->[$_]->drag_group_id == $group_id, - 0..$#{$self->volumes}; - } - $self->on_move->(@volume_idxs); - } - $self->_drag_volume_idx(undef); - $self->_drag_start_pos(undef); - $self->_drag_start_xy(undef); - $self->_dragged(undef); - } elsif ($e->Moving) { -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_mouse_position($self, $pos); +# $self->on_model_update->() +# if ($object_idx_selected != -1 && $self->on_model_update); +# } elsif ($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) { +# # get all volumes belonging to the same group, if any +# my @volume_idxs; +# my $group_id = $self->volumes->[$self->_drag_volume_idx]->drag_group_id; +# if ($group_id == -1) { +# @volume_idxs = ($self->_drag_volume_idx); +# } else { +# @volume_idxs = grep $self->volumes->[$_]->drag_group_id == $group_id, +# 0..$#{$self->volumes}; +# } +# $self->on_move->(@volume_idxs); +# } +# $self->_drag_volume_idx(undef); +# $self->_drag_start_pos(undef); +# $self->_drag_start_xy(undef); +# $self->_dragged(undef); +# } elsif ($e->Moving) { # $self->_mouse_pos($pos); -#============================================================================================================================== - # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor - # hovers over. -#============================================================================================================================== - if (Slic3r::GUI::_3DScene::is_picking_enabled($self)) { +# # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor +# # hovers over. # if ($self->enable_picking) { -#============================================================================================================================== - $self->Update; - $self->Refresh; - } - } else { - $e->Skip(); - } -} - -#============================================================================================================================== +# $self->Update; +# $self->Refresh; +# } +# } else { +# $e->Skip(); +# } +#} +# #sub mouse_wheel_event { # my ($self, $e) = @_; # @@ -1165,23 +1090,25 @@ sub mulquats { @$q1[3] * @$rq[3] - @$q1[0] * @$rq[0] - @$q1[1] * @$rq[1] - @$q1[2] * @$rq[2]) } -# Convert the screen space coordinate to an object space coordinate. -# If the Z screen space coordinate is not provided, a depth buffer value is substituted. -sub mouse_to_3d { - my ($self, $x, $y, $z) = @_; - - return unless $self->GetContext; - $self->SetCurrent($self->GetContext); - - my @viewport = glGetIntegerv_p(GL_VIEWPORT); # 4 items - my @mview = glGetDoublev_p(GL_MODELVIEW_MATRIX); # 16 items - my @proj = glGetDoublev_p(GL_PROJECTION_MATRIX); # 16 items - - $y = $viewport[3] - $y; - $z //= glReadPixels_p($x, $y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT); - my @projected = gluUnProject_p($x, $y, $z, @mview, @proj, @viewport); - return Slic3r::Pointf3->new(@projected); -} +#============================================================================================================================== +## Convert the screen space coordinate to an object space coordinate. +## If the Z screen space coordinate is not provided, a depth buffer value is substituted. +#sub mouse_to_3d { +# my ($self, $x, $y, $z) = @_; +# +# return unless $self->GetContext; +# $self->SetCurrent($self->GetContext); +# +# my @viewport = glGetIntegerv_p(GL_VIEWPORT); # 4 items +# my @mview = glGetDoublev_p(GL_MODELVIEW_MATRIX); # 16 items +# my @proj = glGetDoublev_p(GL_PROJECTION_MATRIX); # 16 items +# +# $y = $viewport[3] - $y; +# $z //= glReadPixels_p($x, $y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT); +# my @projected = gluUnProject_p($x, $y, $z, @mview, @proj, @viewport); +# return Slic3r::Pointf3->new(@projected); +#} +#============================================================================================================================== sub GetContext { my ($self) = @_; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 3d1f75221e..35468aad5b 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -84,13 +84,19 @@ sub new { $self->object_settings_dialog if $self->selected_object; }; my $on_right_click = sub { - my ($canvas, $click_pos) = @_; - +#============================================================================================================================== + my ($canvas, $click_pos_x, $click_pos_y) = @_; +# my ($canvas, $click_pos) = @_; +#============================================================================================================================== + my ($obj_idx, $object) = $self->selected_object; return if !defined $obj_idx; my $menu = $self->object_menu; - $canvas->PopupMenu($menu, $click_pos); +#============================================================================================================================== + $canvas->PopupMenu($menu, $click_pos_x, $click_pos_y); +# $canvas->PopupMenu($menu, $click_pos); +#============================================================================================================================== $menu->Destroy; }; my $on_instances_moved = sub { @@ -111,8 +117,12 @@ sub new { $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config}); $self->{preview_notebook}->AddPage($self->{canvas3D}, L('3D')); $self->{canvas3D}->set_on_select_object($on_select_object); - $self->{canvas3D}->set_on_double_click($on_double_click); - $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); +#============================================================================================================================== + Slic3r::GUI::_3DScene::register_on_double_click_callback($self->{canvas3D}, $on_double_click); + Slic3r::GUI::_3DScene::register_on_right_click_callback($self->{canvas3D}, sub { $on_right_click->($self->{canvas3D}, @_); }); +# $self->{canvas3D}->set_on_double_click($on_double_click); +# $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); +#============================================================================================================================== $self->{canvas3D}->set_on_arrange(sub { $self->arrange }); $self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') }); $self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') }); diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm index 7beba9299d..120fec8301 100644 --- a/lib/Slic3r/GUI/Plater/2D.pm +++ b/lib/Slic3r/GUI/Plater/2D.pm @@ -222,7 +222,10 @@ sub mouse_event { ]; $self->{drag_object} = [ $obj_idx, $instance_idx ]; } elsif ($event->RightDown) { - $self->{on_right_click}->($pos); +#======================================================================================================================================= + $self->{on_right_click}->($pos->x, $pos->y); +# $self->{on_right_click}->($pos); +#======================================================================================================================================= } last OBJECTS; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index a8db8ccbf1..04aca9aaf1 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -21,9 +21,10 @@ sub new { my $self = $class->SUPER::new($parent); #============================================================================================================================== Slic3r::GUI::_3DScene::enable_picking($self, 1); + Slic3r::GUI::_3DScene::enable_moving($self, 1); # $self->enable_picking(1); +# $self->enable_moving(1); #============================================================================================================================== - $self->enable_moving(1); $self->select_by('object'); $self->drag_by('instance'); @@ -46,6 +47,9 @@ sub new { $self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx) if ($self->{on_select_object}); }); +#============================================================================================================================== + Slic3r::GUI::_3DScene::register_on_select_callback($self, $self->on_select); +#============================================================================================================================== $self->on_move(sub { my @volume_idxs = @_; @@ -125,15 +129,17 @@ sub set_on_select_object { $self->{on_select_object} = $cb; } -sub set_on_double_click { - my ($self, $cb) = @_; - $self->on_double_click($cb); -} - -sub set_on_right_click { - my ($self, $cb) = @_; - $self->on_right_click($cb); -} +#============================================================================================================================== +#sub set_on_double_click { +# my ($self, $cb) = @_; +# $self->on_double_click($cb); +#} +# +#sub set_on_right_click { +# my ($self, $cb) = @_; +# $self->on_right_click($cb); +#} +#============================================================================================================================== sub set_on_arrange { my ($self, $cb) = @_; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index cb87a43d26..12c1fc1803 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1939,6 +1939,11 @@ bool _3DScene::is_picking_enabled(wxGLCanvas* canvas) return s_canvas_mgr.is_picking_enabled(canvas); } +bool _3DScene::is_moving_enabled(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_moving_enabled(canvas); +} + bool _3DScene::is_layers_editing_allowed(wxGLCanvas* canvas) { return s_canvas_mgr.is_layers_editing_allowed(canvas); @@ -1969,6 +1974,11 @@ void _3DScene::enable_picking(wxGLCanvas* canvas, bool enable) s_canvas_mgr.enable_picking(canvas, enable); } +void _3DScene::enable_moving(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_moving(canvas, enable); +} + void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_shader(canvas, enable); @@ -2170,6 +2180,21 @@ void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* c s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); } +void _3DScene::register_on_double_click_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_double_click_callback(canvas, callback); +} + +void _3DScene::register_on_right_click_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_right_click_callback(canvas, callback); +} + +void _3DScene::register_on_select_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_select_callback(canvas, callback); +} + //void _3DScene::_glew_init() //{ // glewInit(); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 22a5e8d618..11969df6c8 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -598,6 +598,7 @@ public: static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_picking_enabled(wxGLCanvas* canvas); + static bool is_moving_enabled(wxGLCanvas* canvas); static bool is_layers_editing_allowed(wxGLCanvas* canvas); static bool is_multisample_allowed(wxGLCanvas* canvas); @@ -605,6 +606,7 @@ public: static void enable_warning_texture(wxGLCanvas* canvas, bool enable); static void enable_legend_texture(wxGLCanvas* canvas, bool enable); static void enable_picking(wxGLCanvas* canvas, bool enable); + static void enable_moving(wxGLCanvas* canvas, bool enable); static void enable_shader(wxGLCanvas* canvas, bool enable); static void allow_multisample(wxGLCanvas* canvas, bool allow); @@ -663,6 +665,9 @@ public: static void perform_layer_editing_action(wxGLCanvas* canvas, int y, bool shift_down, bool right_down); static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); + static void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); + static void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); + static void register_on_select_callback(wxGLCanvas* canvas, void* callback); // static void _glew_init(); //################################################################################################################## diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 4e2355c893..31c7b81b29 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1048,6 +1048,52 @@ void GLCanvas3D::Mouse::set_position(const Pointf& position) m_position = position; } +GLCanvas3D::Drag::Drag() + : m_start_mouse_position(DBL_MAX, DBL_MAX) + , m_volume_idx(-1) +{ +} + +const Point& GLCanvas3D::Drag::get_start_mouse_position() const +{ + return m_start_mouse_position; +} + +void GLCanvas3D::Drag::set_start_mouse_position(const Point& position) +{ + m_start_mouse_position = position; +} + +const Pointf3& GLCanvas3D::Drag::get_start_position_3D() const +{ + return m_start_position_3D; +} + +void GLCanvas3D::Drag::set_start_position_3D(const Pointf3& position) +{ + m_start_position_3D = position; +} + +const Vectorf3& GLCanvas3D::Drag::get_volume_center_offset() const +{ + return m_volume_center_offset; +} + +void GLCanvas3D::Drag::set_volume_center_offset(const Vectorf3& offset) +{ + m_volume_center_offset = offset; +} + +int GLCanvas3D::Drag::get_volume_idx() const +{ + return m_volume_idx; +} + +void GLCanvas3D::Drag::set_volume_idx(int idx) +{ + m_volume_idx = idx; +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -1061,6 +1107,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_warning_texture_enabled(false) , m_legend_texture_enabled(false) , m_picking_enabled(false) + , m_moving_enabled(false) , m_shader_enabled(false) , m_multisample_allowed(false) { @@ -1439,6 +1486,11 @@ bool GLCanvas3D::is_picking_enabled() const return m_picking_enabled; } +bool GLCanvas3D::is_moving_enabled() const +{ + return m_moving_enabled; +} + bool GLCanvas3D::is_layers_editing_allowed() const { return m_layers_editing.is_allowed(); @@ -1469,6 +1521,11 @@ void GLCanvas3D::enable_picking(bool enable) m_picking_enabled = enable; } +void GLCanvas3D::enable_moving(bool enable) +{ + m_moving_enabled = enable; +} + void GLCanvas3D::enable_shader(bool enable) { m_shader_enabled = enable; @@ -1768,6 +1825,24 @@ void GLCanvas3D::register_on_viewport_changed_callback(void* callback) m_on_viewport_changed_callback.register_callback(callback); } +void GLCanvas3D::register_on_double_click_callback(void* callback) +{ + if (callback != nullptr) + m_on_double_click_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_right_click_callback(void* callback) +{ + if (callback != nullptr) + m_on_right_click_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_select_callback(void* callback) +{ + if (callback != nullptr) + m_on_select_callback.register_callback(callback); +} + void GLCanvas3D::on_size(wxSizeEvent& evt) { set_dirty(true); @@ -1805,7 +1880,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) { // key B/b case 66: - case 98: { zoom_to_bed(); break; } + case 98: { zoom_to_bed(); break; } // key Z/z case 90: case 122: { zoom_to_volumes(); break; } @@ -1869,6 +1944,231 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt) _perform_layer_editing_action(); } +void GLCanvas3D::on_mouse(wxMouseEvent& evt) +{ + if ((m_canvas == nullptr) || (m_volumes == nullptr)) + return; + + Point pos(evt.GetX(), evt.GetY()); + + int selected_object_idx = (is_layers_editing_enabled() && (m_print != nullptr)) ? get_layers_editing_first_selected_object_id(m_print->objects.size()) : -1; + set_layers_editing_last_object_id(selected_object_idx); + + set_mouse_dragging(evt.Dragging()); + + if (evt.Entering()) + { +#if defined(__WXMSW__) || defined(__WXGTK__) + // On Windows and Linux needs focus in order to catch key events + m_canvas->SetFocus(); + m_drag.set_start_mouse_position(Point(INT_MAX, INT_MAX)); +#endif + } + else if (evt.LeftDClick()) + { + m_on_double_click_callback.call(); + } + else if (evt.LeftDown() || evt.RightDown()) + { + // If user pressed left or right button we first check whether this happened + // on a volume or not. + int volume_idx = get_hover_volume_id(); + set_layers_editing_state(0); + if ((selected_object_idx != -1) && bar_rect_contains(pos.x, pos.y)) + { + // A volume is selected and the mouse is inside the layer thickness bar. + // Start editing the layer height. + set_layers_editing_state(1); + perform_layer_editing_action(evt.GetY(), evt.ShiftDown(), evt.RightDown()); + } + else if ((selected_object_idx != -1) && reset_rect_contains(pos.x, pos.y)) + { + if (evt.LeftDown()) + { + // A volume is selected and the mouse is inside the reset button. + m_print->get_object(selected_object_idx)->reset_layer_height_profile(); + // Index 2 means no editing, just wait for mouse up event. + set_layers_editing_state(2); + m_canvas->Refresh(); + m_canvas->Update(); + } + } + else + { + // 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 (is_picking_enabled() && ((volume_idx != -1) || !is_layers_editing_enabled())) + { + deselect_volumes(); + select_volume(volume_idx); + + if (volume_idx != -1) + { + int group_id = m_volumes->volumes[volume_idx]->select_group_id; + if (group_id != -1) + { + for (GLVolume* vol : m_volumes->volumes) + { + if ((vol != nullptr) && (vol->select_group_id == group_id)) + vol->selected = true; + } + } + } + + m_canvas->Refresh(); + m_canvas->Update(); + } + + // propagate event through callback + m_on_select_callback.call(volume_idx); + + // 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. + Pointf3 pos3d = (volume_idx == -1) ? Pointf3(DBL_MAX, DBL_MAX) : _mouse_to_3d(pos); + + if (volume_idx != -1) + { + if (evt.LeftDown() && is_moving_enabled()) + { + // Only accept the initial position, if it is inside the volume bounding box. + BoundingBoxf3 volume_bbox = m_volumes->volumes[volume_idx]->transformed_bounding_box(); + volume_bbox.offset(1.0); + if (volume_bbox.contains(pos3d)) + { + // The dragging operation is initiated. + m_drag.set_volume_idx(volume_idx); + m_drag.set_start_position_3D(pos3d); + // Remember the shift to to the object center.The object center will later be used + // to limit the object placement close to the bed. + m_drag.set_volume_center_offset(pos3d.vector_to(volume_bbox.center())); + } + } + else if (evt.RightDown()) + { + // if right clicking on volume, propagate event through callback + if (m_volumes->volumes[volume_idx]->hover) + { + m_on_right_click_callback.call(pos.x, pos.y); + } + } + } + } + } +// } elsif($e->Dragging && $e->LeftIsDown && (Slic3r::GUI::_3DScene::get_layers_editing_state($self) == 0) && defined($self->_drag_volume_idx)) { +// # Get new position at the same Z of the initial click point. +// my $cur_pos = Slic3r::Linef3->new( +// $self->mouse_to_3d($e->GetX, $e->GetY, 0), +// $self->mouse_to_3d($e->GetX, $e->GetY, 1)) +// ->intersect_plane($self->_drag_start_pos->z); +// +// # Clip the new position, so the object center remains close to the bed. +// { +// $cur_pos->translate(@{$self->_drag_volume_center_offset}); +// my $cur_pos2 = Slic3r::Point->new(scale($cur_pos->x), scale($cur_pos->y)); +// if (!$self->bed_polygon->contains_point($cur_pos2)) { +// my $ip = $self->bed_polygon->point_projection($cur_pos2); +// $cur_pos->set_x(unscale($ip->x)); +// $cur_pos->set_y(unscale($ip->y)); +// } +// $cur_pos->translate(@{$self->_drag_volume_center_offset->negative}); +// } +// +// # Calculate the translation vector. +// my $vector = $self->_drag_start_pos->vector_to($cur_pos); +// # Get the volume being dragged. +// my $volume = $self->volumes->[$self->_drag_volume_idx]; +// # Get all volumes belonging to the same group, if any. +// my @volumes = ($volume->drag_group_id == -1) ? +// ($volume) : +// grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes}; +// # Apply new temporary volume origin and ignore Z. +// $_->translate($vector->x, $vector->y, 0) for @volumes; +// $self->_drag_start_pos($cur_pos); +// $self->_dragged(1); +// $self->Refresh; +// $self->Update; +// } elsif($e->Dragging) { +// if ((Slic3r::GUI::_3DScene::get_layers_editing_state($self) > 0) && ($object_idx_selected != -1)) { +// Slic3r::GUI::_3DScene::perform_layer_editing_action($self, $e->GetY, $e->ShiftDown, $e->RightIsDown) if (Slic3r::GUI::_3DScene::get_layers_editing_state($self) == 1); +// } elsif($e->LeftIsDown) { +//# if dragging over blank area with left button, rotate +// if (defined $self->_drag_start_pos) { +// my $orig = $self->_drag_start_pos; +// if (TURNTABLE_MODE) { +// # Turntable mode is enabled by default. +// Slic3r::GUI::_3DScene::set_camera_phi($self, Slic3r::GUI::_3DScene::get_camera_phi($self) + ($pos->x - $orig->x) * TRACKBALLSIZE); +// Slic3r::GUI::_3DScene::set_camera_theta($self, Slic3r::GUI::_3DScene::get_camera_theta($self) - ($pos->y - $orig->y) * TRACKBALLSIZE); +// } +// else { +// my $size = $self->GetClientSize; +// my @quat = trackball( +// $orig->x / ($size->width / 2) - 1, +// 1 - $orig->y / ($size->height / 2), # / +// $pos->x / ($size->width / 2) - 1, +// 1 - $pos->y / ($size->height / 2), # / +// ); +// $self->_quat(mulquats($self->_quat, \@quat)); +// } +// $self->on_viewport_changed->() if $self->on_viewport_changed; +// $self->Refresh; +// $self->Update; +// } +// $self->_drag_start_pos($pos); +// } elsif($e->MiddleIsDown || $e->RightIsDown) { +// # If dragging over blank area with right button, pan. +// if (defined $self->_drag_start_xy) { +// # get point in model space at Z = 0 +// my $cur_pos = $self->mouse_to_3d($e->GetX, $e->GetY, 0); +// my $orig = $self->mouse_to_3d($self->_drag_start_xy->x, $self->_drag_start_xy->y, 0); +// my $camera_target = Slic3r::GUI::_3DScene::get_camera_target($self); +// $camera_target->translate(@{$orig->vector_to($cur_pos)->negative}); +// Slic3r::GUI::_3DScene::set_camera_target($self, $camera_target); +// $self->on_viewport_changed->() if $self->on_viewport_changed; +// $self->Refresh; +// $self->Update; +// } +// $self->_drag_start_xy($pos); +// } +// } elsif($e->LeftUp || $e->MiddleUp || $e->RightUp) { +// if (Slic3r::GUI::_3DScene::get_layers_editing_state($self) > 0) { +// Slic3r::GUI::_3DScene::set_layers_editing_state($self, 0); +// Slic3r::GUI::_3DScene::stop_timer($self); +// $self->on_model_update->() +// if ($object_idx_selected != -1 && $self->on_model_update); +// } elsif($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) { +// # get all volumes belonging to the same group, if any +// my @volume_idxs; +// my $group_id = $self->volumes->[$self->_drag_volume_idx]->drag_group_id; +// if ($group_id == -1) { +// @volume_idxs = ($self->_drag_volume_idx); +// } +// else { +// @volume_idxs = grep $self->volumes->[$_]->drag_group_id == $group_id, +// 0..$#{$self->volumes}; +// } +// $self->on_move->(@volume_idxs); +// } +// $self->_drag_volume_idx(undef); +// $self->_drag_start_pos(undef); +// $self->_drag_start_xy(undef); +// $self->_dragged(undef); +// } + else if (evt.Moving()) + { + set_mouse_position(Pointf((coordf_t)pos.x, (coordf_t)pos.y)); + // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over. + if (is_picking_enabled()) + { + m_canvas->Refresh(); + m_canvas->Update(); + } + } + else + evt.Skip(); +} + Size GLCanvas3D::get_canvas_size() const { int w = 0; @@ -1997,6 +2297,9 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co void GLCanvas3D::_deregister_callbacks() { m_on_viewport_changed_callback.deregister_callback(); + m_on_double_click_callback.deregister_callback(); + m_on_right_click_callback.deregister_callback(); + m_on_select_callback.deregister_callback(); } void GLCanvas3D::_mark_volumes_for_layer_height() const @@ -2337,5 +2640,29 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) start_timer(); } +Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) +{ + if (!set_current()) + return Pointf3(DBL_MAX, DBL_MAX, DBL_MAX); + + GLint viewport[4]; + ::glGetIntegerv(GL_VIEWPORT, viewport); + GLdouble modelview_matrix[16]; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix); + GLdouble projection_matrix[16]; + ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); + + GLint y = viewport[3] - (GLint)mouse_pos.y; + GLfloat mouse_z; + if (z == nullptr) + ::glReadPixels((GLint)mouse_pos.x, (GLint)mouse_pos.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z); + else + mouse_z = *z; + + GLdouble out_x, out_y, out_z; + ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)mouse_pos.y, mouse_z, modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); + return Pointf3((coordf_t)out_x, (coordf_t)out_y, (coordf_t)out_z); +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 22f561cbde..7d490984f0 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -306,6 +306,29 @@ public: void set_position(const Pointf& position); }; + class Drag + { + Point m_start_mouse_position; + Pointf3 m_start_position_3D; + Vectorf3 m_volume_center_offset; + int m_volume_idx; + + public: + Drag(); + + const Point& get_start_mouse_position() const; + void set_start_mouse_position(const Point& mouse_position); + + const Pointf3& get_start_position_3D() const; + void set_start_position_3D(const Pointf3& position); + + const Vectorf3& get_volume_center_offset() const; + void set_volume_center_offset(const Vectorf3& offset); + + int get_volume_idx() const; + void set_volume_idx(int idx); + }; + private: wxGLCanvas* m_canvas; wxGLContext* m_context; @@ -317,6 +340,7 @@ private: LayersEditing m_layers_editing; Shader m_shader; Mouse m_mouse; + Drag m_drag; GLVolumeCollection* m_volumes; DynamicPrintConfig* m_config; @@ -328,10 +352,14 @@ private: bool m_warning_texture_enabled; bool m_legend_texture_enabled; bool m_picking_enabled; + bool m_moving_enabled; bool m_shader_enabled; bool m_multisample_allowed; PerlCallback m_on_viewport_changed_callback; + PerlCallback m_on_double_click_callback; + PerlCallback m_on_right_click_callback; + PerlCallback m_on_select_callback; public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); @@ -398,6 +426,7 @@ public: bool is_layers_editing_enabled() const; bool is_picking_enabled() const; + bool is_moving_enabled() const; bool is_layers_editing_allowed() const; bool is_multisample_allowed() const; @@ -405,6 +434,7 @@ public: void enable_warning_texture(bool enable); void enable_legend_texture(bool enable); void enable_picking(bool enable); + void enable_moving(bool enable); void enable_shader(bool enable); void allow_multisample(bool allow); @@ -459,12 +489,16 @@ public: void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(void* callback); + void register_on_double_click_callback(void* callback); + void register_on_right_click_callback(void* callback); + void register_on_select_callback(void* callback); void on_size(wxSizeEvent& evt); void on_idle(wxIdleEvent& evt); void on_char(wxKeyEvent& evt); void on_mouse_wheel(wxMouseEvent& evt); void on_timer(wxTimerEvent& evt); + void on_mouse(wxMouseEvent& evt); Size get_canvas_size() const; Point get_local_mouse_position() const; @@ -494,6 +528,10 @@ private: void _render_layer_editing_overlay() const; void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); + + // 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. + Pointf3 _mouse_to_3d(const Point& mouse_pos, float* z = nullptr); }; } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 2d692cc273..c1eb7f37ac 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -78,6 +78,18 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) canvas->Bind(wxEVT_CHAR, [canvas3D](wxKeyEvent& evt) { canvas3D->on_char(evt); }); canvas->Bind(wxEVT_MOUSEWHEEL, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse_wheel(evt); }); canvas->Bind(wxEVT_TIMER, [canvas3D](wxTimerEvent& evt) { canvas3D->on_timer(evt); }); + canvas->Bind(wxEVT_LEFT_DOWN, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_LEFT_UP, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_MIDDLE_DOWN, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_MIDDLE_UP, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_RIGHT_DOWN, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_RIGHT_UP, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_MOTION, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_ENTER_WINDOW, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_LEAVE_WINDOW, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_LEFT_DCLICK, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_MIDDLE_DCLICK, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_RIGHT_DCLICK, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); @@ -384,6 +396,12 @@ bool GLCanvas3DManager::is_picking_enabled(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_picking_enabled() : false; } +bool GLCanvas3DManager::is_moving_enabled(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_moving_enabled() : false; +} + bool GLCanvas3DManager::is_layers_editing_allowed(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -424,6 +442,13 @@ void GLCanvas3DManager::enable_picking(wxGLCanvas* canvas, bool enable) it->second->enable_picking(enable); } +void GLCanvas3DManager::enable_moving(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_moving(enable); +} + void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -692,6 +717,27 @@ void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas it->second->register_on_viewport_changed_callback(callback); } +void GLCanvas3DManager::register_on_double_click_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_double_click_callback(callback); +} + +void GLCanvas3DManager::register_on_right_click_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_right_click_callback(callback); +} + +void GLCanvas3DManager::register_on_select_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_select_callback(callback); +} + GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 1cb4c316e0..da6e00e7bd 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -98,6 +98,7 @@ public: bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_picking_enabled(wxGLCanvas* canvas) const; + bool is_moving_enabled(wxGLCanvas* canvas) const; bool is_layers_editing_allowed(wxGLCanvas* canvas) const; bool is_multisample_allowed(wxGLCanvas* canvas) const; @@ -105,6 +106,7 @@ public: void enable_warning_texture(wxGLCanvas* canvas, bool enable); void enable_legend_texture(wxGLCanvas* canvas, bool enable); void enable_picking(wxGLCanvas* canvas, bool enable); + void enable_moving(wxGLCanvas* canvas, bool enable); void enable_shader(wxGLCanvas* canvas, bool enable); void allow_multisample(wxGLCanvas* canvas, bool allow); @@ -163,6 +165,9 @@ public: void perform_layer_editing_action(wxGLCanvas* canvas, int y, bool shift_down, bool right_down); void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); + void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); + void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); + void register_on_select_callback(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 0ffe3677bb..8023efab2b 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -467,6 +467,14 @@ is_picking_enabled(canvas) OUTPUT: RETVAL +bool +is_moving_enabled(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_moving_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + bool is_layers_editing_allowed(canvas) SV *canvas; @@ -503,7 +511,7 @@ enable_legend_texture(canvas, enable) bool enable; CODE: _3DScene::enable_legend_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); - + void enable_picking(canvas, enable) SV *canvas; @@ -511,6 +519,13 @@ enable_picking(canvas, enable) CODE: _3DScene::enable_picking((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); +void +enable_moving(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_moving((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void enable_shader(canvas, enable) SV *canvas; @@ -810,7 +825,27 @@ register_on_viewport_changed_callback(canvas, callback) SV *callback; CODE: _3DScene::register_on_viewport_changed_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); - + +void +register_on_double_click_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_double_click_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_right_click_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_right_click_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_select_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_select_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); unsigned int From 6bf009edee89e6713c5c85af1e5dcf764c23bad5 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 31 May 2018 16:04:59 +0200 Subject: [PATCH 047/117] 3DScene mouse event handler partially moved to c++ - part 2 --- lib/Slic3r/GUI/3DScene.pm | 2 - lib/Slic3r/GUI/Plater.pm | 13 +- lib/Slic3r/GUI/Plater/3D.pm | 22 +- xs/src/libslic3r/Utils.hpp | 2 +- xs/src/libslic3r/utils.cpp | 17 +- xs/src/slic3r/GUI/3DScene.cpp | 21 +- xs/src/slic3r/GUI/3DScene.hpp | 5 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 273 ++++++++++++++---------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 12 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 27 +-- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 5 +- xs/xsp/GUI_3DScene.xsp | 31 ++- 12 files changed, 257 insertions(+), 173 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 4715b6ecfe..0198739367 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -37,8 +37,6 @@ use Slic3r::Geometry qw(PI); __PACKAGE__->mk_accessors( qw(_quat init on_viewport_changed on_select - on_move - on_model_update volumes _drag_volume_idx diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 35468aad5b..2ff798899a 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -143,7 +143,8 @@ sub new { $cfg->set('wipe_tower_y', $new_pos_3f->y); $self->GetFrame->{options_tabs}{print}->load_config($cfg); }); - $self->{canvas3D}->set_on_model_update(sub { +#============================================================================================================================== + Slic3r::GUI::_3DScene::register_on_model_update_callback($self->{canvas3D}, sub { if (wxTheApp->{app_config}->get("background_processing")) { $self->schedule_background_process; } else { @@ -151,6 +152,16 @@ sub new { $self->{"print_info_box_show"}->(0); } }); + +# $self->{canvas3D}->set_on_model_update(sub { +# if (wxTheApp->{app_config}->get("background_processing")) { +# $self->schedule_background_process; +# } else { +# # Hide the print info box, it is no more valid. +# $self->{"print_info_box_show"}->(0); +# } +# }); +#============================================================================================================================== $self->{canvas3D}->on_viewport_changed(sub { #============================================================================================================================== Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 04aca9aaf1..ff4e2ad293 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -50,9 +50,19 @@ sub new { #============================================================================================================================== Slic3r::GUI::_3DScene::register_on_select_callback($self, $self->on_select); #============================================================================================================================== - $self->on_move(sub { + +#============================================================================================================================== + Slic3r::GUI::_3DScene::register_on_move_callback($self, sub { +# $self->on_move(sub { +#============================================================================================================================== my @volume_idxs = @_; + +#============================================================================================================================== + print "cucu"; +#============================================================================================================================== + + my %done = (); # prevent moving instances twice my $object_moved; my $wipe_tower_moved; @@ -186,10 +196,12 @@ sub set_on_wipe_tower_moved { $self->{on_wipe_tower_moved} = $cb; } -sub set_on_model_update { - my ($self, $cb) = @_; - $self->on_model_update($cb); -} +#============================================================================================================================== +#sub set_on_model_update { +# my ($self, $cb) = @_; +# $self->on_model_update($cb); +#} +#============================================================================================================================== sub set_on_enable_action_buttons { my ($self, $cb) = @_; diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 8e4d654dea..45e83b1b8d 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -95,7 +95,7 @@ public: void call() const; void call(int i) const; void call(int i, int j) const; - // void call(const std::vector &ints); + void call(const std::vector& ints) const; // void call(); // void call(int i); // void call(int i, int j); diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index c691073a4b..883f5e753d 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -238,9 +238,8 @@ void PerlCallback::call(int i, int j) const LEAVE; } -/* //############################################################################################################## -void PerlCallback::call(const std::vector &ints) const +void PerlCallback::call(const std::vector& ints) const //void PerlCallback::call(const std::vector &ints) //############################################################################################################## { @@ -250,16 +249,22 @@ void PerlCallback::call(const std::vector &ints) const ENTER; SAVETMPS; PUSHMARK(SP); - AV* av = newAV(); +//############################################################################################################## for (int i : ints) - av_push(av, newSViv(i)); - XPUSHs(av); + { + XPUSHs(sv_2mortal(newSViv(i))); + } + +// AV* av = newAV(); +// for (int i : ints) +// av_push(av, newSViv(i)); +// XPUSHs(av); +//############################################################################################################## PUTBACK; perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); FREETMPS; LEAVE; } -*/ #ifdef WIN32 #ifndef NOMINMAX diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 12c1fc1803..d2ae0a4af3 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1999,17 +1999,6 @@ void _3DScene::set_mouse_dragging(wxGLCanvas* canvas, bool dragging) s_canvas_mgr.set_mouse_dragging(canvas, dragging); } -Pointf _3DScene::get_mouse_position(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_mouse_position(canvas); -} - -void _3DScene::set_mouse_position(wxGLCanvas* canvas, const Pointf* position) -{ - if (position != nullptr) - s_canvas_mgr.set_mouse_position(canvas, *position); -} - int _3DScene::get_hover_volume_id(wxGLCanvas* canvas) { return s_canvas_mgr.get_hover_volume_id(canvas); @@ -2195,6 +2184,16 @@ void _3DScene::register_on_select_callback(wxGLCanvas* canvas, void* callback) s_canvas_mgr.register_on_select_callback(canvas, callback); } +void _3DScene::register_on_model_update_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_model_update_callback(canvas, callback); +} + +void _3DScene::register_on_move_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_move_callback(canvas, callback); +} + //void _3DScene::_glew_init() //{ // glewInit(); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 11969df6c8..a411884351 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -613,9 +613,6 @@ public: static bool is_mouse_dragging(wxGLCanvas* canvas); static void set_mouse_dragging(wxGLCanvas* canvas, bool dragging); - static Pointf get_mouse_position(wxGLCanvas* canvas); - static void set_mouse_position(wxGLCanvas* canvas, const Pointf* position); - static int get_hover_volume_id(wxGLCanvas* canvas); static void set_hover_volume_id(wxGLCanvas* canvas, int id); @@ -668,6 +665,8 @@ public: static void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); static void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); static void register_on_select_callback(wxGLCanvas* canvas, void* callback); + static void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); + static void register_on_move_callback(wxGLCanvas* canvas, void* callback); // static void _glew_init(); //################################################################################################################## diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 31c7b81b29..53f301ad5b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -301,6 +301,16 @@ const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const return m_bounding_box; } +bool GLCanvas3D::Bed::contains(const Point& point) const +{ + return m_polygon.contains(point); +} + +Point GLCanvas3D::Bed::point_projection(const Point& point) const +{ + return m_polygon.point_projection(point); +} + void GLCanvas3D::Bed::render() const { unsigned int triangles_vcount = m_triangles.get_data_size() / 3; @@ -1049,19 +1059,19 @@ void GLCanvas3D::Mouse::set_position(const Pointf& position) } GLCanvas3D::Drag::Drag() - : m_start_mouse_position(DBL_MAX, DBL_MAX) + : m_start_position_2D(INT_MAX, INT_MAX) , m_volume_idx(-1) { } -const Point& GLCanvas3D::Drag::get_start_mouse_position() const +const Point& GLCanvas3D::Drag::get_start_position_2D() const { - return m_start_mouse_position; + return m_start_position_2D; } -void GLCanvas3D::Drag::set_start_mouse_position(const Point& position) +void GLCanvas3D::Drag::set_start_position_2D(const Point& position) { - m_start_mouse_position = position; + m_start_position_2D = position; } const Pointf3& GLCanvas3D::Drag::get_start_position_3D() const @@ -1843,6 +1853,18 @@ void GLCanvas3D::register_on_select_callback(void* callback) m_on_select_callback.register_callback(callback); } +void GLCanvas3D::register_on_model_update_callback(void* callback) +{ + if (callback != nullptr) + m_on_model_update_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_move_callback(void* callback) +{ + if (callback != nullptr) + m_on_move_callback.register_callback(callback); +} + void GLCanvas3D::on_size(wxSizeEvent& evt) { set_dirty(true); @@ -1954,14 +1976,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) int selected_object_idx = (is_layers_editing_enabled() && (m_print != nullptr)) ? get_layers_editing_first_selected_object_id(m_print->objects.size()) : -1; set_layers_editing_last_object_id(selected_object_idx); - set_mouse_dragging(evt.Dragging()); +// set_mouse_dragging(false); if (evt.Entering()) { #if defined(__WXMSW__) || defined(__WXGTK__) // On Windows and Linux needs focus in order to catch key events m_canvas->SetFocus(); - m_drag.set_start_mouse_position(Point(INT_MAX, INT_MAX)); + m_drag.set_start_position_2D(Point(INT_MAX, INT_MAX)); #endif } else if (evt.LeftDClick()) @@ -1979,7 +2001,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // A volume is selected and the mouse is inside the layer thickness bar. // Start editing the layer height. set_layers_editing_state(1); - perform_layer_editing_action(evt.GetY(), evt.ShiftDown(), evt.RightDown()); + perform_layer_editing_action(pos.y, evt.ShiftDown(), evt.RightDown()); } else if ((selected_object_idx != -1) && reset_rect_contains(pos.x, pos.y)) { @@ -2056,105 +2078,136 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } } -// } elsif($e->Dragging && $e->LeftIsDown && (Slic3r::GUI::_3DScene::get_layers_editing_state($self) == 0) && defined($self->_drag_volume_idx)) { -// # Get new position at the same Z of the initial click point. -// my $cur_pos = Slic3r::Linef3->new( -// $self->mouse_to_3d($e->GetX, $e->GetY, 0), -// $self->mouse_to_3d($e->GetX, $e->GetY, 1)) -// ->intersect_plane($self->_drag_start_pos->z); -// -// # Clip the new position, so the object center remains close to the bed. -// { -// $cur_pos->translate(@{$self->_drag_volume_center_offset}); -// my $cur_pos2 = Slic3r::Point->new(scale($cur_pos->x), scale($cur_pos->y)); -// if (!$self->bed_polygon->contains_point($cur_pos2)) { -// my $ip = $self->bed_polygon->point_projection($cur_pos2); -// $cur_pos->set_x(unscale($ip->x)); -// $cur_pos->set_y(unscale($ip->y)); -// } -// $cur_pos->translate(@{$self->_drag_volume_center_offset->negative}); -// } -// -// # Calculate the translation vector. -// my $vector = $self->_drag_start_pos->vector_to($cur_pos); -// # Get the volume being dragged. -// my $volume = $self->volumes->[$self->_drag_volume_idx]; -// # Get all volumes belonging to the same group, if any. -// my @volumes = ($volume->drag_group_id == -1) ? -// ($volume) : -// grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes}; -// # Apply new temporary volume origin and ignore Z. -// $_->translate($vector->x, $vector->y, 0) for @volumes; -// $self->_drag_start_pos($cur_pos); -// $self->_dragged(1); -// $self->Refresh; -// $self->Update; -// } elsif($e->Dragging) { -// if ((Slic3r::GUI::_3DScene::get_layers_editing_state($self) > 0) && ($object_idx_selected != -1)) { -// Slic3r::GUI::_3DScene::perform_layer_editing_action($self, $e->GetY, $e->ShiftDown, $e->RightIsDown) if (Slic3r::GUI::_3DScene::get_layers_editing_state($self) == 1); -// } elsif($e->LeftIsDown) { -//# if dragging over blank area with left button, rotate -// if (defined $self->_drag_start_pos) { -// my $orig = $self->_drag_start_pos; -// if (TURNTABLE_MODE) { -// # Turntable mode is enabled by default. -// Slic3r::GUI::_3DScene::set_camera_phi($self, Slic3r::GUI::_3DScene::get_camera_phi($self) + ($pos->x - $orig->x) * TRACKBALLSIZE); -// Slic3r::GUI::_3DScene::set_camera_theta($self, Slic3r::GUI::_3DScene::get_camera_theta($self) - ($pos->y - $orig->y) * TRACKBALLSIZE); -// } -// else { -// my $size = $self->GetClientSize; -// my @quat = trackball( -// $orig->x / ($size->width / 2) - 1, -// 1 - $orig->y / ($size->height / 2), # / -// $pos->x / ($size->width / 2) - 1, -// 1 - $pos->y / ($size->height / 2), # / -// ); -// $self->_quat(mulquats($self->_quat, \@quat)); -// } -// $self->on_viewport_changed->() if $self->on_viewport_changed; -// $self->Refresh; -// $self->Update; -// } -// $self->_drag_start_pos($pos); -// } elsif($e->MiddleIsDown || $e->RightIsDown) { -// # If dragging over blank area with right button, pan. -// if (defined $self->_drag_start_xy) { -// # get point in model space at Z = 0 -// my $cur_pos = $self->mouse_to_3d($e->GetX, $e->GetY, 0); -// my $orig = $self->mouse_to_3d($self->_drag_start_xy->x, $self->_drag_start_xy->y, 0); -// my $camera_target = Slic3r::GUI::_3DScene::get_camera_target($self); -// $camera_target->translate(@{$orig->vector_to($cur_pos)->negative}); -// Slic3r::GUI::_3DScene::set_camera_target($self, $camera_target); -// $self->on_viewport_changed->() if $self->on_viewport_changed; -// $self->Refresh; -// $self->Update; -// } -// $self->_drag_start_xy($pos); -// } -// } elsif($e->LeftUp || $e->MiddleUp || $e->RightUp) { -// if (Slic3r::GUI::_3DScene::get_layers_editing_state($self) > 0) { -// Slic3r::GUI::_3DScene::set_layers_editing_state($self, 0); -// Slic3r::GUI::_3DScene::stop_timer($self); -// $self->on_model_update->() -// if ($object_idx_selected != -1 && $self->on_model_update); -// } elsif($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) { -// # get all volumes belonging to the same group, if any -// my @volume_idxs; -// my $group_id = $self->volumes->[$self->_drag_volume_idx]->drag_group_id; -// if ($group_id == -1) { -// @volume_idxs = ($self->_drag_volume_idx); -// } -// else { -// @volume_idxs = grep $self->volumes->[$_]->drag_group_id == $group_id, -// 0..$#{$self->volumes}; -// } -// $self->on_move->(@volume_idxs); -// } -// $self->_drag_volume_idx(undef); -// $self->_drag_start_pos(undef); -// $self->_drag_start_xy(undef); -// $self->_dragged(undef); -// } + else if (evt.Dragging() && evt.LeftIsDown() && (get_layers_editing_state() == 0) && (m_drag.get_volume_idx() != -1)) + { + // Get new position at the same Z of the initial click point. + float z0 = 0.0f; + float z1 = 1.0f; + Pointf3 cur_pos = Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_drag.get_start_position_3D().z); + + // Clip the new position, so the object center remains close to the bed. + cur_pos.translate(m_drag.get_volume_center_offset()); + Point cur_pos2(scale_(cur_pos.x), scale_(cur_pos.y)); + if (!m_bed.contains(cur_pos2)) + { + Point ip = m_bed.point_projection(cur_pos2); + cur_pos.x = unscale(ip.x); + cur_pos.y = unscale(ip.y); + } + cur_pos.translate(m_drag.get_volume_center_offset().negative()); + + // Calculate the translation vector. + Vectorf3 vector = m_drag.get_start_position_3D().vector_to(cur_pos); + // Get the volume being dragged. + GLVolume* volume = m_volumes->volumes[m_drag.get_volume_idx()]; + // Get all volumes belonging to the same group, if any. + std::vector volumes; + if (volume->drag_group_id == -1) + volumes.push_back(volume); + else + { + for (GLVolume* v : m_volumes->volumes) + { + if ((v != nullptr) && (v->drag_group_id == volume->drag_group_id)) + volumes.push_back(v); + } + } + + // Apply new temporary volume origin and ignore Z. + for (GLVolume* v : volumes) + { + v->origin.translate(vector.x, vector.y, 0.0); + } + + m_drag.set_start_position_3D(cur_pos); + set_mouse_dragging(true); + m_canvas->Refresh(); + m_canvas->Update(); + } + else if (evt.Dragging()) + { + if ((get_layers_editing_state() > 0) && (selected_object_idx != -1)) + { + if (get_layers_editing_state() == 1) + perform_layer_editing_action(pos.y, evt.ShiftDown(), evt.RightIsDown()); + } + // } elsif($e->LeftIsDown) { + //# if dragging over blank area with left button, rotate + // if (defined $self->_drag_start_pos) { + // my $orig = $self->_drag_start_pos; + // if (TURNTABLE_MODE) { + // # Turntable mode is enabled by default. + // Slic3r::GUI::_3DScene::set_camera_phi($self, Slic3r::GUI::_3DScene::get_camera_phi($self) + ($pos->x - $orig->x) * TRACKBALLSIZE); + // Slic3r::GUI::_3DScene::set_camera_theta($self, Slic3r::GUI::_3DScene::get_camera_theta($self) - ($pos->y - $orig->y) * TRACKBALLSIZE); + // } + // else { + // my $size = $self->GetClientSize; + // my @quat = trackball( + // $orig->x / ($size->width / 2) - 1, + // 1 - $orig->y / ($size->height / 2), # / + // $pos->x / ($size->width / 2) - 1, + // 1 - $pos->y / ($size->height / 2), # / + // ); + // $self->_quat(mulquats($self->_quat, \@quat)); + // } + // $self->on_viewport_changed->() if $self->on_viewport_changed; + // $self->Refresh; + // $self->Update; + // } + // $self->_drag_start_pos($pos); + // } elsif($e->MiddleIsDown || $e->RightIsDown) { + // # If dragging over blank area with right button, pan. + // if (defined $self->_drag_start_xy) { + // # get point in model space at Z = 0 + // my $cur_pos = $self->mouse_to_3d($e->GetX, $e->GetY, 0); + // my $orig = $self->mouse_to_3d($self->_drag_start_xy->x, $self->_drag_start_xy->y, 0); + // my $camera_target = Slic3r::GUI::_3DScene::get_camera_target($self); + // $camera_target->translate(@{$orig->vector_to($cur_pos)->negative}); + // Slic3r::GUI::_3DScene::set_camera_target($self, $camera_target); + // $self->on_viewport_changed->() if $self->on_viewport_changed; + // $self->Refresh; + // $self->Update; + // } + // $self->_drag_start_xy($pos); + // } + } + else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) + { + if (get_layers_editing_state() > 0) + { + set_layers_editing_state(0); + stop_timer(); + + if (selected_object_idx != -1) + m_on_model_update_callback.call(); + } + else if ((m_drag.get_volume_idx() != -1) && m_mouse.is_dragging()) + { + // get all volumes belonging to the same group, if any + std::vector volume_idxs; + int vol_id = m_drag.get_volume_idx(); + int group_id = m_volumes->volumes[vol_id]->drag_group_id; + if (group_id == -1) + { + volume_idxs.push_back(vol_id); + } + else + { + for (int i = 0; i < m_volumes->volumes.size(); ++i) + { + if (m_volumes->volumes[i]->drag_group_id == group_id) + volume_idxs.push_back(i); + } + } + + m_on_move_callback.call(volume_idxs); + } + + m_drag.set_volume_idx(-1); + m_drag.set_start_position_3D(Pointf3(DBL_MAX, DBL_MAX, DBL_MAX)); + m_drag.set_start_position_2D(Point(INT_MAX, INT_MAX)); + set_mouse_dragging(false); + } else if (evt.Moving()) { set_mouse_position(Pointf((coordf_t)pos.x, (coordf_t)pos.y)); @@ -2300,6 +2353,8 @@ void GLCanvas3D::_deregister_callbacks() m_on_double_click_callback.deregister_callback(); m_on_right_click_callback.deregister_callback(); m_on_select_callback.deregister_callback(); + m_on_model_update_callback.deregister_callback(); + m_on_move_callback.deregister_callback(); } void GLCanvas3D::_mark_volumes_for_layer_height() const @@ -2655,12 +2710,12 @@ Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) GLint y = viewport[3] - (GLint)mouse_pos.y; GLfloat mouse_z; if (z == nullptr) - ::glReadPixels((GLint)mouse_pos.x, (GLint)mouse_pos.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z); + ::glReadPixels((GLint)mouse_pos.x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z); else mouse_z = *z; GLdouble out_x, out_y, out_z; - ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)mouse_pos.y, mouse_z, modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); + ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)y, mouse_z, modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); return Pointf3((coordf_t)out_x, (coordf_t)out_y, (coordf_t)out_z); } diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 7d490984f0..404d65e268 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -136,6 +136,8 @@ public: void set_shape(const Pointfs& shape); const BoundingBoxf3& get_bounding_box() const; + bool contains(const Point& point) const; + Point point_projection(const Point& point) const; void render() const; @@ -308,7 +310,7 @@ public: class Drag { - Point m_start_mouse_position; + Point m_start_position_2D; Pointf3 m_start_position_3D; Vectorf3 m_volume_center_offset; int m_volume_idx; @@ -316,8 +318,8 @@ public: public: Drag(); - const Point& get_start_mouse_position() const; - void set_start_mouse_position(const Point& mouse_position); + const Point& get_start_position_2D() const; + void set_start_position_2D(const Point& position); const Pointf3& get_start_position_3D() const; void set_start_position_3D(const Pointf3& position); @@ -360,6 +362,8 @@ private: PerlCallback m_on_double_click_callback; PerlCallback m_on_right_click_callback; PerlCallback m_on_select_callback; + PerlCallback m_on_model_update_callback; + PerlCallback m_on_move_callback; public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); @@ -492,6 +496,8 @@ public: void register_on_double_click_callback(void* callback); void register_on_right_click_callback(void* callback); void register_on_select_callback(void* callback); + void register_on_model_update_callback(void* callback); + void register_on_move_callback(void* callback); void on_size(wxSizeEvent& evt); void on_idle(wxIdleEvent& evt); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index c1eb7f37ac..e9a4d6ce8e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -476,19 +476,6 @@ void GLCanvas3DManager::set_mouse_dragging(wxGLCanvas* canvas, bool dragging) it->second->set_mouse_dragging(dragging); } -Pointf GLCanvas3DManager::get_mouse_position(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_mouse_position() : Pointf(); -} - -void GLCanvas3DManager::set_mouse_position(wxGLCanvas* canvas, const Pointf& position) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_mouse_position(position); -} - int GLCanvas3DManager::get_hover_volume_id(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -738,6 +725,20 @@ void GLCanvas3DManager::register_on_select_callback(wxGLCanvas* canvas, void* ca it->second->register_on_select_callback(callback); } +void GLCanvas3DManager::register_on_model_update_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_model_update_callback(callback); +} + +void GLCanvas3DManager::register_on_move_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_move_callback(callback); +} + GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index da6e00e7bd..bf6ac91eeb 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -113,9 +113,6 @@ public: bool is_mouse_dragging(wxGLCanvas* canvas) const; void set_mouse_dragging(wxGLCanvas* canvas, bool dragging); - Pointf get_mouse_position(wxGLCanvas* canvas) const; - void set_mouse_position(wxGLCanvas* canvas, const Pointf& position); - int get_hover_volume_id(wxGLCanvas* canvas) const; void set_hover_volume_id(wxGLCanvas* canvas, int id); @@ -168,6 +165,8 @@ public: void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); void register_on_select_callback(wxGLCanvas* canvas, void* callback); + void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); + void register_on_move_callback(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 8023efab2b..21b872cfdb 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -555,21 +555,6 @@ set_mouse_dragging(canvas, dragging) CODE: _3DScene::set_mouse_dragging((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), dragging); -Clone -get_mouse_position(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_mouse_position((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_mouse_position(canvas, position) - SV *canvas; - Pointf *position; - CODE: - _3DScene::set_mouse_position((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), position); - int get_hover_volume_id(canvas) SV *canvas; @@ -846,7 +831,21 @@ register_on_select_callback(canvas, callback) SV *callback; CODE: _3DScene::register_on_select_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); - + +void +register_on_model_update_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_model_update_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_move_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_move_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + unsigned int finalize_legend_texture() From 94d608c6c14e4a81e4c946d5fd2dbe9fec6cf5a1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 1 Jun 2018 09:00:30 +0200 Subject: [PATCH 048/117] 3DScene mouse event handler move to c++ completed --- lib/Slic3r/GUI/3DScene.pm | 6 --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 87 ++++++++++++++++++-------------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 ++ 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 0198739367..3b79873437 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -38,12 +38,6 @@ __PACKAGE__->mk_accessors( qw(_quat init on_viewport_changed on_select volumes - - _drag_volume_idx - _drag_start_pos - _drag_volume_center_offset - _drag_start_xy - _dragged ) ); #__PACKAGE__->mk_accessors( qw(_quat _dirty init # enable_picking diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 53f301ad5b..3f0c821cbf 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -15,6 +15,7 @@ #include #include +static const float TRACKBALLSIZE = 0.8f; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; static const float GROUND_Z = -0.02f; @@ -1084,6 +1085,16 @@ void GLCanvas3D::Drag::set_start_position_3D(const Pointf3& position) m_start_position_3D = position; } +bool GLCanvas3D::Drag::is_start_position_2D_defined() const +{ + return (m_start_position_2D != Point(INT_MAX, INT_MAX)); +} + +bool GLCanvas3D::Drag::is_start_position_3D_defined() const +{ + return (m_start_position_3D != Pointf3(DBL_MAX, DBL_MAX, DBL_MAX)); +} + const Vectorf3& GLCanvas3D::Drag::get_volume_center_offset() const { return m_volume_center_offset; @@ -2131,45 +2142,43 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (get_layers_editing_state() == 1) perform_layer_editing_action(pos.y, evt.ShiftDown(), evt.RightIsDown()); } - // } elsif($e->LeftIsDown) { - //# if dragging over blank area with left button, rotate - // if (defined $self->_drag_start_pos) { - // my $orig = $self->_drag_start_pos; - // if (TURNTABLE_MODE) { - // # Turntable mode is enabled by default. - // Slic3r::GUI::_3DScene::set_camera_phi($self, Slic3r::GUI::_3DScene::get_camera_phi($self) + ($pos->x - $orig->x) * TRACKBALLSIZE); - // Slic3r::GUI::_3DScene::set_camera_theta($self, Slic3r::GUI::_3DScene::get_camera_theta($self) - ($pos->y - $orig->y) * TRACKBALLSIZE); - // } - // else { - // my $size = $self->GetClientSize; - // my @quat = trackball( - // $orig->x / ($size->width / 2) - 1, - // 1 - $orig->y / ($size->height / 2), # / - // $pos->x / ($size->width / 2) - 1, - // 1 - $pos->y / ($size->height / 2), # / - // ); - // $self->_quat(mulquats($self->_quat, \@quat)); - // } - // $self->on_viewport_changed->() if $self->on_viewport_changed; - // $self->Refresh; - // $self->Update; - // } - // $self->_drag_start_pos($pos); - // } elsif($e->MiddleIsDown || $e->RightIsDown) { - // # If dragging over blank area with right button, pan. - // if (defined $self->_drag_start_xy) { - // # get point in model space at Z = 0 - // my $cur_pos = $self->mouse_to_3d($e->GetX, $e->GetY, 0); - // my $orig = $self->mouse_to_3d($self->_drag_start_xy->x, $self->_drag_start_xy->y, 0); - // my $camera_target = Slic3r::GUI::_3DScene::get_camera_target($self); - // $camera_target->translate(@{$orig->vector_to($cur_pos)->negative}); - // Slic3r::GUI::_3DScene::set_camera_target($self, $camera_target); - // $self->on_viewport_changed->() if $self->on_viewport_changed; - // $self->Refresh; - // $self->Update; - // } - // $self->_drag_start_xy($pos); - // } + else if (evt.LeftIsDown()) + { + // if dragging over blank area with left button, rotate + if (m_drag.is_start_position_3D_defined()) + { + Pointf3 orig = m_drag.get_start_position_3D(); + set_camera_phi(get_camera_phi() + ((float)pos.x - (float)orig.x) * TRACKBALLSIZE); + set_camera_theta(get_camera_theta() - ((float)pos.y - (float)orig.y) * TRACKBALLSIZE); + + m_on_viewport_changed_callback.call(); + + m_canvas->Refresh(); + m_canvas->Update(); + } + m_drag.set_start_position_3D(Pointf3((coordf_t)pos.x, (coordf_t)pos.y, 0.0)); + } + else if (evt.MiddleIsDown() || evt.RightIsDown()) + { + // If dragging over blank area with right button, pan. + if (m_drag.is_start_position_2D_defined()) + { + // get point in model space at Z = 0 + float z = 0.0f; + const Pointf3& cur_pos = _mouse_to_3d(pos, &z); + Pointf3 orig = _mouse_to_3d(m_drag.get_start_position_2D(), &z); + Pointf3 camera_target = get_camera_target(); + camera_target.translate(orig.vector_to(cur_pos).negative()); + set_camera_target(camera_target); + + m_on_viewport_changed_callback.call(); + + m_canvas->Refresh(); + m_canvas->Update(); + } + + m_drag.set_start_position_2D(pos); + } } else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) { diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 404d65e268..cb0e4a724b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -324,6 +324,9 @@ public: const Pointf3& get_start_position_3D() const; void set_start_position_3D(const Pointf3& position); + bool is_start_position_2D_defined() const; + bool is_start_position_3D_defined() const; + const Vectorf3& get_volume_center_offset() const; void set_volume_center_offset(const Vectorf3& offset); From 2bccb4312218ef3d3dda850b8354c82968502c0b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 1 Jun 2018 09:18:10 +0200 Subject: [PATCH 049/117] Attempt to fix 3DScene key event on Linux --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 3f0c821cbf..0165b09404 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1987,20 +1987,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) int selected_object_idx = (is_layers_editing_enabled() && (m_print != nullptr)) ? get_layers_editing_first_selected_object_id(m_print->objects.size()) : -1; set_layers_editing_last_object_id(selected_object_idx); -// set_mouse_dragging(false); - if (evt.Entering()) { -#if defined(__WXMSW__) || defined(__WXGTK__) +#if defined(__WXMSW__) || defined(__linux__) // On Windows and Linux needs focus in order to catch key events m_canvas->SetFocus(); m_drag.set_start_position_2D(Point(INT_MAX, INT_MAX)); #endif } else if (evt.LeftDClick()) - { m_on_double_click_callback.call(); - } else if (evt.LeftDown() || evt.RightDown()) { // If user pressed left or right button we first check whether this happened @@ -2197,9 +2193,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) int vol_id = m_drag.get_volume_idx(); int group_id = m_volumes->volumes[vol_id]->drag_group_id; if (group_id == -1) - { volume_idxs.push_back(vol_id); - } else { for (int i = 0; i < m_volumes->volumes.size(); ++i) From 364134515bf6f37b7ef15d5454237e8bef4f8dc6 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 1 Jun 2018 15:54:41 +0200 Subject: [PATCH 050/117] Refactoring and cleanup --- lib/Slic3r/GUI/3DScene.pm | 9 +- lib/Slic3r/GUI/Plater/3D.pm | 7 - xs/src/slic3r/GUI/3DScene.cpp | 272 ------ xs/src/slic3r/GUI/3DScene.hpp | 76 -- xs/src/slic3r/GUI/GLCanvas3D.cpp | 1038 +++++++---------------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 226 ++--- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 350 -------- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 76 -- xs/xsp/GUI_3DScene.xsp | 418 --------- 9 files changed, 371 insertions(+), 2101 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 3b79873437..68d7493058 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -34,7 +34,7 @@ use Slic3r::Geometry qw(PI); # volumes: reference to vector of Slic3r::GUI::3DScene::Volume. # _camera_type: 'perspective' or 'ortho' #============================================================================================================================== -__PACKAGE__->mk_accessors( qw(_quat init +__PACKAGE__->mk_accessors( qw(init on_viewport_changed on_select volumes @@ -83,7 +83,7 @@ __PACKAGE__->mk_accessors( qw(_quat init # # ) ); #============================================================================================================================== - + use constant TRACKBALLSIZE => 0.8; use constant TURNTABLE_MODE => 1; #============================================================================================================================== @@ -160,9 +160,7 @@ sub new { # # $self->{can_multisample} = $can_multisample; # $self->background(1); -#============================================================================================================================== - $self->_quat((0, 0, 0, 1)); -#============================================================================================================================== +# $self->_quat((0, 0, 0, 1)); # $self->_stheta(45); # $self->_sphi(45); # $self->_zoom(1); @@ -268,7 +266,6 @@ sub new { sub Destroy { my ($self) = @_; #============================================================================================================================== - Slic3r::GUI::_3DScene::stop_timer($self); # $self->{layer_height_edit_timer}->Stop; #============================================================================================================================== $self->DestroyGL; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index ff4e2ad293..f630908357 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -56,13 +56,6 @@ sub new { # $self->on_move(sub { #============================================================================================================================== my @volume_idxs = @_; - - -#============================================================================================================================== - print "cucu"; -#============================================================================================================================== - - my %done = (); # prevent moving instances twice my $object_moved; my $wipe_tower_moved; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index d2ae0a4af3..45d1d5b760 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1757,31 +1757,11 @@ bool _3DScene::init(wxGLCanvas* canvas, bool useVBOs) return s_canvas_mgr.init(canvas, useVBOs); } -bool _3DScene::is_dirty(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_dirty(canvas); -} - -void _3DScene::set_dirty(wxGLCanvas* canvas, bool dirty) -{ - s_canvas_mgr.set_dirty(canvas, dirty); -} - bool _3DScene::is_shown_on_screen(wxGLCanvas* canvas) { return s_canvas_mgr.is_shown_on_screen(canvas); } -void _3DScene::resize(wxGLCanvas* canvas, unsigned int w, unsigned int h) -{ - s_canvas_mgr.resize(canvas, w, h); -} - -GLVolumeCollection* _3DScene::get_volumes(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_volumes(canvas); -} - void _3DScene::set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes) { s_canvas_mgr.set_volumes(canvas, volumes); @@ -1822,37 +1802,11 @@ void _3DScene::set_auto_bed_shape(wxGLCanvas* canvas) return s_canvas_mgr.set_auto_bed_shape(canvas); } -BoundingBoxf3 _3DScene::get_bed_bounding_box(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_bed_bounding_box(canvas); -} - BoundingBoxf3 _3DScene::get_volumes_bounding_box(wxGLCanvas* canvas) { return s_canvas_mgr.get_volumes_bounding_box(canvas); } -BoundingBoxf3 _3DScene::get_max_bounding_box(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_max_bounding_box(canvas); -} - -Pointf3 _3DScene::get_axes_origin(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_axes_origin(canvas); -} - -void _3DScene::set_axes_origin(wxGLCanvas* canvas, const Pointf3* origin) -{ - if (origin != nullptr) - s_canvas_mgr.set_axes_origin(canvas, *origin); -} - -float _3DScene::get_axes_length(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_axes_length(canvas); -} - void _3DScene::set_axes_length(wxGLCanvas* canvas, float length) { s_canvas_mgr.set_axes_length(canvas, length); @@ -1863,97 +1817,16 @@ void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& return s_canvas_mgr.set_cutting_plane(canvas, z, polygons); } -unsigned int _3DScene::get_camera_type(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_camera_type(canvas); -} - -void _3DScene::set_camera_type(wxGLCanvas* canvas, unsigned int type) -{ - s_canvas_mgr.set_camera_type(canvas, type); -} - -std::string _3DScene::get_camera_type_as_string(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_camera_type_as_string(canvas); -} - -float _3DScene::get_camera_zoom(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_camera_zoom(canvas); -} - -void _3DScene::set_camera_zoom(wxGLCanvas* canvas, float zoom) -{ - s_canvas_mgr.set_camera_zoom(canvas, zoom); -} - -float _3DScene::get_camera_phi(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_camera_phi(canvas); -} - -void _3DScene::set_camera_phi(wxGLCanvas* canvas, float phi) -{ - s_canvas_mgr.set_camera_phi(canvas, phi); -} - -float _3DScene::get_camera_theta(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_camera_theta(canvas); -} - -void _3DScene::set_camera_theta(wxGLCanvas* canvas, float theta) -{ - s_canvas_mgr.set_camera_theta(canvas, theta); -} - -float _3DScene::get_camera_distance(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_camera_distance(canvas); -} - -void _3DScene::set_camera_distance(wxGLCanvas* canvas, float distance) -{ - s_canvas_mgr.set_camera_distance(canvas, distance); -} - -Pointf3 _3DScene::get_camera_target(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_camera_target(canvas); -} - -void _3DScene::set_camera_target(wxGLCanvas* canvas, const Pointf3* target) -{ - if (target != nullptr) - s_canvas_mgr.set_camera_target(canvas, *target); -} - bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) { return s_canvas_mgr.is_layers_editing_enabled(canvas); } -bool _3DScene::is_picking_enabled(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_picking_enabled(canvas); -} - -bool _3DScene::is_moving_enabled(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_moving_enabled(canvas); -} - bool _3DScene::is_layers_editing_allowed(wxGLCanvas* canvas) { return s_canvas_mgr.is_layers_editing_allowed(canvas); } -bool _3DScene::is_multisample_allowed(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_multisample_allowed(canvas); -} - void _3DScene::enable_layers_editing(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_layers_editing(canvas, enable); @@ -1989,116 +1862,6 @@ void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow) s_canvas_mgr.allow_multisample(canvas, allow); } -bool _3DScene::is_mouse_dragging(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_mouse_dragging(canvas); -} - -void _3DScene::set_mouse_dragging(wxGLCanvas* canvas, bool dragging) -{ - s_canvas_mgr.set_mouse_dragging(canvas, dragging); -} - -int _3DScene::get_hover_volume_id(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_hover_volume_id(canvas); -} - -void _3DScene::set_hover_volume_id(wxGLCanvas* canvas, int id) -{ - s_canvas_mgr.set_hover_volume_id(canvas, id); -} - -unsigned int _3DScene::get_layers_editing_z_texture_id(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_layers_editing_z_texture_id(canvas); -} - -unsigned int _3DScene::get_layers_editing_state(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_layers_editing_state(canvas); -} - -void _3DScene::set_layers_editing_state(wxGLCanvas* canvas, unsigned int state) -{ - s_canvas_mgr.set_layers_editing_state(canvas, state); -} - -float _3DScene::get_layers_editing_band_width(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_layers_editing_band_width(canvas); -} - -void _3DScene::set_layers_editing_band_width(wxGLCanvas* canvas, float band_width) -{ - s_canvas_mgr.set_layers_editing_band_width(canvas, band_width); -} - -float _3DScene::get_layers_editing_strength(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_layers_editing_strength(canvas); -} - -void _3DScene::set_layers_editing_strength(wxGLCanvas* canvas, float strength) -{ - s_canvas_mgr.set_layers_editing_strength(canvas, strength); -} - -int _3DScene::get_layers_editing_last_object_id(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_layers_editing_last_object_id(canvas); -} - -void _3DScene::set_layers_editing_last_object_id(wxGLCanvas* canvas, int id) -{ - s_canvas_mgr.set_layers_editing_last_object_id(canvas, id); -} - -float _3DScene::get_layers_editing_last_z(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_layers_editing_last_z(canvas); -} - -void _3DScene::set_layers_editing_last_z(wxGLCanvas* canvas, float z) -{ - s_canvas_mgr.set_layers_editing_last_z(canvas, z); -} - -unsigned int _3DScene::get_layers_editing_last_action(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_layers_editing_last_action(canvas); -} - -void _3DScene::set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int action) -{ - s_canvas_mgr.set_layers_editing_last_action(canvas, action); -} - -const GLShader* _3DScene::get_layers_editing_shader(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_layers_editing_shader(canvas); -} - -float _3DScene::get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_layers_editing_cursor_z_relative(canvas); -} - -int _3DScene::get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count) -{ - return s_canvas_mgr.get_layers_editing_first_selected_object_id(canvas, objects_count); -} - -bool _3DScene::bar_rect_contains(wxGLCanvas* canvas, float x, float y) -{ - return s_canvas_mgr.bar_rect_contains(canvas, x, y); -} - -bool _3DScene::reset_rect_contains(wxGLCanvas* canvas, float x, float y) -{ - return s_canvas_mgr.reset_rect_contains(canvas, x, y); -} - void _3DScene::zoom_to_bed(wxGLCanvas* canvas) { s_canvas_mgr.zoom_to_bed(canvas); @@ -2124,46 +1887,11 @@ void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas) s_canvas_mgr.update_volumes_colors_by_extruder(canvas); } -bool _3DScene::start_using_shader(wxGLCanvas* canvas) -{ - return s_canvas_mgr.start_using_shader(canvas); -} - -void _3DScene::stop_using_shader(wxGLCanvas* canvas) -{ - s_canvas_mgr.stop_using_shader(canvas); -} - void _3DScene::render(wxGLCanvas* canvas) { s_canvas_mgr.render(canvas); } -void _3DScene::render_volumes(wxGLCanvas* canvas, bool fake_colors) -{ - s_canvas_mgr.render_volumes(canvas, fake_colors); -} - -void _3DScene::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) -{ - s_canvas_mgr.render_texture(canvas, tex_id, left, right, bottom, top); -} - -void _3DScene::start_timer(wxGLCanvas* canvas) -{ - s_canvas_mgr.start_timer(canvas); -} - -void _3DScene::stop_timer(wxGLCanvas* canvas) -{ - s_canvas_mgr.stop_timer(canvas); -} - -void _3DScene::perform_layer_editing_action(wxGLCanvas* canvas, int y, bool shift_down, bool right_down) -{ - s_canvas_mgr.perform_layer_editing_action(canvas, y, shift_down, right_down); -} - void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index a411884351..615655a001 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -546,14 +546,8 @@ public: static bool init(wxGLCanvas* canvas, bool useVBOs); - static bool is_dirty(wxGLCanvas* canvas); - static void set_dirty(wxGLCanvas* canvas, bool dirty); - static bool is_shown_on_screen(wxGLCanvas* canvas); - static void resize(wxGLCanvas* canvas, unsigned int w, unsigned int h); - - static GLVolumeCollection* get_volumes(wxGLCanvas* canvas); static void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); static void reset_volumes(wxGLCanvas* canvas); static void deselect_volumes(wxGLCanvas* canvas); @@ -565,42 +559,14 @@ public: static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); static void set_auto_bed_shape(wxGLCanvas* canvas); - static BoundingBoxf3 get_bed_bounding_box(wxGLCanvas* canvas); static BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); - static BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); - static Pointf3 get_axes_origin(wxGLCanvas* canvas); - static void set_axes_origin(wxGLCanvas* canvas, const Pointf3* origin); - - static float get_axes_length(wxGLCanvas* canvas); static void set_axes_length(wxGLCanvas* canvas, float length); static void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); - static unsigned int get_camera_type(wxGLCanvas* canvas); - static void set_camera_type(wxGLCanvas* canvas, unsigned int type); - static std::string get_camera_type_as_string(wxGLCanvas* canvas); - - static float get_camera_zoom(wxGLCanvas* canvas); - static void set_camera_zoom(wxGLCanvas* canvas, float zoom); - - static float get_camera_phi(wxGLCanvas* canvas); - static void set_camera_phi(wxGLCanvas* canvas, float phi); - - static float get_camera_theta(wxGLCanvas* canvas); - static void set_camera_theta(wxGLCanvas* canvas, float theta); - - static float get_camera_distance(wxGLCanvas* canvas); - static void set_camera_distance(wxGLCanvas* canvas, float distance); - - static Pointf3 get_camera_target(wxGLCanvas* canvas); - static void set_camera_target(wxGLCanvas* canvas, const Pointf3* target); - static bool is_layers_editing_enabled(wxGLCanvas* canvas); - static bool is_picking_enabled(wxGLCanvas* canvas); - static bool is_moving_enabled(wxGLCanvas* canvas); static bool is_layers_editing_allowed(wxGLCanvas* canvas); - static bool is_multisample_allowed(wxGLCanvas* canvas); static void enable_layers_editing(wxGLCanvas* canvas, bool enable); static void enable_warning_texture(wxGLCanvas* canvas, bool enable); @@ -610,39 +576,6 @@ public: static void enable_shader(wxGLCanvas* canvas, bool enable); static void allow_multisample(wxGLCanvas* canvas, bool allow); - static bool is_mouse_dragging(wxGLCanvas* canvas); - static void set_mouse_dragging(wxGLCanvas* canvas, bool dragging); - - static int get_hover_volume_id(wxGLCanvas* canvas); - static void set_hover_volume_id(wxGLCanvas* canvas, int id); - - static unsigned int get_layers_editing_z_texture_id(wxGLCanvas* canvas); - - static unsigned int get_layers_editing_state(wxGLCanvas* canvas); - static void set_layers_editing_state(wxGLCanvas* canvas, unsigned int state); - - static float get_layers_editing_band_width(wxGLCanvas* canvas); - static void set_layers_editing_band_width(wxGLCanvas* canvas, float band_width); - - static float get_layers_editing_strength(wxGLCanvas* canvas); - static void set_layers_editing_strength(wxGLCanvas* canvas, float strength); - - static int get_layers_editing_last_object_id(wxGLCanvas* canvas); - static void set_layers_editing_last_object_id(wxGLCanvas* canvas, int id); - - static float get_layers_editing_last_z(wxGLCanvas* canvas); - static void set_layers_editing_last_z(wxGLCanvas* canvas, float z); - - static unsigned int get_layers_editing_last_action(wxGLCanvas* canvas); - static void set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int action); - - static const GLShader* get_layers_editing_shader(wxGLCanvas* canvas); - - static float get_layers_editing_cursor_z_relative(wxGLCanvas* canvas); - static int get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count); - static bool bar_rect_contains(wxGLCanvas* canvas, float x, float y); - static bool reset_rect_contains(wxGLCanvas* canvas, float x, float y); - static void zoom_to_bed(wxGLCanvas* canvas); static void zoom_to_volumes(wxGLCanvas* canvas); static void select_view(wxGLCanvas* canvas, const std::string& direction); @@ -650,16 +583,7 @@ public: static void update_volumes_colors_by_extruder(wxGLCanvas* canvas); - static bool start_using_shader(wxGLCanvas* canvas); - static void stop_using_shader(wxGLCanvas* canvas); - static void render(wxGLCanvas* canvas); - static void render_volumes(wxGLCanvas* canvas, bool fake_colors); - static void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top); - - static void start_timer(wxGLCanvas* canvas); - static void stop_timer(wxGLCanvas* canvas); - static void perform_layer_editing_action(wxGLCanvas* canvas, int y, bool shift_down, bool right_down); static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); static void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 0165b09404..50913ae3a5 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -182,59 +182,29 @@ void Rect::set_bottom(float bottom) } GLCanvas3D::Camera::Camera() - : m_type(CT_Ortho) - , m_zoom(1.0f) - , m_phi(45.0f) + : type(Ortho) + , zoom(1.0f) + , phi(45.0f) + , distance(0.0f) + , target(0.0, 0.0, 0.0) , m_theta(45.0f) - , m_distance(0.0f) - , m_target(0.0, 0.0, 0.0) { } -GLCanvas3D::Camera::EType GLCanvas3D::Camera::get_type() const -{ - return m_type; -} - -void GLCanvas3D::Camera::set_type(GLCanvas3D::Camera::EType type) -{ - m_type = type; -} - std::string GLCanvas3D::Camera::get_type_as_string() const { - switch (m_type) + switch (type) { default: - case CT_Unknown: + case Unknown: return "unknown"; - case CT_Perspective: + case Perspective: return "perspective"; - case CT_Ortho: + case Ortho: return "ortho"; }; } -float GLCanvas3D::Camera::get_zoom() const -{ - return m_zoom; -} - -void GLCanvas3D::Camera::set_zoom(float zoom) -{ - m_zoom = zoom; -} - -float GLCanvas3D::Camera::get_phi() const -{ - return m_phi; -} - -void GLCanvas3D::Camera::set_phi(float phi) -{ - m_phi = phi; -} - float GLCanvas3D::Camera::get_theta() const { return m_theta; @@ -242,34 +212,7 @@ float GLCanvas3D::Camera::get_theta() const void GLCanvas3D::Camera::set_theta(float theta) { - m_theta = theta; - - // clamp angle - if (m_theta > GIMBALL_LOCK_THETA_MAX) - m_theta = GIMBALL_LOCK_THETA_MAX; - - if (m_theta < 0.0f) - m_theta = 0.0f; -} - -float GLCanvas3D::Camera::get_distance() const -{ - return m_distance; -} - -void GLCanvas3D::Camera::set_distance(float distance) -{ - m_distance = distance; -} - -const Pointf3& GLCanvas3D::Camera::get_target() const -{ - return m_target; -} - -void GLCanvas3D::Camera::set_target(const Pointf3& target) -{ - m_target = target; + m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta); } const Pointfs& GLCanvas3D::Bed::get_shape() const @@ -396,30 +339,10 @@ void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& } GLCanvas3D::Axes::Axes() - : m_length(0.0f) + : length(0.0f) { } -const Pointf3& GLCanvas3D::Axes::get_origin() const -{ - return m_origin; -} - -void GLCanvas3D::Axes::set_origin(const Pointf3& origin) -{ - m_origin = origin; -} - -float GLCanvas3D::Axes::get_length() const -{ - return m_length; -} - -void GLCanvas3D::Axes::set_length(float length) -{ - m_length = length; -} - void GLCanvas3D::Axes::render() const { ::glDisable(GL_LIGHTING); @@ -429,20 +352,20 @@ void GLCanvas3D::Axes::render() const ::glBegin(GL_LINES); // draw line for x axis ::glColor3f(1.0f, 0.0f, 0.0f); - ::glVertex3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z); - ::glVertex3f((float)m_origin.x + m_length, (float)m_origin.y, (float)m_origin.z); - // draw line for y axis + ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z); + ::glVertex3f((GLfloat)origin.x + length, (GLfloat)origin.y, (GLfloat)origin.z); + // draw line for y axis ::glColor3f(0.0f, 1.0f, 0.0f); - ::glVertex3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z); - ::glVertex3f((float)m_origin.x, (float)m_origin.y + m_length, (float)m_origin.z); + ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z); + ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y + length, (GLfloat)origin.z); ::glEnd(); // draw line for Z axis // (re-enable depth test so that axis is correctly shown when objects are behind it) ::glEnable(GL_DEPTH_TEST); ::glBegin(GL_LINES); ::glColor3f(0.0f, 0.0f, 1.0f); - ::glVertex3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z); - ::glVertex3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z + m_length); + ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z); + ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z + length); ::glEnd(); } @@ -600,15 +523,15 @@ GLCanvas3D::LayersEditing::GLTextureData::GLTextureData(unsigned int id, int wid } GLCanvas3D::LayersEditing::LayersEditing() - : m_state(Unknown) - , m_use_legacy_opengl(false) + : m_use_legacy_opengl(false) , m_enabled(false) , m_z_texture_id(0) - , m_band_width(2.0f) - , m_strength(0.005f) - , m_last_object_id(-1) - , m_last_z(0.0f) - , m_last_action(0) + , state(Unknown) + , band_width(2.0f) + , strength(0.005f) + , last_object_id(-1) + , last_z(0.0f) + , last_action(0) { } @@ -650,16 +573,6 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, return true; } -GLCanvas3D::LayersEditing::EState GLCanvas3D::LayersEditing::get_state() const -{ - return m_state; -} - -void GLCanvas3D::LayersEditing::set_state(GLCanvas3D::LayersEditing::EState state) -{ - m_state = state; -} - bool GLCanvas3D::LayersEditing::is_allowed() const { return m_use_legacy_opengl && m_shader.is_initialized(); @@ -685,56 +598,6 @@ unsigned int GLCanvas3D::LayersEditing::get_z_texture_id() const return m_z_texture_id; } -float GLCanvas3D::LayersEditing::get_band_width() const -{ - return m_band_width; -} - -void GLCanvas3D::LayersEditing::set_band_width(float band_width) -{ - m_band_width = band_width; -} - -float GLCanvas3D::LayersEditing::get_strength() const -{ - return m_strength; -} - -void GLCanvas3D::LayersEditing::set_strength(float strength) -{ - m_strength = strength; -} - -int GLCanvas3D::LayersEditing::get_last_object_id() const -{ - return m_last_object_id; -} - -void GLCanvas3D::LayersEditing::set_last_object_id(int id) -{ - m_last_object_id = id; -} - -float GLCanvas3D::LayersEditing::get_last_z() const -{ - return m_last_z; -} - -void GLCanvas3D::LayersEditing::set_last_z(float z) -{ - m_last_z = z; -} - -unsigned int GLCanvas3D::LayersEditing::get_last_action() const -{ - return m_last_action; -} - -void GLCanvas3D::LayersEditing::set_last_action(unsigned int action) -{ - m_last_action = action; -} - void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const { if (!m_enabled) @@ -761,9 +624,10 @@ void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObje glEnable(GL_DEPTH_TEST); } -const GLShader* GLCanvas3D::LayersEditing::get_shader() const +int GLCanvas3D::LayersEditing::get_shader_program_id() const { - return m_shader.get_shader(); + const GLShader* shader = m_shader.get_shader(); + return (shader != nullptr) ? shader->shader_program_id : -1; } float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) @@ -902,7 +766,7 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas m_shader.set_uniform("z_to_texture_row", (float)volume.layer_height_texture_z_to_row_id()); m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height()); m_shader.set_uniform("z_cursor", max_z * get_cursor_z_relative(canvas)); - m_shader.set_uniform("z_cursor_band_width", get_band_width()); + m_shader.set_uniform("z_cursor_band_width", band_width); GLsizei w = (GLsizei)volume.layer_height_texture_width(); GLsizei h = (GLsizei)volume.layer_height_texture_height(); @@ -1034,85 +898,40 @@ GLCanvas3D::LayersEditing::GLTextureData GLCanvas3D::LayersEditing::_load_textur return GLTextureData((unsigned int)tex_id, width, height); } +const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); +const Pointf3 GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX); + +GLCanvas3D::Mouse::Drag::Drag() + : start_position_2D(Invalid_2D_Point) + , start_position_3D(Invalid_3D_Point) + , volume_idx(-1) +{ +} + GLCanvas3D::Mouse::Mouse() - : m_dragging(false) + : dragging(false) + , position(DBL_MAX, DBL_MAX) { } -bool GLCanvas3D::Mouse::is_dragging() const +void GLCanvas3D::Mouse::set_start_position_2D_as_invalid() { - return m_dragging; + drag.start_position_2D = Drag::Invalid_2D_Point; } -void GLCanvas3D::Mouse::set_dragging(bool dragging) +void GLCanvas3D::Mouse::set_start_position_3D_as_invalid() { - m_dragging = dragging; + drag.start_position_3D = Drag::Invalid_3D_Point; } -const Pointf& GLCanvas3D::Mouse::get_position() const +bool GLCanvas3D::Mouse::is_start_position_2D_defined() const { - return m_position; + return (drag.start_position_2D != Drag::Invalid_2D_Point); } -void GLCanvas3D::Mouse::set_position(const Pointf& position) +bool GLCanvas3D::Mouse::is_start_position_3D_defined() const { - m_position = position; -} - -GLCanvas3D::Drag::Drag() - : m_start_position_2D(INT_MAX, INT_MAX) - , m_volume_idx(-1) -{ -} - -const Point& GLCanvas3D::Drag::get_start_position_2D() const -{ - return m_start_position_2D; -} - -void GLCanvas3D::Drag::set_start_position_2D(const Point& position) -{ - m_start_position_2D = position; -} - -const Pointf3& GLCanvas3D::Drag::get_start_position_3D() const -{ - return m_start_position_3D; -} - -void GLCanvas3D::Drag::set_start_position_3D(const Pointf3& position) -{ - m_start_position_3D = position; -} - -bool GLCanvas3D::Drag::is_start_position_2D_defined() const -{ - return (m_start_position_2D != Point(INT_MAX, INT_MAX)); -} - -bool GLCanvas3D::Drag::is_start_position_3D_defined() const -{ - return (m_start_position_3D != Pointf3(DBL_MAX, DBL_MAX, DBL_MAX)); -} - -const Vectorf3& GLCanvas3D::Drag::get_volume_center_offset() const -{ - return m_volume_center_offset; -} - -void GLCanvas3D::Drag::set_volume_center_offset(const Vectorf3& offset) -{ - m_volume_center_offset = offset; -} - -int GLCanvas3D::Drag::get_volume_idx() const -{ - return m_volume_idx; -} - -void GLCanvas3D::Drag::set_volume_idx(int idx) -{ - m_volume_idx = idx; + return (drag.start_position_3D != Drag::Invalid_3D_Point); } GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) @@ -1150,8 +969,8 @@ GLCanvas3D::~GLCanvas3D() bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) { ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f); - ::glClearDepth(1.0f); + ::glDepthFunc(GL_LESS); ::glEnable(GL_DEPTH_TEST); @@ -1193,7 +1012,7 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) ::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); ::glEnable(GL_COLOR_MATERIAL); - if (is_multisample_allowed()) + if (m_multisample_allowed) ::glEnable(GL_MULTISAMPLE); if (useVBOs && !m_shader.init("gouraud.vs", "gouraud.fs")) @@ -1218,93 +1037,11 @@ bool GLCanvas3D::set_current() return false; } -bool GLCanvas3D::is_dirty() const -{ - return m_dirty; -} - -void GLCanvas3D::set_dirty(bool dirty) -{ - m_dirty = dirty; -} - bool GLCanvas3D::is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; } -void GLCanvas3D::resize(unsigned int w, unsigned int h) -{ - if (m_context == nullptr) - return; - - set_current(); - ::glViewport(0, 0, w, h); - - ::glMatrixMode(GL_PROJECTION); - ::glLoadIdentity(); - - BoundingBoxf3 bbox = max_bounding_box(); - - switch (get_camera_type()) - { - case Camera::CT_Ortho: - { - float w2 = w; - float h2 = h; - float two_zoom = 2.0f * get_camera_zoom(); - if (two_zoom != 0.0f) - { - float inv_two_zoom = 1.0f / two_zoom; - w2 *= inv_two_zoom; - h2 *= inv_two_zoom; - } - - // FIXME: calculate a tighter value for depth will improve z-fighting - float depth = 5.0f * (float)bbox.max_size(); - ::glOrtho(-w2, w2, -h2, h2, -depth, depth); - - break; - } - case Camera::CT_Perspective: - { - float bbox_r = (float)bbox.radius(); - float fov = PI * 45.0f / 180.0f; - float fov_tan = tan(0.5f * fov); - float cam_distance = 0.5f * bbox_r / fov_tan; - set_camera_distance(cam_distance); - - float nr = cam_distance - bbox_r * 1.1f; - float fr = cam_distance + bbox_r * 1.1f; - if (nr < 1.0f) - nr = 1.0f; - - if (fr < nr + 1.0f) - fr = nr + 1.0f; - - float h2 = fov_tan * nr; - float w2 = h2 * w / h; - ::glFrustum(-w2, w2, -h2, h2, nr, fr); - - break; - } - default: - { - throw std::runtime_error("Invalid camera type."); - break; - } - } - - ::glMatrixMode(GL_MODELVIEW); - - set_dirty(false); -} - -GLVolumeCollection* GLCanvas3D::get_volumes() -{ - return m_volumes; -} - void GLCanvas3D::set_volumes(GLVolumeCollection* volumes) { m_volumes = volumes; @@ -1316,7 +1053,7 @@ void GLCanvas3D::reset_volumes() { m_volumes->release_geometry(); m_volumes->clear(); - set_dirty(true); + m_dirty = true; } } @@ -1357,8 +1094,8 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) m_bed.set_shape(shape); // Set the origin and size for painting of the coordinate system axes. - set_axes_origin(Pointf3(0.0, 0.0, (coordf_t)GROUND_Z)); - set_axes_length(0.3f * (float)bed_bounding_box().max_size()); + m_axes.origin = Pointf3(0.0, 0.0, (coordf_t)GROUND_Z); + set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size()); } void GLCanvas3D::set_auto_bed_shape() @@ -1378,27 +1115,12 @@ void GLCanvas3D::set_auto_bed_shape() set_bed_shape(bed_shape); // Set the origin for painting of the coordinate system axes. - set_axes_origin(Pointf3(center.x, center.y, (coordf_t)GROUND_Z)); -} - -const Pointf3& GLCanvas3D::get_axes_origin() const -{ - return m_axes.get_origin(); -} - -void GLCanvas3D::set_axes_origin(const Pointf3& origin) -{ - m_axes.set_origin(origin); -} - -float GLCanvas3D::get_axes_length() const -{ - return m_axes.get_length(); + m_axes.origin = Pointf3(center.x, center.y, (coordf_t)GROUND_Z); } void GLCanvas3D::set_axes_length(float length) { - return m_axes.set_length(length); + m_axes.length = length; } void GLCanvas3D::set_cutting_plane(float z, const ExPolygons& polygons) @@ -1406,74 +1128,9 @@ void GLCanvas3D::set_cutting_plane(float z, const ExPolygons& polygons) m_cutting_plane.set(z, polygons); } -GLCanvas3D::Camera::EType GLCanvas3D::get_camera_type() const -{ - return m_camera.get_type(); -} - -void GLCanvas3D::set_camera_type(GLCanvas3D::Camera::EType type) -{ - m_camera.set_type(type); -} - -std::string GLCanvas3D::get_camera_type_as_string() const -{ - return m_camera.get_type_as_string(); -} - float GLCanvas3D::get_camera_zoom() const { - return m_camera.get_zoom(); -} - -void GLCanvas3D::set_camera_zoom(float zoom) -{ - m_camera.set_zoom(zoom); -} - -float GLCanvas3D::get_camera_phi() const -{ - return m_camera.get_phi(); -} - -void GLCanvas3D::set_camera_phi(float phi) -{ - m_camera.set_phi(phi); -} - -float GLCanvas3D::get_camera_theta() const -{ - return m_camera.get_theta(); -} - -void GLCanvas3D::set_camera_theta(float theta) -{ - m_camera.set_theta(theta); -} - -float GLCanvas3D::get_camera_distance() const -{ - return m_camera.get_distance(); -} - -void GLCanvas3D::set_camera_distance(float distance) -{ - m_camera.set_distance(distance); -} - -const Pointf3& GLCanvas3D::get_camera_target() const -{ - return m_camera.get_target(); -} - -void GLCanvas3D::set_camera_target(const Pointf3& target) -{ - m_camera.set_target(target); -} - -BoundingBoxf3 GLCanvas3D::bed_bounding_box() const -{ - return m_bed.get_bounding_box(); + return m_camera.zoom; } BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const @@ -1490,38 +1147,16 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const return bb; } -BoundingBoxf3 GLCanvas3D::max_bounding_box() const -{ - BoundingBoxf3 bb = bed_bounding_box(); - bb.merge(volumes_bounding_box()); - return bb; -} - bool GLCanvas3D::is_layers_editing_enabled() const { return m_layers_editing.is_enabled(); } -bool GLCanvas3D::is_picking_enabled() const -{ - return m_picking_enabled; -} - -bool GLCanvas3D::is_moving_enabled() const -{ - return m_moving_enabled; -} - bool GLCanvas3D::is_layers_editing_allowed() const { return m_layers_editing.is_allowed(); } -bool GLCanvas3D::is_multisample_allowed() const -{ - return m_multisample_allowed; -} - void GLCanvas3D::enable_layers_editing(bool enable) { m_layers_editing.set_enabled(enable); @@ -1556,39 +1191,9 @@ void GLCanvas3D::allow_multisample(bool allow) m_multisample_allowed = allow; } -bool GLCanvas3D::is_mouse_dragging() const -{ - return m_mouse.is_dragging(); -} - -void GLCanvas3D::set_mouse_dragging(bool dragging) -{ - m_mouse.set_dragging(dragging); -} - -const Pointf& GLCanvas3D::get_mouse_position() const -{ - return m_mouse.get_position(); -} - -void GLCanvas3D::set_mouse_position(const Pointf& position) -{ - m_mouse.set_position(position); -} - -int GLCanvas3D::get_hover_volume_id() const -{ - return m_hover_volume_id; -} - -void GLCanvas3D::set_hover_volume_id(int id) -{ - m_hover_volume_id = id; -} - void GLCanvas3D::zoom_to_bed() { - _zoom_to_bounding_box(bed_bounding_box()); + _zoom_to_bounding_box(m_bed.get_bounding_box()); } void GLCanvas3D::zoom_to_volumes() @@ -1619,7 +1224,7 @@ void GLCanvas3D::select_view(const std::string& direction) if ((dir_vec != nullptr) && !empty(volumes_bounding_box())) { - m_camera.set_phi(dir_vec[0]); + m_camera.phi = dir_vec[0]; m_camera.set_theta(dir_vec[1]); m_on_viewport_changed_callback.call(); @@ -1631,11 +1236,11 @@ void GLCanvas3D::select_view(const std::string& direction) void GLCanvas3D::set_viewport_from_scene(const GLCanvas3D& other) { - set_camera_phi(other.get_camera_phi()); - set_camera_theta(other.get_camera_theta()); - set_camera_target(other.get_camera_target()); - set_camera_zoom(other.get_camera_zoom()); - set_dirty(true); + m_camera.phi = other.m_camera.phi; + m_camera.set_theta(other.m_camera.get_theta()); + m_camera.target = other.m_camera.target; + m_camera.zoom = other.m_camera.zoom; + m_dirty = true; } void GLCanvas3D::update_volumes_colors_by_extruder() @@ -1646,16 +1251,6 @@ void GLCanvas3D::update_volumes_colors_by_extruder() m_volumes->update_colors_by_extruder(m_config); } -bool GLCanvas3D::start_using_shader() const -{ - return m_shader.start_using(); -} - -void GLCanvas3D::stop_using_shader() const -{ - m_shader.stop_using(); -} - void GLCanvas3D::render(bool useVBOs) const { if (m_canvas == nullptr) @@ -1675,146 +1270,6 @@ void GLCanvas3D::render(bool useVBOs) const m_canvas->SwapBuffers(); } -unsigned int GLCanvas3D::get_layers_editing_z_texture_id() const -{ - return m_layers_editing.get_z_texture_id(); -} - -unsigned int GLCanvas3D::get_layers_editing_state() const -{ - return (unsigned int)m_layers_editing.get_state(); -} - -void GLCanvas3D::set_layers_editing_state(unsigned int state) -{ - if (state < (unsigned int)LayersEditing::Num_States) - m_layers_editing.set_state((LayersEditing::EState)state); -} - -float GLCanvas3D::get_layers_editing_band_width() const -{ - return m_layers_editing.get_band_width(); -} - -void GLCanvas3D::set_layers_editing_band_width(float band_width) -{ - m_layers_editing.set_band_width(band_width); -} - -float GLCanvas3D::get_layers_editing_strength() const -{ - return m_layers_editing.get_strength(); -} - -void GLCanvas3D::set_layers_editing_strength(float strength) -{ - m_layers_editing.set_strength(strength); -} - -int GLCanvas3D::get_layers_editing_last_object_id() const -{ - return m_layers_editing.get_last_object_id(); -} - -void GLCanvas3D::set_layers_editing_last_object_id(int id) -{ - m_layers_editing.set_last_object_id(id); -} - -float GLCanvas3D::get_layers_editing_last_z() const -{ - return m_layers_editing.get_last_z(); -} - -void GLCanvas3D::set_layers_editing_last_z(float z) -{ - m_layers_editing.set_last_z(z); -} - -unsigned int GLCanvas3D::get_layers_editing_last_action() const -{ - return m_layers_editing.get_last_action(); -} - -void GLCanvas3D::set_layers_editing_last_action(unsigned int action) -{ - m_layers_editing.set_last_action(action); -} - -const GLShader* GLCanvas3D::get_layers_editing_shader() const -{ - return m_layers_editing.get_shader(); -} - -float GLCanvas3D::get_layers_editing_cursor_z_relative() const -{ - return m_layers_editing.get_cursor_z_relative(*this); -} - -int GLCanvas3D::get_layers_editing_first_selected_object_id(unsigned int objects_count) const -{ - return (m_volumes != nullptr) ? m_layers_editing.get_first_selected_object_id(*m_volumes, objects_count) : -1; -} - -bool GLCanvas3D::bar_rect_contains(float x, float y) const -{ - return m_layers_editing.bar_rect_contains(*this, x, y); -} - -bool GLCanvas3D::reset_rect_contains(float x, float y) const -{ - return m_layers_editing.reset_rect_contains(*this, x, y); -} - -void GLCanvas3D::render_volumes(bool fake_colors) const -{ - static const float INV_255 = 1.0f / 255.0f; - - if (m_volumes == nullptr) - return; - - if (fake_colors) - ::glDisable(GL_LIGHTING); - else - ::glEnable(GL_LIGHTING); - - // do not cull backfaces to show broken geometry, if any - ::glDisable(GL_CULL_FACE); - - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - - unsigned int volume_id = 0; - for (GLVolume* vol : m_volumes->volumes) - { - if (fake_colors) - { - // Object picking mode. Render the object with a color encoding the object index. - unsigned int r = (volume_id & 0x000000FF) >> 0; - unsigned int g = (volume_id & 0x0000FF00) >> 8; - unsigned int b = (volume_id & 0x00FF0000) >> 16; - ::glColor4f((float)r * INV_255, (float)g * INV_255, (float)b * INV_255, 1.0f); - } - else - { - vol->set_render_color(); - ::glColor4f(vol->render_color[0], vol->render_color[1], vol->render_color[2], vol->render_color[3]); - } - - vol->render(); - ++volume_id; - } - - ::glDisableClientState(GL_NORMAL_ARRAY); - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisable(GL_BLEND); - - ::glEnable(GL_CULL_FACE); -} - void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const { ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); @@ -1878,12 +1333,12 @@ void GLCanvas3D::register_on_move_callback(void* callback) void GLCanvas3D::on_size(wxSizeEvent& evt) { - set_dirty(true); + m_dirty = true; } void GLCanvas3D::on_idle(wxIdleEvent& evt) { - if (!is_dirty()) + if (!m_dirty) return; _refresh_if_shown_on_screen(); @@ -1933,14 +1388,14 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) // Performs layers editing updates, if enabled if (is_layers_editing_enabled() && (m_print != nullptr)) { - int object_idx_selected = get_layers_editing_first_selected_object_id((unsigned int)m_print->objects.size()); + int object_idx_selected = _get_layers_editing_first_selected_object_id((unsigned int)m_print->objects.size()); if (object_idx_selected != -1) { // A volume is selected. Test, whether hovering over a layer thickness bar. - if (bar_rect_contains((float)evt.GetX(), (float)evt.GetY())) + if (_bar_rect_contains((float)evt.GetX(), (float)evt.GetY())) { // Adjust the width of the selection. - set_layers_editing_band_width(std::max(std::min(get_layers_editing_band_width() * (1.0f + 0.1f * (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta()), 10.0f), 1.5f)); + m_layers_editing.band_width = std::max(std::min(m_layers_editing.band_width * (1.0f + 0.1f * (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta()), 10.0f), 1.5f); if (m_canvas != nullptr) m_canvas->Refresh(); @@ -1955,7 +1410,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) zoom = get_camera_zoom() / (1.0f - zoom); // Don't allow to zoom too far outside the scene. - float zoom_min = _get_zoom_to_bounding_box_factor(max_bounding_box()); + float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box()); if (zoom_min > 0.0f) { zoom_min *= 0.4f; @@ -1963,7 +1418,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) zoom = zoom_min; } - set_camera_zoom(zoom); + m_camera.zoom = zoom; m_on_viewport_changed_callback.call(); _refresh_if_shown_on_screen(); @@ -1971,7 +1426,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) void GLCanvas3D::on_timer(wxTimerEvent& evt) { - if (get_layers_editing_state() != 1) + if (m_layers_editing.state != LayersEditing::Editing) return; _perform_layer_editing_action(); @@ -1979,20 +1434,22 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt) void GLCanvas3D::on_mouse(wxMouseEvent& evt) { - if ((m_canvas == nullptr) || (m_volumes == nullptr)) + if (m_volumes == nullptr) return; Point pos(evt.GetX(), evt.GetY()); - int selected_object_idx = (is_layers_editing_enabled() && (m_print != nullptr)) ? get_layers_editing_first_selected_object_id(m_print->objects.size()) : -1; - set_layers_editing_last_object_id(selected_object_idx); + int selected_object_idx = (is_layers_editing_enabled() && (m_print != nullptr)) ? _get_layers_editing_first_selected_object_id(m_print->objects.size()) : -1; + m_layers_editing.last_object_id = selected_object_idx; if (evt.Entering()) { #if defined(__WXMSW__) || defined(__linux__) // On Windows and Linux needs focus in order to catch key events - m_canvas->SetFocus(); - m_drag.set_start_position_2D(Point(INT_MAX, INT_MAX)); + if (m_canvas != nullptr) + m_canvas->SetFocus(); + + m_mouse.set_start_position_2D_as_invalid(); #endif } else if (evt.LeftDClick()) @@ -2001,25 +1458,25 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { // If user pressed left or right button we first check whether this happened // on a volume or not. - int volume_idx = get_hover_volume_id(); - set_layers_editing_state(0); - if ((selected_object_idx != -1) && bar_rect_contains(pos.x, pos.y)) + int volume_idx = m_hover_volume_id; + m_layers_editing.state = LayersEditing::Unknown; + if ((selected_object_idx != -1) && _bar_rect_contains(pos.x, pos.y)) { // A volume is selected and the mouse is inside the layer thickness bar. // Start editing the layer height. - set_layers_editing_state(1); - perform_layer_editing_action(pos.y, evt.ShiftDown(), evt.RightDown()); + m_layers_editing.state = LayersEditing::Editing; + _perform_layer_editing_action(&evt); } - else if ((selected_object_idx != -1) && reset_rect_contains(pos.x, pos.y)) + else if ((selected_object_idx != -1) && _reset_rect_contains(pos.x, pos.y)) { if (evt.LeftDown()) { // A volume is selected and the mouse is inside the reset button. m_print->get_object(selected_object_idx)->reset_layer_height_profile(); // Index 2 means no editing, just wait for mouse up event. - set_layers_editing_state(2); - m_canvas->Refresh(); - m_canvas->Update(); + m_layers_editing.state = LayersEditing::Completed; + + m_dirty = true; } } else @@ -2028,7 +1485,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Don't deselect a volume if layer editing is enabled. We want the object to stay selected // during the scene manipulation. - if (is_picking_enabled() && ((volume_idx != -1) || !is_layers_editing_enabled())) + if (m_picking_enabled && ((volume_idx != -1) || !is_layers_editing_enabled())) { deselect_volumes(); select_volume(volume_idx); @@ -2046,8 +1503,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } - m_canvas->Refresh(); - m_canvas->Update(); + m_dirty = true; } // propagate event through callback @@ -2059,7 +1515,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (volume_idx != -1) { - if (evt.LeftDown() && is_moving_enabled()) + if (evt.LeftDown() && m_moving_enabled) { // Only accept the initial position, if it is inside the volume bounding box. BoundingBoxf3 volume_bbox = m_volumes->volumes[volume_idx]->transformed_bounding_box(); @@ -2067,33 +1523,33 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (volume_bbox.contains(pos3d)) { // The dragging operation is initiated. - m_drag.set_volume_idx(volume_idx); - m_drag.set_start_position_3D(pos3d); + m_mouse.drag.volume_idx = volume_idx; + m_mouse.drag.start_position_3D = pos3d; // Remember the shift to to the object center.The object center will later be used // to limit the object placement close to the bed. - m_drag.set_volume_center_offset(pos3d.vector_to(volume_bbox.center())); + m_mouse.drag.volume_center_offset = pos3d.vector_to(volume_bbox.center()); } } else if (evt.RightDown()) { // if right clicking on volume, propagate event through callback if (m_volumes->volumes[volume_idx]->hover) - { m_on_right_click_callback.call(pos.x, pos.y); - } } } } } - else if (evt.Dragging() && evt.LeftIsDown() && (get_layers_editing_state() == 0) && (m_drag.get_volume_idx() != -1)) + else if (evt.Dragging() && evt.LeftIsDown() && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1)) { + m_mouse.dragging = true; + // Get new position at the same Z of the initial click point. float z0 = 0.0f; float z1 = 1.0f; - Pointf3 cur_pos = Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_drag.get_start_position_3D().z); + Pointf3 cur_pos = Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D.z); // Clip the new position, so the object center remains close to the bed. - cur_pos.translate(m_drag.get_volume_center_offset()); + cur_pos.translate(m_mouse.drag.volume_center_offset); Point cur_pos2(scale_(cur_pos.x), scale_(cur_pos.y)); if (!m_bed.contains(cur_pos2)) { @@ -2101,12 +1557,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) cur_pos.x = unscale(ip.x); cur_pos.y = unscale(ip.y); } - cur_pos.translate(m_drag.get_volume_center_offset().negative()); + cur_pos.translate(m_mouse.drag.volume_center_offset.negative()); // Calculate the translation vector. - Vectorf3 vector = m_drag.get_start_position_3D().vector_to(cur_pos); + Vectorf3 vector = m_mouse.drag.start_position_3D.vector_to(cur_pos); // Get the volume being dragged. - GLVolume* volume = m_volumes->volumes[m_drag.get_volume_idx()]; + GLVolume* volume = m_volumes->volumes[m_mouse.drag.volume_idx]; // Get all volumes belonging to the same group, if any. std::vector volumes; if (volume->drag_group_id == -1) @@ -2126,71 +1582,70 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) v->origin.translate(vector.x, vector.y, 0.0); } - m_drag.set_start_position_3D(cur_pos); - set_mouse_dragging(true); - m_canvas->Refresh(); - m_canvas->Update(); + m_mouse.drag.start_position_3D = cur_pos; + + m_dirty = true; } else if (evt.Dragging()) { - if ((get_layers_editing_state() > 0) && (selected_object_idx != -1)) + m_mouse.dragging = true; + + if ((m_layers_editing.state != LayersEditing::Unknown) && (selected_object_idx != -1)) { - if (get_layers_editing_state() == 1) - perform_layer_editing_action(pos.y, evt.ShiftDown(), evt.RightIsDown()); + if (m_layers_editing.state == LayersEditing::Editing) + _perform_layer_editing_action(&evt); } else if (evt.LeftIsDown()) { // if dragging over blank area with left button, rotate - if (m_drag.is_start_position_3D_defined()) + if (m_mouse.is_start_position_3D_defined()) { - Pointf3 orig = m_drag.get_start_position_3D(); - set_camera_phi(get_camera_phi() + ((float)pos.x - (float)orig.x) * TRACKBALLSIZE); - set_camera_theta(get_camera_theta() - ((float)pos.y - (float)orig.y) * TRACKBALLSIZE); + const Pointf3& orig = m_mouse.drag.start_position_3D; + m_camera.phi += (((float)pos.x - (float)orig.x) * TRACKBALLSIZE); + m_camera.set_theta(m_camera.get_theta() - ((float)pos.y - (float)orig.y) * TRACKBALLSIZE); m_on_viewport_changed_callback.call(); - m_canvas->Refresh(); - m_canvas->Update(); + m_dirty = true; } - m_drag.set_start_position_3D(Pointf3((coordf_t)pos.x, (coordf_t)pos.y, 0.0)); + m_mouse.drag.start_position_3D = Pointf3((coordf_t)pos.x, (coordf_t)pos.y, 0.0); } else if (evt.MiddleIsDown() || evt.RightIsDown()) { // If dragging over blank area with right button, pan. - if (m_drag.is_start_position_2D_defined()) + if (m_mouse.is_start_position_2D_defined()) { // get point in model space at Z = 0 float z = 0.0f; const Pointf3& cur_pos = _mouse_to_3d(pos, &z); - Pointf3 orig = _mouse_to_3d(m_drag.get_start_position_2D(), &z); - Pointf3 camera_target = get_camera_target(); + Pointf3 orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z); + Pointf3 camera_target = m_camera.target; camera_target.translate(orig.vector_to(cur_pos).negative()); - set_camera_target(camera_target); + m_camera.target = camera_target; m_on_viewport_changed_callback.call(); - m_canvas->Refresh(); - m_canvas->Update(); + m_dirty = true; } - m_drag.set_start_position_2D(pos); + m_mouse.drag.start_position_2D = pos; } } else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) { - if (get_layers_editing_state() > 0) + if (m_layers_editing.state != LayersEditing::Unknown) { - set_layers_editing_state(0); - stop_timer(); + m_layers_editing.state = LayersEditing::Unknown; + _stop_timer(); if (selected_object_idx != -1) m_on_model_update_callback.call(); } - else if ((m_drag.get_volume_idx() != -1) && m_mouse.is_dragging()) + else if ((m_mouse.drag.volume_idx != -1) && m_mouse.dragging) { // get all volumes belonging to the same group, if any std::vector volume_idxs; - int vol_id = m_drag.get_volume_idx(); + int vol_id = m_mouse.drag.volume_idx; int group_id = m_volumes->volumes[vol_id]->drag_group_id; if (group_id == -1) volume_idxs.push_back(vol_id); @@ -2206,20 +1661,17 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_on_move_callback.call(volume_idxs); } - m_drag.set_volume_idx(-1); - m_drag.set_start_position_3D(Pointf3(DBL_MAX, DBL_MAX, DBL_MAX)); - m_drag.set_start_position_2D(Point(INT_MAX, INT_MAX)); - set_mouse_dragging(false); + m_mouse.drag.volume_idx = -1; + m_mouse.set_start_position_3D_as_invalid(); + m_mouse.set_start_position_2D_as_invalid(); + m_mouse.dragging = false; } else if (evt.Moving()) { - set_mouse_position(Pointf((coordf_t)pos.x, (coordf_t)pos.y)); + m_mouse.position = Pointf((coordf_t)pos.x, (coordf_t)pos.y); // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over. - if (is_picking_enabled()) - { - m_canvas->Refresh(); - m_canvas->Update(); - } + if (m_picking_enabled) + m_dirty = true; } else evt.Skip(); @@ -2245,26 +1697,78 @@ Point GLCanvas3D::get_local_mouse_position() const return Point(mouse_pos.x, mouse_pos.y); } -void GLCanvas3D::start_timer() +void GLCanvas3D::_resize(unsigned int w, unsigned int h) { - if (m_timer != nullptr) - m_timer->Start(100, wxTIMER_CONTINUOUS); + if (m_context == nullptr) + return; + + set_current(); + ::glViewport(0, 0, w, h); + + ::glMatrixMode(GL_PROJECTION); + ::glLoadIdentity(); + + const BoundingBoxf3& bbox = _max_bounding_box(); + + switch (m_camera.type) + { + case Camera::Ortho: + { + float w2 = w; + float h2 = h; + float two_zoom = 2.0f * get_camera_zoom(); + if (two_zoom != 0.0f) + { + float inv_two_zoom = 1.0f / two_zoom; + w2 *= inv_two_zoom; + h2 *= inv_two_zoom; + } + + // FIXME: calculate a tighter value for depth will improve z-fighting + float depth = 5.0f * (float)bbox.max_size(); + ::glOrtho(-w2, w2, -h2, h2, -depth, depth); + + break; + } + case Camera::Perspective: + { + float bbox_r = (float)bbox.radius(); + float fov = PI * 45.0f / 180.0f; + float fov_tan = tan(0.5f * fov); + float cam_distance = 0.5f * bbox_r / fov_tan; + m_camera.distance = cam_distance; + + float nr = cam_distance - bbox_r * 1.1f; + float fr = cam_distance + bbox_r * 1.1f; + if (nr < 1.0f) + nr = 1.0f; + + if (fr < nr + 1.0f) + fr = nr + 1.0f; + + float h2 = fov_tan * nr; + float w2 = h2 * w / h; + ::glFrustum(-w2, w2, -h2, h2, nr, fr); + + break; + } + default: + { + throw std::runtime_error("Invalid camera type."); + break; + } + } + + ::glMatrixMode(GL_MODELVIEW); + + m_dirty = false; } -void GLCanvas3D::stop_timer() +BoundingBoxf3 GLCanvas3D::_max_bounding_box() const { - if (m_timer != nullptr) - m_timer->Stop(); -} - -void GLCanvas3D::perform_layer_editing_action(int y, bool shift_down, bool right_down) -{ - wxMouseEvent evt; - evt.m_y = (wxCoord)y; - evt.SetShiftDown(shift_down); - evt.SetRightDown(right_down); - - _perform_layer_editing_action(&evt); + BoundingBoxf3 bb = m_bed.get_bounding_box(); + bb.merge(volumes_bounding_box()); + return bb; } void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) @@ -2273,9 +1777,9 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) float zoom = _get_zoom_to_bounding_box_factor(bbox); if (zoom > 0.0f) { - set_camera_zoom(zoom); + m_camera.zoom = zoom; // center view around bounding box center - set_camera_target(bbox.center()); + m_camera.target = bbox.center(); m_on_viewport_changed_callback.call(); @@ -2368,13 +1872,13 @@ void GLCanvas3D::_mark_volumes_for_layer_height() const for (GLVolume* vol : m_volumes->volumes) { int object_id = int(vol->select_group_id / 1000000); - const GLShader* shader = get_layers_editing_shader(); + int shader_id = m_layers_editing.get_shader_program_id(); - if (is_layers_editing_enabled() && (shader != nullptr) && vol->selected && + if (is_layers_editing_enabled() && (shader_id != -1) && vol->selected && vol->has_layer_height_texture() && (object_id < (int)m_print->objects.size())) { - vol->set_layer_height_texture_data(get_layers_editing_z_texture_id(), shader->shader_program_id, - m_print->get_object(object_id), get_layers_editing_cursor_z_relative(), get_layers_editing_band_width()); + vol->set_layer_height_texture_data(m_layers_editing.get_z_texture_id(), shader_id, + m_print->get_object(object_id), _get_layers_editing_cursor_z_relative(), m_layers_editing.band_width); } else vol->reset_layer_height_texture_data(); @@ -2386,7 +1890,7 @@ void GLCanvas3D::_refresh_if_shown_on_screen() if (is_shown_on_screen()) { const Size& cnv_size = get_canvas_size(); - resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); + _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); if (m_canvas != nullptr) m_canvas->Refresh(); } @@ -2397,22 +1901,24 @@ void GLCanvas3D::_camera_tranform() const ::glMatrixMode(GL_MODELVIEW); ::glLoadIdentity(); - ::glRotatef(-get_camera_theta(), 1.0f, 0.0f, 0.0f); // pitch - ::glRotatef(get_camera_phi(), 0.0f, 0.0f, 1.0f); // yaw + ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch + ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw - Pointf3 neg_target = get_camera_target().negative(); + Pointf3 neg_target = m_camera.target.negative(); ::glTranslatef((GLfloat)neg_target.x, (GLfloat)neg_target.y, (GLfloat)neg_target.z); } void GLCanvas3D::_picking_pass() const { - if (is_picking_enabled() && !is_mouse_dragging() && (m_volumes != nullptr)) + const Pointf& pos = m_mouse.position; + + if (m_picking_enabled && !m_mouse.dragging && (pos != Pointf(DBL_MAX, DBL_MAX)) && (m_volumes != nullptr)) { // 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. - if (is_multisample_allowed()) + if (m_multisample_allowed) ::glDisable(GL_MULTISAMPLE); ::glDisable(GL_LIGHTING); @@ -2422,16 +1928,15 @@ void GLCanvas3D::_picking_pass() const ::glPushAttrib(GL_ENABLE_BIT); - render_volumes(true); + _render_volumes(true); ::glPopAttrib(); - if (is_multisample_allowed()) + if (m_multisample_allowed) ::glEnable(GL_MULTISAMPLE); const Size& cnv_size = get_canvas_size(); - const Pointf& pos = get_mouse_position(); GLubyte color[4]; ::glReadPixels(pos.x, cnv_size.get_height() - pos.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); int volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256; @@ -2511,16 +2016,16 @@ void GLCanvas3D::_render_objects(bool useVBOs) const ::glEnable(GL_LIGHTING); if (!m_shader_enabled) - render_volumes(false); + _render_volumes(false); else if (useVBOs) { - if (is_picking_enabled()) + if (m_picking_enabled) { _mark_volumes_for_layer_height(); if (m_config != nullptr) { - const BoundingBoxf3& bed_bb = bed_bounding_box(); + const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(); m_volumes->set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height")); m_volumes->check_outside_state(m_config); } @@ -2528,22 +2033,22 @@ void GLCanvas3D::_render_objects(bool useVBOs) const ::glDisable(GL_CULL_FACE); } - start_using_shader(); + m_shader.start_using(); m_volumes->render_VBOs(); - stop_using_shader(); + m_shader.stop_using(); - if (is_picking_enabled()) + if (m_picking_enabled) ::glEnable(GL_CULL_FACE); } else { // do not cull backfaces to show broken geometry, if any - if (is_picking_enabled()) + if (m_picking_enabled) ::glDisable(GL_CULL_FACE); m_volumes->render_legacy(); - if (is_picking_enabled()) + if (m_picking_enabled) ::glEnable(GL_CULL_FACE); } } @@ -2650,9 +2155,68 @@ void GLCanvas3D::_render_layer_editing_overlay() const m_layers_editing.render(*this, *print_object, *volume); } +void GLCanvas3D::_render_volumes(bool fake_colors) const +{ + static const float INV_255 = 1.0f / 255.0f; + + if (m_volumes == nullptr) + return; + + if (fake_colors) + ::glDisable(GL_LIGHTING); + else + ::glEnable(GL_LIGHTING); + + // do not cull backfaces to show broken geometry, if any + ::glDisable(GL_CULL_FACE); + + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + + unsigned int volume_id = 0; + for (GLVolume* vol : m_volumes->volumes) + { + if (fake_colors) + { + // Object picking mode. Render the object with a color encoding the object index. + unsigned int r = (volume_id & 0x000000FF) >> 0; + unsigned int g = (volume_id & 0x0000FF00) >> 8; + unsigned int b = (volume_id & 0x00FF0000) >> 16; + ::glColor4f((float)r * INV_255, (float)g * INV_255, (float)b * INV_255, 1.0f); + } + else + { + vol->set_render_color(); + ::glColor4f(vol->render_color[0], vol->render_color[1], vol->render_color[2], vol->render_color[3]); + } + + vol->render(); + ++volume_id; + } + + ::glDisableClientState(GL_NORMAL_ARRAY); + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisable(GL_BLEND); + + ::glEnable(GL_CULL_FACE); +} + +float GLCanvas3D::_get_layers_editing_cursor_z_relative() const +{ + return m_layers_editing.get_cursor_z_relative(*this); +} + +int GLCanvas3D::_get_layers_editing_first_selected_object_id(unsigned int objects_count) const +{ + return (m_volumes != nullptr) ? m_layers_editing.get_first_selected_object_id(*m_volumes, objects_count) : -1; +} + void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) { - int object_idx_selected = get_layers_editing_last_object_id(); + int object_idx_selected = m_layers_editing.last_object_id; if (object_idx_selected == -1) return; @@ -2668,14 +2232,14 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) { const Rect& rect = LayersEditing::get_bar_rect_screen(*this); float b = rect.get_bottom(); - set_layers_editing_last_z(unscale(selected_obj->size.z) * (b - evt->GetY() - 1.0f) / (b - rect.get_top())); - set_layers_editing_last_action(evt->ShiftDown() ? (evt->RightIsDown() ? 3 : 2) : (evt->RightIsDown() ? 0 : 1)); + m_layers_editing.last_z = unscale(selected_obj->size.z) * (b - evt->GetY() - 1.0f) / (b - rect.get_top()); + m_layers_editing.last_action = evt->ShiftDown() ? (evt->RightIsDown() ? 3 : 2) : (evt->RightIsDown() ? 0 : 1); } // Mark the volume as modified, so Print will pick its layer height profile ? Where to mark it ? // Start a timer to refresh the print ? schedule_background_process() ? // The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself. - selected_obj->adjust_layer_height_profile(get_layers_editing_last_z(), get_layers_editing_strength(), get_layers_editing_band_width(), get_layers_editing_last_action()); + selected_obj->adjust_layer_height_profile(m_layers_editing.last_z, m_layers_editing.strength, m_layers_editing.band_width, m_layers_editing.last_action); // searches the id of the first volume of the selected object int volume_idx = 0; @@ -2695,7 +2259,17 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) _refresh_if_shown_on_screen(); // Automatic action on mouse down with the same coordinate. - start_timer(); + _start_timer(); +} + +bool GLCanvas3D::_bar_rect_contains(float x, float y) const +{ + return m_layers_editing.bar_rect_contains(*this, x, y); +} + +bool GLCanvas3D::_reset_rect_contains(float x, float y) const +{ + return m_layers_editing.reset_rect_contains(*this, x, y); } Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) @@ -2722,5 +2296,17 @@ Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) return Pointf3((coordf_t)out_x, (coordf_t)out_y, (coordf_t)out_z); } +void GLCanvas3D::_start_timer() +{ + if (m_timer != nullptr) + m_timer->Start(100, wxTIMER_CONTINUOUS); +} + +void GLCanvas3D::_stop_timer() +{ + if (m_timer != nullptr) + m_timer->Stop(); +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index cb0e4a724b..1caf69c0c5 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -81,46 +81,32 @@ public: class GLCanvas3D { public: - class Camera + struct Camera { - public: enum EType : unsigned char { - CT_Unknown, - CT_Perspective, - CT_Ortho, - CT_Count + Unknown, + Perspective, + Ortho, + Num_types }; + EType type; + float zoom; + float phi; + float distance; + Pointf3 target; + private: - EType m_type; - float m_zoom; - float m_phi; float m_theta; - float m_distance; - Pointf3 m_target; public: Camera(); - Camera::EType get_type() const; - void set_type(Camera::EType type); std::string get_type_as_string() const; - float get_zoom() const; - void set_zoom(float zoom); - - float get_phi() const; - void set_phi(float phi); - float get_theta() const; void set_theta(float theta); - - float get_distance() const; - void set_distance(float distance); - - const Pointf3& get_target() const; - void set_target(const Pointf3& target); }; class Bed @@ -147,20 +133,13 @@ public: void _calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); }; - class Axes + struct Axes { - Pointf3 m_origin; - float m_length; + Pointf3 origin; + float length; - public: Axes(); - const Pointf3& get_origin() const; - void set_origin(const Pointf3& origin); - - float get_length() const; - void set_length(float length); - void render() const; }; @@ -226,28 +205,26 @@ public: GLTextureData(unsigned int id, int width, int height); }; - EState m_state; bool m_use_legacy_opengl; bool m_enabled; Shader m_shader; unsigned int m_z_texture_id; mutable GLTextureData m_tooltip_texture; mutable GLTextureData m_reset_texture; - float m_band_width; - float m_strength; - int m_last_object_id; - float m_last_z; - unsigned int m_last_action; public: + EState state; + float band_width; + float strength; + int last_object_id; + float last_z; + unsigned int last_action; + LayersEditing(); ~LayersEditing(); bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); - EState get_state() const; - void set_state(EState state); - bool is_allowed() const; void set_use_legacy_opengl(bool use_legacy_opengl); @@ -256,24 +233,9 @@ public: unsigned int get_z_texture_id() const; - float get_band_width() const; - void set_band_width(float band_width); - - float get_strength() const; - void set_strength(float strength); - - int get_last_object_id() const; - void set_last_object_id(int id); - - float get_last_z() const; - void set_last_z(float z); - - unsigned int get_last_action() const; - void set_last_action(unsigned int action); - void render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const; - const GLShader* get_shader() const; + int get_shader_program_id() const; static float get_cursor_z_relative(const GLCanvas3D& canvas); static int get_first_selected_object_id(const GLVolumeCollection& volumes, unsigned int objects_count); @@ -293,45 +255,33 @@ public: static GLTextureData _load_texture_from_file(const std::string& filename); }; - class Mouse + struct Mouse { - bool m_dragging; - Pointf m_position; + struct Drag + { + static const Point Invalid_2D_Point; + static const Pointf3 Invalid_3D_Point; + + Point start_position_2D; + Pointf3 start_position_3D; + Vectorf3 volume_center_offset; + int volume_idx; + + public: + Drag(); + }; + + bool dragging; + Pointf position; + Drag drag; - public: Mouse(); - bool is_dragging() const; - void set_dragging(bool dragging); - - const Pointf& get_position() const; - void set_position(const Pointf& position); - }; - - class Drag - { - Point m_start_position_2D; - Pointf3 m_start_position_3D; - Vectorf3 m_volume_center_offset; - int m_volume_idx; - - public: - Drag(); - - const Point& get_start_position_2D() const; - void set_start_position_2D(const Point& position); - - const Pointf3& get_start_position_3D() const; - void set_start_position_3D(const Pointf3& position); + void set_start_position_2D_as_invalid(); + void set_start_position_3D_as_invalid(); bool is_start_position_2D_defined() const; bool is_start_position_3D_defined() const; - - const Vectorf3& get_volume_center_offset() const; - void set_volume_center_offset(const Vectorf3& offset); - - int get_volume_idx() const; - void set_volume_idx(int idx); }; private: @@ -345,7 +295,6 @@ private: LayersEditing m_layers_editing; Shader m_shader; Mouse m_mouse; - Drag m_drag; GLVolumeCollection* m_volumes; DynamicPrintConfig* m_config; @@ -376,14 +325,8 @@ public: bool set_current(); - bool is_dirty() const; - void set_dirty(bool dirty); - bool is_shown_on_screen() const; - void resize(unsigned int w, unsigned int h); - - GLVolumeCollection* get_volumes(); void set_volumes(GLVolumeCollection* volumes); void reset_volumes(); void deselect_volumes(); @@ -400,42 +343,16 @@ public: // Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane to support the scene objects. void set_auto_bed_shape(); - const Pointf3& get_axes_origin() const; - void set_axes_origin(const Pointf3& origin); - - float get_axes_length() const; void set_axes_length(float length); void set_cutting_plane(float z, const ExPolygons& polygons); - - Camera::EType get_camera_type() const; - void set_camera_type(Camera::EType type); - std::string get_camera_type_as_string() const; - + float get_camera_zoom() const; - void set_camera_zoom(float zoom); - float get_camera_phi() const; - void set_camera_phi(float phi); - - float get_camera_theta() const; - void set_camera_theta(float theta); - - float get_camera_distance() const; - void set_camera_distance(float distance); - - const Pointf3& get_camera_target() const; - void set_camera_target(const Pointf3& target); - - BoundingBoxf3 bed_bounding_box() const; BoundingBoxf3 volumes_bounding_box() const; - BoundingBoxf3 max_bounding_box() const; bool is_layers_editing_enabled() const; - bool is_picking_enabled() const; - bool is_moving_enabled() const; bool is_layers_editing_allowed() const; - bool is_multisample_allowed() const; void enable_layers_editing(bool enable); void enable_warning_texture(bool enable); @@ -445,42 +362,6 @@ public: void enable_shader(bool enable); void allow_multisample(bool allow); - bool is_mouse_dragging() const; - void set_mouse_dragging(bool dragging); - - const Pointf& get_mouse_position() const; - void set_mouse_position(const Pointf& position); - - int get_hover_volume_id() const; - void set_hover_volume_id(int id); - - unsigned int get_layers_editing_z_texture_id() const; - - unsigned int get_layers_editing_state() const; - void set_layers_editing_state(unsigned int state); - - float get_layers_editing_band_width() const; - void set_layers_editing_band_width(float band_width); - - float get_layers_editing_strength() const; - void set_layers_editing_strength(float strength); - - int get_layers_editing_last_object_id() const; - void set_layers_editing_last_object_id(int id); - - float get_layers_editing_last_z() const; - void set_layers_editing_last_z(float z); - - unsigned int get_layers_editing_last_action() const; - void set_layers_editing_last_action(unsigned int action); - - const GLShader* get_layers_editing_shader() const; - - float get_layers_editing_cursor_z_relative() const; - int get_layers_editing_first_selected_object_id(unsigned int objects_count) const; - bool bar_rect_contains(float x, float y) const; - bool reset_rect_contains(float x, float y) const; - void zoom_to_bed(); void zoom_to_volumes(); void select_view(const std::string& direction); @@ -488,11 +369,7 @@ public: void update_volumes_colors_by_extruder(); - bool start_using_shader() const; - void stop_using_shader() const; - void render(bool useVBOs) const; - void render_volumes(bool fake_colors) const; void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(void* callback); @@ -512,11 +389,11 @@ public: Size get_canvas_size() const; Point get_local_mouse_position() const; - void start_timer(); - void stop_timer(); - void perform_layer_editing_action(int y, bool shift_down, bool right_down); - private: + void _resize(unsigned int w, unsigned int h); + + BoundingBoxf3 _max_bounding_box() const; + void _zoom_to_bounding_box(const BoundingBoxf3& bbox); float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; @@ -535,12 +412,21 @@ private: void _render_warning_texture() const; void _render_legend_texture() const; void _render_layer_editing_overlay() const; + void _render_volumes(bool fake_colors) const; + float _get_layers_editing_cursor_z_relative() const; + int _get_layers_editing_first_selected_object_id(unsigned int objects_count) const; void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); + bool _bar_rect_contains(float x, float y) const; + bool _reset_rect_contains(float x, float y) const; + // 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. Pointf3 _mouse_to_3d(const Point& mouse_pos, float* z = nullptr); + + void _start_timer(); + void _stop_timer(); }; } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index e9a4d6ce8e..7a53e2fc3c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -158,38 +158,12 @@ bool GLCanvas3DManager::init(wxGLCanvas* canvas, bool useVBOs) return (it != m_canvases.end()) ? it->second->init(useVBOs, m_use_legacy_opengl) : false; } -bool GLCanvas3DManager::is_dirty(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_dirty() : false; -} - -void GLCanvas3DManager::set_dirty(wxGLCanvas* canvas, bool dirty) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_dirty(dirty); -} - bool GLCanvas3DManager::is_shown_on_screen(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); return (it != m_canvases.end()) ? it->second->is_shown_on_screen() : false; } -void GLCanvas3DManager::resize(wxGLCanvas* canvas, unsigned int w, unsigned int h) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->resize(w, h); -} - -GLVolumeCollection* GLCanvas3DManager::get_volumes(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_volumes() : nullptr; -} - void GLCanvas3DManager::set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -246,43 +220,12 @@ void GLCanvas3DManager::set_auto_bed_shape(wxGLCanvas* canvas) it->second->set_auto_bed_shape(); } -BoundingBoxf3 GLCanvas3DManager::get_bed_bounding_box(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->bed_bounding_box() : BoundingBoxf3(); -} - BoundingBoxf3 GLCanvas3DManager::get_volumes_bounding_box(wxGLCanvas* canvas) { CanvasesMap::const_iterator it = _get_canvas(canvas); return (it != m_canvases.end()) ? it->second->volumes_bounding_box() : BoundingBoxf3(); } -BoundingBoxf3 GLCanvas3DManager::get_max_bounding_box(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->max_bounding_box() : BoundingBoxf3(); -} - -Pointf3 GLCanvas3DManager::get_axes_origin(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_axes_origin() : Pointf3(); -} - -void GLCanvas3DManager::set_axes_origin(wxGLCanvas* canvas, const Pointf3& origin) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_axes_origin(origin); -} - -float GLCanvas3DManager::get_axes_length(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_axes_length() : 0.0f; -} - void GLCanvas3DManager::set_axes_length(wxGLCanvas* canvas, float length) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -297,123 +240,18 @@ void GLCanvas3DManager::set_cutting_plane(wxGLCanvas* canvas, float z, const ExP it->second->set_cutting_plane(z, polygons); } -unsigned int GLCanvas3DManager::get_camera_type(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? (unsigned int)it->second->get_camera_type() : 0; -} - -void GLCanvas3DManager::set_camera_type(wxGLCanvas* canvas, unsigned int type) -{ - if ((type <= (unsigned int)GLCanvas3D::Camera::CT_Unknown) || ((unsigned int)GLCanvas3D::Camera::CT_Count <= type)) - return; - - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_camera_type((GLCanvas3D::Camera::EType)type); -} - -std::string GLCanvas3DManager::get_camera_type_as_string(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_camera_type_as_string() : "unknown"; -} - -float GLCanvas3DManager::get_camera_zoom(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_camera_zoom() : 1.0f; -} - -void GLCanvas3DManager::set_camera_zoom(wxGLCanvas* canvas, float zoom) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_camera_zoom(zoom); -} - -float GLCanvas3DManager::get_camera_phi(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_camera_phi() : 0.0f; -} - -void GLCanvas3DManager::set_camera_phi(wxGLCanvas* canvas, float phi) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_camera_phi(phi); -} - -float GLCanvas3DManager::get_camera_theta(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_camera_theta() : 0.0f; -} - -void GLCanvas3DManager::set_camera_theta(wxGLCanvas* canvas, float theta) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_camera_theta(theta); -} - -float GLCanvas3DManager::get_camera_distance(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_camera_distance() : 0.0f; -} - -void GLCanvas3DManager::set_camera_distance(wxGLCanvas* canvas, float distance) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_camera_distance(distance); -} - -Pointf3 GLCanvas3DManager::get_camera_target(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_camera_target() : Pointf3(0.0, 0.0, 0.0); -} - -void GLCanvas3DManager::set_camera_target(wxGLCanvas* canvas, const Pointf3& target) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_camera_target(target); -} - bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); return (it != m_canvases.end()) ? it->second->is_layers_editing_enabled() : false; } -bool GLCanvas3DManager::is_picking_enabled(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_picking_enabled() : false; -} - -bool GLCanvas3DManager::is_moving_enabled(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_moving_enabled() : false; -} - bool GLCanvas3DManager::is_layers_editing_allowed(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); return (it != m_canvases.end()) ? it->second->is_layers_editing_allowed() : false; } -bool GLCanvas3DManager::is_multisample_allowed(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_multisample_allowed() : false; -} - void GLCanvas3DManager::enable_layers_editing(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -463,146 +301,6 @@ void GLCanvas3DManager::allow_multisample(wxGLCanvas* canvas, bool allow) it->second->allow_multisample(allow); } -bool GLCanvas3DManager::is_mouse_dragging(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_mouse_dragging() : false; -} - -void GLCanvas3DManager::set_mouse_dragging(wxGLCanvas* canvas, bool dragging) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_mouse_dragging(dragging); -} - -int GLCanvas3DManager::get_hover_volume_id(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_hover_volume_id() : -1; -} - -void GLCanvas3DManager::set_hover_volume_id(wxGLCanvas* canvas, int id) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_hover_volume_id(id); -} - -unsigned int GLCanvas3DManager::get_layers_editing_z_texture_id(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_z_texture_id() : 0; -} - -unsigned int GLCanvas3DManager::get_layers_editing_state(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_state() : 0; -} - -void GLCanvas3DManager::set_layers_editing_state(wxGLCanvas* canvas, unsigned int state) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_layers_editing_state(state); -} - -float GLCanvas3DManager::get_layers_editing_band_width(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_band_width() : 0.0f; -} - -void GLCanvas3DManager::set_layers_editing_band_width(wxGLCanvas* canvas, float band_width) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_layers_editing_band_width(band_width); -} - -float GLCanvas3DManager::get_layers_editing_strength(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_strength() : 0.0f; -} - -void GLCanvas3DManager::set_layers_editing_strength(wxGLCanvas* canvas, float strength) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_layers_editing_strength(strength); -} - -int GLCanvas3DManager::get_layers_editing_last_object_id(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_last_object_id() : -1; -} - -void GLCanvas3DManager::set_layers_editing_last_object_id(wxGLCanvas* canvas, int id) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_layers_editing_last_object_id(id); -} - -float GLCanvas3DManager::get_layers_editing_last_z(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_last_z() : 0.0f; -} - -void GLCanvas3DManager::set_layers_editing_last_z(wxGLCanvas* canvas, float z) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_layers_editing_last_z(z); -} - -unsigned int GLCanvas3DManager::get_layers_editing_last_action(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_last_action() : 0; -} - -void GLCanvas3DManager::set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int action) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_layers_editing_last_action(action); -} - -const GLShader* GLCanvas3DManager::get_layers_editing_shader(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_shader() : nullptr; -} - -float GLCanvas3DManager::get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_cursor_z_relative() : 0.0f; -} - -int GLCanvas3DManager::get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_layers_editing_first_selected_object_id(objects_count) : 0.; -} - -bool GLCanvas3DManager::bar_rect_contains(wxGLCanvas* canvas, float x, float y) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->bar_rect_contains(x, y) : false; -} - -bool GLCanvas3DManager::reset_rect_contains(wxGLCanvas* canvas, float x, float y) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->reset_rect_contains(x, y) : false; -} - void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -642,19 +340,6 @@ void GLCanvas3DManager::update_volumes_colors_by_extruder(wxGLCanvas* canvas) it->second->update_volumes_colors_by_extruder(); } -bool GLCanvas3DManager::start_using_shader(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->start_using_shader() : false; -} - -void GLCanvas3DManager::stop_using_shader(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->stop_using_shader(); -} - void GLCanvas3DManager::render(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -662,41 +347,6 @@ void GLCanvas3DManager::render(wxGLCanvas* canvas) const it->second->render(m_use_VBOs); } -void GLCanvas3DManager::render_volumes(wxGLCanvas* canvas, bool fake_colors) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render_volumes(fake_colors); -} - -void GLCanvas3DManager::render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render_texture(tex_id, left, right, bottom, top); -} - -void GLCanvas3DManager::start_timer(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->start_timer(); -} - -void GLCanvas3DManager::stop_timer(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->stop_timer(); -} - -void GLCanvas3DManager::perform_layer_editing_action(wxGLCanvas* canvas, int y, bool shift_down, bool right_down) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->perform_layer_editing_action(y, shift_down, right_down); -} - void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index bf6ac91eeb..1b22863d01 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -46,14 +46,8 @@ public: bool init(wxGLCanvas* canvas, bool useVBOs); - bool is_dirty(wxGLCanvas* canvas) const; - void set_dirty(wxGLCanvas* canvas, bool dirty); - bool is_shown_on_screen(wxGLCanvas* canvas) const; - void resize(wxGLCanvas* canvas, unsigned int w, unsigned int h); - - GLVolumeCollection* get_volumes(wxGLCanvas* canvas); void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); void reset_volumes(wxGLCanvas* canvas); void deselect_volumes(wxGLCanvas* canvas); @@ -65,42 +59,14 @@ public: void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); void set_auto_bed_shape(wxGLCanvas* canvas); - BoundingBoxf3 get_bed_bounding_box(wxGLCanvas* canvas); BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); - BoundingBoxf3 get_max_bounding_box(wxGLCanvas* canvas); - Pointf3 get_axes_origin(wxGLCanvas* canvas) const; - void set_axes_origin(wxGLCanvas* canvas, const Pointf3& origin); - - float get_axes_length(wxGLCanvas* canvas) const; void set_axes_length(wxGLCanvas* canvas, float length); void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); - unsigned int get_camera_type(wxGLCanvas* canvas) const; - void set_camera_type(wxGLCanvas* canvas, unsigned int type); - std::string get_camera_type_as_string(wxGLCanvas* canvas) const; - - float get_camera_zoom(wxGLCanvas* canvas) const; - void set_camera_zoom(wxGLCanvas* canvas, float zoom); - - float get_camera_phi(wxGLCanvas* canvas) const; - void set_camera_phi(wxGLCanvas* canvas, float phi); - - float get_camera_theta(wxGLCanvas* canvas) const; - void set_camera_theta(wxGLCanvas* canvas, float theta); - - float get_camera_distance(wxGLCanvas* canvas) const; - void set_camera_distance(wxGLCanvas* canvas, float distance); - - Pointf3 get_camera_target(wxGLCanvas* canvas) const; - void set_camera_target(wxGLCanvas* canvas, const Pointf3& target); - bool is_layers_editing_enabled(wxGLCanvas* canvas) const; - bool is_picking_enabled(wxGLCanvas* canvas) const; - bool is_moving_enabled(wxGLCanvas* canvas) const; bool is_layers_editing_allowed(wxGLCanvas* canvas) const; - bool is_multisample_allowed(wxGLCanvas* canvas) const; void enable_layers_editing(wxGLCanvas* canvas, bool enable); void enable_warning_texture(wxGLCanvas* canvas, bool enable); @@ -110,39 +76,6 @@ public: void enable_shader(wxGLCanvas* canvas, bool enable); void allow_multisample(wxGLCanvas* canvas, bool allow); - bool is_mouse_dragging(wxGLCanvas* canvas) const; - void set_mouse_dragging(wxGLCanvas* canvas, bool dragging); - - int get_hover_volume_id(wxGLCanvas* canvas) const; - void set_hover_volume_id(wxGLCanvas* canvas, int id); - - unsigned int get_layers_editing_z_texture_id(wxGLCanvas* canvas) const; - - unsigned int get_layers_editing_state(wxGLCanvas* canvas) const; - void set_layers_editing_state(wxGLCanvas* canvas, unsigned int state); - - float get_layers_editing_band_width(wxGLCanvas* canvas) const; - void set_layers_editing_band_width(wxGLCanvas* canvas, float band_width); - - float get_layers_editing_strength(wxGLCanvas* canvas) const; - void set_layers_editing_strength(wxGLCanvas* canvas, float strength); - - int get_layers_editing_last_object_id(wxGLCanvas* canvas) const; - void set_layers_editing_last_object_id(wxGLCanvas* canvas, int id); - - float get_layers_editing_last_z(wxGLCanvas* canvas) const; - void set_layers_editing_last_z(wxGLCanvas* canvas, float z); - - unsigned int get_layers_editing_last_action(wxGLCanvas* canvas) const; - void set_layers_editing_last_action(wxGLCanvas* canvas, unsigned int action); - - const GLShader* get_layers_editing_shader(wxGLCanvas* canvas) const; - - float get_layers_editing_cursor_z_relative(wxGLCanvas* canvas) const; - int get_layers_editing_first_selected_object_id(wxGLCanvas* canvas, unsigned int objects_count) const; - bool bar_rect_contains(wxGLCanvas* canvas, float x, float y) const; - bool reset_rect_contains(wxGLCanvas* canvas, float x, float y) const; - void zoom_to_bed(wxGLCanvas* canvas); void zoom_to_volumes(wxGLCanvas* canvas); void select_view(wxGLCanvas* canvas, const std::string& direction); @@ -150,16 +83,7 @@ public: void update_volumes_colors_by_extruder(wxGLCanvas* canvas); - bool start_using_shader(wxGLCanvas* canvas) const; - void stop_using_shader(wxGLCanvas* canvas) const; - void render(wxGLCanvas* canvas) const; - void render_volumes(wxGLCanvas* canvas, bool fake_colors) const; - void render_texture(wxGLCanvas* canvas, unsigned int tex_id, float left, float right, float bottom, float top) const; - - void start_timer(wxGLCanvas* canvas); - void stop_timer(wxGLCanvas* canvas); - void perform_layer_editing_action(wxGLCanvas* canvas, int y, bool shift_down, bool right_down); void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 21b872cfdb..0d57155641 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -199,21 +199,6 @@ init(canvas, useVBOs) OUTPUT: RETVAL -bool -is_dirty(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::is_dirty((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_dirty(canvas, dirty) - SV *canvas; - bool dirty; - CODE: - _3DScene::set_dirty((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), dirty); - bool is_shown_on_screen(canvas) SV *canvas; @@ -222,22 +207,6 @@ is_shown_on_screen(canvas) OUTPUT: RETVAL -void -resize(canvas, w, h) - SV *canvas; - unsigned int w; - unsigned int h; - CODE: - _3DScene::resize((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), w, h); - -GLVolumeCollection* -get_volumes(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - void set_volumes(canvas, volumes) SV *canvas; @@ -291,14 +260,6 @@ set_auto_bed_shape(canvas) CODE: _3DScene::set_auto_bed_shape((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); -Clone -get_bed_bounding_box(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_bed_bounding_box((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - Clone get_volumes_bounding_box(canvas) SV *canvas; @@ -306,37 +267,6 @@ get_volumes_bounding_box(canvas) RETVAL = _3DScene::get_volumes_bounding_box((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); OUTPUT: RETVAL - -Clone -get_max_bounding_box(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_max_bounding_box((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -Clone -get_axes_origin(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_axes_origin((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_axes_origin(canvas, origin) - SV *canvas; - Pointf3 *origin; - CODE: - _3DScene::set_axes_origin((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), origin); - -float -get_axes_length(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_axes_length((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL void set_axes_length(canvas, length) @@ -352,104 +282,6 @@ set_cutting_plane(canvas, z, polygons) ExPolygons polygons; CODE: _3DScene::set_cutting_plane((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), z, polygons); - -unsigned int -get_camera_type(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_camera_type((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_camera_type(canvas, type) - SV *canvas; - unsigned int type; - CODE: - _3DScene::set_camera_type((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), type); - -std::string -get_camera_type_as_string(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_camera_type_as_string((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -float -get_camera_zoom(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_camera_zoom((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_camera_zoom(canvas, zoom) - SV *canvas; - float zoom; - CODE: - _3DScene::set_camera_zoom((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), zoom); - -float -get_camera_phi(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_camera_phi((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_camera_phi(canvas, phi) - SV *canvas; - float phi; - CODE: - _3DScene::set_camera_phi((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), phi); - -float -get_camera_theta(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_camera_theta((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_camera_theta(canvas, theta) - SV *canvas; - float theta; - CODE: - _3DScene::set_camera_theta((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), theta); - -float -get_camera_distance(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_camera_distance((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_camera_distance(canvas, distance) - SV *canvas; - float distance; - CODE: - _3DScene::set_camera_distance((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), distance); - -Clone -get_camera_target(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_camera_target((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_camera_target(canvas, target) - SV *canvas; - Pointf3 *target; - CODE: - _3DScene::set_camera_target((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), target); bool is_layers_editing_enabled(canvas) @@ -459,22 +291,6 @@ is_layers_editing_enabled(canvas) OUTPUT: RETVAL -bool -is_picking_enabled(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::is_picking_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -bool -is_moving_enabled(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::is_moving_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - bool is_layers_editing_allowed(canvas) SV *canvas; @@ -482,14 +298,6 @@ is_layers_editing_allowed(canvas) RETVAL = _3DScene::is_layers_editing_allowed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); OUTPUT: RETVAL - -bool -is_multisample_allowed(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::is_multisample_allowed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL void enable_layers_editing(canvas, enable) @@ -539,179 +347,6 @@ allow_multisample(canvas, allow) bool allow; CODE: _3DScene::allow_multisample((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), allow); - -bool -is_mouse_dragging(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::is_mouse_dragging((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_mouse_dragging(canvas, dragging) - SV *canvas; - bool dragging; - CODE: - _3DScene::set_mouse_dragging((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), dragging); - -int -get_hover_volume_id(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_hover_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_hover_volume_id(canvas, id) - SV *canvas; - int id; - CODE: - _3DScene::set_hover_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id); - -unsigned int -get_layers_editing_z_texture_id(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_layers_editing_z_texture_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -unsigned int -get_layers_editing_state(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_layers_editing_state((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_layers_editing_state(canvas, state) - SV *canvas; - unsigned int state; - CODE: - _3DScene::set_layers_editing_state((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), state); - -float -get_layers_editing_band_width(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_layers_editing_band_width((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_layers_editing_band_width(canvas, band_width) - SV *canvas; - float band_width; - CODE: - _3DScene::set_layers_editing_band_width((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), band_width); - -float -get_layers_editing_strength(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_layers_editing_strength((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_layers_editing_strength(canvas, strength) - SV *canvas; - float strength; - CODE: - _3DScene::set_layers_editing_strength((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), strength); - -int -get_layers_editing_last_object_id(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_layers_editing_last_object_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_layers_editing_last_object_id(canvas, id) - SV *canvas; - int id; - CODE: - _3DScene::set_layers_editing_last_object_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id); - -float -get_layers_editing_last_z(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_layers_editing_last_z((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_layers_editing_last_z(canvas, z) - SV *canvas; - float z; - CODE: - _3DScene::set_layers_editing_last_z((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), z); - -unsigned int -get_layers_editing_last_action(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_layers_editing_last_action((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -set_layers_editing_last_action(canvas, action) - SV *canvas; - unsigned int action; - CODE: - _3DScene::set_layers_editing_last_action((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), action); - -Ref -get_layers_editing_shader(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_layers_editing_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -float -get_layers_editing_cursor_z_relative(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::get_layers_editing_cursor_z_relative((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -int -get_layers_editing_first_selected_object_id(canvas, objects_count) - SV *canvas; - unsigned int objects_count; - CODE: - RETVAL = _3DScene::get_layers_editing_first_selected_object_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), objects_count); - OUTPUT: - RETVAL - -bool -bar_rect_contains(canvas, x, y) - SV *canvas; - float x; - float y; - CODE: - RETVAL = _3DScene::bar_rect_contains((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), x, y); - OUTPUT: - RETVAL - -bool -reset_rect_contains(canvas, x, y) - SV *canvas; - float x; - float y; - CODE: - RETVAL = _3DScene::reset_rect_contains((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), x, y); - OUTPUT: - RETVAL void zoom_to_bed(canvas) @@ -745,65 +380,12 @@ update_volumes_colors_by_extruder(canvas) CODE: _3DScene::update_volumes_colors_by_extruder((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); -bool -start_using_shader(canvas) - SV *canvas; - CODE: - RETVAL = _3DScene::start_using_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL - -void -stop_using_shader(canvas) - SV *canvas; - CODE: - _3DScene::stop_using_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - void render(canvas) SV *canvas; CODE: _3DScene::render((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); -void -render_volumes(canvas, fake_colors) - SV *canvas; - bool fake_colors; - CODE: - _3DScene::render_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), fake_colors); - -void -render_texture(canvas, tex_id, left, right, bottom, top) - SV *canvas; - unsigned int tex_id; - float left; - float right; - float bottom; - float top; - CODE: - _3DScene::render_texture((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), tex_id, left, right, bottom, top); - -void -start_timer(canvas) - SV *canvas; - CODE: - _3DScene::start_timer((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - -void -stop_timer(canvas) - SV *canvas; - CODE: - _3DScene::stop_timer((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - -void -perform_layer_editing_action(canvas, y, shift_down, right_down) - SV *canvas; - int y; - bool shift_down; - bool right_down; - CODE: - _3DScene::perform_layer_editing_action((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), y, shift_down, right_down); - void register_on_viewport_changed_callback(canvas, callback) SV *canvas; From 8911cf605193e8fc8cdafd381ff0ae60373a7522 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Jun 2018 10:14:09 +0200 Subject: [PATCH 051/117] OpenGL info moved to c++ --- lib/Slic3r/GUI.pm | 8 +- lib/Slic3r/GUI/3DScene.pm | 62 ++++++------ xs/src/slic3r/GUI/3DScene.cpp | 5 + xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 125 +++++++++++++++++++----- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 17 ++-- xs/xsp/GUI_3DScene.xsp | 9 ++ 7 files changed, 162 insertions(+), 65 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 04dc803231..d04d470f66 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -218,8 +218,12 @@ sub system_info { my $opengl_info_txt = ''; if (defined($self->{mainframe}) && defined($self->{mainframe}->{plater}) && defined($self->{mainframe}->{plater}->{canvas3D})) { - $opengl_info = $self->{mainframe}->{plater}->{canvas3D}->opengl_info(format => 'html'); - $opengl_info_txt = $self->{mainframe}->{plater}->{canvas3D}->opengl_info; +#============================================================================================================================== + $opengl_info = Slic3r::GUI::_3DScene::get_gl_info(1, 1); + $opengl_info_txt = Slic3r::GUI::_3DScene::get_gl_info(0, 1); +# $opengl_info = $self->{mainframe}->{plater}->{canvas3D}->opengl_info(format => 'html'); +# $opengl_info_txt = $self->{mainframe}->{plater}->{canvas3D}->opengl_info; +#============================================================================================================================== } my $about = Slic3r::GUI::SystemInfo->new( parent => undef, diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 68d7493058..eca90890e1 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1839,39 +1839,39 @@ sub Render { # my ($self, $config) = @_; # $self->volumes->update_colors_by_extruder($config); #} +# +#sub opengl_info +#{ +# my ($self, %params) = @_; +# my %tag = Slic3r::tags($params{format}); +# +# my $gl_version = glGetString(GL_VERSION); +# my $gl_vendor = glGetString(GL_VENDOR); +# my $gl_renderer = glGetString(GL_RENDERER); +# my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION); +# +# my $out = ''; +# $out .= "$tag{h2start}OpenGL installation$tag{h2end}$tag{eol}"; +# $out .= " $tag{bstart}Using POGL$tag{bend} v$OpenGL::BUILD_VERSION$tag{eol}"; +# $out .= " $tag{bstart}GL version: $tag{bend}${gl_version}$tag{eol}"; +# $out .= " $tag{bstart}vendor: $tag{bend}${gl_vendor}$tag{eol}"; +# $out .= " $tag{bstart}renderer: $tag{bend}${gl_renderer}$tag{eol}"; +# $out .= " $tag{bstart}GLSL version: $tag{bend}${glsl_version}$tag{eol}"; +# +# # Check for other OpenGL extensions +# $out .= "$tag{h2start}Installed extensions (* implemented in the module):$tag{h2end}$tag{eol}"; +# my $extensions = glGetString(GL_EXTENSIONS); +# my @extensions = split(' ',$extensions); +# foreach my $ext (sort @extensions) { +# my $stat = glpCheckExtension($ext); +# $out .= sprintf("%s ${ext}$tag{eol}", $stat?' ':'*'); +# $out .= sprintf(" ${stat}$tag{eol}") if ($stat && $stat !~ m|^$ext |); +# } +# +# return $out; +#} #============================================================================================================================== -sub opengl_info -{ - my ($self, %params) = @_; - my %tag = Slic3r::tags($params{format}); - - my $gl_version = glGetString(GL_VERSION); - my $gl_vendor = glGetString(GL_VENDOR); - my $gl_renderer = glGetString(GL_RENDERER); - my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION); - - my $out = ''; - $out .= "$tag{h2start}OpenGL installation$tag{h2end}$tag{eol}"; - $out .= " $tag{bstart}Using POGL$tag{bend} v$OpenGL::BUILD_VERSION$tag{eol}"; - $out .= " $tag{bstart}GL version: $tag{bend}${gl_version}$tag{eol}"; - $out .= " $tag{bstart}vendor: $tag{bend}${gl_vendor}$tag{eol}"; - $out .= " $tag{bstart}renderer: $tag{bend}${gl_renderer}$tag{eol}"; - $out .= " $tag{bstart}GLSL version: $tag{bend}${glsl_version}$tag{eol}"; - - # Check for other OpenGL extensions - $out .= "$tag{h2start}Installed extensions (* implemented in the module):$tag{h2end}$tag{eol}"; - my $extensions = glGetString(GL_EXTENSIONS); - my @extensions = split(' ',$extensions); - foreach my $ext (sort @extensions) { - my $stat = glpCheckExtension($ext); - $out .= sprintf("%s ${ext}$tag{eol}", $stat?' ':'*'); - $out .= sprintf(" ${stat}$tag{eol}") if ($stat && $stat !~ m|^$ext |); - } - - return $out; -} - sub _report_opengl_state { my ($self, $comment) = @_; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 45d1d5b760..328e7dcd5b 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1729,6 +1729,11 @@ void _3DScene::init_gl() s_canvas_mgr.init_gl(); } +std::string _3DScene::get_gl_info(bool format_as_html, bool extensions) +{ + return s_canvas_mgr.get_gl_info(format_as_html, extensions); +} + bool _3DScene::use_VBOs() { return s_canvas_mgr.use_VBOs(); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 615655a001..1ab958d9e5 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -538,6 +538,7 @@ class _3DScene public: //################################################################################################################## static void init_gl(); + static std::string get_gl_info(bool format_as_html, bool extensions); static bool use_VBOs(); static bool add_canvas(wxGLCanvas* canvas, wxGLContext* context); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 7a53e2fc3c..10f8252019 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -17,20 +17,47 @@ namespace Slic3r { namespace GUI { -GLCanvas3DManager::GLVersion::GLVersion() - : vn_major(0) - , vn_minor(0) +GLCanvas3DManager::GLInfo::GLInfo() + : version("") + , glsl_version("") + , vendor("") + , renderer("") { } -bool GLCanvas3DManager::GLVersion::detect() +bool GLCanvas3DManager::GLInfo::detect() { - const char* gl_version = (const char*)::glGetString(GL_VERSION); - if (gl_version == nullptr) + const char* data = (const char*)::glGetString(GL_VERSION); + if (data == nullptr) return false; + version = data; + + data = (const char*)::glGetString(GL_SHADING_LANGUAGE_VERSION); + if (data == nullptr) + return false; + + glsl_version = data; + + data = (const char*)::glGetString(GL_VENDOR); + if (data == nullptr) + return false; + + vendor = data; + + data = (const char*)::glGetString(GL_RENDERER); + if (data == nullptr) + return false; + + renderer = data; + + return true; +} + +bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const +{ std::vector tokens; - boost::split(tokens, gl_version, boost::is_any_of(" "), boost::token_compress_on); + boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on); if (tokens.empty()) return false; @@ -38,23 +65,61 @@ bool GLCanvas3DManager::GLVersion::detect() std::vector numbers; boost::split(numbers, tokens[0], boost::is_any_of("."), boost::token_compress_on); + unsigned int gl_major = 0; + unsigned int gl_minor = 0; + if (numbers.size() > 0) - vn_major = ::atoi(numbers[0].c_str()); + gl_major = ::atoi(numbers[0].c_str()); if (numbers.size() > 1) - vn_minor = ::atoi(numbers[1].c_str()); + gl_minor = ::atoi(numbers[1].c_str()); - return true; -} - -bool GLCanvas3DManager::GLVersion::is_greater_or_equal_to(unsigned int major, unsigned int minor) const -{ - if (vn_major < major) + if (gl_major < major) return false; - else if (vn_major > major) + else if (gl_major > major) return true; else - return vn_minor >= minor; + return gl_minor >= minor; +} + +std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool extensions) const +{ + std::stringstream out; + + std::string h2_start = format_as_html ? "" : ""; + std::string h2_end = format_as_html ? "" : ""; + std::string b_start = format_as_html ? "" : ""; + std::string b_end = format_as_html ? "" : ""; + std::string line_end = format_as_html ? "
" : "\n"; + + out << h2_start << "OpenGL installation" << h2_end << line_end; + out << b_start << "GL version: " << b_end << version << line_end; + out << b_start << "Vendor: " << b_end << vendor << line_end; + out << b_start << "Renderer: " << b_end << renderer << line_end; + out << b_start << "GLSL version: " << b_end << glsl_version << line_end; + + if (extensions) + { + out << h2_start << "Installed extensions:" << h2_end << line_end; + + std::vector extensions_list; + GLint num_extensions; + ::glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + + for (unsigned int i = 0; i < num_extensions; ++i) + { + const char* e = (const char*)::glGetStringi(GL_EXTENSIONS, i); + extensions_list.push_back(e); + } + + std::sort(extensions_list.begin(), extensions_list.end()); + for (const std::string& ext : extensions_list) + { + out << ext << line_end; + } + } + + return out.str(); } GLCanvas3DManager::GLCanvas3DManager() @@ -134,19 +199,27 @@ void GLCanvas3DManager::init_gl() std::cout << "GLCanvas3DManager::init_gl()" << std::endl; glewInit(); - m_gl_version.detect(); + if (m_gl_info.detect()) + { + const AppConfig* config = GUI::get_app_config(); + m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1"); + m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0); + m_gl_initialized = true; - const AppConfig* config = GUI::get_app_config(); - m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1"); - m_use_VBOs = !m_use_legacy_opengl && m_gl_version.is_greater_or_equal_to(2, 0); - m_gl_initialized = true; - - std::cout << "DETECTED OPENGL: " << m_gl_version.vn_major << "." << m_gl_version.vn_minor << std::endl; - std::cout << "USE VBOS = " << (m_use_VBOs ? "YES" : "NO") << std::endl; - std::cout << "LAYER EDITING ALLOWED = " << (!m_use_legacy_opengl ? "YES" : "NO") << std::endl; + std::cout << "DETECTED OPENGL: " << m_gl_info.version << std::endl; + std::cout << "USE VBOS = " << (m_use_VBOs ? "YES" : "NO") << std::endl; + std::cout << "LAYER EDITING ALLOWED = " << (!m_use_legacy_opengl ? "YES" : "NO") << std::endl; + } + else + throw std::runtime_error(std::string("Unable to initialize OpenGL driver\n")); } } +std::string GLCanvas3DManager::get_gl_info(bool format_as_html, bool extensions) const +{ + return m_gl_info.to_string(format_as_html, extensions); +} + bool GLCanvas3DManager::use_VBOs() const { return m_use_VBOs; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 1b22863d01..1a8689c810 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -10,21 +10,25 @@ namespace GUI { class GLCanvas3DManager { - struct GLVersion + struct GLInfo { - unsigned int vn_major; - unsigned int vn_minor; + std::string version; + std::string glsl_version; + std::string vendor; + std::string renderer; + + GLInfo(); - GLVersion(); bool detect(); + bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const; - bool is_greater_or_equal_to(unsigned int major, unsigned int minor) const; + std::string to_string(bool format_as_html, bool extensions) const; }; typedef std::map CanvasesMap; CanvasesMap m_canvases; - GLVersion m_gl_version; + GLInfo m_gl_info; bool m_gl_initialized; bool m_use_legacy_opengl; bool m_use_VBOs; @@ -40,6 +44,7 @@ public: unsigned int count() const; void init_gl(); + std::string get_gl_info(bool format_as_html, bool extensions) const; bool use_VBOs() const; bool layer_editing_allowed() const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 0d57155641..862b9645c0 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -161,6 +161,15 @@ init_gl() CODE: _3DScene::init_gl(); +std::string +get_gl_info(format_as_html, extensions) + bool format_as_html; + bool extensions; + CODE: + RETVAL = _3DScene::get_gl_info(format_as_html, extensions); + OUTPUT: + RETVAL + bool use_VBOs() CODE: From 95e7d96f522886002bfc515797a91ad9e9758c91 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Jun 2018 12:26:39 +0200 Subject: [PATCH 052/117] 3DScene paint event handler moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 341 +++++++++++------------- xs/src/slic3r/GUI/GLCanvas3D.cpp | 106 +++++--- xs/src/slic3r/GUI/GLCanvas3D.hpp | 14 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 16 +- 4 files changed, 261 insertions(+), 216 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index eca90890e1..c13cd48adf 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -17,7 +17,6 @@ use warnings; use Wx qw(wxTheApp :timer :bitmap :icon :dialog); #============================================================================================================================== -use Wx::Event qw(EVT_PAINT EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER); #use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER); #============================================================================================================================== # must load OpenGL *before* Wx::GLCanvas @@ -82,11 +81,9 @@ __PACKAGE__->mk_accessors( qw(init # _mouse_dragging # # ) ); -#============================================================================================================================== - -use constant TRACKBALLSIZE => 0.8; -use constant TURNTABLE_MODE => 1; -#============================================================================================================================== +# +#use constant TRACKBALLSIZE => 0.8; +#use constant TURNTABLE_MODE => 1; #use constant GROUND_Z => -0.02; ## For mesh selection: Not selected - bright yellow. #use constant DEFAULT_COLOR => [1,1,0]; @@ -109,17 +106,17 @@ use constant TURNTABLE_MODE => 1; #use constant MANIPULATION_LAYER_HEIGHT => 2; # #use constant GIMBALL_LOCK_THETA_MAX => 180; +# +#use constant VARIABLE_LAYER_THICKNESS_BAR_WIDTH => 70; +#use constant VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT => 22; +# +## make OpenGL::Array thread-safe +#{ +# no warnings 'redefine'; +# *OpenGL::Array::CLONE_SKIP = sub { 1 }; +#} #============================================================================================================================== -use constant VARIABLE_LAYER_THICKNESS_BAR_WIDTH => 70; -use constant VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT => 22; - -# make OpenGL::Array thread-safe -{ - no warnings 'redefine'; - *OpenGL::Array::CLONE_SKIP = sub { 1 }; -} - sub new { my ($class, $parent) = @_; @@ -192,13 +189,11 @@ sub new { # $self->{layer_height_edit_last_action} = 0; # # $self->reset_objects; -#============================================================================================================================== - - EVT_PAINT($self, sub { - my $dc = Wx::PaintDC->new($self); - $self->Render($dc); - }); -#======================================================================================================================= +# +# EVT_PAINT($self, sub { +# my $dc = Wx::PaintDC->new($self); +# $self->Render($dc); +# }); # EVT_SIZE($self, sub { $self->_dirty(1) }); # EVT_IDLE($self, sub { # return unless $self->_dirty; @@ -970,116 +965,114 @@ sub Destroy { # } # $self->cut_lines_vertices(OpenGL::Array->new_list(GL_FLOAT, @verts)); #} -#============================================================================================================================== - -# Given an axis and angle, compute quaternion. -sub axis_to_quat { - my ($ax, $phi) = @_; - - my $lena = sqrt(reduce { $a + $b } (map { $_ * $_ } @$ax)); - my @q = map { $_ * (1 / $lena) } @$ax; - @q = map { $_ * sin($phi / 2.0) } @q; - $q[$#q + 1] = cos($phi / 2.0); - return @q; -} - -# Project a point on the virtual trackball. -# If it is inside the sphere, map it to the sphere, if it outside map it -# to a hyperbola. -sub project_to_sphere { - my ($r, $x, $y) = @_; - - my $d = sqrt($x * $x + $y * $y); - if ($d < $r * 0.70710678118654752440) { # Inside sphere - return sqrt($r * $r - $d * $d); - } else { # On hyperbola - my $t = $r / 1.41421356237309504880; - return $t * $t / $d; - } -} - -sub cross { - my ($v1, $v2) = @_; - - return (@$v1[1] * @$v2[2] - @$v1[2] * @$v2[1], - @$v1[2] * @$v2[0] - @$v1[0] * @$v2[2], - @$v1[0] * @$v2[1] - @$v1[1] * @$v2[0]); -} - -# Simulate a track-ball. Project the points onto the virtual trackball, -# then figure out the axis of rotation, which is the cross product of -# P1 P2 and O P1 (O is the center of the ball, 0,0,0) Note: This is a -# deformed trackball-- is a trackball in the center, but is deformed -# into a hyperbolic sheet of rotation away from the center. -# It is assumed that the arguments to this routine are in the range -# (-1.0 ... 1.0). -sub trackball { - my ($p1x, $p1y, $p2x, $p2y) = @_; - - if ($p1x == $p2x && $p1y == $p2y) { - # zero rotation - return (0.0, 0.0, 0.0, 1.0); - } - - # First, figure out z-coordinates for projection of P1 and P2 to - # deformed sphere - my @p1 = ($p1x, $p1y, project_to_sphere(TRACKBALLSIZE, $p1x, $p1y)); - my @p2 = ($p2x, $p2y, project_to_sphere(TRACKBALLSIZE, $p2x, $p2y)); - - # axis of rotation (cross product of P1 and P2) - my @a = cross(\@p2, \@p1); - - # Figure out how much to rotate around that axis. - my @d = map { $_ * $_ } (map { $p1[$_] - $p2[$_] } 0 .. $#p1); - my $t = sqrt(reduce { $a + $b } @d) / (2.0 * TRACKBALLSIZE); - - # Avoid problems with out-of-control values... - $t = 1.0 if ($t > 1.0); - $t = -1.0 if ($t < -1.0); - my $phi = 2.0 * asin($t); - - return axis_to_quat(\@a, $phi); -} - -# Build a rotation matrix, given a quaternion rotation. -sub quat_to_rotmatrix { - my ($q) = @_; - - my @m = (); - - $m[0] = 1.0 - 2.0 * (@$q[1] * @$q[1] + @$q[2] * @$q[2]); - $m[1] = 2.0 * (@$q[0] * @$q[1] - @$q[2] * @$q[3]); - $m[2] = 2.0 * (@$q[2] * @$q[0] + @$q[1] * @$q[3]); - $m[3] = 0.0; - - $m[4] = 2.0 * (@$q[0] * @$q[1] + @$q[2] * @$q[3]); - $m[5] = 1.0 - 2.0 * (@$q[2] * @$q[2] + @$q[0] * @$q[0]); - $m[6] = 2.0 * (@$q[1] * @$q[2] - @$q[0] * @$q[3]); - $m[7] = 0.0; - - $m[8] = 2.0 * (@$q[2] * @$q[0] - @$q[1] * @$q[3]); - $m[9] = 2.0 * (@$q[1] * @$q[2] + @$q[0] * @$q[3]); - $m[10] = 1.0 - 2.0 * (@$q[1] * @$q[1] + @$q[0] * @$q[0]); - $m[11] = 0.0; - - $m[12] = 0.0; - $m[13] = 0.0; - $m[14] = 0.0; - $m[15] = 1.0; - - return @m; -} - -sub mulquats { - my ($q1, $rq) = @_; - - return (@$q1[3] * @$rq[0] + @$q1[0] * @$rq[3] + @$q1[1] * @$rq[2] - @$q1[2] * @$rq[1], - @$q1[3] * @$rq[1] + @$q1[1] * @$rq[3] + @$q1[2] * @$rq[0] - @$q1[0] * @$rq[2], - @$q1[3] * @$rq[2] + @$q1[2] * @$rq[3] + @$q1[0] * @$rq[1] - @$q1[1] * @$rq[0], - @$q1[3] * @$rq[3] - @$q1[0] * @$rq[0] - @$q1[1] * @$rq[1] - @$q1[2] * @$rq[2]) -} - -#============================================================================================================================== +# +## Given an axis and angle, compute quaternion. +#sub axis_to_quat { +# my ($ax, $phi) = @_; +# +# my $lena = sqrt(reduce { $a + $b } (map { $_ * $_ } @$ax)); +# my @q = map { $_ * (1 / $lena) } @$ax; +# @q = map { $_ * sin($phi / 2.0) } @q; +# $q[$#q + 1] = cos($phi / 2.0); +# return @q; +#} +# +## Project a point on the virtual trackball. +## If it is inside the sphere, map it to the sphere, if it outside map it +## to a hyperbola. +#sub project_to_sphere { +# my ($r, $x, $y) = @_; +# +# my $d = sqrt($x * $x + $y * $y); +# if ($d < $r * 0.70710678118654752440) { # Inside sphere +# return sqrt($r * $r - $d * $d); +# } else { # On hyperbola +# my $t = $r / 1.41421356237309504880; +# return $t * $t / $d; +# } +#} +# +#sub cross { +# my ($v1, $v2) = @_; +# +# return (@$v1[1] * @$v2[2] - @$v1[2] * @$v2[1], +# @$v1[2] * @$v2[0] - @$v1[0] * @$v2[2], +# @$v1[0] * @$v2[1] - @$v1[1] * @$v2[0]); +#} +# +## Simulate a track-ball. Project the points onto the virtual trackball, +## then figure out the axis of rotation, which is the cross product of +## P1 P2 and O P1 (O is the center of the ball, 0,0,0) Note: This is a +## deformed trackball-- is a trackball in the center, but is deformed +## into a hyperbolic sheet of rotation away from the center. +## It is assumed that the arguments to this routine are in the range +## (-1.0 ... 1.0). +#sub trackball { +# my ($p1x, $p1y, $p2x, $p2y) = @_; +# +# if ($p1x == $p2x && $p1y == $p2y) { +# # zero rotation +# return (0.0, 0.0, 0.0, 1.0); +# } +# +# # First, figure out z-coordinates for projection of P1 and P2 to +# # deformed sphere +# my @p1 = ($p1x, $p1y, project_to_sphere(TRACKBALLSIZE, $p1x, $p1y)); +# my @p2 = ($p2x, $p2y, project_to_sphere(TRACKBALLSIZE, $p2x, $p2y)); +# +# # axis of rotation (cross product of P1 and P2) +# my @a = cross(\@p2, \@p1); +# +# # Figure out how much to rotate around that axis. +# my @d = map { $_ * $_ } (map { $p1[$_] - $p2[$_] } 0 .. $#p1); +# my $t = sqrt(reduce { $a + $b } @d) / (2.0 * TRACKBALLSIZE); +# +# # Avoid problems with out-of-control values... +# $t = 1.0 if ($t > 1.0); +# $t = -1.0 if ($t < -1.0); +# my $phi = 2.0 * asin($t); +# +# return axis_to_quat(\@a, $phi); +#} +# +## Build a rotation matrix, given a quaternion rotation. +#sub quat_to_rotmatrix { +# my ($q) = @_; +# +# my @m = (); +# +# $m[0] = 1.0 - 2.0 * (@$q[1] * @$q[1] + @$q[2] * @$q[2]); +# $m[1] = 2.0 * (@$q[0] * @$q[1] - @$q[2] * @$q[3]); +# $m[2] = 2.0 * (@$q[2] * @$q[0] + @$q[1] * @$q[3]); +# $m[3] = 0.0; +# +# $m[4] = 2.0 * (@$q[0] * @$q[1] + @$q[2] * @$q[3]); +# $m[5] = 1.0 - 2.0 * (@$q[2] * @$q[2] + @$q[0] * @$q[0]); +# $m[6] = 2.0 * (@$q[1] * @$q[2] - @$q[0] * @$q[3]); +# $m[7] = 0.0; +# +# $m[8] = 2.0 * (@$q[2] * @$q[0] - @$q[1] * @$q[3]); +# $m[9] = 2.0 * (@$q[1] * @$q[2] + @$q[0] * @$q[3]); +# $m[10] = 1.0 - 2.0 * (@$q[1] * @$q[1] + @$q[0] * @$q[0]); +# $m[11] = 0.0; +# +# $m[12] = 0.0; +# $m[13] = 0.0; +# $m[14] = 0.0; +# $m[15] = 1.0; +# +# return @m; +#} +# +#sub mulquats { +# my ($q1, $rq) = @_; +# +# return (@$q1[3] * @$rq[0] + @$q1[0] * @$rq[3] + @$q1[1] * @$rq[2] - @$q1[2] * @$rq[1], +# @$q1[3] * @$rq[1] + @$q1[1] * @$rq[3] + @$q1[2] * @$rq[0] - @$q1[0] * @$rq[2], +# @$q1[3] * @$rq[2] + @$q1[2] * @$rq[3] + @$q1[0] * @$rq[1] - @$q1[1] * @$rq[0], +# @$q1[3] * @$rq[3] - @$q1[0] * @$rq[0] - @$q1[1] * @$rq[1] - @$q1[2] * @$rq[2]) +#} +# ## Convert the screen space coordinate to an object space coordinate. ## If the Z screen space coordinate is not provided, a depth buffer value is substituted. #sub mouse_to_3d { @@ -1196,20 +1189,14 @@ sub InitGL { $self->init(1); #============================================================================================================================== - Slic3r::GUI::_3DScene::init_gl; -#============================================================================================================================== - - # 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); - -#============================================================================================================================== - Slic3r::GUI::_3DScene::zoom_to_bed($self); - Slic3r::GUI::_3DScene::init($self, $self->UseVBOs); - +# +## # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized +## # first when an OpenGL widget is shown for the first time. How ugly. +## # In that case the volumes are wainting to be moved to Vertex Buffer Objects +## # after the OpenGL context is being initialized. +## $self->volumes->finalize_geometry(1) +## if ($^O eq 'linux' && $self->UseVBOs); +# # $self->zoom_to_bed; # # glClearColor(0, 0, 0, 1); @@ -1870,38 +1857,36 @@ sub Render { # # return $out; #} -#============================================================================================================================== - -sub _report_opengl_state -{ - my ($self, $comment) = @_; - my $err = glGetError(); - return 0 if ($err == 0); - - # gluErrorString() hangs. Don't use it. -# my $errorstr = gluErrorString(); - my $errorstr = ''; - if ($err == 0x0500) { - $errorstr = 'GL_INVALID_ENUM'; - } elsif ($err == GL_INVALID_VALUE) { - $errorstr = 'GL_INVALID_VALUE'; - } elsif ($err == GL_INVALID_OPERATION) { - $errorstr = 'GL_INVALID_OPERATION'; - } elsif ($err == GL_STACK_OVERFLOW) { - $errorstr = 'GL_STACK_OVERFLOW'; - } elsif ($err == GL_OUT_OF_MEMORY) { - $errorstr = 'GL_OUT_OF_MEMORY'; - } else { - $errorstr = 'unknown'; - } - if (defined($comment)) { - printf("OpenGL error at %s, nr %d (0x%x): %s\n", $comment, $err, $err, $errorstr); - } else { - printf("OpenGL error nr %d (0x%x): %s\n", $err, $err, $errorstr); - } -} - -#=================================================================================================================================== +# +#sub _report_opengl_state +#{ +# my ($self, $comment) = @_; +# my $err = glGetError(); +# return 0 if ($err == 0); +# +# # gluErrorString() hangs. Don't use it. +## my $errorstr = gluErrorString(); +# my $errorstr = ''; +# if ($err == 0x0500) { +# $errorstr = 'GL_INVALID_ENUM'; +# } elsif ($err == GL_INVALID_VALUE) { +# $errorstr = 'GL_INVALID_VALUE'; +# } elsif ($err == GL_INVALID_OPERATION) { +# $errorstr = 'GL_INVALID_OPERATION'; +# } elsif ($err == GL_STACK_OVERFLOW) { +# $errorstr = 'GL_STACK_OVERFLOW'; +# } elsif ($err == GL_OUT_OF_MEMORY) { +# $errorstr = 'GL_OUT_OF_MEMORY'; +# } else { +# $errorstr = 'unknown'; +# } +# if (defined($comment)) { +# printf("OpenGL error at %s, nr %d (0x%x): %s\n", $comment, $err, $err, $errorstr); +# } else { +# printf("OpenGL error nr %d (0x%x): %s\n", $err, $err, $errorstr); +# } +#} +# #sub _vertex_shader_Gouraud { # return <<'VERTEX'; ##version 110 diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 50913ae3a5..008324a621 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -185,7 +185,7 @@ GLCanvas3D::Camera::Camera() : type(Ortho) , zoom(1.0f) , phi(45.0f) - , distance(0.0f) +// , distance(0.0f) , target(0.0, 0.0, 0.0) , m_theta(45.0f) { @@ -198,8 +198,8 @@ std::string GLCanvas3D::Camera::get_type_as_string() const default: case Unknown: return "unknown"; - case Perspective: - return "perspective"; +// case Perspective: +// return "perspective"; case Ortho: return "ortho"; }; @@ -942,6 +942,8 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_config(nullptr) , m_print(nullptr) , m_dirty(true) + , m_use_VBOs(false) + , m_late_init(false) , m_apply_zoom_to_volumes_filter(false) , m_hover_volume_id(-1) , m_warning_texture_enabled(false) @@ -968,6 +970,9 @@ GLCanvas3D::~GLCanvas3D() bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) { + if (!set_current()) + return false; + ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f); ::glClearDepth(1.0f); @@ -990,16 +995,12 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) ::glEnable(GL_LIGHT1); // light from camera - GLfloat position[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; - ::glLightfv(GL_LIGHT1, GL_POSITION, position); GLfloat specular[4] = { 0.3f, 0.3f, 0.3f, 1.0f }; ::glLightfv(GL_LIGHT1, GL_SPECULAR, specular); GLfloat diffuse[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; ::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse); // light from above - GLfloat position1[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; - ::glLightfv(GL_LIGHT0, GL_POSITION, position1); GLfloat specular1[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; ::glLightfv(GL_LIGHT0, GL_SPECULAR, specular1); GLfloat diffuse1[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; @@ -1021,6 +1022,7 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (useVBOs && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) return false; + m_use_VBOs = useVBOs; m_layers_editing.set_use_legacy_opengl(!use_legacy_opengl); return true; @@ -1251,17 +1253,32 @@ void GLCanvas3D::update_volumes_colors_by_extruder() m_volumes->update_colors_by_extruder(m_config); } -void GLCanvas3D::render(bool useVBOs) const +void GLCanvas3D::render() { if (m_canvas == nullptr) return; + if (!is_shown_on_screen()) + return; + + if (!set_current()) + return; + + if (!m_late_init) + _late_init(); + _camera_tranform(); + + GLfloat position[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; + ::glLightfv(GL_LIGHT1, GL_POSITION, position); + GLfloat position1[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; + ::glLightfv(GL_LIGHT0, GL_POSITION, position1); + _picking_pass(); _render_background(); _render_bed(); _render_axes(); - _render_objects(useVBOs); + _render_objects(); _render_cutting_plane(); _render_warning_texture(); _render_legend_texture(); @@ -1677,6 +1694,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) evt.Skip(); } +void GLCanvas3D::on_paint(wxPaintEvent& evt) +{ + render(); +} + Size GLCanvas3D::get_canvas_size() const { int w = 0; @@ -1697,6 +1719,24 @@ Point GLCanvas3D::get_local_mouse_position() const return Point(mouse_pos.x, mouse_pos.y); } +void GLCanvas3D::_late_init() +{ + // 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. +#if defined(__LINUX__) + if (() && m_use_VBOs && (m_volumes != nullptr) + { + m_volumes->finalize_geometry(m_use_VBOs); + if ($^O eq 'linux' && $self->UseVBOs); + } +#endif // __LINUX__ + + zoom_to_bed(); + m_late_init = true; +} + void GLCanvas3D::_resize(unsigned int w, unsigned int h) { if (m_context == nullptr) @@ -1730,28 +1770,28 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) break; } - case Camera::Perspective: - { - float bbox_r = (float)bbox.radius(); - float fov = PI * 45.0f / 180.0f; - float fov_tan = tan(0.5f * fov); - float cam_distance = 0.5f * bbox_r / fov_tan; - m_camera.distance = cam_distance; - - float nr = cam_distance - bbox_r * 1.1f; - float fr = cam_distance + bbox_r * 1.1f; - if (nr < 1.0f) - nr = 1.0f; - - if (fr < nr + 1.0f) - fr = nr + 1.0f; - - float h2 = fov_tan * nr; - float w2 = h2 * w / h; - ::glFrustum(-w2, w2, -h2, h2, nr, fr); - - break; - } +// case Camera::Perspective: +// { +// float bbox_r = (float)bbox.radius(); +// float fov = PI * 45.0f / 180.0f; +// float fov_tan = tan(0.5f * fov); +// float cam_distance = 0.5f * bbox_r / fov_tan; +// m_camera.distance = cam_distance; +// +// float nr = cam_distance - bbox_r * 1.1f; +// float fr = cam_distance + bbox_r * 1.1f; +// if (nr < 1.0f) +// nr = 1.0f; +// +// if (fr < nr + 1.0f) +// fr = nr + 1.0f; +// +// float h2 = fov_tan * nr; +// float w2 = h2 * w / h; +// ::glFrustum(-w2, w2, -h2, h2, nr, fr); +// +// break; +// } default: { throw std::runtime_error("Invalid camera type."); @@ -2008,7 +2048,7 @@ void GLCanvas3D::_render_axes() const m_axes.render(); } -void GLCanvas3D::_render_objects(bool useVBOs) const +void GLCanvas3D::_render_objects() const { if ((m_volumes == nullptr) || m_volumes->empty()) return; @@ -2017,7 +2057,7 @@ void GLCanvas3D::_render_objects(bool useVBOs) const if (!m_shader_enabled) _render_volumes(false); - else if (useVBOs) + else if (m_use_VBOs) { if (m_picking_enabled) { diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 1caf69c0c5..8e2cc1d0a3 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -13,6 +13,7 @@ class wxIdleEvent; class wxKeyEvent; class wxMouseEvent; class wxTimerEvent; +class wxPaintEvent; namespace Slic3r { @@ -86,7 +87,7 @@ public: enum EType : unsigned char { Unknown, - Perspective, +// Perspective, Ortho, Num_types }; @@ -94,7 +95,7 @@ public: EType type; float zoom; float phi; - float distance; +// float distance; Pointf3 target; private: @@ -301,6 +302,8 @@ private: Print* m_print; bool m_dirty; + bool m_use_VBOs; + bool m_late_init; bool m_apply_zoom_to_volumes_filter; mutable int m_hover_volume_id; bool m_warning_texture_enabled; @@ -369,7 +372,7 @@ public: void update_volumes_colors_by_extruder(); - void render(bool useVBOs) const; + void render(); void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; void register_on_viewport_changed_callback(void* callback); @@ -385,11 +388,14 @@ public: void on_mouse_wheel(wxMouseEvent& evt); void on_timer(wxTimerEvent& evt); void on_mouse(wxMouseEvent& evt); + void on_paint(wxPaintEvent& evt); Size get_canvas_size() const; Point get_local_mouse_position() const; private: + void _late_init(); + void _resize(unsigned int w, unsigned int h); BoundingBoxf3 _max_bounding_box() const; @@ -407,7 +413,7 @@ private: void _render_background() const; void _render_bed() const; void _render_axes() const; - void _render_objects(bool useVBOs) const; + void _render_objects() const; void _render_cutting_plane() const; void _render_warning_texture() const; void _render_legend_texture() const; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 10f8252019..4a35d61fd2 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -138,6 +138,19 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) if (canvas3D == nullptr) return false; + if (!m_gl_initialized) + { + canvas3D->set_current(); + init_gl(); + } + + if (!canvas3D->init(m_use_VBOs, m_use_legacy_opengl)) + { + delete canvas3D; + canvas3D = nullptr; + return false; + } + canvas->Bind(wxEVT_SIZE, [canvas3D](wxSizeEvent& evt) { canvas3D->on_size(evt); }); canvas->Bind(wxEVT_IDLE, [canvas3D](wxIdleEvent& evt) { canvas3D->on_idle(evt); }); canvas->Bind(wxEVT_CHAR, [canvas3D](wxKeyEvent& evt) { canvas3D->on_char(evt); }); @@ -155,6 +168,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) canvas->Bind(wxEVT_LEFT_DCLICK, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); canvas->Bind(wxEVT_MIDDLE_DCLICK, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); canvas->Bind(wxEVT_RIGHT_DCLICK, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); + canvas->Bind(wxEVT_PAINT, [canvas3D](wxPaintEvent& evt) { canvas3D->on_paint(evt); }); m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); @@ -417,7 +431,7 @@ void GLCanvas3DManager::render(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); if (it != m_canvases.end()) - it->second->render(m_use_VBOs); + it->second->render(); } void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) From 676210d6f416e30516e7641bdec2f0038dda00c8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Jun 2018 12:30:40 +0200 Subject: [PATCH 053/117] Fixed typo --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 008324a621..892fa5bc55 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1726,7 +1726,7 @@ void GLCanvas3D::_late_init() // In that case the volumes are wainting to be moved to Vertex Buffer Objects // after the OpenGL context is being initialized. #if defined(__LINUX__) - if (() && m_use_VBOs && (m_volumes != nullptr) + if (m_use_VBOs && (m_volumes != nullptr) { m_volumes->finalize_geometry(m_use_VBOs); if ($^O eq 'linux' && $self->UseVBOs); From adca3035f9e053f970a95f22ae7f2875d8e90833 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Jun 2018 12:32:23 +0200 Subject: [PATCH 054/117] Fixed Linux compile --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 892fa5bc55..dbf7c76547 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1726,11 +1726,8 @@ void GLCanvas3D::_late_init() // In that case the volumes are wainting to be moved to Vertex Buffer Objects // after the OpenGL context is being initialized. #if defined(__LINUX__) - if (m_use_VBOs && (m_volumes != nullptr) - { - m_volumes->finalize_geometry(m_use_VBOs); - if ($^O eq 'linux' && $self->UseVBOs); - } + if (m_use_VBOs && (m_volumes != nullptr)) + m_volumes->finalize_geometry(m_use_VBOs); #endif // __LINUX__ zoom_to_bed(); From ac47ba5864133156fa2db78de592079929135416 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Jun 2018 13:15:28 +0200 Subject: [PATCH 055/117] 1st attempt to fix opengl initialization on linux --- xs/src/slic3r/GUI/3DScene.cpp | 11 +++- xs/src/slic3r/GUI/3DScene.hpp | 5 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 67 ++++++++++++++++++------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 10 +++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 54 ++++++++++++++------ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 9 +++- xs/xsp/GUI_3DScene.xsp | 14 ------ 7 files changed, 119 insertions(+), 51 deletions(-) diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 328e7dcd5b..fc609caac6 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1757,10 +1757,17 @@ void _3DScene::remove_all_canvases() std::cout << "# canvases not yet released: " << s_canvas_mgr.count() << std::endl; s_canvas_mgr.remove_all(); } -bool _3DScene::init(wxGLCanvas* canvas, bool useVBOs) + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +bool _3DScene::init(wxGLCanvas* canvas) { - return s_canvas_mgr.init(canvas, useVBOs); + return s_canvas_mgr.init(canvas); } +//bool _3DScene::init(wxGLCanvas* canvas, bool useVBOs) +//{ +// return s_canvas_mgr.init(canvas, useVBOs); +//} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool _3DScene::is_shown_on_screen(wxGLCanvas* canvas) { diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 1ab958d9e5..266c48ce5a 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -545,7 +545,10 @@ public: static bool remove_canvas(wxGLCanvas* canvas); static void remove_all_canvases(); - static bool init(wxGLCanvas* canvas, bool useVBOs); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + static bool init(wxGLCanvas* canvas); +// static bool init(wxGLCanvas* canvas, bool useVBOs); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static bool is_shown_on_screen(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index dbf7c76547..e4db61072d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -575,7 +575,7 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, bool GLCanvas3D::LayersEditing::is_allowed() const { - return m_use_legacy_opengl && m_shader.is_initialized(); + return !m_use_legacy_opengl && m_shader.is_initialized(); } void GLCanvas3D::LayersEditing::set_use_legacy_opengl(bool use_legacy_opengl) @@ -943,7 +943,10 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_print(nullptr) , m_dirty(true) , m_use_VBOs(false) - , m_late_init(false) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + , m_first_render(true) +// , m_late_init(false) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ , m_apply_zoom_to_volumes_filter(false) , m_hover_volume_id(-1) , m_warning_texture_enabled(false) @@ -970,8 +973,10 @@ GLCanvas3D::~GLCanvas3D() bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) { - if (!set_current()) - return false; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// if (!set_current()) +// return false; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f); ::glClearDepth(1.0f); @@ -1022,8 +1027,19 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (useVBOs && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) return false; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // 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. +#if defined(__LINUX__) + if (use_VBOs && (m_volumes != nullptr)) + m_volumes->finalize_geometry(use_VBOs); +#endif // __LINUX__ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_use_VBOs = useVBOs; - m_layers_editing.set_use_legacy_opengl(!use_legacy_opengl); + m_layers_editing.set_use_legacy_opengl(use_legacy_opengl); return true; } @@ -1264,8 +1280,17 @@ void GLCanvas3D::render() if (!set_current()) return; - if (!m_late_init) - _late_init(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (!_3DScene::init(m_canvas)) + return; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (m_first_render) + _before_first_render(); +// if (!m_late_init) +// _late_init(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ _camera_tranform(); @@ -1719,19 +1744,27 @@ Point GLCanvas3D::get_local_mouse_position() const return Point(mouse_pos.x, mouse_pos.y); } -void GLCanvas3D::_late_init() +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void GLCanvas3D::_before_first_render() +//void GLCanvas3D::_late_init() +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { - // 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. -#if defined(__LINUX__) - if (m_use_VBOs && (m_volumes != nullptr)) - m_volumes->finalize_geometry(m_use_VBOs); -#endif // __LINUX__ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// // 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. +//#if defined(__LINUX__) +// if (m_use_VBOs && (m_volumes != nullptr)) +// m_volumes->finalize_geometry(m_use_VBOs); +//#endif // __LINUX__ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ zoom_to_bed(); - m_late_init = true; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_first_render = false; +// m_late_init = true; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::_resize(unsigned int w, unsigned int h) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 8e2cc1d0a3..f718273274 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -303,7 +303,10 @@ private: bool m_dirty; bool m_use_VBOs; - bool m_late_init; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + bool m_first_render; +// bool m_late_init; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool m_apply_zoom_to_volumes_filter; mutable int m_hover_volume_id; bool m_warning_texture_enabled; @@ -394,7 +397,10 @@ public: Point get_local_mouse_position() const; private: - void _late_init(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void _before_first_render(); +// void _late_init(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _resize(unsigned int w, unsigned int h); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 4a35d61fd2..85caa24769 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -138,18 +138,20 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) if (canvas3D == nullptr) return false; - if (!m_gl_initialized) - { - canvas3D->set_current(); - init_gl(); - } - - if (!canvas3D->init(m_use_VBOs, m_use_legacy_opengl)) - { - delete canvas3D; - canvas3D = nullptr; - return false; - } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// if (!m_gl_initialized) +// { +// canvas3D->set_current(); +// init_gl(); +// } +// +// if (!canvas3D->init(m_use_VBOs, m_use_legacy_opengl)) +// { +// delete canvas3D; +// canvas3D = nullptr; +// return false; +// } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ canvas->Bind(wxEVT_SIZE, [canvas3D](wxSizeEvent& evt) { canvas3D->on_size(evt); }); canvas->Bind(wxEVT_IDLE, [canvas3D](wxIdleEvent& evt) { canvas3D->on_idle(evt); }); @@ -239,12 +241,23 @@ bool GLCanvas3DManager::use_VBOs() const return m_use_VBOs; } -bool GLCanvas3DManager::init(wxGLCanvas* canvas, bool useVBOs) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +bool GLCanvas3DManager::init(wxGLCanvas* canvas) { CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->init(useVBOs, m_use_legacy_opengl) : false; + if (it != m_canvases.end()) + return (it->second != nullptr) ? _init(*it->second) : false; + else + return false; } +//bool GLCanvas3DManager::init(wxGLCanvas* canvas, bool useVBOs) +//{ +// CanvasesMap::const_iterator it = _get_canvas(canvas); +// return (it != m_canvases.end()) ? it->second->init(useVBOs, m_use_legacy_opengl) : false; +//} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + bool GLCanvas3DManager::is_shown_on_screen(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -486,5 +499,18 @@ GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::_get_canvas(wx return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +bool GLCanvas3DManager::_init(GLCanvas3D& canvas) +{ + if (!m_gl_initialized) + { +// canvas.set_current(); + init_gl(); + } + + return canvas.init(m_use_VBOs, m_use_legacy_opengl); +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 1a8689c810..1ebf39320b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -49,7 +49,10 @@ public: bool use_VBOs() const; bool layer_editing_allowed() const; - bool init(wxGLCanvas* canvas, bool useVBOs); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + bool init(wxGLCanvas* canvas); +// bool init(wxGLCanvas* canvas, bool useVBOs); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool is_shown_on_screen(wxGLCanvas* canvas) const; @@ -100,6 +103,10 @@ public: private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const; + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + bool _init(GLCanvas3D& canvas); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ }; } // namespace GUI diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 862b9645c0..ac1ae1420e 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -156,11 +156,6 @@ GLVolumeCollection::arrayref() %package{Slic3r::GUI::_3DScene}; %{ -void -init_gl() - CODE: - _3DScene::init_gl(); - std::string get_gl_info(format_as_html, extensions) bool format_as_html; @@ -199,15 +194,6 @@ remove_all_canvases() CODE: _3DScene::remove_all_canvases(); -bool -init(canvas, useVBOs) - SV *canvas; - bool useVBOs; - CODE: - RETVAL = _3DScene::init((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), useVBOs); - OUTPUT: - RETVAL - bool is_shown_on_screen(canvas) SV *canvas; From fa60917580fb5375366aae432c4e9313a9243c65 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Jun 2018 13:18:04 +0200 Subject: [PATCH 056/117] Fixed Linux compile --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index e4db61072d..72d27a00ac 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1033,8 +1033,8 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) // In that case the volumes are wainting to be moved to Vertex Buffer Objects // after the OpenGL context is being initialized. #if defined(__LINUX__) - if (use_VBOs && (m_volumes != nullptr)) - m_volumes->finalize_geometry(use_VBOs); + if (useVBOs && (m_volumes != nullptr)) + m_volumes->finalize_geometry(useVBOs); #endif // __LINUX__ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ From 548f773074239aa318bef613aca27e1a1fff0cd7 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Jun 2018 13:52:57 +0200 Subject: [PATCH 057/117] 2nd attempt to fix opengl initialization on linux --- lib/Slic3r/GUI/3DScene.pm | 111 ++++++++++++++++++++++-------------- lib/Slic3r/GUI/Plater/3D.pm | 5 +- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index c13cd48adf..e787fb5a13 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1102,13 +1102,10 @@ sub SetCurrent { return $self->SUPER::SetCurrent($context); } -sub UseVBOs { - my ($self) = @_; - #============================================================================================================================== - return 0 if (! $self->init && $^O eq 'linux'); - return Slic3r::GUI::_3DScene::use_VBOs(); - +#sub UseVBOs { +# my ($self) = @_; +# # if (! defined ($self->{use_VBOs})) { # my $use_legacy = wxTheApp->{app_config}->get('use_legacy_opengl'); # if ($use_legacy eq '1') { @@ -1134,10 +1131,8 @@ sub UseVBOs { # } # } # return $self->{use_VBOs}; -#============================================================================================================================== -} - -#============================================================================================================================== +#} +# #sub Resize { # my ($self, $x, $y) = @_; # @@ -1179,16 +1174,13 @@ sub UseVBOs { # } # glMatrixMode(GL_MODELVIEW); #} -#============================================================================================================================== - -sub InitGL { - my $self = shift; - - return if $self->init; - return unless $self->GetContext; - $self->init(1); - -#============================================================================================================================== +# +#sub InitGL { +# my $self = shift; +# +# return if $self->init; +# return unless $self->GetContext; +# $self->init(1); # ## # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized ## # first when an OpenGL widget is shown for the first time. How ugly. @@ -1255,8 +1247,8 @@ sub InitGL { # $self->{plain_shader} = $shader; # } # } +#} #=================================================================================================================================== -} sub DestroyGL { my $self = shift; @@ -1278,19 +1270,17 @@ sub DestroyGL { sub Render { my ($self, $dc) = @_; - - # prevent calling SetCurrent() when window is not shown yet -#============================================================================================================================== - return unless Slic3r::GUI::_3DScene::is_shown_on_screen($self); -# return unless $self->IsShownOnScreen; -#============================================================================================================================== - return unless my $context = $self->GetContext; - $self->SetCurrent($context); - $self->InitGL; - + #============================================================================================================================== Slic3r::GUI::_3DScene::render($self); - + +# # prevent calling SetCurrent() when window is not shown yet +# return unless Slic3r::GUI::_3DScene::is_shown_on_screen($self); +## return unless $self->IsShownOnScreen; +# return unless my $context = $self->GetContext; +# $self->SetCurrent($context); +# $self->InitGL; +# # glClearColor(1, 1, 1, 1); # glClearDepth(1); # glDepthFunc(GL_LESS); @@ -2185,7 +2175,12 @@ sub new { sub load_object { my ($self, $model, $print, $obj_idx, $instance_idxs) = @_; - $self->SetCurrent($self->GetContext) if $self->UseVBOs; +#============================================================================================================================== + my $useVBOs = Slic3r::GUI::_3DScene::use_VBOs(); + $self->SetCurrent($self->GetContext) if $useVBOs; + +# $self->SetCurrent($self->GetContext) if $useVBOs; +#============================================================================================================================== my $model_object; if ($model->isa('Slic3r::Model::Object')) { @@ -2197,9 +2192,13 @@ sub load_object { } $instance_idxs ||= [0..$#{$model_object->instances}]; +#============================================================================================================================== my $volume_indices = $self->volumes->load_object( - $model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by, - $self->UseVBOs); + $model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by, $useVBOs); +# my $volume_indices = $self->volumes->load_object( +# $model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by, +# $self->UseVBOs); +#============================================================================================================================== return @{$volume_indices}; } @@ -2208,9 +2207,16 @@ sub load_object { sub load_print_toolpaths { my ($self, $print, $colors) = @_; - $self->SetCurrent($self->GetContext) if $self->UseVBOs; - Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes, $colors, $self->UseVBOs) +#============================================================================================================================== + my $useVBOs = Slic3r::GUI::_3DScene::use_VBOs(); + $self->SetCurrent($self->GetContext) if $useVBOs; + Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes, $colors, $useVBOs) if ($print->step_done(STEP_SKIRT) && $print->step_done(STEP_BRIM)); + +# $self->SetCurrent($self->GetContext) if $self->UseVBOs; +# Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes, $colors, $self->UseVBOs) +# if ($print->step_done(STEP_SKIRT) && $print->step_done(STEP_BRIM)); +#============================================================================================================================== } # Create 3D thick extrusion lines for object forming extrusions. @@ -2219,24 +2225,43 @@ sub load_print_toolpaths { sub load_print_object_toolpaths { my ($self, $object, $colors) = @_; - $self->SetCurrent($self->GetContext) if $self->UseVBOs; - Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $colors, $self->UseVBOs); +#============================================================================================================================== + my $useVBOs = Slic3r::GUI::_3DScene::use_VBOs(); + $self->SetCurrent($self->GetContext) if $useVBOs; + Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $colors, $useVBOs); + +# $self->SetCurrent($self->GetContext) if $self->UseVBOs; +# Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $colors, $self->UseVBOs); +#============================================================================================================================== } # Create 3D thick extrusion lines for wipe tower extrusions. sub load_wipe_tower_toolpaths { my ($self, $print, $colors) = @_; - $self->SetCurrent($self->GetContext) if $self->UseVBOs; - Slic3r::GUI::_3DScene::_load_wipe_tower_toolpaths($print, $self->volumes, $colors, $self->UseVBOs) +#============================================================================================================================== + my $useVBOs = Slic3r::GUI::_3DScene::use_VBOs(); + $self->SetCurrent($self->GetContext) if $useVBOs; + Slic3r::GUI::_3DScene::_load_wipe_tower_toolpaths($print, $self->volumes, $colors, $useVBOs) if ($print->step_done(STEP_WIPE_TOWER)); + +# $self->SetCurrent($self->GetContext) if $self->UseVBOs; +# Slic3r::GUI::_3DScene::_load_wipe_tower_toolpaths($print, $self->volumes, $colors, $self->UseVBOs) +# if ($print->step_done(STEP_WIPE_TOWER)); +#============================================================================================================================== } sub load_gcode_preview { my ($self, $print, $gcode_preview_data, $colors) = @_; - $self->SetCurrent($self->GetContext) if $self->UseVBOs; - Slic3r::GUI::_3DScene::load_gcode_preview($print, $gcode_preview_data, $self->volumes, $colors, $self->UseVBOs); +#============================================================================================================================== + my $useVBOs = Slic3r::GUI::_3DScene::use_VBOs(); + $self->SetCurrent($self->GetContext) if $useVBOs; + Slic3r::GUI::_3DScene::load_gcode_preview($print, $gcode_preview_data, $self->volumes, $colors, $useVBOs); + +# $self->SetCurrent($self->GetContext) if $self->UseVBOs; +# Slic3r::GUI::_3DScene::load_gcode_preview($print, $gcode_preview_data, $self->volumes, $colors, $self->UseVBOs); +#============================================================================================================================== } sub set_toolpaths_range { diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index f630908357..7740275408 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -252,7 +252,10 @@ sub reload_scene { $self->{config}->wipe_tower_x, $self->{config}->wipe_tower_y, $self->{config}->wipe_tower_width, #$self->{config}->wipe_tower_per_color_wipe# 15 * ($extruders_count - 1), # this is just a hack when the config parameter became obsolete 15 * ($extruders_count - 1), - $self->{model}->bounding_box->z_max, $self->{config}->wipe_tower_rotation_angle, $self->UseVBOs); +#============================================================================================================================== + $self->{model}->bounding_box->z_max, $self->{config}->wipe_tower_rotation_angle, Slic3r::GUI::_3DScene::use_VBOs()); +# $self->{model}->bounding_box->z_max, $self->{config}->wipe_tower_rotation_angle, $self->UseVBOs); +#============================================================================================================================== } } From af8e86988063f3beb9735ce50cdb6ad4df0563c7 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Jun 2018 14:28:59 +0200 Subject: [PATCH 058/117] 3rd attempt to fix opengl initialization on linux --- lib/Slic3r/GUI/3DScene.pm | 2 +- xs/src/slic3r/GUI/3DScene.cpp | 5 +++++ xs/src/slic3r/GUI/3DScene.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 5 +++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 1 + xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 6 ++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 1 + xs/xsp/GUI_3DScene.xsp | 8 ++++++++ 8 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index e787fb5a13..b8a45dd46e 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -2194,7 +2194,7 @@ sub load_object { $instance_idxs ||= [0..$#{$model_object->instances}]; #============================================================================================================================== my $volume_indices = $self->volumes->load_object( - $model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by, $useVBOs); + $model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by, $useVBOs && Slic3r::GUI::_3DScene::is_shader_enabled($self)); # my $volume_indices = $self->volumes->load_object( # $model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by, # $self->UseVBOs); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index fc609caac6..e03ed3cc34 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1839,6 +1839,11 @@ bool _3DScene::is_layers_editing_allowed(wxGLCanvas* canvas) return s_canvas_mgr.is_layers_editing_allowed(canvas); } +bool _3DScene::is_shader_enabled(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_shader_enabled(canvas); +} + void _3DScene::enable_layers_editing(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_layers_editing(canvas, enable); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 266c48ce5a..06500cf338 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -571,6 +571,7 @@ public: static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_layers_editing_allowed(wxGLCanvas* canvas); + static bool is_shader_enabled(wxGLCanvas* canvas); static void enable_layers_editing(wxGLCanvas* canvas, bool enable); static void enable_warning_texture(wxGLCanvas* canvas, bool enable); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 72d27a00ac..8a4bdd9a29 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1175,6 +1175,11 @@ bool GLCanvas3D::is_layers_editing_allowed() const return m_layers_editing.is_allowed(); } +bool GLCanvas3D::is_shader_enabled() const +{ + return m_shader_enabled; +} + void GLCanvas3D::enable_layers_editing(bool enable) { m_layers_editing.set_enabled(enable); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index f718273274..cb6ed64e92 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -359,6 +359,7 @@ public: bool is_layers_editing_enabled() const; bool is_layers_editing_allowed() const; + bool is_shader_enabled() const; void enable_layers_editing(bool enable); void enable_warning_texture(bool enable); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 85caa24769..1dd679c0cf 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -352,6 +352,12 @@ bool GLCanvas3DManager::is_layers_editing_allowed(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_layers_editing_allowed() : false; } +bool GLCanvas3DManager::is_shader_enabled(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_shader_enabled() : false; +} + void GLCanvas3DManager::enable_layers_editing(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 1ebf39320b..6739b03dfa 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -75,6 +75,7 @@ public: bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_layers_editing_allowed(wxGLCanvas* canvas) const; + bool is_shader_enabled(wxGLCanvas* canvas) const; void enable_layers_editing(wxGLCanvas* canvas, bool enable); void enable_warning_texture(wxGLCanvas* canvas, bool enable); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index ac1ae1420e..60c86eaa2f 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -294,6 +294,14 @@ is_layers_editing_allowed(canvas) OUTPUT: RETVAL +bool +is_shader_enabled(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_shader_enabled((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + void enable_layers_editing(canvas, enable) SV *canvas; From 9729c71691741e56559dfcd663270470e96e1f8d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Jun 2018 14:38:41 +0200 Subject: [PATCH 059/117] Fixed opengl initialization on linux --- xs/src/slic3r/GUI/3DScene.cpp | 6 ---- xs/src/slic3r/GUI/3DScene.hpp | 3 -- xs/src/slic3r/GUI/GLCanvas3D.cpp | 42 ------------------------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 6 ---- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 28 ----------------- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 5 --- 6 files changed, 90 deletions(-) diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index e03ed3cc34..eed1e8a1c2 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1758,16 +1758,10 @@ void _3DScene::remove_all_canvases() s_canvas_mgr.remove_all(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool _3DScene::init(wxGLCanvas* canvas) { return s_canvas_mgr.init(canvas); } -//bool _3DScene::init(wxGLCanvas* canvas, bool useVBOs) -//{ -// return s_canvas_mgr.init(canvas, useVBOs); -//} -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool _3DScene::is_shown_on_screen(wxGLCanvas* canvas) { diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 06500cf338..df395bccbd 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -545,10 +545,7 @@ public: static bool remove_canvas(wxGLCanvas* canvas); static void remove_all_canvases(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static bool init(wxGLCanvas* canvas); -// static bool init(wxGLCanvas* canvas, bool useVBOs); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static bool is_shown_on_screen(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 8a4bdd9a29..240581aec7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -943,10 +943,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_print(nullptr) , m_dirty(true) , m_use_VBOs(false) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ , m_first_render(true) -// , m_late_init(false) -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ , m_apply_zoom_to_volumes_filter(false) , m_hover_volume_id(-1) , m_warning_texture_enabled(false) @@ -973,11 +970,6 @@ GLCanvas3D::~GLCanvas3D() bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// if (!set_current()) -// return false; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f); ::glClearDepth(1.0f); @@ -1027,17 +1019,6 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (useVBOs && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) return false; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - // 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. -#if defined(__LINUX__) - if (useVBOs && (m_volumes != nullptr)) - m_volumes->finalize_geometry(useVBOs); -#endif // __LINUX__ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - m_use_VBOs = useVBOs; m_layers_editing.set_use_legacy_opengl(use_legacy_opengl); @@ -1285,17 +1266,11 @@ void GLCanvas3D::render() if (!set_current()) return; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (!_3DScene::init(m_canvas)) return; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_first_render) _before_first_render(); -// if (!m_late_init) -// _late_init(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ _camera_tranform(); @@ -1749,27 +1724,10 @@ Point GLCanvas3D::get_local_mouse_position() const return Point(mouse_pos.x, mouse_pos.y); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GLCanvas3D::_before_first_render() -//void GLCanvas3D::_late_init() -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// // 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. -//#if defined(__LINUX__) -// if (m_use_VBOs && (m_volumes != nullptr)) -// m_volumes->finalize_geometry(m_use_VBOs); -//#endif // __LINUX__ -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - zoom_to_bed(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_first_render = false; -// m_late_init = true; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::_resize(unsigned int w, unsigned int h) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index cb6ed64e92..fe27de0b2c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -303,10 +303,7 @@ private: bool m_dirty; bool m_use_VBOs; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool m_first_render; -// bool m_late_init; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool m_apply_zoom_to_volumes_filter; mutable int m_hover_volume_id; bool m_warning_texture_enabled; @@ -398,10 +395,7 @@ public: Point get_local_mouse_position() const; private: -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _before_first_render(); -// void _late_init(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _resize(unsigned int w, unsigned int h); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 1dd679c0cf..a136f47662 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -138,21 +138,6 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) if (canvas3D == nullptr) return false; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// if (!m_gl_initialized) -// { -// canvas3D->set_current(); -// init_gl(); -// } -// -// if (!canvas3D->init(m_use_VBOs, m_use_legacy_opengl)) -// { -// delete canvas3D; -// canvas3D = nullptr; -// return false; -// } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - canvas->Bind(wxEVT_SIZE, [canvas3D](wxSizeEvent& evt) { canvas3D->on_size(evt); }); canvas->Bind(wxEVT_IDLE, [canvas3D](wxIdleEvent& evt) { canvas3D->on_idle(evt); }); canvas->Bind(wxEVT_CHAR, [canvas3D](wxKeyEvent& evt) { canvas3D->on_char(evt); }); @@ -241,7 +226,6 @@ bool GLCanvas3DManager::use_VBOs() const return m_use_VBOs; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GLCanvas3DManager::init(wxGLCanvas* canvas) { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -251,13 +235,6 @@ bool GLCanvas3DManager::init(wxGLCanvas* canvas) return false; } -//bool GLCanvas3DManager::init(wxGLCanvas* canvas, bool useVBOs) -//{ -// CanvasesMap::const_iterator it = _get_canvas(canvas); -// return (it != m_canvases.end()) ? it->second->init(useVBOs, m_use_legacy_opengl) : false; -//} -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - bool GLCanvas3DManager::is_shown_on_screen(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); @@ -505,18 +482,13 @@ GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::_get_canvas(wx return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GLCanvas3DManager::_init(GLCanvas3D& canvas) { if (!m_gl_initialized) - { -// canvas.set_current(); init_gl(); - } return canvas.init(m_use_VBOs, m_use_legacy_opengl); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 6739b03dfa..dd8f965d68 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -49,10 +49,7 @@ public: bool use_VBOs() const; bool layer_editing_allowed() const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool init(wxGLCanvas* canvas); -// bool init(wxGLCanvas* canvas, bool useVBOs); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool is_shown_on_screen(wxGLCanvas* canvas) const; @@ -105,9 +102,7 @@ private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool _init(GLCanvas3D& canvas); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ }; } // namespace GUI From d74b85f3fe7d9cf52d94b483d67e978057798539 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Jun 2018 15:42:34 +0200 Subject: [PATCH 060/117] Another set of 3DScene methods moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 53 +++++++++++------------ lib/Slic3r/GUI/Plater.pm | 29 ++++++------- lib/Slic3r/GUI/Plater/3D.pm | 12 +++-- lib/Slic3r/GUI/Plater/3DPreview.pm | 30 ++++++++++--- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 3 +- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 23 +++++++--- xs/src/slic3r/GUI/3DScene.cpp | 10 +++++ xs/src/slic3r/GUI/3DScene.hpp | 3 ++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 11 +++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 ++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 13 ++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 3 ++ xs/xsp/GUI_3DScene.xsp | 20 +++++++-- 13 files changed, 151 insertions(+), 62 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index b8a45dd46e..204e19d3d3 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -24,8 +24,11 @@ use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); use base qw(Wx::GLCanvas Class::Accessor); use Math::Trig qw(asin tan); use List::Util qw(reduce min max first); -use Slic3r::Geometry qw(X Y normalize scale unscale scaled_epsilon); -use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl JT_ROUND); +#============================================================================================================================== +use Slic3r::Geometry qw(X Y); +#use Slic3r::Geometry qw(X Y normalize scale unscale scaled_epsilon); +#use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl JT_ROUND); +#============================================================================================================================== use Wx::GLCanvas qw(:all); use Slic3r::Geometry qw(PI); @@ -33,9 +36,7 @@ use Slic3r::Geometry qw(PI); # volumes: reference to vector of Slic3r::GUI::3DScene::Volume. # _camera_type: 'perspective' or 'ortho' #============================================================================================================================== -__PACKAGE__->mk_accessors( qw(init - on_viewport_changed - on_select +__PACKAGE__->mk_accessors( qw( volumes ) ); #__PACKAGE__->mk_accessors( qw(_quat _dirty init @@ -1268,12 +1269,10 @@ sub DestroyGL { } } -sub Render { - my ($self, $dc) = @_; - #============================================================================================================================== - Slic3r::GUI::_3DScene::render($self); - +#sub Render { +# my ($self, $dc) = @_; +# # # prevent calling SetCurrent() when window is not shown yet # return unless Slic3r::GUI::_3DScene::is_shown_on_screen($self); ## return unless $self->IsShownOnScreen; @@ -1497,10 +1496,8 @@ sub Render { # $self->draw_active_object_annotations; # # $self->SwapBuffers(); -#============================================================================================================================== -} - -#============================================================================================================================== +#} +# #sub draw_volumes { # # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. # my ($self, $fakecolor) = @_; @@ -2264,18 +2261,20 @@ sub load_gcode_preview { #============================================================================================================================== } -sub set_toolpaths_range { - my ($self, $min_z, $max_z) = @_; - $self->volumes->set_range($min_z, $max_z); -} - -sub reset_legend_texture { - Slic3r::GUI::_3DScene::reset_legend_texture(); -} - -sub get_current_print_zs { - my ($self, $active_only) = @_; - return $self->volumes->get_current_print_zs($active_only); -} +#============================================================================================================================== +#sub set_toolpaths_range { +# my ($self, $min_z, $max_z) = @_; +# $self->volumes->set_range($min_z, $max_z); +#} +# +#sub reset_legend_texture { +# Slic3r::GUI::_3DScene::reset_legend_texture(); +#} +# +#sub get_current_print_zs { +# my ($self, $active_only) = @_; +# return $self->volumes->get_current_print_zs($active_only); +#} +#============================================================================================================================== 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 2ff798899a..fafd5da7c1 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -152,6 +152,8 @@ sub new { $self->{"print_info_box_show"}->(0); } }); + + Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); # $self->{canvas3D}->set_on_model_update(sub { # if (wxTheApp->{app_config}->get("background_processing")) { @@ -161,15 +163,9 @@ sub new { # $self->{"print_info_box_show"}->(0); # } # }); -#============================================================================================================================== - $self->{canvas3D}->on_viewport_changed(sub { -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); +# $self->{canvas3D}->on_viewport_changed(sub { # $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); -#============================================================================================================================== - }); -#============================================================================================================================== - Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); +# }); #============================================================================================================================== } @@ -184,14 +180,11 @@ sub new { # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); - $self->{preview3D}->canvas->on_viewport_changed(sub { -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); -# $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); -#============================================================================================================================== - }); #============================================================================================================================== Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); }); +# $self->{preview3D}->canvas->on_viewport_changed(sub { +# $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); +# }); #============================================================================================================================== $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview')); $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; @@ -1900,9 +1893,10 @@ sub list_item_deselected { $self->{canvas}->Refresh; #============================================================================================================================== Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}) if $self->{canvas3D}; + Slic3r::GUI::_3DScene::render($self->{canvas3D}) if $self->{canvas3D}; # $self->{canvas3D}->deselect_volumes if $self->{canvas3D}; +# $self->{canvas3D}->Render if $self->{canvas3D}; #============================================================================================================================== - $self->{canvas3D}->Render if $self->{canvas3D}; } undef $self->{_lecursor}; } @@ -1915,7 +1909,10 @@ sub list_item_selected { $self->select_object($obj_idx); $self->{canvas}->Refresh; $self->{canvas3D}->update_volumes_selection if $self->{canvas3D}; - $self->{canvas3D}->Render if $self->{canvas3D}; +#============================================================================================================================== + Slic3r::GUI::_3DScene::render($self->{canvas3D}) if $self->{canvas3D}; +# $self->{canvas3D}->Render if $self->{canvas3D}; +#============================================================================================================================== undef $self->{_lecursor}; } diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 7740275408..d6b7acf2b3 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -42,13 +42,17 @@ sub new { $self->{objects_volumes_idxs} = []; - $self->on_select(sub { +#============================================================================================================================== + Slic3r::GUI::_3DScene::register_on_select_callback($self, sub { my ($volume_idx) = @_; $self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx) if ($self->{on_select_object}); - }); -#============================================================================================================================== - Slic3r::GUI::_3DScene::register_on_select_callback($self, $self->on_select); + }); +# $self->on_select(sub { +# my ($volume_idx) = @_; +# $self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx) +# if ($self->{on_select_object}); +# }); #============================================================================================================================== #============================================================================================================================== diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 390e1b85e0..2b7f56cc10 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -310,7 +310,10 @@ sub refresh_print { sub reset_gcode_preview_data { my ($self) = @_; $self->gcode_preview_data->reset; - $self->canvas->reset_legend_texture(); +#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_legend_texture(); +# $self->canvas->reset_legend_texture(); +#============================================================================================================================== } sub load_print { @@ -335,7 +338,10 @@ sub load_print { if ($n_layers == 0) { $self->reset_sliders; - $self->canvas->reset_legend_texture(); +#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_legend_texture(); +# $self->canvas->reset_legend_texture(); +#============================================================================================================================== $self->canvas->Refresh; # clears canvas return; } @@ -379,14 +385,20 @@ sub load_print { #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; } $self->show_hide_ui_elements('simple'); - $self->canvas->reset_legend_texture(); +#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_legend_texture(); +# $self->canvas->reset_legend_texture(); +#============================================================================================================================== } else { $self->{force_sliders_full_range} = (scalar(@{$self->canvas->volumes}) == 0); $self->canvas->load_gcode_preview($self->print, $self->gcode_preview_data, \@colors); $self->show_hide_ui_elements('full'); # recalculates zs and update sliders accordingly - $self->{layers_z} = $self->canvas->get_current_print_zs(1); +#============================================================================================================================== + $self->{layers_z} = Slic3r::GUI::_3DScene::get_current_print_zs($self->canvas, 1); +# $self->{layers_z} = $self->canvas->get_current_print_zs(1); +#============================================================================================================================== $n_layers = scalar(@{$self->{layers_z}}); if ($n_layers == 0) { # all layers filtered out @@ -472,7 +484,10 @@ sub set_z_range $self->{z_label_low}->SetLabel(sprintf '%.2f', $z_low); $self->{z_label_high}->SetLabel(sprintf '%.2f', $z_high); - my $layers_z = $self->canvas->get_current_print_zs(0); +#============================================================================================================================== + my $layers_z = Slic3r::GUI::_3DScene::get_current_print_zs($self->canvas, 0); +# my $layers_z = $self->canvas->get_current_print_zs(0); +#============================================================================================================================== for (my $i = 0; $i < scalar(@{$layers_z}); $i += 1) { if (($z_low - 1e-6 < @{$layers_z}[$i]) && (@{$layers_z}[$i] < $z_low + 1e-6)) { $self->{z_label_low_idx}->SetLabel(sprintf '%d', $i + 1); @@ -486,7 +501,10 @@ sub set_z_range } } - $self->canvas->set_toolpaths_range($z_low - 1e-6, $z_high + 1e-6); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_toolpaths_range($self->canvas, $z_low - 1e-6, $z_high + 1e-6); +# $self->canvas->set_toolpaths_range($z_low - 1e-6, $z_high + 1e-6); +#============================================================================================================================== $self->canvas->Refresh if $self->IsShown; } diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index bbb3543bb7..bb9ecd41c4 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -267,13 +267,14 @@ sub _update { #============================================================================================================================== Slic3r::GUI::_3DScene::set_cutting_plane($self->{canvas}, $self->{cut_options}{z}, [@expolygons]); Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); + Slic3r::GUI::_3DScene::render($self->{canvas}); # $self->{canvas}->SetCuttingPlane( # $self->{cut_options}{z}, # [@expolygons], # ); # $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->{config}); +# $self->{canvas}->Render; #============================================================================================================================== - $self->{canvas}->Render; } } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index c2e7be5f26..6fff40769e 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -159,11 +159,18 @@ sub new { #============================================================================================================================== $canvas->select_by('volume'); - $canvas->on_select(sub { +#============================================================================================================================== + Slic3r::GUI::_3DScene::register_on_select_callback($canvas, sub { my ($volume_idx) = @_; # convert scene volume to model object volume $self->reload_tree(($volume_idx == -1) ? undef : $canvas->volumes->[$volume_idx]->volume_idx); }); +# $canvas->on_select(sub { +# my ($volume_idx) = @_; +# # convert scene volume to model object volume +# $self->reload_tree(($volume_idx == -1) ? undef : $canvas->volumes->[$volume_idx]->volume_idx); +# }); +#============================================================================================================================== $canvas->load_object($self->{model_object}, undef, undef, [0]); #============================================================================================================================== @@ -348,7 +355,10 @@ sub selection_changed { $self->{settings_panel}->enable; } - $self->{canvas}->Render if $self->{canvas}; +#============================================================================================================================== + Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas}; +# $self->{canvas}->Render if $self->{canvas}; +#============================================================================================================================== } sub on_btn_load { @@ -510,10 +520,11 @@ sub _parts_changed { #============================================================================================================================== Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas}); Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); + Slic3r::GUI::_3DScene::render($self->{canvas}); # $self->{canvas}->zoom_to_volumes; # $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); +# $self->{canvas}->Render; #============================================================================================================================== - $self->{canvas}->Render; } } @@ -569,9 +580,10 @@ sub _update_canvas { #============================================================================================================================== Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); + Slic3r::GUI::_3DScene::render($self->{canvas}); # $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); +# $self->{canvas}->Render; #============================================================================================================================== - $self->{canvas}->Render; } } @@ -600,9 +612,10 @@ sub _update { $self->{canvas}->load_object($_, undef, [0]) for @objects; #============================================================================================================================== Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); + Slic3r::GUI::_3DScene::render($self->{canvas}); # $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); +# $self->{canvas}->Render; #============================================================================================================================== - $self->{canvas}->Render; } 1; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index eed1e8a1c2..e55985b0ee 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1903,6 +1903,16 @@ void _3DScene::render(wxGLCanvas* canvas) s_canvas_mgr.render(canvas); } +std::vector _3DScene::get_current_print_zs(wxGLCanvas* canvas, bool active_only) +{ + return s_canvas_mgr.get_current_print_zs(canvas, active_only); +} + +void _3DScene::set_toolpaths_range(wxGLCanvas* canvas, double low, double high) +{ + s_canvas_mgr.set_toolpaths_range(canvas, low, high); +} + void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index df395bccbd..aff40f4d7f 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -587,6 +587,9 @@ public: static void render(wxGLCanvas* canvas); + static std::vector get_current_print_zs(wxGLCanvas* canvas, bool active_only); + static void set_toolpaths_range(wxGLCanvas* canvas, double low, double high); + static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); static void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); static void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 240581aec7..d48cc0ccc1 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1317,6 +1317,17 @@ void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, fl ::glEnable(GL_LIGHTING); } +std::vector GLCanvas3D::get_current_print_zs(bool active_only) const +{ + return (m_volumes != nullptr) ? m_volumes->get_current_print_zs(active_only) : std::vector(); +} + +void GLCanvas3D::set_toolpaths_range(double low, double high) +{ + if (m_volumes != nullptr) + m_volumes->set_range(low, high); +} + void GLCanvas3D::register_on_viewport_changed_callback(void* callback) { if (callback != nullptr) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index fe27de0b2c..d2c9f259cc 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -376,6 +376,9 @@ public: void render(); void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; + std::vector get_current_print_zs(bool active_only) const; + void set_toolpaths_range(double low, double high); + void register_on_viewport_changed_callback(void* callback); void register_on_double_click_callback(void* callback); void register_on_right_click_callback(void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index a136f47662..cbd9c15cb9 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -430,6 +430,19 @@ void GLCanvas3DManager::render(wxGLCanvas* canvas) const it->second->render(); } +std::vector GLCanvas3DManager::get_current_print_zs(wxGLCanvas* canvas, bool active_only) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_current_print_zs(active_only) : std::vector(); +} + +void GLCanvas3DManager::set_toolpaths_range(wxGLCanvas* canvas, double low, double high) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_toolpaths_range(low, high); +} + void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index dd8f965d68..e28a0aba9e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -91,6 +91,9 @@ public: void render(wxGLCanvas* canvas) const; + std::vector get_current_print_zs(wxGLCanvas* canvas, bool active_only) const; + void set_toolpaths_range(wxGLCanvas* canvas, double low, double high); + void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 60c86eaa2f..fac52a02ee 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -93,9 +93,6 @@ int count() %code{% RETVAL = THIS->volumes.size(); %}; - std::vector get_current_print_zs(bool active_only) - %code{% RETVAL = THIS->get_current_print_zs(active_only); %}; - void set_range(double low, double high); void render_VBOs() const; @@ -389,6 +386,23 @@ render(canvas) CODE: _3DScene::render((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); +std::vector +get_current_print_zs(canvas, active_only) + SV *canvas; + bool active_only; + CODE: + RETVAL = _3DScene::get_current_print_zs((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), active_only); + OUTPUT: + RETVAL + +void +set_toolpaths_range(canvas, low, high) + SV *canvas; + double low; + double high; + CODE: + _3DScene::set_toolpaths_range((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), low, high); + void register_on_viewport_changed_callback(canvas, callback) SV *canvas; From e65fac5e8484b7737f90948d40dfd05a49a35969 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 4 Jun 2018 17:27:33 +0200 Subject: [PATCH 061/117] Added initial implementation of fixing 3MFs through the Netfabb API provided through the Windows 10 Universal Windows Platform API. --- xs/CMakeLists.txt | 2 + xs/src/slic3r/Utils/FixModelByWin10.cpp | 287 ++++++++++++++++++++++++ xs/src/slic3r/Utils/FixModelByWin10.hpp | 18 ++ 3 files changed, 307 insertions(+) create mode 100644 xs/src/slic3r/Utils/FixModelByWin10.cpp create mode 100644 xs/src/slic3r/Utils/FixModelByWin10.hpp diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 77747cd07e..64f4b92749 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -232,6 +232,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp ${LIBDIR}/slic3r/Utils/Http.cpp ${LIBDIR}/slic3r/Utils/Http.hpp + ${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp + ${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp ${LIBDIR}/slic3r/Utils/OctoPrint.hpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp new file mode 100644 index 0000000000..e4b1952a4f --- /dev/null +++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp @@ -0,0 +1,287 @@ +#ifdef HAS_WIN10SDK + +#include "FixModelByWin10.hpp" + +#include +#include +#include +// for ComPtr +#include +// from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ +#include +#include +#include + +extern "C"{ + // from rapi.h + typedef HRESULT (__stdcall* FunctionRoInitialize)(int); + typedef HRESULT (__stdcall* FunctionRoUninitialize)(); + typedef HRESULT (__stdcall* FunctionRoActivateInstance)(HSTRING activatableClassId, IInspectable **instance); + typedef HRESULT (__stdcall* FunctionRoGetActivationFactory)(HSTRING activatableClassId, REFIID iid, void **factory); + // from winstring.h + typedef HRESULT (__stdcall* FunctionWindowsCreateString)(LPCWSTR sourceString, UINT32 length, HSTRING *string); + typedef HRESULT (__stdcall* FunctionWindowsDelteString)(HSTRING string); +} + +HMODULE s_hRuntimeObjectLibrary = nullptr; +FunctionRoInitialize s_RoInitialize = nullptr; +FunctionRoUninitialize s_RoUninitialize = nullptr; +FunctionRoActivateInstance s_RoActivateInstance = nullptr; +FunctionRoGetActivationFactory s_RoGetActivationFactory = nullptr; +FunctionWindowsCreateString s_WindowsCreateString = nullptr; +FunctionWindowsDelteString s_WindowsDeleteString = nullptr; + +bool winrt_load_runtime_object_library() +{ + if (s_hRuntimeObjectLibrary == nullptr) + s_hRuntimeObjectLibrary = LoadLibrary(L"ComBase.dll"); + if (s_hRuntimeObjectLibrary != nullptr) { + s_RoInitialize = (FunctionRoInitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoInitialize"); + s_RoUninitialize = (FunctionRoUninitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoUninitialize"); + s_RoActivateInstance = (FunctionRoActivateInstance) GetProcAddress(s_hRuntimeObjectLibrary, "RoActivateInstance"); + s_RoGetActivationFactory = (FunctionRoGetActivationFactory) GetProcAddress(s_hRuntimeObjectLibrary, "RoGetActivationFactory"); + s_WindowsCreateString = (FunctionWindowsCreateString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsCreateString"); + s_WindowsDeleteString = (FunctionWindowsDelteString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsDeleteString"); + } + return s_RoInitialize && s_RoUninitialize && s_RoActivateInstance && s_WindowsCreateString && s_WindowsDeleteString; +} + +static HRESULT winrt_activate_instance(const std::wstring &class_name, IInspectable **pinst) +{ + HSTRING hClassName; + HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName); + if (S_OK != hr) + return hr; + hr = (*s_RoActivateInstance)(hClassName, pinst); + (*s_WindowsDeleteString)(hClassName); + return hr; +} + +template +static HRESULT winrt_activate_instance(const std::wstring &class_name, TYPE **pinst) +{ + IInspectable *pinspectable = nullptr; + HRESULT hr = winrt_activate_instance(class_name, &pinspectable); + if (S_OK != hr) + return hr; + hr = pinspectable->QueryInterface(__uuidof(TYPE), (void**)pinst); + pinspectable->Release(); + return hr; +} + +static HRESULT winrt_get_activation_factory(const std::wstring &class_name, REFIID iid, void **pinst) +{ + HSTRING hClassName; + HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName); + if (S_OK != hr) + return hr; + hr = (*s_RoGetActivationFactory)(hClassName, iid, pinst); + (*s_WindowsDeleteString)(hClassName); + return hr; +} + +template +static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE **pinst) +{ + return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast(pinst)); +} + +template +static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr &asyncAction, int blocking_tick_ms = 300) +{ + Microsoft::WRL::ComPtr asyncInfo; + asyncAction.As(&asyncInfo); + AsyncStatus status; + // Ugly blocking loop until the RepairAsync call finishes. +//FIXME replace with a callback. +// https://social.msdn.microsoft.com/Forums/en-US/a5038fb4-b7b7-4504-969d-c102faa389fb/trying-to-block-an-async-operation-and-wait-for-a-particular-time?forum=vclanguage + for (;;) { + asyncInfo->get_Status(&status); + if (status != AsyncStatus::Started) + return status; + ::Sleep(blocking_tick_ms); + } +} + +static HRESULT winrt_open_file_stream( + const std::wstring &path, + ABI::Windows::Storage::FileAccessMode mode, + ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream) +{ + // Get the file factory. + Microsoft::WRL::ComPtr fileFactory; + HRESULT hr = winrt_get_activation_factory(L"Windows.Storage.StorageFile", fileFactory.GetAddressOf()); + if (FAILED(hr)) return hr; + + // Open the file asynchronously. + HSTRING hstr_path; + hr = (*s_WindowsCreateString)(path.c_str(), path.size(), &hstr_path); + if (FAILED(hr)) return hr; + Microsoft::WRL::ComPtr> fileOpenAsync; + hr = fileFactory->GetFileFromPathAsync(hstr_path, fileOpenAsync.GetAddressOf()); + if (FAILED(hr)) return hr; + (*s_WindowsDeleteString)(hstr_path); + + // Wait until the file gets open, get the actual file. + AsyncStatus status = winrt_async_await(fileOpenAsync); + Microsoft::WRL::ComPtr storageFile; + if (status == AsyncStatus::Completed) { + hr = fileOpenAsync->GetResults(storageFile.GetAddressOf()); + } else { + Microsoft::WRL::ComPtr asyncInfo; + hr = fileOpenAsync.As(&asyncInfo); + if (FAILED(hr)) return hr; + HRESULT err; + hr = asyncInfo->get_ErrorCode(&err); + return FAILED(hr) ? hr : err; + } + + Microsoft::WRL::ComPtr> fileStreamAsync; + hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf()); + if (FAILED(hr)) return hr; + + status = winrt_async_await(fileStreamAsync); + if (status == AsyncStatus::Completed) { + hr = fileStreamAsync->GetResults(fileStream); + } else { + Microsoft::WRL::ComPtr asyncInfo; + hr = fileStreamAsync.As(&asyncInfo); + if (FAILED(hr)) return hr; + HRESULT err; + hr = asyncInfo->get_ErrorCode(&err); + if (!FAILED(hr)) + hr = err; + } + return hr; +} + +bool is_windows10() +{ + HKEY hKey; + LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey); + if (lRes == ERROR_SUCCESS) { + WCHAR szBuffer[512]; + DWORD dwBufferSize = sizeof(szBuffer); + lRes = RegQueryValueExW(hKey, L"ProductName", 0, nullptr, (LPBYTE)szBuffer, &dwBufferSize); + if (lRes == ERROR_SUCCESS) + return wcsncmp(szBuffer, L"Windows 10", 10) == 0; + RegCloseKey(hKey); + } + return false; +} + +bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst) +{ + if (! is_windows10()) { + return false; + } + + if (! winrt_load_runtime_object_library()) { + printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n"); + return -1; + } + + (*s_RoInitialize)(RO_INIT_MULTITHREADED); + { + Microsoft::WRL::ComPtr fileStream; + HRESULT hr = winrt_open_file_stream(L"D:\\3dprint\\bug.3mf", ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); + + Microsoft::WRL::ComPtr printing3d3mfpackage; + hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf()); + + Microsoft::WRL::ComPtr> modelAsync; + hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf()); + + AsyncStatus status = winrt_async_await(modelAsync); + Microsoft::WRL::ComPtr model; + if (status == AsyncStatus::Completed) { + hr = modelAsync->GetResults(model.GetAddressOf()); + } else { + printf("Failed loading the input model. Exiting.\n"); + return -1; + } + + Microsoft::WRL::ComPtr> meshes; + hr = model->get_Meshes(meshes.GetAddressOf()); + unsigned num_meshes = 0; + hr = meshes->get_Size(&num_meshes); + + Microsoft::WRL::ComPtr repairAsync; + hr = model->RepairAsync(repairAsync.GetAddressOf()); + status = winrt_async_await(repairAsync); + if (status != AsyncStatus::Completed) { + printf("Mesh repair failed. Exiting.\n"); + return -1; + } + printf("Mesh repair finished successfully.\n"); + repairAsync->GetResults(); + + // Verify the number of meshes returned after the repair action. + meshes.Reset(); + hr = model->get_Meshes(meshes.GetAddressOf()); + hr = meshes->get_Size(&num_meshes); + + // Save model to this class' Printing3D3MFPackage. + Microsoft::WRL::ComPtr saveToPackageAsync; + hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf()); + status = winrt_async_await(saveToPackageAsync); + if (status != AsyncStatus::Completed) { + printf("Saving mesh into the 3MF container failed. Exiting.\n"); + return -1; + } + hr = saveToPackageAsync->GetResults(); + + Microsoft::WRL::ComPtr> generatorStreamAsync; + hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf()); + status = winrt_async_await(generatorStreamAsync); + if (status != AsyncStatus::Completed) { + printf("Saving mesh into the 3MF container failed. Exiting.\n"); + return -1; + } + Microsoft::WRL::ComPtr generatorStream; + hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); + + // Go to the beginning of the stream. + generatorStream->Seek(0); + Microsoft::WRL::ComPtr inputStream; + hr = generatorStream.As(&inputStream); + + // Get the buffer factory. + Microsoft::WRL::ComPtr bufferFactory; + hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf()); + + // Open the destination file. + FILE *fout = ::fopen("D:\\3dprint\\bug-repaired.3mf", "wb"); + + Microsoft::WRL::ComPtr buffer; + byte *buffer_ptr; + bufferFactory->Create(65536 * 2048, buffer.GetAddressOf()); + { + Microsoft::WRL::ComPtr bufferByteAccess; + buffer.As(&bufferByteAccess); + hr = bufferByteAccess->Buffer(&buffer_ptr); + } + uint32_t length; + hr = buffer->get_Length(&length); + + Microsoft::WRL::ComPtr> asyncRead; + for (;;) { + hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf()); + status = winrt_async_await(asyncRead); + if (status != AsyncStatus::Completed) { + printf("Saving mesh into the 3MF container failed. Exiting.\n"); + return -1; + } + hr = buffer->get_Length(&length); + if (length == 0) + break; + fwrite(buffer_ptr, length, 1, fout); + } + fclose(fout); + // Here all the COM objects will be released through the ComPtr destructors. + } + (*s_RoUninitialize)(); + return 0; +} + +#endif /* HAS_WIN10SDK */ diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp new file mode 100644 index 0000000000..268b614efc --- /dev/null +++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp @@ -0,0 +1,18 @@ +#ifndef slic3r_GUI_Utils_FixModelByWin10_hpp_ +#define slic3r_GUI_Utils_FixModelByWin10_hpp_ + +#include + +#ifdef HAS_WIN10SDK + +extern bool is_windows10(); +extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst); + +#else /* HAS_WIN10SDK */ + +inline bool is_windows10() { return false; } +inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; } + +#endif /* HAS_WIN10SDK * + +#endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */ From d05d3cb652de8dfc156b62e940aaf8a7422dee20 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 4 Jun 2018 21:22:42 +0200 Subject: [PATCH 062/117] Initial working implementation of the "Fix by Netfabb" function. --- CMakeLists.txt | 16 ++++++ lib/Slic3r/GUI/Plater.pm | 33 ++++++++++++ xs/CMakeLists.txt | 7 +++ xs/src/slic3r/Utils/FixModelByWin10.cpp | 72 +++++++++++++++++++++---- xs/src/slic3r/Utils/FixModelByWin10.hpp | 12 ++++- xs/xsp/GUI.xsp | 7 +++ 6 files changed, 136 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8b2a6faaa..60d67553b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,22 @@ if(NOT DEFINED CMAKE_PREFIX_PATH) endif() endif() +# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory. +# We pick it from environment if it is not defined in another way +if(WIN32) + if(NOT DEFINED WIN10SDK_PATH) + if(DEFINED ENV{WIN10SDK_PATH}) + set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}") + endif() + endif() + if(DEFINED WIN10SDK_PATH AND NOT EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h") + message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}") + message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found") + message("STL fixing by the Netfabb service will not be compiled") + unset(WIN10SDK_PATH) + endif() +endif() + add_subdirectory(xs) get_filename_component(PERL_BIN_PATH "${PERL_EXECUTABLE}" DIRECTORY) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d27be785c4..40c2a13512 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1620,6 +1620,34 @@ sub export_object_stl { $self->statusbar->SetStatusText(L("STL file exported to ").$output_file); } +sub fix_through_netfabb { + my ($self) = @_; + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_fixed = Slic3r::Model->new; + Slic3r::GUI::fix_model_by_win10_sdk_gui($model_object, $self->{print}, $model_fixed); + + my @new_obj_idx = $self->load_model_objects(@{$model_fixed->objects}); + return if !@new_obj_idx; + + foreach my $new_obj_idx (@new_obj_idx) { + my $o = $self->{model}->objects->[$new_obj_idx]; + $o->clear_instances; + $o->add_instance($_) for @{$model_object->instances}; + #$o->invalidate_bounding_box; + + if ($o->volumes_count == $model_object->volumes_count) { + for my $i (0..($o->volumes_count-1)) { + $o->get_volume($i)->config->apply($model_object->get_volume($i)->config); + } + } + #FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid, + } + + $self->remove($obj_idx); +} + sub export_amf { my ($self) = @_; return if !@{$self->{objects}}; @@ -2178,6 +2206,11 @@ sub object_menu { $frame->_append_menu_item($menu, L("Export object as STL…"), L('Export this single object as STL file'), sub { $self->export_object_stl; }, undef, 'brick_go.png'); + if (Slic3r::GUI::is_windows10) { + $frame->_append_menu_item($menu, L("Fix STL through Netfabb"), L('Fix the model by sending it to a Netfabb cloud service through Windows 10 API'), sub { + $self->fix_through_netfabb; + }, undef, 'brick_go.png'); + } return $menu; } diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 64f4b92749..866374584d 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -27,6 +27,13 @@ if(WIN32) # BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking. add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB) # -D_ITERATOR_DEBUG_LEVEL) + if(WIN10SDK_PATH) + message("Building with Win10 Netfabb STL fixing service support") + add_definitions(-DHAS_WIN10SDK) + include_directories("${WIN10SDK_PATH}/Include") + else() + message("Building without Win10 Netfabb STL fixing service support") + endif() endif() add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE) diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp index e4b1952a4f..27a79f84eb 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.cpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp @@ -1,10 +1,15 @@ #ifdef HAS_WIN10SDK +#ifndef NOMINMAX +# define NOMINMAX +#endif + #include "FixModelByWin10.hpp" #include #include #include +#include // for ComPtr #include // from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ @@ -12,6 +17,18 @@ #include #include +#include +#include +#include + +#include "libslic3r/Model.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/Format/3mf.hpp" +#include "../GUI/GUI.hpp" +#include "../GUI/PresetBundle.hpp" + +#include + extern "C"{ // from rapi.h typedef HRESULT (__stdcall* FunctionRoInitialize)(int); @@ -23,6 +40,8 @@ extern "C"{ typedef HRESULT (__stdcall* FunctionWindowsDelteString)(HSTRING string); } +namespace Slic3r { + HMODULE s_hRuntimeObjectLibrary = nullptr; FunctionRoInitialize s_RoInitialize = nullptr; FunctionRoUninitialize s_RoUninitialize = nullptr; @@ -178,13 +197,13 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path if (! winrt_load_runtime_object_library()) { printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n"); - return -1; + return false; } - (*s_RoInitialize)(RO_INIT_MULTITHREADED); + HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED); { Microsoft::WRL::ComPtr fileStream; - HRESULT hr = winrt_open_file_stream(L"D:\\3dprint\\bug.3mf", ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); + hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); Microsoft::WRL::ComPtr printing3d3mfpackage; hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf()); @@ -198,7 +217,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path hr = modelAsync->GetResults(model.GetAddressOf()); } else { printf("Failed loading the input model. Exiting.\n"); - return -1; + return false; } Microsoft::WRL::ComPtr> meshes; @@ -211,7 +230,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(repairAsync); if (status != AsyncStatus::Completed) { printf("Mesh repair failed. Exiting.\n"); - return -1; + return false; } printf("Mesh repair finished successfully.\n"); repairAsync->GetResults(); @@ -227,7 +246,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(saveToPackageAsync); if (status != AsyncStatus::Completed) { printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return -1; + return false; } hr = saveToPackageAsync->GetResults(); @@ -236,7 +255,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(generatorStreamAsync); if (status != AsyncStatus::Completed) { printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return -1; + return false; } Microsoft::WRL::ComPtr generatorStream; hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); @@ -251,7 +270,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf()); // Open the destination file. - FILE *fout = ::fopen("D:\\3dprint\\bug-repaired.3mf", "wb"); + FILE *fout = boost::nowide::fopen(path_dst.c_str(), "wb"); Microsoft::WRL::ComPtr buffer; byte *buffer_ptr; @@ -270,7 +289,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(asyncRead); if (status != AsyncStatus::Completed) { printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return -1; + return false; } hr = buffer->get_Length(&length); if (length == 0) @@ -281,7 +300,40 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path // Here all the COM objects will be released through the ComPtr destructors. } (*s_RoUninitialize)(); - return 0; + return true; } +void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result) +{ + enum { PROGRESS_RANGE = 1000 }; + wxProgressDialog progress_dialog( + _(L("Model fixing")), + _(L("Exporting model...")), + PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); + progress_dialog.Pulse(); + + // Executing the calculation in a background thread, so that the COM context could be created with its own threading model. + // (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context). + auto worker = std::thread([&model_object, &print, &result, &progress_dialog](){ + boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_src += ".3mf"; + Model model; + model.add_object(model_object); + bool res = Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast(&print), false); + model.clear_objects(); + model.clear_materials(); + + boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_dst += ".3mf"; + res = fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string()); + boost::filesystem::remove(path_src); + PresetBundle bundle; + res = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result); + boost::filesystem::remove(path_dst); + }); + worker.join(); +} + +} // namespace Slic3r + #endif /* HAS_WIN10SDK */ diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp index 268b614efc..299c9b75bf 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.hpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp @@ -3,16 +3,26 @@ #include +namespace Slic3r { + +class Model; +class ModelObject; +class Print; + #ifdef HAS_WIN10SDK extern bool is_windows10(); extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst); +extern void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result); #else /* HAS_WIN10SDK */ inline bool is_windows10() { return false; } inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; } +inline void fix_model_by_win10_sdk_gui(const ModelObject &, const Print &, Model &) {} -#endif /* HAS_WIN10SDK * +#endif /* HAS_WIN10SDK */ + +} // namespace Slic3r #endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */ diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 897e636935..1d860d2848 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -4,6 +4,7 @@ #include #include "slic3r/GUI/GUI.hpp" #include "slic3r/Utils/ASCIIFolding.hpp" +#include "slic3r/Utils/FixModelByWin10.hpp" #include "slic3r/Utils/Serial.hpp" %} @@ -28,6 +29,9 @@ bool debugged() void break_to_debugger() %code{% Slic3r::GUI::break_to_debugger(); %}; +bool is_windows10() + %code{% RETVAL=Slic3r::is_windows10(); %}; + void set_wxapp(SV *ui) %code%{ Slic3r::GUI::set_wxapp((wxApp*)wxPli_sv_2_object(aTHX_ ui, "Wx::App")); %}; @@ -98,3 +102,6 @@ int get_export_option(SV *ui) void desktop_open_datadir_folder() %code%{ Slic3r::GUI::desktop_open_datadir_folder(); %}; + +void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst) + %code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %}; From 5392008916aa5b951771c4a23579528c797070c8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 5 Jun 2018 10:56:55 +0200 Subject: [PATCH 063/117] Generation of gcode paths moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 16 +- lib/Slic3r/GUI/Plater.pm | 1 + lib/Slic3r/GUI/Plater/3DPreview.pm | 6 +- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 1 + lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 1 + xs/src/slic3r/GUI/3DScene.cpp | 1279 +++++++++++---------- xs/src/slic3r/GUI/3DScene.hpp | 122 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 661 ++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 55 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 17 + xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 3 + xs/xsp/GUI_3DScene.xsp | 15 +- 12 files changed, 1488 insertions(+), 689 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 204e19d3d3..1476e8c221 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -2248,20 +2248,14 @@ sub load_wipe_tower_toolpaths { #============================================================================================================================== } -sub load_gcode_preview { - my ($self, $print, $gcode_preview_data, $colors) = @_; - #============================================================================================================================== - my $useVBOs = Slic3r::GUI::_3DScene::use_VBOs(); - $self->SetCurrent($self->GetContext) if $useVBOs; - Slic3r::GUI::_3DScene::load_gcode_preview($print, $gcode_preview_data, $self->volumes, $colors, $useVBOs); - +#sub load_gcode_preview { +# my ($self, $print, $gcode_preview_data, $colors) = @_; +# # $self->SetCurrent($self->GetContext) if $self->UseVBOs; # Slic3r::GUI::_3DScene::load_gcode_preview($print, $gcode_preview_data, $self->volumes, $colors, $self->UseVBOs); -#============================================================================================================================== -} - -#============================================================================================================================== +#} +# #sub set_toolpaths_range { # my ($self, $min_z, $max_z) = @_; # $self->volumes->set_range($min_z, $max_z); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index fafd5da7c1..309c2e5ed5 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -134,6 +134,7 @@ sub new { $self->{canvas3D}->set_on_enable_action_buttons($enable_action_buttons); #=================================================================================================================================== Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); + Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); # $self->{canvas3D}->use_plain_shader(1); #=================================================================================================================================== $self->{canvas3D}->set_on_wipe_tower_moved(sub { diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 2b7f56cc10..9ea5069b41 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -391,7 +391,11 @@ sub load_print { #============================================================================================================================== } else { $self->{force_sliders_full_range} = (scalar(@{$self->canvas->volumes}) == 0); - $self->canvas->load_gcode_preview($self->print, $self->gcode_preview_data, \@colors); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print); + Slic3r::GUI::_3DScene::load_gcode_preview($self->canvas, $self->gcode_preview_data, \@colors); +# $self->canvas->load_gcode_preview($self->print, $self->gcode_preview_data, \@colors); +#============================================================================================================================== $self->show_hide_ui_elements('full'); # recalculates zs and update sliders accordingly diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index bb9ecd41c4..84177dd0fd 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -126,6 +126,7 @@ sub new { $canvas->SetMinSize($canvas->GetSize); #============================================================================================================================== Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->{config}); + Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1); #============================================================================================================================== } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 6fff40769e..ad9c4df94c 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -183,6 +183,7 @@ sub new { #============================================================================================================================== Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->GetParent->GetParent->{config}); Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($canvas); + Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1); # $canvas->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); #============================================================================================================================== } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index e55985b0ee..47095e1b17 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1377,7 +1377,10 @@ static void thick_point_to_verts(const Point3& point, } // Fill in the qverts and tverts with quads and triangles for the extrusion_path. -static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) +//################################################################################################################## +void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) +//static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) +//################################################################################################################## { Lines lines = extrusion_path.polyline.lines(); std::vector widths(lines.size(), extrusion_path.width); @@ -1386,7 +1389,10 @@ static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, } // Fill in the qverts and tverts with quads and triangles for the extrusion_path. -static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) +//################################################################################################################## +void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) +//static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) +//################################################################################################################## { Polyline polyline = extrusion_path.polyline; polyline.remove_duplicate_points(); @@ -1398,7 +1404,10 @@ static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, } // Fill in the qverts and tverts with quads and triangles for the extrusion_loop. -static inline void extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) +//################################################################################################################## +void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) +//static inline void extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) +//################################################################################################################## { Lines lines; std::vector widths; @@ -1416,7 +1425,10 @@ static inline void extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, } // Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path. -static inline void extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) +//################################################################################################################## +void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) +//static inline void extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) +//################################################################################################################## { Lines lines; std::vector widths; @@ -1433,15 +1445,23 @@ static inline void extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_ thick_lines_to_verts(lines, widths, heights, false, print_z, volume); } -static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume); +//################################################################################################################## +//static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume); +//################################################################################################################## -static inline void extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) +//################################################################################################################## +void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) +//static inline void extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) +//################################################################################################################## { for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities) extrusionentity_to_verts(extrusion_entity, print_z, copy, volume); } -static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) +//################################################################################################################## +void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) +//static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) +//################################################################################################################## { if (extrusion_entity != nullptr) { auto *extrusion_path = dynamic_cast(extrusion_entity); @@ -1468,7 +1488,10 @@ static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, fl } } -static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume) +//################################################################################################################## +void _3DScene::polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume) +//static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume) +//################################################################################################################## { Lines3 lines = polyline.lines(); std::vector widths(lines.size(), width); @@ -1476,12 +1499,17 @@ static void polyline3_to_verts(const Polyline3& polyline, double width, double h thick_lines_to_verts(lines, widths, heights, false, volume); } -static void point3_to_verts(const Point3& point, double width, double height, GLVolume& volume) +//################################################################################################################## +void _3DScene::point3_to_verts(const Point3& point, double width, double height, GLVolume& volume) +//static void point3_to_verts(const Point3& point, double width, double height, GLVolume& volume) +//################################################################################################################## { thick_point_to_verts(point, width, height, volume); } -_3DScene::GCodePreviewVolumeIndex _3DScene::s_gcode_preview_volume_index; +//################################################################################################################## +//_3DScene::GCodePreviewVolumeIndex _3DScene::s_gcode_preview_volume_index; +//################################################################################################################## _3DScene::LegendTexture _3DScene::s_legend_texture; _3DScene::WarningTexture _3DScene::s_warning_texture; //################################################################################################################## @@ -1868,6 +1896,11 @@ void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable) s_canvas_mgr.enable_shader(canvas, enable); } +void _3DScene::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_force_zoom_to_bed(canvas, enable); +} + void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow) { s_canvas_mgr.allow_multisample(canvas, allow); @@ -1976,40 +2009,54 @@ static inline std::vector parse_colors(const std::vector &sc return output; } -void _3DScene::load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector& str_tool_colors, bool use_VBOs) +//################################################################################################################## +void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors) { - if ((preview_data == nullptr) || (volumes == nullptr)) - return; - - if (volumes->empty()) - { - std::vector tool_colors = parse_colors(str_tool_colors); - - s_gcode_preview_volume_index.reset(); - - _load_gcode_extrusion_paths(*preview_data, *volumes, tool_colors, use_VBOs); - _load_gcode_travel_paths(*preview_data, *volumes, tool_colors, use_VBOs); - _load_gcode_retractions(*preview_data, *volumes, use_VBOs); - _load_gcode_unretractions(*preview_data, *volumes, use_VBOs); - - if (volumes->empty()) - reset_legend_texture(); - else - { - _generate_legend_texture(*preview_data, tool_colors); - - // removes empty volumes - volumes->volumes.erase(std::remove_if(volumes->volumes.begin(), volumes->volumes.end(), - [](const GLVolume *volume) { return volume->print_zs.empty(); }), - volumes->volumes.end()); - - _load_shells(*print, *volumes, use_VBOs); - } - } - - _update_gcode_volumes_visibility(*preview_data, *volumes); + s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors); } +//void _3DScene::load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector& str_tool_colors, bool use_VBOs) +//{ +// if ((preview_data == nullptr) || (volumes == nullptr)) +// return; +// +// if (volumes->empty()) +// { +// std::vector tool_colors = parse_colors(str_tool_colors); +// +// s_gcode_preview_volume_index.reset(); +// +// _load_gcode_extrusion_paths(*preview_data, *volumes, tool_colors, use_VBOs); +// _load_gcode_travel_paths(*preview_data, *volumes, tool_colors, use_VBOs); +// _load_gcode_retractions(*preview_data, *volumes, use_VBOs); +// _load_gcode_unretractions(*preview_data, *volumes, use_VBOs); +// +// if (volumes->empty()) +// reset_legend_texture(); +// else +// { +// _generate_legend_texture(*preview_data, tool_colors); +// +// // removes empty volumes +// volumes->volumes.erase(std::remove_if(volumes->volumes.begin(), volumes->volumes.end(), +// [](const GLVolume *volume) { return volume->print_zs.empty(); }), +// volumes->volumes.end()); +// +// _load_shells(*print, *volumes, use_VBOs); +// } +// } +// +// _update_gcode_volumes_visibility(*preview_data, *volumes); +//} +//################################################################################################################## + +//################################################################################################################## +void _3DScene::generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors) +{ + s_legend_texture.generate(preview_data, tool_colors); +} +//################################################################################################################## + unsigned int _3DScene::get_legend_texture_width() { return s_legend_texture.get_texture_width(); @@ -2414,579 +2461,581 @@ void _3DScene::_load_wipe_tower_toolpaths( BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; } -void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs) -{ - // helper functions to select data in dependence of the extrusion view type - struct Helper - { - static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path) - { - switch (type) - { - case GCodePreviewData::Extrusion::FeatureType: - return (float)path.role(); - case GCodePreviewData::Extrusion::Height: - return path.height; - case GCodePreviewData::Extrusion::Width: - return path.width; - case GCodePreviewData::Extrusion::Feedrate: - return path.feedrate; - case GCodePreviewData::Extrusion::VolumetricRate: - return path.feedrate * (float)path.mm3_per_mm; - case GCodePreviewData::Extrusion::Tool: - return (float)path.extruder_id; - } - - return 0.0f; - } - - static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector& tool_colors, float value) - { - switch (data.extrusion.view_type) - { - case GCodePreviewData::Extrusion::FeatureType: - return data.get_extrusion_role_color((ExtrusionRole)(int)value); - case GCodePreviewData::Extrusion::Height: - return data.get_height_color(value); - case GCodePreviewData::Extrusion::Width: - return data.get_width_color(value); - case GCodePreviewData::Extrusion::Feedrate: - return data.get_feedrate_color(value); - case GCodePreviewData::Extrusion::VolumetricRate: - return data.get_volumetric_rate_color(value); - case GCodePreviewData::Extrusion::Tool: - { - GCodePreviewData::Color color; - ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float)); - return color; - } - } - - return GCodePreviewData::Color::Dummy; - } - }; - - // Helper structure for filters - struct Filter - { - float value; - ExtrusionRole role; - GLVolume* volume; - - Filter(float value, ExtrusionRole role) - : value(value) - , role(role) - , volume(nullptr) - { - } - - bool operator == (const Filter& other) const - { - if (value != other.value) - return false; - - if (role != other.role) - return false; - - return true; - } - }; - - typedef std::vector FiltersList; - size_t initial_volumes_count = volumes.volumes.size(); - - // detects filters - FiltersList filters; - for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) - { - for (const ExtrusionPath& path : layer.paths) - { - ExtrusionRole role = path.role(); - float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); - if (std::find(filters.begin(), filters.end(), Filter(path_filter, role)) == filters.end()) - filters.emplace_back(path_filter, role); - } - } - - // nothing to render, return - if (filters.empty()) - return; - - // creates a new volume for each filter - for (Filter& filter : filters) - { - s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)volumes.volumes.size()); - GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba); - if (volume != nullptr) - { - filter.volume = volume; - volumes.volumes.emplace_back(volume); - } - else - { - // an error occourred - restore to previous state and return - s_gcode_preview_volume_index.first_volumes.pop_back(); - if (initial_volumes_count != volumes.volumes.size()) - { - std::vector::iterator begin = volumes.volumes.begin() + initial_volumes_count; - std::vector::iterator end = volumes.volumes.end(); - for (std::vector::iterator it = begin; it < end; ++it) - { - GLVolume* volume = *it; - delete volume; - } - volumes.volumes.erase(begin, end); - return; - } - } - } - - // populates volumes - for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) - { - for (const ExtrusionPath& path : layer.paths) - { - float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); - FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role())); - if (filter != filters.end()) - { - filter->volume->print_zs.push_back(layer.z); - filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.quad_indices.size()); - filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.triangle_indices.size()); - - extrusionentity_to_verts(path, layer.z, *filter->volume); - } - } - } - - // finalize volumes and sends geometry to gpu - if (volumes.volumes.size() > initial_volumes_count) - { - for (size_t i = initial_volumes_count; i < volumes.volumes.size(); ++i) - { - GLVolume* volume = volumes.volumes[i]; - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(use_VBOs); - } - } -} - -void _3DScene::_load_gcode_travel_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs) -{ - size_t initial_volumes_count = volumes.volumes.size(); - s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count); - - bool res = true; - switch (preview_data.extrusion.view_type) - { - case GCodePreviewData::Extrusion::Feedrate: - { - res = _travel_paths_by_feedrate(preview_data, volumes); - break; - } - case GCodePreviewData::Extrusion::Tool: - { - res = _travel_paths_by_tool(preview_data, volumes, tool_colors); - break; - } - default: - { - res = _travel_paths_by_type(preview_data, volumes); - break; - } - } - - if (!res) - { - // an error occourred - restore to previous state and return - if (initial_volumes_count != volumes.volumes.size()) - { - std::vector::iterator begin = volumes.volumes.begin() + initial_volumes_count; - std::vector::iterator end = volumes.volumes.end(); - for (std::vector::iterator it = begin; it < end; ++it) - { - GLVolume* volume = *it; - delete volume; - } - volumes.volumes.erase(begin, end); - } - - return; - } - - // finalize volumes and sends geometry to gpu - if (volumes.volumes.size() > initial_volumes_count) - { - for (size_t i = initial_volumes_count; i < volumes.volumes.size(); ++i) - { - GLVolume* volume = volumes.volumes[i]; - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(use_VBOs); - } - } -} - -bool _3DScene::_travel_paths_by_type(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) -{ - // Helper structure for types - struct Type - { - GCodePreviewData::Travel::EType value; - GLVolume* volume; - - explicit Type(GCodePreviewData::Travel::EType value) - : value(value) - , volume(nullptr) - { - } - - bool operator == (const Type& other) const - { - return value == other.value; - } - }; - - typedef std::vector TypesList; - - // colors travels by travel type - - // detects types - TypesList types; - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - if (std::find(types.begin(), types.end(), Type(polyline.type)) == types.end()) - types.emplace_back(polyline.type); - } - - // nothing to render, return - if (types.empty()) - return true; - - // creates a new volume for each type - for (Type& type : types) - { - GLVolume* volume = new GLVolume(preview_data.travel.type_colors[type.value].rgba); - if (volume == nullptr) - return false; - else - { - type.volume = volume; - volumes.volumes.emplace_back(volume); - } - } - - // populates volumes - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type)); - if (type != types.end()) - { - type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); - type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size()); - type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size()); - - polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *type->volume); - } - } - - return true; -} - -bool _3DScene::_travel_paths_by_feedrate(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) -{ - // Helper structure for feedrate - struct Feedrate - { - float value; - GLVolume* volume; - - explicit Feedrate(float value) - : value(value) - , volume(nullptr) - { - } - - bool operator == (const Feedrate& other) const - { - return value == other.value; - } - }; - - typedef std::vector FeedratesList; - - // colors travels by feedrate - - // detects feedrates - FeedratesList feedrates; - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - if (std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)) == feedrates.end()) - feedrates.emplace_back(polyline.feedrate); - } - - // nothing to render, return - if (feedrates.empty()) - return true; - - // creates a new volume for each feedrate - for (Feedrate& feedrate : feedrates) - { - GLVolume* volume = new GLVolume(preview_data.get_feedrate_color(feedrate.value).rgba); - if (volume == nullptr) - return false; - else - { - feedrate.volume = volume; - volumes.volumes.emplace_back(volume); - } - } - - // populates volumes - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)); - if (feedrate != feedrates.end()) - { - feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); - feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size()); - feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size()); - - polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *feedrate->volume); - } - } - - return true; -} - -bool _3DScene::_travel_paths_by_tool(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors) -{ - // Helper structure for tool - struct Tool - { - unsigned int value; - GLVolume* volume; - - explicit Tool(unsigned int value) - : value(value) - , volume(nullptr) - { - } - - bool operator == (const Tool& other) const - { - return value == other.value; - } - }; - - typedef std::vector ToolsList; - - // colors travels by tool - - // detects tools - ToolsList tools; - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - if (std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)) == tools.end()) - tools.emplace_back(polyline.extruder_id); - } - - // nothing to render, return - if (tools.empty()) - return true; - - // creates a new volume for each tool - for (Tool& tool : tools) - { - GLVolume* volume = new GLVolume(tool_colors.data() + tool.value * 4); - if (volume == nullptr) - return false; - else - { - tool.volume = volume; - volumes.volumes.emplace_back(volume); - } - } - - // populates volumes - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)); - if (tool != tools.end()) - { - tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); - tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size()); - tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size()); - - polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume); - } - } - - return true; -} - -void _3DScene::_load_gcode_retractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs) -{ - s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)volumes.volumes.size()); - - // nothing to render, return - if (preview_data.retraction.positions.empty()) - return; - - GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba); - if (volume != nullptr) - { - volumes.volumes.emplace_back(volume); - - GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions); - std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); - - for (const GCodePreviewData::Retraction::Position& position : copy) - { - volume->print_zs.push_back(unscale(position.position.z)); - volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); - volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); - - point3_to_verts(position.position, position.width, position.height, *volume); - } - - // finalize volumes and sends geometry to gpu - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(use_VBOs); - } -} - -void _3DScene::_load_gcode_unretractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs) -{ - s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)volumes.volumes.size()); - - // nothing to render, return - if (preview_data.unretraction.positions.empty()) - return; - - GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba); - if (volume != nullptr) - { - volumes.volumes.emplace_back(volume); - - GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions); - std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); - - for (const GCodePreviewData::Retraction::Position& position : copy) - { - volume->print_zs.push_back(unscale(position.position.z)); - volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); - volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); - - point3_to_verts(position.position, position.width, position.height, *volume); - } - - // finalize volumes and sends geometry to gpu - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(use_VBOs); - } -} - -void _3DScene::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) -{ - unsigned int size = (unsigned int)s_gcode_preview_volume_index.first_volumes.size(); - for (unsigned int i = 0; i < size; ++i) - { - std::vector::iterator begin = volumes.volumes.begin() + s_gcode_preview_volume_index.first_volumes[i].id; - std::vector::iterator end = (i + 1 < size) ? volumes.volumes.begin() + s_gcode_preview_volume_index.first_volumes[i + 1].id : volumes.volumes.end(); - - for (std::vector::iterator it = begin; it != end; ++it) - { - GLVolume* volume = *it; - volume->outside_printer_detection_enabled = false; - - switch (s_gcode_preview_volume_index.first_volumes[i].type) - { - case GCodePreviewVolumeIndex::Extrusion: - { - if ((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag == erCustom) - volume->zoom_to_volumes = false; - - volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag); - break; - } - case GCodePreviewVolumeIndex::Travel: - { - volume->is_active = preview_data.travel.is_visible; - volume->zoom_to_volumes = false; - break; - } - case GCodePreviewVolumeIndex::Retraction: - { - volume->is_active = preview_data.retraction.is_visible; - volume->zoom_to_volumes = false; - break; - } - case GCodePreviewVolumeIndex::Unretraction: - { - volume->is_active = preview_data.unretraction.is_visible; - volume->zoom_to_volumes = false; - break; - } - case GCodePreviewVolumeIndex::Shell: - { - volume->is_active = preview_data.shell.is_visible; - volume->color[3] = 0.25f; - volume->zoom_to_volumes = false; - break; - } - default: - { - volume->is_active = false; - volume->zoom_to_volumes = false; - break; - } - } - } - } -} - -void _3DScene::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors) -{ - s_legend_texture.generate(preview_data, tool_colors); -} - -void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs) -{ - size_t initial_volumes_count = volumes.volumes.size(); - s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count); - - if (print.objects.empty()) - // nothing to render, return - return; - - // adds objects' volumes - unsigned int object_id = 0; - for (PrintObject* obj : print.objects) - { - ModelObject* model_obj = obj->model_object(); - - std::vector instance_ids(model_obj->instances.size()); - for (int i = 0; i < model_obj->instances.size(); ++i) - { - instance_ids[i] = i; - } - - for (ModelInstance* instance : model_obj->instances) - { - volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", use_VBOs); - } - - ++object_id; - } - - // adds wipe tower's volume - coordf_t max_z = print.objects[0]->model_object()->get_model()->bounding_box().max.z; - const PrintConfig& config = print.config; - unsigned int extruders_count = config.nozzle_diameter.size(); - if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) { - const float width_per_extruder = 15.f; // a simple workaround after wipe_tower_per_color_wipe got obsolete - volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, use_VBOs); - } -} +//################################################################################################################## +//void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs) +//{ +// // helper functions to select data in dependence of the extrusion view type +// struct Helper +// { +// static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path) +// { +// switch (type) +// { +// case GCodePreviewData::Extrusion::FeatureType: +// return (float)path.role(); +// case GCodePreviewData::Extrusion::Height: +// return path.height; +// case GCodePreviewData::Extrusion::Width: +// return path.width; +// case GCodePreviewData::Extrusion::Feedrate: +// return path.feedrate; +// case GCodePreviewData::Extrusion::VolumetricRate: +// return path.feedrate * (float)path.mm3_per_mm; +// case GCodePreviewData::Extrusion::Tool: +// return (float)path.extruder_id; +// } +// +// return 0.0f; +// } +// +// static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector& tool_colors, float value) +// { +// switch (data.extrusion.view_type) +// { +// case GCodePreviewData::Extrusion::FeatureType: +// return data.get_extrusion_role_color((ExtrusionRole)(int)value); +// case GCodePreviewData::Extrusion::Height: +// return data.get_height_color(value); +// case GCodePreviewData::Extrusion::Width: +// return data.get_width_color(value); +// case GCodePreviewData::Extrusion::Feedrate: +// return data.get_feedrate_color(value); +// case GCodePreviewData::Extrusion::VolumetricRate: +// return data.get_volumetric_rate_color(value); +// case GCodePreviewData::Extrusion::Tool: +// { +// GCodePreviewData::Color color; +// ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float)); +// return color; +// } +// } +// +// return GCodePreviewData::Color::Dummy; +// } +// }; +// +// // Helper structure for filters +// struct Filter +// { +// float value; +// ExtrusionRole role; +// GLVolume* volume; +// +// Filter(float value, ExtrusionRole role) +// : value(value) +// , role(role) +// , volume(nullptr) +// { +// } +// +// bool operator == (const Filter& other) const +// { +// if (value != other.value) +// return false; +// +// if (role != other.role) +// return false; +// +// return true; +// } +// }; +// +// typedef std::vector FiltersList; +// size_t initial_volumes_count = volumes.volumes.size(); +// +// // detects filters +// FiltersList filters; +// for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) +// { +// for (const ExtrusionPath& path : layer.paths) +// { +// ExtrusionRole role = path.role(); +// float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); +// if (std::find(filters.begin(), filters.end(), Filter(path_filter, role)) == filters.end()) +// filters.emplace_back(path_filter, role); +// } +// } +// +// // nothing to render, return +// if (filters.empty()) +// return; +// +// // creates a new volume for each filter +// for (Filter& filter : filters) +// { +// s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)volumes.volumes.size()); +// GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba); +// if (volume != nullptr) +// { +// filter.volume = volume; +// volumes.volumes.emplace_back(volume); +// } +// else +// { +// // an error occourred - restore to previous state and return +// s_gcode_preview_volume_index.first_volumes.pop_back(); +// if (initial_volumes_count != volumes.volumes.size()) +// { +// std::vector::iterator begin = volumes.volumes.begin() + initial_volumes_count; +// std::vector::iterator end = volumes.volumes.end(); +// for (std::vector::iterator it = begin; it < end; ++it) +// { +// GLVolume* volume = *it; +// delete volume; +// } +// volumes.volumes.erase(begin, end); +// return; +// } +// } +// } +// +// // populates volumes +// for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) +// { +// for (const ExtrusionPath& path : layer.paths) +// { +// float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); +// FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role())); +// if (filter != filters.end()) +// { +// filter->volume->print_zs.push_back(layer.z); +// filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.quad_indices.size()); +// filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.triangle_indices.size()); +// +// extrusionentity_to_verts(path, layer.z, *filter->volume); +// } +// } +// } +// +// // finalize volumes and sends geometry to gpu +// if (volumes.volumes.size() > initial_volumes_count) +// { +// for (size_t i = initial_volumes_count; i < volumes.volumes.size(); ++i) +// { +// GLVolume* volume = volumes.volumes[i]; +// volume->bounding_box = volume->indexed_vertex_array.bounding_box(); +// volume->indexed_vertex_array.finalize_geometry(use_VBOs); +// } +// } +//} +// +//void _3DScene::_load_gcode_travel_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs) +//{ +// size_t initial_volumes_count = volumes.volumes.size(); +// s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count); +// +// bool res = true; +// switch (preview_data.extrusion.view_type) +// { +// case GCodePreviewData::Extrusion::Feedrate: +// { +// res = _travel_paths_by_feedrate(preview_data, volumes); +// break; +// } +// case GCodePreviewData::Extrusion::Tool: +// { +// res = _travel_paths_by_tool(preview_data, volumes, tool_colors); +// break; +// } +// default: +// { +// res = _travel_paths_by_type(preview_data, volumes); +// break; +// } +// } +// +// if (!res) +// { +// // an error occourred - restore to previous state and return +// if (initial_volumes_count != volumes.volumes.size()) +// { +// std::vector::iterator begin = volumes.volumes.begin() + initial_volumes_count; +// std::vector::iterator end = volumes.volumes.end(); +// for (std::vector::iterator it = begin; it < end; ++it) +// { +// GLVolume* volume = *it; +// delete volume; +// } +// volumes.volumes.erase(begin, end); +// } +// +// return; +// } +// +// // finalize volumes and sends geometry to gpu +// if (volumes.volumes.size() > initial_volumes_count) +// { +// for (size_t i = initial_volumes_count; i < volumes.volumes.size(); ++i) +// { +// GLVolume* volume = volumes.volumes[i]; +// volume->bounding_box = volume->indexed_vertex_array.bounding_box(); +// volume->indexed_vertex_array.finalize_geometry(use_VBOs); +// } +// } +//} +// +//bool _3DScene::_travel_paths_by_type(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) +//{ +// // Helper structure for types +// struct Type +// { +// GCodePreviewData::Travel::EType value; +// GLVolume* volume; +// +// explicit Type(GCodePreviewData::Travel::EType value) +// : value(value) +// , volume(nullptr) +// { +// } +// +// bool operator == (const Type& other) const +// { +// return value == other.value; +// } +// }; +// +// typedef std::vector TypesList; +// +// // colors travels by travel type +// +// // detects types +// TypesList types; +// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) +// { +// if (std::find(types.begin(), types.end(), Type(polyline.type)) == types.end()) +// types.emplace_back(polyline.type); +// } +// +// // nothing to render, return +// if (types.empty()) +// return true; +// +// // creates a new volume for each type +// for (Type& type : types) +// { +// GLVolume* volume = new GLVolume(preview_data.travel.type_colors[type.value].rgba); +// if (volume == nullptr) +// return false; +// else +// { +// type.volume = volume; +// volumes.volumes.emplace_back(volume); +// } +// } +// +// // populates volumes +// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) +// { +// TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type)); +// if (type != types.end()) +// { +// type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); +// type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size()); +// type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size()); +// +// polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *type->volume); +// } +// } +// +// return true; +//} +// +//bool _3DScene::_travel_paths_by_feedrate(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) +//{ +// // Helper structure for feedrate +// struct Feedrate +// { +// float value; +// GLVolume* volume; +// +// explicit Feedrate(float value) +// : value(value) +// , volume(nullptr) +// { +// } +// +// bool operator == (const Feedrate& other) const +// { +// return value == other.value; +// } +// }; +// +// typedef std::vector FeedratesList; +// +// // colors travels by feedrate +// +// // detects feedrates +// FeedratesList feedrates; +// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) +// { +// if (std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)) == feedrates.end()) +// feedrates.emplace_back(polyline.feedrate); +// } +// +// // nothing to render, return +// if (feedrates.empty()) +// return true; +// +// // creates a new volume for each feedrate +// for (Feedrate& feedrate : feedrates) +// { +// GLVolume* volume = new GLVolume(preview_data.get_feedrate_color(feedrate.value).rgba); +// if (volume == nullptr) +// return false; +// else +// { +// feedrate.volume = volume; +// volumes.volumes.emplace_back(volume); +// } +// } +// +// // populates volumes +// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) +// { +// FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)); +// if (feedrate != feedrates.end()) +// { +// feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); +// feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size()); +// feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size()); +// +// polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *feedrate->volume); +// } +// } +// +// return true; +//} +// +//bool _3DScene::_travel_paths_by_tool(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors) +//{ +// // Helper structure for tool +// struct Tool +// { +// unsigned int value; +// GLVolume* volume; +// +// explicit Tool(unsigned int value) +// : value(value) +// , volume(nullptr) +// { +// } +// +// bool operator == (const Tool& other) const +// { +// return value == other.value; +// } +// }; +// +// typedef std::vector ToolsList; +// +// // colors travels by tool +// +// // detects tools +// ToolsList tools; +// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) +// { +// if (std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)) == tools.end()) +// tools.emplace_back(polyline.extruder_id); +// } +// +// // nothing to render, return +// if (tools.empty()) +// return true; +// +// // creates a new volume for each tool +// for (Tool& tool : tools) +// { +// GLVolume* volume = new GLVolume(tool_colors.data() + tool.value * 4); +// if (volume == nullptr) +// return false; +// else +// { +// tool.volume = volume; +// volumes.volumes.emplace_back(volume); +// } +// } +// +// // populates volumes +// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) +// { +// ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)); +// if (tool != tools.end()) +// { +// tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); +// tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size()); +// tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size()); +// +// polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume); +// } +// } +// +// return true; +//} +// +//void _3DScene::_load_gcode_retractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs) +//{ +// s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)volumes.volumes.size()); +// +// // nothing to render, return +// if (preview_data.retraction.positions.empty()) +// return; +// +// GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba); +// if (volume != nullptr) +// { +// volumes.volumes.emplace_back(volume); +// +// GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions); +// std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); +// +// for (const GCodePreviewData::Retraction::Position& position : copy) +// { +// volume->print_zs.push_back(unscale(position.position.z)); +// volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); +// volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); +// +// point3_to_verts(position.position, position.width, position.height, *volume); +// } +// +// // finalize volumes and sends geometry to gpu +// volume->bounding_box = volume->indexed_vertex_array.bounding_box(); +// volume->indexed_vertex_array.finalize_geometry(use_VBOs); +// } +//} +// +//void _3DScene::_load_gcode_unretractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs) +//{ +// s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)volumes.volumes.size()); +// +// // nothing to render, return +// if (preview_data.unretraction.positions.empty()) +// return; +// +// GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba); +// if (volume != nullptr) +// { +// volumes.volumes.emplace_back(volume); +// +// GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions); +// std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); +// +// for (const GCodePreviewData::Retraction::Position& position : copy) +// { +// volume->print_zs.push_back(unscale(position.position.z)); +// volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); +// volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); +// +// point3_to_verts(position.position, position.width, position.height, *volume); +// } +// +// // finalize volumes and sends geometry to gpu +// volume->bounding_box = volume->indexed_vertex_array.bounding_box(); +// volume->indexed_vertex_array.finalize_geometry(use_VBOs); +// } +//} +// +//void _3DScene::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) +//{ +// unsigned int size = (unsigned int)s_gcode_preview_volume_index.first_volumes.size(); +// for (unsigned int i = 0; i < size; ++i) +// { +// std::vector::iterator begin = volumes.volumes.begin() + s_gcode_preview_volume_index.first_volumes[i].id; +// std::vector::iterator end = (i + 1 < size) ? volumes.volumes.begin() + s_gcode_preview_volume_index.first_volumes[i + 1].id : volumes.volumes.end(); +// +// for (std::vector::iterator it = begin; it != end; ++it) +// { +// GLVolume* volume = *it; +// volume->outside_printer_detection_enabled = false; +// +// switch (s_gcode_preview_volume_index.first_volumes[i].type) +// { +// case GCodePreviewVolumeIndex::Extrusion: +// { +// if ((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag == erCustom) +// volume->zoom_to_volumes = false; +// +// volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag); +// break; +// } +// case GCodePreviewVolumeIndex::Travel: +// { +// volume->is_active = preview_data.travel.is_visible; +// volume->zoom_to_volumes = false; +// break; +// } +// case GCodePreviewVolumeIndex::Retraction: +// { +// volume->is_active = preview_data.retraction.is_visible; +// volume->zoom_to_volumes = false; +// break; +// } +// case GCodePreviewVolumeIndex::Unretraction: +// { +// volume->is_active = preview_data.unretraction.is_visible; +// volume->zoom_to_volumes = false; +// break; +// } +// case GCodePreviewVolumeIndex::Shell: +// { +// volume->is_active = preview_data.shell.is_visible; +// volume->color[3] = 0.25f; +// volume->zoom_to_volumes = false; +// break; +// } +// default: +// { +// volume->is_active = false; +// volume->zoom_to_volumes = false; +// break; +// } +// } +// } +// } +//} +// +//void _3DScene::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors) +//{ +// s_legend_texture.generate(preview_data, tool_colors); +//} +// +//void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs) +//{ +// size_t initial_volumes_count = volumes.volumes.size(); +// s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count); +// +// if (print.objects.empty()) +// // nothing to render, return +// return; +// +// // adds objects' volumes +// unsigned int object_id = 0; +// for (PrintObject* obj : print.objects) +// { +// ModelObject* model_obj = obj->model_object(); +// +// std::vector instance_ids(model_obj->instances.size()); +// for (int i = 0; i < model_obj->instances.size(); ++i) +// { +// instance_ids[i] = i; +// } +// +// for (ModelInstance* instance : model_obj->instances) +// { +// volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", use_VBOs); +// } +// +// ++object_id; +// } +// +// // adds wipe tower's volume +// coordf_t max_z = print.objects[0]->model_object()->get_model()->bounding_box().max.z; +// const PrintConfig& config = print.config; +// unsigned int extruders_count = config.nozzle_diameter.size(); +// if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) { +// const float width_per_extruder = 15.f; // a simple workaround after wipe_tower_per_color_wipe got obsolete +// volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, use_VBOs); +// } +//} +//################################################################################################################## } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index aff40f4d7f..69530b84f2 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -23,6 +23,13 @@ class Model; class ModelObject; class GCodePreviewData; class DynamicPrintConfig; +//################################################################################################################## +class ExtrusionPath; +class ExtrusionMultiPath; +class ExtrusionLoop; +class ExtrusionEntity; +class ExtrusionEntityCollection; +//################################################################################################################## // A container for interleaved arrays of 3D vertices and normals, // possibly indexed by triangles and / or quads. @@ -443,34 +450,36 @@ private: class _3DScene { - struct GCodePreviewVolumeIndex - { - enum EType - { - Extrusion, - Travel, - Retraction, - Unretraction, - Shell, - Num_Geometry_Types - }; - - struct FirstVolume - { - EType type; - unsigned int flag; - // Index of the first volume in a GLVolumeCollection. - unsigned int id; - - FirstVolume(EType type, unsigned int flag, unsigned int id) : type(type), flag(flag), id(id) {} - }; - - std::vector first_volumes; - - void reset() { first_volumes.clear(); } - }; - - static GCodePreviewVolumeIndex s_gcode_preview_volume_index; +//################################################################################################################## +// struct GCodePreviewVolumeIndex +// { +// enum EType +// { +// Extrusion, +// Travel, +// Retraction, +// Unretraction, +// Shell, +// Num_Geometry_Types +// }; +// +// struct FirstVolume +// { +// EType type; +// unsigned int flag; +// // Index of the first volume in a GLVolumeCollection. +// unsigned int id; +// +// FirstVolume(EType type, unsigned int flag, unsigned int id) : type(type), flag(flag), id(id) {} +// }; +// +// std::vector first_volumes; +// +// void reset() { first_volumes.clear(); } +// }; +// +// static GCodePreviewVolumeIndex s_gcode_preview_volume_index; +//################################################################################################################## class TextureBase { @@ -576,6 +585,7 @@ public: static void enable_picking(wxGLCanvas* canvas, bool enable); static void enable_moving(wxGLCanvas* canvas, bool enable); static void enable_shader(wxGLCanvas* canvas, bool enable); + static void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); static void allow_multisample(wxGLCanvas* canvas, bool allow); static void zoom_to_bed(wxGLCanvas* canvas); @@ -600,8 +610,15 @@ public: // static void _glew_init(); //################################################################################################################## - static void load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector& str_tool_colors, bool use_VBOs); +//################################################################################################################## + static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); +// static void load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector& str_tool_colors, bool use_VBOs); +//################################################################################################################## +//################################################################################################################## + // generates the legend texture in dependence of the current shown view type + static void generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors); +//################################################################################################################## static unsigned int get_legend_texture_width(); static unsigned int get_legend_texture_height(); @@ -634,24 +651,37 @@ public: const std::vector &tool_colors_str, bool use_VBOs); +//################################################################################################################## + static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); + static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume); + static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume); + static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GLVolume& volume); + static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GLVolume& volume); + static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume); + static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume); + static void point3_to_verts(const Point3& point, double width, double height, GLVolume& volume); +//################################################################################################################## + private: - // generates gcode extrusion paths geometry - static void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs); - // generates gcode travel paths geometry - static void _load_gcode_travel_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs); - static bool _travel_paths_by_type(const GCodePreviewData& preview_data, GLVolumeCollection& volumes); - static bool _travel_paths_by_feedrate(const GCodePreviewData& preview_data, GLVolumeCollection& volumes); - static bool _travel_paths_by_tool(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors); - // generates gcode retractions geometry - static void _load_gcode_retractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs); - // generates gcode unretractions geometry - static void _load_gcode_unretractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs); - // sets gcode geometry visibility according to user selection - static void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data, GLVolumeCollection& volumes); - // generates the legend texture in dependence of the current shown view type - static void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors); - // generates objects and wipe tower geometry - static void _load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs); +//################################################################################################################## +// // generates gcode extrusion paths geometry +// static void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs); +// // generates gcode travel paths geometry +// static void _load_gcode_travel_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs); +// static bool _travel_paths_by_type(const GCodePreviewData& preview_data, GLVolumeCollection& volumes); +// static bool _travel_paths_by_feedrate(const GCodePreviewData& preview_data, GLVolumeCollection& volumes); +// static bool _travel_paths_by_tool(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors); +// // generates gcode retractions geometry +// static void _load_gcode_retractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs); +// // generates gcode unretractions geometry +// static void _load_gcode_unretractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs); +// // sets gcode geometry visibility according to user selection +// static void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data, GLVolumeCollection& volumes); +// // generates the legend texture in dependence of the current shown view type +// static void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors); +// // generates objects and wipe tower geometry +// static void _load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs); +//################################################################################################################## }; } diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index d48cc0ccc1..7251e600b5 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -5,6 +5,7 @@ #include "../../libslic3r/ClipperUtils.hpp" #include "../../libslic3r/PrintConfig.hpp" #include "../../libslic3r/Print.hpp" +#include "../../libslic3r/GCode/PreviewData.hpp" #include @@ -943,7 +944,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_print(nullptr) , m_dirty(true) , m_use_VBOs(false) - , m_first_render(true) + , m_force_zoom_to_bed_enabled(false) , m_apply_zoom_to_volumes_filter(false) , m_hover_volume_id(-1) , m_warning_texture_enabled(false) @@ -1190,6 +1191,12 @@ void GLCanvas3D::enable_shader(bool enable) { m_shader_enabled = enable; } + +void GLCanvas3D::enable_force_zoom_to_bed(bool enable) +{ + m_force_zoom_to_bed_enabled = enable; +} + void GLCanvas3D::allow_multisample(bool allow) { m_multisample_allowed = allow; @@ -1263,14 +1270,12 @@ void GLCanvas3D::render() if (!is_shown_on_screen()) return; - if (!set_current()) + // ensures that the proper context is selected and that this canvas is initialized + if (!set_current() || !_3DScene::init(m_canvas)) return; - if (!_3DScene::init(m_canvas)) - return; - - if (m_first_render) - _before_first_render(); + if (m_force_zoom_to_bed_enabled) + _force_zoom_to_bed(); _camera_tranform(); @@ -1328,6 +1333,44 @@ void GLCanvas3D::set_toolpaths_range(double low, double high) m_volumes->set_range(low, high); } +void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors) +{ + if ((m_canvas != nullptr) && (m_volumes != nullptr) && (m_print != nullptr)) + { + // ensures that the proper context is selected and that this canvas is initialized + if (!set_current() || !_3DScene::init(m_canvas)) + return; + + if (m_volumes->empty()) + { + std::vector tool_colors = _parse_colors(str_tool_colors); + + m_gcode_preview_volume_index.reset(); + + _load_gcode_extrusion_paths(preview_data, tool_colors); + _load_gcode_travel_paths(preview_data, tool_colors); + _load_gcode_retractions(preview_data); + _load_gcode_unretractions(preview_data); + + if (m_volumes->empty()) + _3DScene::reset_legend_texture(); + else + { + _3DScene::generate_legend_texture(preview_data, tool_colors); + + // removes empty volumes + m_volumes->volumes.erase(std::remove_if(m_volumes->volumes.begin(), m_volumes->volumes.end(), + [](const GLVolume* volume) { return volume->print_zs.empty(); }), + m_volumes->volumes.end()); + + _load_shells(); + } + } + + _update_gcode_volumes_visibility(preview_data); + } +} + void GLCanvas3D::register_on_viewport_changed_callback(void* callback) { if (callback != nullptr) @@ -1735,10 +1778,10 @@ Point GLCanvas3D::get_local_mouse_position() const return Point(mouse_pos.x, mouse_pos.y); } -void GLCanvas3D::_before_first_render() +void GLCanvas3D::_force_zoom_to_bed() { zoom_to_bed(); - m_first_render = false; + m_force_zoom_to_bed_enabled = false; } void GLCanvas3D::_resize(unsigned int w, unsigned int h) @@ -2352,5 +2395,605 @@ void GLCanvas3D::_stop_timer() m_timer->Stop(); } +static inline int hex_digit_to_int(const char c) +{ + return + (c >= '0' && c <= '9') ? int(c - '0') : + (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : + (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; +} + +std::vector GLCanvas3D::_parse_colors(const std::vector& colors) +{ + std::vector output(colors.size() * 4, 1.0f); + for (size_t i = 0; i < colors.size(); ++i) { + const std::string& color = colors[i]; + const char* c = color.data() + 1; + if ((color.size() == 7) && (color.front() == '#')) + { + for (size_t j = 0; j < 3; ++j) + { + int digit1 = hex_digit_to_int(*c++); + int digit2 = hex_digit_to_int(*c++); + if ((digit1 == -1) || (digit2 == -1)) + break; + + output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.0f; + } + } + } + return output; +} + +void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors) +{ + // helper functions to select data in dependence of the extrusion view type + struct Helper + { + static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path) + { + switch (type) + { + case GCodePreviewData::Extrusion::FeatureType: + return (float)path.role(); + case GCodePreviewData::Extrusion::Height: + return path.height; + case GCodePreviewData::Extrusion::Width: + return path.width; + case GCodePreviewData::Extrusion::Feedrate: + return path.feedrate; + case GCodePreviewData::Extrusion::VolumetricRate: + return path.feedrate * (float)path.mm3_per_mm; + case GCodePreviewData::Extrusion::Tool: + return (float)path.extruder_id; + } + + return 0.0f; + } + + static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector& tool_colors, float value) + { + switch (data.extrusion.view_type) + { + case GCodePreviewData::Extrusion::FeatureType: + return data.get_extrusion_role_color((ExtrusionRole)(int)value); + case GCodePreviewData::Extrusion::Height: + return data.get_height_color(value); + case GCodePreviewData::Extrusion::Width: + return data.get_width_color(value); + case GCodePreviewData::Extrusion::Feedrate: + return data.get_feedrate_color(value); + case GCodePreviewData::Extrusion::VolumetricRate: + return data.get_volumetric_rate_color(value); + case GCodePreviewData::Extrusion::Tool: + { + GCodePreviewData::Color color; + ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float)); + return color; + } + } + + return GCodePreviewData::Color::Dummy; + } + }; + + // Helper structure for filters + struct Filter + { + float value; + ExtrusionRole role; + GLVolume* volume; + + Filter(float value, ExtrusionRole role) + : value(value) + , role(role) + , volume(nullptr) + { + } + + bool operator == (const Filter& other) const + { + if (value != other.value) + return false; + + if (role != other.role) + return false; + + return true; + } + }; + + typedef std::vector FiltersList; + size_t initial_volumes_count = m_volumes->volumes.size(); + + // detects filters + FiltersList filters; + for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) + { + for (const ExtrusionPath& path : layer.paths) + { + ExtrusionRole role = path.role(); + float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); + if (std::find(filters.begin(), filters.end(), Filter(path_filter, role)) == filters.end()) + filters.emplace_back(path_filter, role); + } + } + + // nothing to render, return + if (filters.empty()) + return; + + // creates a new volume for each filter + for (Filter& filter : filters) + { + m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)m_volumes->volumes.size()); + GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba); + if (volume != nullptr) + { + filter.volume = volume; + m_volumes->volumes.emplace_back(volume); + } + else + { + // an error occourred - restore to previous state and return + m_gcode_preview_volume_index.first_volumes.pop_back(); + if (initial_volumes_count != m_volumes->volumes.size()) + { + std::vector::iterator begin = m_volumes->volumes.begin() + initial_volumes_count; + std::vector::iterator end = m_volumes->volumes.end(); + for (std::vector::iterator it = begin; it < end; ++it) + { + GLVolume* volume = *it; + delete volume; + } + m_volumes->volumes.erase(begin, end); + return; + } + } + } + + // populates volumes + for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) + { + for (const ExtrusionPath& path : layer.paths) + { + float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); + FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role())); + if (filter != filters.end()) + { + filter->volume->print_zs.push_back(layer.z); + filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.quad_indices.size()); + filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.triangle_indices.size()); + + _3DScene::extrusionentity_to_verts(path, layer.z, *filter->volume); + } + } + } + + // finalize volumes and sends geometry to gpu + if (m_volumes->volumes.size() > initial_volumes_count) + { + for (size_t i = initial_volumes_count; i < m_volumes->volumes.size(); ++i) + { + GLVolume* volume = m_volumes->volumes[i]; + volume->bounding_box = volume->indexed_vertex_array.bounding_box(); + volume->indexed_vertex_array.finalize_geometry(m_use_VBOs); + } + } +} + +void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors) +{ + size_t initial_volumes_count = m_volumes->volumes.size(); + m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count); + + bool res = true; + switch (preview_data.extrusion.view_type) + { + case GCodePreviewData::Extrusion::Feedrate: + { + res = _travel_paths_by_feedrate(preview_data); + break; + } + case GCodePreviewData::Extrusion::Tool: + { + res = _travel_paths_by_tool(preview_data, tool_colors); + break; + } + default: + { + res = _travel_paths_by_type(preview_data); + break; + } + } + + if (!res) + { + // an error occourred - restore to previous state and return + if (initial_volumes_count != m_volumes->volumes.size()) + { + std::vector::iterator begin = m_volumes->volumes.begin() + initial_volumes_count; + std::vector::iterator end = m_volumes->volumes.end(); + for (std::vector::iterator it = begin; it < end; ++it) + { + GLVolume* volume = *it; + delete volume; + } + m_volumes->volumes.erase(begin, end); + } + + return; + } + + // finalize volumes and sends geometry to gpu + if (m_volumes->volumes.size() > initial_volumes_count) + { + for (size_t i = initial_volumes_count; i < m_volumes->volumes.size(); ++i) + { + GLVolume* volume = m_volumes->volumes[i]; + volume->bounding_box = volume->indexed_vertex_array.bounding_box(); + volume->indexed_vertex_array.finalize_geometry(m_use_VBOs); + } + } +} + +bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data) +{ + // Helper structure for types + struct Type + { + GCodePreviewData::Travel::EType value; + GLVolume* volume; + + explicit Type(GCodePreviewData::Travel::EType value) + : value(value) + , volume(nullptr) + { + } + + bool operator == (const Type& other) const + { + return value == other.value; + } + }; + + typedef std::vector TypesList; + + // colors travels by travel type + + // detects types + TypesList types; + for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) + { + if (std::find(types.begin(), types.end(), Type(polyline.type)) == types.end()) + types.emplace_back(polyline.type); + } + + // nothing to render, return + if (types.empty()) + return true; + + // creates a new volume for each type + for (Type& type : types) + { + GLVolume* volume = new GLVolume(preview_data.travel.type_colors[type.value].rgba); + if (volume == nullptr) + return false; + else + { + type.volume = volume; + m_volumes->volumes.emplace_back(volume); + } + } + + // populates volumes + for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) + { + TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type)); + if (type != types.end()) + { + type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); + type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size()); + type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size()); + + _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *type->volume); + } + } + + return true; +} + +bool GLCanvas3D::_travel_paths_by_feedrate(const GCodePreviewData& preview_data) +{ + // Helper structure for feedrate + struct Feedrate + { + float value; + GLVolume* volume; + + explicit Feedrate(float value) + : value(value) + , volume(nullptr) + { + } + + bool operator == (const Feedrate& other) const + { + return value == other.value; + } + }; + + typedef std::vector FeedratesList; + + // colors travels by feedrate + + // detects feedrates + FeedratesList feedrates; + for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) + { + if (std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)) == feedrates.end()) + feedrates.emplace_back(polyline.feedrate); + } + + // nothing to render, return + if (feedrates.empty()) + return true; + + // creates a new volume for each feedrate + for (Feedrate& feedrate : feedrates) + { + GLVolume* volume = new GLVolume(preview_data.get_feedrate_color(feedrate.value).rgba); + if (volume == nullptr) + return false; + else + { + feedrate.volume = volume; + m_volumes->volumes.emplace_back(volume); + } + } + + // populates volumes + for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) + { + FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)); + if (feedrate != feedrates.end()) + { + feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); + feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size()); + feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size()); + + _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *feedrate->volume); + } + } + + return true; +} + +bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, const std::vector& tool_colors) +{ + // Helper structure for tool + struct Tool + { + unsigned int value; + GLVolume* volume; + + explicit Tool(unsigned int value) + : value(value) + , volume(nullptr) + { + } + + bool operator == (const Tool& other) const + { + return value == other.value; + } + }; + + typedef std::vector ToolsList; + + // colors travels by tool + + // detects tools + ToolsList tools; + for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) + { + if (std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)) == tools.end()) + tools.emplace_back(polyline.extruder_id); + } + + // nothing to render, return + if (tools.empty()) + return true; + + // creates a new volume for each tool + for (Tool& tool : tools) + { + GLVolume* volume = new GLVolume(tool_colors.data() + tool.value * 4); + if (volume == nullptr) + return false; + else + { + tool.volume = volume; + m_volumes->volumes.emplace_back(volume); + } + } + + // populates volumes + for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) + { + ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)); + if (tool != tools.end()) + { + tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); + tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size()); + tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size()); + + _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume); + } + } + + return true; +} + +void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data) +{ + m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)m_volumes->volumes.size()); + + // nothing to render, return + if (preview_data.retraction.positions.empty()) + return; + + GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba); + if (volume != nullptr) + { + m_volumes->volumes.emplace_back(volume); + + GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions); + std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); + + for (const GCodePreviewData::Retraction::Position& position : copy) + { + volume->print_zs.push_back(unscale(position.position.z)); + volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); + volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); + + _3DScene::point3_to_verts(position.position, position.width, position.height, *volume); + } + + // finalize volumes and sends geometry to gpu + volume->bounding_box = volume->indexed_vertex_array.bounding_box(); + volume->indexed_vertex_array.finalize_geometry(m_use_VBOs); + } +} + +void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data) +{ + m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)m_volumes->volumes.size()); + + // nothing to render, return + if (preview_data.unretraction.positions.empty()) + return; + + GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba); + if (volume != nullptr) + { + m_volumes->volumes.emplace_back(volume); + + GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions); + std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); + + for (const GCodePreviewData::Retraction::Position& position : copy) + { + volume->print_zs.push_back(unscale(position.position.z)); + volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); + volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); + + _3DScene::point3_to_verts(position.position, position.width, position.height, *volume); + } + + // finalize volumes and sends geometry to gpu + volume->bounding_box = volume->indexed_vertex_array.bounding_box(); + volume->indexed_vertex_array.finalize_geometry(m_use_VBOs); + } +} + +void GLCanvas3D::_load_shells() +{ + size_t initial_volumes_count = m_volumes->volumes.size(); + m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count); + + if (m_print->objects.empty()) + // nothing to render, return + return; + + // adds objects' volumes + unsigned int object_id = 0; + for (PrintObject* obj : m_print->objects) + { + ModelObject* model_obj = obj->model_object(); + + std::vector instance_ids(model_obj->instances.size()); + for (int i = 0; i < model_obj->instances.size(); ++i) + { + instance_ids[i] = i; + } + + for (ModelInstance* instance : model_obj->instances) + { + m_volumes->load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs); + } + + ++object_id; + } + + // adds wipe tower's volume + coordf_t max_z = m_print->objects[0]->model_object()->get_model()->bounding_box().max.z; + const PrintConfig& config = m_print->config; + unsigned int extruders_count = config.nozzle_diameter.size(); + if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) { + const float width_per_extruder = 15.f; // a simple workaround after wipe_tower_per_color_wipe got obsolete + m_volumes->load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, m_use_VBOs); + } +} + +void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data) +{ + unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size(); + for (unsigned int i = 0; i < size; ++i) + { + std::vector::iterator begin = m_volumes->volumes.begin() + m_gcode_preview_volume_index.first_volumes[i].id; + std::vector::iterator end = (i + 1 < size) ? m_volumes->volumes.begin() + m_gcode_preview_volume_index.first_volumes[i + 1].id : m_volumes->volumes.end(); + + for (std::vector::iterator it = begin; it != end; ++it) + { + GLVolume* volume = *it; + volume->outside_printer_detection_enabled = false; + + switch (m_gcode_preview_volume_index.first_volumes[i].type) + { + case GCodePreviewVolumeIndex::Extrusion: + { + if ((ExtrusionRole)m_gcode_preview_volume_index.first_volumes[i].flag == erCustom) + volume->zoom_to_volumes = false; + + volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)m_gcode_preview_volume_index.first_volumes[i].flag); + break; + } + case GCodePreviewVolumeIndex::Travel: + { + volume->is_active = preview_data.travel.is_visible; + volume->zoom_to_volumes = false; + break; + } + case GCodePreviewVolumeIndex::Retraction: + { + volume->is_active = preview_data.retraction.is_visible; + volume->zoom_to_volumes = false; + break; + } + case GCodePreviewVolumeIndex::Unretraction: + { + volume->is_active = preview_data.unretraction.is_visible; + volume->zoom_to_volumes = false; + break; + } + case GCodePreviewVolumeIndex::Shell: + { + volume->is_active = preview_data.shell.is_visible; + volume->color[3] = 0.25f; + volume->zoom_to_volumes = false; + break; + } + default: + { + volume->is_active = false; + volume->zoom_to_volumes = false; + break; + } + } + } + } +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index d2c9f259cc..b7ba7ef6b4 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -24,6 +24,7 @@ class GLShader; class ExPolygon; class Print; class PrintObject; +class GCodePreviewData; namespace GUI { @@ -81,6 +82,33 @@ public: class GLCanvas3D { + struct GCodePreviewVolumeIndex + { + enum EType + { + Extrusion, + Travel, + Retraction, + Unretraction, + Shell, + Num_Geometry_Types + }; + + struct FirstVolume + { + EType type; + unsigned int flag; + // Index of the first volume in a GLVolumeCollection. + unsigned int id; + + FirstVolume(EType type, unsigned int flag, unsigned int id) : type(type), flag(flag), id(id) {} + }; + + std::vector first_volumes; + + void reset() { first_volumes.clear(); } + }; + public: struct Camera { @@ -303,7 +331,7 @@ private: bool m_dirty; bool m_use_VBOs; - bool m_first_render; + bool m_force_zoom_to_bed_enabled; bool m_apply_zoom_to_volumes_filter; mutable int m_hover_volume_id; bool m_warning_texture_enabled; @@ -313,6 +341,8 @@ private: bool m_shader_enabled; bool m_multisample_allowed; + GCodePreviewVolumeIndex m_gcode_preview_volume_index; + PerlCallback m_on_viewport_changed_callback; PerlCallback m_on_double_click_callback; PerlCallback m_on_right_click_callback; @@ -364,6 +394,7 @@ public: void enable_picking(bool enable); void enable_moving(bool enable); void enable_shader(bool enable); + void enable_force_zoom_to_bed(bool enable); void allow_multisample(bool allow); void zoom_to_bed(); @@ -379,6 +410,8 @@ public: std::vector get_current_print_zs(bool active_only) const; void set_toolpaths_range(double low, double high); + void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); + void register_on_viewport_changed_callback(void* callback); void register_on_double_click_callback(void* callback); void register_on_right_click_callback(void* callback); @@ -398,7 +431,7 @@ public: Point get_local_mouse_position() const; private: - void _before_first_render(); + void _force_zoom_to_bed(); void _resize(unsigned int w, unsigned int h); @@ -437,6 +470,24 @@ private: void _start_timer(); void _stop_timer(); + + static std::vector _parse_colors(const std::vector& colors); + + // generates gcode extrusion paths geometry + void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors); + // generates gcode travel paths geometry + void _load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors); + bool _travel_paths_by_type(const GCodePreviewData& preview_data); + bool _travel_paths_by_feedrate(const GCodePreviewData& preview_data); + bool _travel_paths_by_tool(const GCodePreviewData& preview_data, const std::vector& tool_colors); + // generates gcode retractions geometry + void _load_gcode_retractions(const GCodePreviewData& preview_data); + // generates gcode unretractions geometry + void _load_gcode_unretractions(const GCodePreviewData& preview_data); + // generates objects and wipe tower geometry + void _load_shells(); + // sets gcode geometry visibility according to user selection + void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data); }; } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index cbd9c15cb9..850ccc6973 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -377,6 +377,13 @@ void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) it->second->enable_shader(enable); } +void GLCanvas3DManager::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_force_zoom_to_bed(enable); +} + void GLCanvas3DManager::allow_multisample(wxGLCanvas* canvas, bool allow) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -443,6 +450,16 @@ void GLCanvas3DManager::set_toolpaths_range(wxGLCanvas* canvas, double low, doub it->second->set_toolpaths_range(low, high); } +void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors) +{ + if (preview_data == nullptr) + return; + + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->load_gcode_preview(*preview_data, str_tool_colors); +} + void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index e28a0aba9e..5a874de120 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -80,6 +80,7 @@ public: void enable_picking(wxGLCanvas* canvas, bool enable); void enable_moving(wxGLCanvas* canvas, bool enable); void enable_shader(wxGLCanvas* canvas, bool enable); + void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); void allow_multisample(wxGLCanvas* canvas, bool allow); void zoom_to_bed(wxGLCanvas* canvas); @@ -94,6 +95,8 @@ public: std::vector get_current_print_zs(wxGLCanvas* canvas, bool active_only) const; void set_toolpaths_range(wxGLCanvas* canvas, double low, double high); + void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); + void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index fac52a02ee..8b454d3c3d 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -341,6 +341,13 @@ enable_shader(canvas, enable) CODE: _3DScene::enable_shader((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); +void +enable_force_zoom_to_bed(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_force_zoom_to_bed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void allow_multisample(canvas, allow) SV *canvas; @@ -531,13 +538,11 @@ _load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs) _3DScene::_load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs != 0); void -load_gcode_preview(print, preview_data, volumes, str_tool_colors, use_VBOs) - Print *print; +load_gcode_preview(canvas, preview_data, str_tool_colors) + SV *canvas; GCodePreviewData *preview_data; - GLVolumeCollection *volumes; std::vector str_tool_colors; - int use_VBOs; CODE: - _3DScene::load_gcode_preview(print, preview_data, volumes, str_tool_colors, use_VBOs != 0); + _3DScene::load_gcode_preview((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), preview_data, str_tool_colors); %} From f262ec9094ee47d793d24bfc099bc79bd5181838 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 5 Jun 2018 12:24:26 +0200 Subject: [PATCH 064/117] Modified logic to finalize volumes geometry --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 31 ++++++++++++++++++++++--------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 1 + 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 7251e600b5..37f1d6b50d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -943,6 +943,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_config(nullptr) , m_print(nullptr) , m_dirty(true) + , m_initialized(false) , m_use_VBOs(false) , m_force_zoom_to_bed_enabled(false) , m_apply_zoom_to_volumes_filter(false) @@ -971,6 +972,11 @@ GLCanvas3D::~GLCanvas3D() bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) { + if (m_initialized) + return true; + + std::cout << "init: " << (void*)m_canvas << " (" << (void*)this << ")" << std::endl; + ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f); ::glClearDepth(1.0f); @@ -1023,6 +1029,13 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) m_use_VBOs = useVBOs; m_layers_editing.set_use_legacy_opengl(use_legacy_opengl); + // on linux the gl context is not valid until the canvas is not shown on screen + // we defer the geometry finalization of volumes until the first call to render() + if ((m_volumes != nullptr) && !m_volumes->empty()) + m_volumes->finalize_geometry(m_use_VBOs); + + m_initialized = true; + return true; } @@ -1337,8 +1350,8 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const { if ((m_canvas != nullptr) && (m_volumes != nullptr) && (m_print != nullptr)) { - // ensures that the proper context is selected and that this canvas is initialized - if (!set_current() || !_3DScene::init(m_canvas)) + // ensures that the proper context is selected + if (!set_current()) return; if (m_volumes->empty()) @@ -2577,7 +2590,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat { GLVolume* volume = m_volumes->volumes[i]; volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(m_use_VBOs); + volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); } } } @@ -2632,7 +2645,7 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, { GLVolume* volume = m_volumes->volumes[i]; volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(m_use_VBOs); + volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); } } } @@ -2862,7 +2875,7 @@ void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data) // finalize volumes and sends geometry to gpu volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(m_use_VBOs); + volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); } } @@ -2893,7 +2906,7 @@ void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data) // finalize volumes and sends geometry to gpu volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(m_use_VBOs); + volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); } } @@ -2920,7 +2933,7 @@ void GLCanvas3D::_load_shells() for (ModelInstance* instance : model_obj->instances) { - m_volumes->load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs); + m_volumes->load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs && m_initialized); } ++object_id; @@ -2931,8 +2944,8 @@ void GLCanvas3D::_load_shells() const PrintConfig& config = m_print->config; unsigned int extruders_count = config.nozzle_diameter.size(); if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) { - const float width_per_extruder = 15.f; // a simple workaround after wipe_tower_per_color_wipe got obsolete - m_volumes->load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, m_use_VBOs); + const float width_per_extruder = 15.0f; // a simple workaround after wipe_tower_per_color_wipe got obsolete + m_volumes->load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, m_use_VBOs && m_initialized); } } diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index b7ba7ef6b4..69934a8221 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -330,6 +330,7 @@ private: Print* m_print; bool m_dirty; + bool m_initialized; bool m_use_VBOs; bool m_force_zoom_to_bed_enabled; bool m_apply_zoom_to_volumes_filter; From a8254e005317bbd9c6b95cd211adf5a6dd1fb4bd Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 5 Jun 2018 14:09:36 +0200 Subject: [PATCH 065/117] Generation of preview paths moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 62 +- lib/Slic3r/GUI/Plater/3DPreview.pm | 14 +- xs/src/slic3r/GUI/3DScene.cpp | 745 ++++++++++++------------ xs/src/slic3r/GUI/3DScene.hpp | 41 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 414 ++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 13 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 24 + xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 3 + xs/xsp/GUI_3DScene.xsp | 26 +- 9 files changed, 879 insertions(+), 463 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 1476e8c221..a55c4ab16b 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -2199,56 +2199,36 @@ sub load_object { return @{$volume_indices}; } -# Create 3D thick extrusion lines for a skirt and brim. -# Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes. -sub load_print_toolpaths { - my ($self, $print, $colors) = @_; - #============================================================================================================================== - my $useVBOs = Slic3r::GUI::_3DScene::use_VBOs(); - $self->SetCurrent($self->GetContext) if $useVBOs; - Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes, $colors, $useVBOs) - if ($print->step_done(STEP_SKIRT) && $print->step_done(STEP_BRIM)); - +## Create 3D thick extrusion lines for a skirt and brim. +## Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes. +#sub load_print_toolpaths { +# my ($self, $print, $colors) = @_; +# # $self->SetCurrent($self->GetContext) if $self->UseVBOs; # Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes, $colors, $self->UseVBOs) # if ($print->step_done(STEP_SKIRT) && $print->step_done(STEP_BRIM)); -#============================================================================================================================== -} - -# Create 3D thick extrusion lines for object forming extrusions. -# Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, -# one for perimeters, one for infill and one for supports. -sub load_print_object_toolpaths { - my ($self, $object, $colors) = @_; - -#============================================================================================================================== - my $useVBOs = Slic3r::GUI::_3DScene::use_VBOs(); - $self->SetCurrent($self->GetContext) if $useVBOs; - Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $colors, $useVBOs); - +#} +# +## Create 3D thick extrusion lines for object forming extrusions. +## Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, +## one for perimeters, one for infill and one for supports. +#sub load_print_object_toolpaths { +# my ($self, $object, $colors) = @_; +# # $self->SetCurrent($self->GetContext) if $self->UseVBOs; # Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $colors, $self->UseVBOs); -#============================================================================================================================== -} - -# Create 3D thick extrusion lines for wipe tower extrusions. -sub load_wipe_tower_toolpaths { - my ($self, $print, $colors) = @_; - -#============================================================================================================================== - my $useVBOs = Slic3r::GUI::_3DScene::use_VBOs(); - $self->SetCurrent($self->GetContext) if $useVBOs; - Slic3r::GUI::_3DScene::_load_wipe_tower_toolpaths($print, $self->volumes, $colors, $useVBOs) - if ($print->step_done(STEP_WIPE_TOWER)); - +#} +# +## Create 3D thick extrusion lines for wipe tower extrusions. +#sub load_wipe_tower_toolpaths { +# my ($self, $print, $colors) = @_; +# # $self->SetCurrent($self->GetContext) if $self->UseVBOs; # Slic3r::GUI::_3DScene::_load_wipe_tower_toolpaths($print, $self->volumes, $colors, $self->UseVBOs) # if ($print->step_done(STEP_WIPE_TOWER)); -#============================================================================================================================== -} - -#============================================================================================================================== +#} +# #sub load_gcode_preview { # my ($self, $print, $gcode_preview_data, $colors) = @_; # diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 9ea5069b41..7b713b46fc 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -376,10 +376,18 @@ sub load_print { if ($self->gcode_preview_data->empty) { # load skirt and brim - $self->canvas->load_print_toolpaths($self->print, \@colors); - $self->canvas->load_wipe_tower_toolpaths($self->print, \@colors); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print); + Slic3r::GUI::_3DScene::load_print_toolpaths($self->canvas); + Slic3r::GUI::_3DScene::load_wipe_tower_toolpaths($self->canvas, \@colors); +# $self->canvas->load_print_toolpaths($self->print, \@colors); +# $self->canvas->load_wipe_tower_toolpaths($self->print, \@colors); +#============================================================================================================================== foreach my $object (@{$self->print->objects}) { - $self->canvas->load_print_object_toolpaths($object, \@colors); +#============================================================================================================================== + Slic3r::GUI::_3DScene::load_print_object_toolpaths($self->canvas, $object, \@colors); +# $self->canvas->load_print_object_toolpaths($object, \@colors); +#============================================================================================================================== # Show the objects in very transparent color. #my @volume_ids = $self->canvas->load_object($object->model_object); #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 47095e1b17..89b7d0a118 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1348,8 +1348,11 @@ static void point_to_indexed_vertex_array(const Point3& point, volume.push_triangle(idxs[0], idxs[3], idxs[4]); } -static void thick_lines_to_verts( - const Lines &lines, +//################################################################################################################## +void _3DScene::thick_lines_to_verts( +//static void thick_lines_to_verts( +//################################################################################################################## + const Lines &lines, const std::vector &widths, const std::vector &heights, bool closed, @@ -1359,7 +1362,10 @@ static void thick_lines_to_verts( thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, top_z, volume.indexed_vertex_array); } -static void thick_lines_to_verts(const Lines3& lines, +//################################################################################################################## +void _3DScene::thick_lines_to_verts(const Lines3& lines, +//static void thick_lines_to_verts(const Lines3& lines, +//################################################################################################################## const std::vector& widths, const std::vector& heights, bool closed, @@ -2010,6 +2016,21 @@ static inline std::vector parse_colors(const std::vector &sc } //################################################################################################################## +void _3DScene::load_print_toolpaths(wxGLCanvas* canvas) +{ + s_canvas_mgr.load_print_toolpaths(canvas); +} + +void _3DScene::load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& str_tool_colors) +{ + s_canvas_mgr.load_print_object_toolpaths(canvas, print_object, str_tool_colors); +} + +void _3DScene::load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector& str_tool_colors) +{ + s_canvas_mgr.load_wipe_tower_toolpaths(canvas, str_tool_colors); +} + void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors) { s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors); @@ -2102,366 +2123,366 @@ unsigned int _3DScene::finalize_warning_texture() return s_warning_texture.finalize(); } -// Create 3D thick extrusion lines for a skirt and brim. -// Adds a new Slic3r::GUI::3DScene::Volume to volumes. -void _3DScene::_load_print_toolpaths( - const Print *print, - GLVolumeCollection *volumes, - const std::vector &tool_colors, - bool use_VBOs) -{ - if (!print->has_skirt() && print->config.brim_width.value == 0) - return; - - const float color[] = { 0.5f, 1.0f, 0.5f, 1.f }; // greenish - - // number of skirt layers - size_t total_layer_count = 0; - for (const PrintObject *print_object : print->objects) - total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); - size_t skirt_height = print->has_infinite_skirt() ? - total_layer_count : - std::min(print->config.skirt_height.value, total_layer_count); - if (skirt_height == 0 && print->config.brim_width.value > 0) - skirt_height = 1; - - // get first skirt_height layers (maybe this should be moved to a PrintObject method?) - const PrintObject *object0 = print->objects.front(); - std::vector print_zs; - print_zs.reserve(skirt_height * 2); - for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++ i) - print_zs.push_back(float(object0->layers[i]->print_z)); - //FIXME why there are support layers? - for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++ i) - print_zs.push_back(float(object0->support_layers[i]->print_z)); - sort_remove_duplicates(print_zs); - if (print_zs.size() > skirt_height) - print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); - - volumes->volumes.emplace_back(new GLVolume(color)); - GLVolume &volume = *volumes->volumes.back(); - for (size_t i = 0; i < skirt_height; ++ i) { - volume.print_zs.push_back(print_zs[i]); - volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size()); - volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size()); - if (i == 0) - extrusionentity_to_verts(print->brim, print_zs[i], Point(0, 0), volume); - extrusionentity_to_verts(print->skirt, print_zs[i], Point(0, 0), volume); - } - volume.bounding_box = volume.indexed_vertex_array.bounding_box(); - volume.indexed_vertex_array.finalize_geometry(use_VBOs); -} - -// Create 3D thick extrusion lines for object forming extrusions. -// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, -// one for perimeters, one for infill and one for supports. -void _3DScene::_load_print_object_toolpaths( - const PrintObject *print_object, - GLVolumeCollection *volumes, - const std::vector &tool_colors_str, - bool use_VBOs) -{ - std::vector tool_colors = parse_colors(tool_colors_str); - - struct Ctxt - { - const Points *shifted_copies; - std::vector layers; - bool has_perimeters; - bool has_infill; - bool has_support; - const std::vector* tool_colors; - - // Number of vertices (each vertex is 6x4=24 bytes long) - static const size_t alloc_size_max () { return 131072; } // 3.15MB -// static const size_t alloc_size_max () { return 65536; } // 1.57MB -// static const size_t alloc_size_max () { return 32768; } // 786kB - static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } - - static const float* color_perimeters () { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow - static const float* color_infill () { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish - static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish - - // For cloring by a tool, return a parsed color. - bool color_by_tool() const { return tool_colors != nullptr; } - size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } - const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } - int volume_idx(int extruder, int feature) const - { return this->color_by_tool() ? std::min(this->number_tools() - 1, std::max(extruder - 1, 0)) : feature; } - } ctxt; - - ctxt.shifted_copies = &print_object->_shifted_copies; - - // order layers by print_z - ctxt.layers.reserve(print_object->layers.size() + print_object->support_layers.size()); - for (const Layer *layer : print_object->layers) - ctxt.layers.push_back(layer); - for (const Layer *layer : print_object->support_layers) - ctxt.layers.push_back(layer); - std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); - - // Maximum size of an allocation block: 32MB / sizeof(float) - ctxt.has_perimeters = print_object->state.is_done(posPerimeters); - ctxt.has_infill = print_object->state.is_done(posInfill); - ctxt.has_support = print_object->state.is_done(posSupportMaterial); - ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; - - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; - - //FIXME Improve the heuristics for a grain size. - size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); - tbb::spin_mutex new_volume_mutex; - auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { - auto *volume = new GLVolume(color); - new_volume_mutex.lock(); - volume->outside_printer_detection_enabled = false; - volumes->volumes.emplace_back(volume); - new_volume_mutex.unlock(); - return volume; - }; - const size_t volumes_cnt_initial = volumes->volumes.size(); - std::vector volumes_per_thread(ctxt.layers.size()); - tbb::parallel_for( - tbb::blocked_range(0, ctxt.layers.size(), grain_size), - [&ctxt, &new_volume](const tbb::blocked_range& range) { - std::vector vols; - if (ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++ i) - vols.emplace_back(new_volume(ctxt.color_tool(i))); - } else - vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; - for (GLVolume *vol : vols) - vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - const Layer *layer = ctxt.layers[idx_layer]; - for (size_t i = 0; i < vols.size(); ++ i) { - GLVolume &vol = *vols[i]; - if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) { - vol.print_zs.push_back(layer->print_z); - vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); - vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); - } - } - for (const Point ©: *ctxt.shifted_copies) { - for (const LayerRegion *layerm : layer->regions) { - if (ctxt.has_perimeters) - extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, - *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]); - if (ctxt.has_infill) { - for (const ExtrusionEntity *ee : layerm->fills.entities) { - // fill represents infill extrusions of a single island. - const auto *fill = dynamic_cast(ee); - if (! fill->entities.empty()) - extrusionentity_to_verts(*fill, float(layer->print_z), copy, - *vols[ctxt.volume_idx( - is_solid_infill(fill->entities.front()->role()) ? - layerm->region()->config.solid_infill_extruder : - layerm->region()->config.infill_extruder, - 1)]); - } - } - } - if (ctxt.has_support) { - const SupportLayer *support_layer = dynamic_cast(layer); - if (support_layer) { - for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) - extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, - *vols[ctxt.volume_idx( - (extrusion_entity->role() == erSupportMaterial) ? - support_layer->object()->config.support_material_extruder : - support_layer->object()->config.support_material_interface_extruder, - 2)]); - } - } - } - for (size_t i = 0; i < vols.size(); ++ i) { - GLVolume &vol = *vols[i]; - if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { - // Store the vertex arrays and restart their containers, - vols[i] = new_volume(vol.color); - GLVolume &vol_new = *vols[i]; - // Assign the large pre-allocated buffers to the new GLVolume. - vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); - // Copy the content back to the old GLVolume. - vol.indexed_vertex_array = vol_new.indexed_vertex_array; - // Finalize a bounding box of the old GLVolume. - vol.bounding_box = vol.indexed_vertex_array.bounding_box(); - // Clear the buffers, but keep them pre-allocated. - vol_new.indexed_vertex_array.clear(); - // Just make sure that clear did not clear the reserved memory. - vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - } - } - } - for (GLVolume *vol : vols) { - vol->bounding_box = vol->indexed_vertex_array.bounding_box(); - vol->indexed_vertex_array.shrink_to_fit(); - } - }); - - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results"; - // Remove empty volumes from the newly added volumes. - volumes->volumes.erase( - std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(), - [](const GLVolume *volume) { return volume->empty(); }), - volumes->volumes.end()); - for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i) - volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs); - - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end"; -} - -void _3DScene::_load_wipe_tower_toolpaths( - const Print *print, - GLVolumeCollection *volumes, - const std::vector &tool_colors_str, - bool use_VBOs) -{ - if (print->m_wipe_tower_tool_changes.empty()) - return; - - std::vector tool_colors = parse_colors(tool_colors_str); - - struct Ctxt - { - const Print *print; - const std::vector *tool_colors; - - // Number of vertices (each vertex is 6x4=24 bytes long) - static const size_t alloc_size_max () { return 131072; } // 3.15MB - static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } - - static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish - - // For cloring by a tool, return a parsed color. - bool color_by_tool() const { return tool_colors != nullptr; } - size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } - const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } - int volume_idx(int tool, int feature) const - { return this->color_by_tool() ? std::min(this->number_tools() - 1, std::max(tool, 0)) : feature; } - - const std::vector& tool_change(size_t idx) { - return priming.empty() ? - ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) : - ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]); - } - std::vector priming; - std::vector final; - } ctxt; - - ctxt.print = print; - ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; - if (print->m_wipe_tower_priming) - ctxt.priming.emplace_back(*print->m_wipe_tower_priming.get()); - if (print->m_wipe_tower_final_purge) - ctxt.final.emplace_back(*print->m_wipe_tower_final_purge.get()); - - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start"; - - //FIXME Improve the heuristics for a grain size. - size_t n_items = print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); - size_t grain_size = std::max(n_items / 128, size_t(1)); - tbb::spin_mutex new_volume_mutex; - auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { - auto *volume = new GLVolume(color); - new_volume_mutex.lock(); - volume->outside_printer_detection_enabled = false; - volumes->volumes.emplace_back(volume); - new_volume_mutex.unlock(); - return volume; - }; - const size_t volumes_cnt_initial = volumes->volumes.size(); - std::vector volumes_per_thread(n_items); - tbb::parallel_for( - tbb::blocked_range(0, n_items, grain_size), - [&ctxt, &new_volume](const tbb::blocked_range& range) { - // Bounding box of this slab of a wipe tower. - std::vector vols; - if (ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++ i) - vols.emplace_back(new_volume(ctxt.color_tool(i))); - } else - vols = { new_volume(ctxt.color_support()) }; - for (GLVolume *volume : vols) - volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - const std::vector &layer = ctxt.tool_change(idx_layer); - for (size_t i = 0; i < vols.size(); ++ i) { - GLVolume &vol = *vols[i]; - if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { - vol.print_zs.push_back(layer.front().print_z); - vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); - vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); - } - } - for (const WipeTower::ToolChangeResult &extrusions : layer) { - for (size_t i = 1; i < extrusions.extrusions.size();) { - const WipeTower::Extrusion &e = extrusions.extrusions[i]; - if (e.width == 0.) { - ++ i; - continue; - } - size_t j = i + 1; - if (ctxt.color_by_tool()) - for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++ j) ; - else - for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++ j) ; - size_t n_lines = j - i; - Lines lines; - std::vector widths; - std::vector heights; - lines.reserve(n_lines); - widths.reserve(n_lines); - heights.assign(n_lines, extrusions.layer_height); - for (; i < j; ++ i) { - const WipeTower::Extrusion &e = extrusions.extrusions[i]; - assert(e.width > 0.f); - const WipeTower::Extrusion &e_prev = *(&e - 1); - lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y)); - widths.emplace_back(e.width); - } - thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, - *vols[ctxt.volume_idx(e.tool, 0)]); - } - } - } - for (size_t i = 0; i < vols.size(); ++ i) { - GLVolume &vol = *vols[i]; - if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { - // Store the vertex arrays and restart their containers, - vols[i] = new_volume(vol.color); - GLVolume &vol_new = *vols[i]; - // Assign the large pre-allocated buffers to the new GLVolume. - vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); - // Copy the content back to the old GLVolume. - vol.indexed_vertex_array = vol_new.indexed_vertex_array; - // Finalize a bounding box of the old GLVolume. - vol.bounding_box = vol.indexed_vertex_array.bounding_box(); - // Clear the buffers, but keep them pre-allocated. - vol_new.indexed_vertex_array.clear(); - // Just make sure that clear did not clear the reserved memory. - vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - } - } - for (GLVolume *vol : vols) { - vol->bounding_box = vol->indexed_vertex_array.bounding_box(); - vol->indexed_vertex_array.shrink_to_fit(); - } - }); - - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results"; - // Remove empty volumes from the newly added volumes. - volumes->volumes.erase( - std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(), - [](const GLVolume *volume) { return volume->empty(); }), - volumes->volumes.end()); - for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i) - volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs); - - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; -} - //################################################################################################################## +//// Create 3D thick extrusion lines for a skirt and brim. +//// Adds a new Slic3r::GUI::3DScene::Volume to volumes. +//void _3DScene::_load_print_toolpaths( +// const Print *print, +// GLVolumeCollection *volumes, +// const std::vector &tool_colors, +// bool use_VBOs) +//{ +// if (!print->has_skirt() && print->config.brim_width.value == 0) +// return; +// +// const float color[] = { 0.5f, 1.0f, 0.5f, 1.f }; // greenish +// +// // number of skirt layers +// size_t total_layer_count = 0; +// for (const PrintObject *print_object : print->objects) +// total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); +// size_t skirt_height = print->has_infinite_skirt() ? +// total_layer_count : +// std::min(print->config.skirt_height.value, total_layer_count); +// if (skirt_height == 0 && print->config.brim_width.value > 0) +// skirt_height = 1; +// +// // get first skirt_height layers (maybe this should be moved to a PrintObject method?) +// const PrintObject *object0 = print->objects.front(); +// std::vector print_zs; +// print_zs.reserve(skirt_height * 2); +// for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++ i) +// print_zs.push_back(float(object0->layers[i]->print_z)); +// //FIXME why there are support layers? +// for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++ i) +// print_zs.push_back(float(object0->support_layers[i]->print_z)); +// sort_remove_duplicates(print_zs); +// if (print_zs.size() > skirt_height) +// print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); +// +// volumes->volumes.emplace_back(new GLVolume(color)); +// GLVolume &volume = *volumes->volumes.back(); +// for (size_t i = 0; i < skirt_height; ++ i) { +// volume.print_zs.push_back(print_zs[i]); +// volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size()); +// volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size()); +// if (i == 0) +// extrusionentity_to_verts(print->brim, print_zs[i], Point(0, 0), volume); +// extrusionentity_to_verts(print->skirt, print_zs[i], Point(0, 0), volume); +// } +// volume.bounding_box = volume.indexed_vertex_array.bounding_box(); +// volume.indexed_vertex_array.finalize_geometry(use_VBOs); +//} +// +//// Create 3D thick extrusion lines for object forming extrusions. +//// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, +//// one for perimeters, one for infill and one for supports. +//void _3DScene::_load_print_object_toolpaths( +// const PrintObject *print_object, +// GLVolumeCollection *volumes, +// const std::vector &tool_colors_str, +// bool use_VBOs) +//{ +// std::vector tool_colors = parse_colors(tool_colors_str); +// +// struct Ctxt +// { +// const Points *shifted_copies; +// std::vector layers; +// bool has_perimeters; +// bool has_infill; +// bool has_support; +// const std::vector* tool_colors; +// +// // Number of vertices (each vertex is 6x4=24 bytes long) +// static const size_t alloc_size_max () { return 131072; } // 3.15MB +//// static const size_t alloc_size_max () { return 65536; } // 1.57MB +//// static const size_t alloc_size_max () { return 32768; } // 786kB +// static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } +// +// static const float* color_perimeters () { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow +// static const float* color_infill () { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish +// static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish +// +// // For cloring by a tool, return a parsed color. +// bool color_by_tool() const { return tool_colors != nullptr; } +// size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } +// const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } +// int volume_idx(int extruder, int feature) const +// { return this->color_by_tool() ? std::min(this->number_tools() - 1, std::max(extruder - 1, 0)) : feature; } +// } ctxt; +// +// ctxt.shifted_copies = &print_object->_shifted_copies; +// +// // order layers by print_z +// ctxt.layers.reserve(print_object->layers.size() + print_object->support_layers.size()); +// for (const Layer *layer : print_object->layers) +// ctxt.layers.push_back(layer); +// for (const Layer *layer : print_object->support_layers) +// ctxt.layers.push_back(layer); +// std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); +// +// // Maximum size of an allocation block: 32MB / sizeof(float) +// ctxt.has_perimeters = print_object->state.is_done(posPerimeters); +// ctxt.has_infill = print_object->state.is_done(posInfill); +// ctxt.has_support = print_object->state.is_done(posSupportMaterial); +// ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; +// +// BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; +// +// //FIXME Improve the heuristics for a grain size. +// size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); +// tbb::spin_mutex new_volume_mutex; +// auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { +// auto *volume = new GLVolume(color); +// new_volume_mutex.lock(); +// volume->outside_printer_detection_enabled = false; +// volumes->volumes.emplace_back(volume); +// new_volume_mutex.unlock(); +// return volume; +// }; +// const size_t volumes_cnt_initial = volumes->volumes.size(); +// std::vector volumes_per_thread(ctxt.layers.size()); +// tbb::parallel_for( +// tbb::blocked_range(0, ctxt.layers.size(), grain_size), +// [&ctxt, &new_volume](const tbb::blocked_range& range) { +// std::vector vols; +// if (ctxt.color_by_tool()) { +// for (size_t i = 0; i < ctxt.number_tools(); ++ i) +// vols.emplace_back(new_volume(ctxt.color_tool(i))); +// } else +// vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; +// for (GLVolume *vol : vols) +// vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); +// for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { +// const Layer *layer = ctxt.layers[idx_layer]; +// for (size_t i = 0; i < vols.size(); ++ i) { +// GLVolume &vol = *vols[i]; +// if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) { +// vol.print_zs.push_back(layer->print_z); +// vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); +// vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); +// } +// } +// for (const Point ©: *ctxt.shifted_copies) { +// for (const LayerRegion *layerm : layer->regions) { +// if (ctxt.has_perimeters) +// extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, +// *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]); +// if (ctxt.has_infill) { +// for (const ExtrusionEntity *ee : layerm->fills.entities) { +// // fill represents infill extrusions of a single island. +// const auto *fill = dynamic_cast(ee); +// if (! fill->entities.empty()) +// extrusionentity_to_verts(*fill, float(layer->print_z), copy, +// *vols[ctxt.volume_idx( +// is_solid_infill(fill->entities.front()->role()) ? +// layerm->region()->config.solid_infill_extruder : +// layerm->region()->config.infill_extruder, +// 1)]); +// } +// } +// } +// if (ctxt.has_support) { +// const SupportLayer *support_layer = dynamic_cast(layer); +// if (support_layer) { +// for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) +// extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, +// *vols[ctxt.volume_idx( +// (extrusion_entity->role() == erSupportMaterial) ? +// support_layer->object()->config.support_material_extruder : +// support_layer->object()->config.support_material_interface_extruder, +// 2)]); +// } +// } +// } +// for (size_t i = 0; i < vols.size(); ++ i) { +// GLVolume &vol = *vols[i]; +// if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { +// // Store the vertex arrays and restart their containers, +// vols[i] = new_volume(vol.color); +// GLVolume &vol_new = *vols[i]; +// // Assign the large pre-allocated buffers to the new GLVolume. +// vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); +// // Copy the content back to the old GLVolume. +// vol.indexed_vertex_array = vol_new.indexed_vertex_array; +// // Finalize a bounding box of the old GLVolume. +// vol.bounding_box = vol.indexed_vertex_array.bounding_box(); +// // Clear the buffers, but keep them pre-allocated. +// vol_new.indexed_vertex_array.clear(); +// // Just make sure that clear did not clear the reserved memory. +// vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); +// } +// } +// } +// for (GLVolume *vol : vols) { +// vol->bounding_box = vol->indexed_vertex_array.bounding_box(); +// vol->indexed_vertex_array.shrink_to_fit(); +// } +// }); +// +// BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results"; +// // Remove empty volumes from the newly added volumes. +// volumes->volumes.erase( +// std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(), +// [](const GLVolume *volume) { return volume->empty(); }), +// volumes->volumes.end()); +// for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i) +// volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs); +// +// BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end"; +//} +// +//void _3DScene::_load_wipe_tower_toolpaths( +// const Print *print, +// GLVolumeCollection *volumes, +// const std::vector &tool_colors_str, +// bool use_VBOs) +//{ +// if (print->m_wipe_tower_tool_changes.empty()) +// return; +// +// std::vector tool_colors = parse_colors(tool_colors_str); +// +// struct Ctxt +// { +// const Print *print; +// const std::vector *tool_colors; +// +// // Number of vertices (each vertex is 6x4=24 bytes long) +// static const size_t alloc_size_max () { return 131072; } // 3.15MB +// static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } +// +// static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish +// +// // For cloring by a tool, return a parsed color. +// bool color_by_tool() const { return tool_colors != nullptr; } +// size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } +// const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } +// int volume_idx(int tool, int feature) const +// { return this->color_by_tool() ? std::min(this->number_tools() - 1, std::max(tool, 0)) : feature; } +// +// const std::vector& tool_change(size_t idx) { +// return priming.empty() ? +// ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) : +// ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]); +// } +// std::vector priming; +// std::vector final; +// } ctxt; +// +// ctxt.print = print; +// ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; +// if (print->m_wipe_tower_priming) +// ctxt.priming.emplace_back(*print->m_wipe_tower_priming.get()); +// if (print->m_wipe_tower_final_purge) +// ctxt.final.emplace_back(*print->m_wipe_tower_final_purge.get()); +// +// BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start"; +// +// //FIXME Improve the heuristics for a grain size. +// size_t n_items = print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); +// size_t grain_size = std::max(n_items / 128, size_t(1)); +// tbb::spin_mutex new_volume_mutex; +// auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { +// auto *volume = new GLVolume(color); +// new_volume_mutex.lock(); +// volume->outside_printer_detection_enabled = false; +// volumes->volumes.emplace_back(volume); +// new_volume_mutex.unlock(); +// return volume; +// }; +// const size_t volumes_cnt_initial = volumes->volumes.size(); +// std::vector volumes_per_thread(n_items); +// tbb::parallel_for( +// tbb::blocked_range(0, n_items, grain_size), +// [&ctxt, &new_volume](const tbb::blocked_range& range) { +// // Bounding box of this slab of a wipe tower. +// std::vector vols; +// if (ctxt.color_by_tool()) { +// for (size_t i = 0; i < ctxt.number_tools(); ++ i) +// vols.emplace_back(new_volume(ctxt.color_tool(i))); +// } else +// vols = { new_volume(ctxt.color_support()) }; +// for (GLVolume *volume : vols) +// volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); +// for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { +// const std::vector &layer = ctxt.tool_change(idx_layer); +// for (size_t i = 0; i < vols.size(); ++ i) { +// GLVolume &vol = *vols[i]; +// if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { +// vol.print_zs.push_back(layer.front().print_z); +// vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); +// vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); +// } +// } +// for (const WipeTower::ToolChangeResult &extrusions : layer) { +// for (size_t i = 1; i < extrusions.extrusions.size();) { +// const WipeTower::Extrusion &e = extrusions.extrusions[i]; +// if (e.width == 0.) { +// ++ i; +// continue; +// } +// size_t j = i + 1; +// if (ctxt.color_by_tool()) +// for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++ j) ; +// else +// for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++ j) ; +// size_t n_lines = j - i; +// Lines lines; +// std::vector widths; +// std::vector heights; +// lines.reserve(n_lines); +// widths.reserve(n_lines); +// heights.assign(n_lines, extrusions.layer_height); +// for (; i < j; ++ i) { +// const WipeTower::Extrusion &e = extrusions.extrusions[i]; +// assert(e.width > 0.f); +// const WipeTower::Extrusion &e_prev = *(&e - 1); +// lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y)); +// widths.emplace_back(e.width); +// } +// thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, +// *vols[ctxt.volume_idx(e.tool, 0)]); +// } +// } +// } +// for (size_t i = 0; i < vols.size(); ++ i) { +// GLVolume &vol = *vols[i]; +// if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { +// // Store the vertex arrays and restart their containers, +// vols[i] = new_volume(vol.color); +// GLVolume &vol_new = *vols[i]; +// // Assign the large pre-allocated buffers to the new GLVolume. +// vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); +// // Copy the content back to the old GLVolume. +// vol.indexed_vertex_array = vol_new.indexed_vertex_array; +// // Finalize a bounding box of the old GLVolume. +// vol.bounding_box = vol.indexed_vertex_array.bounding_box(); +// // Clear the buffers, but keep them pre-allocated. +// vol_new.indexed_vertex_array.clear(); +// // Just make sure that clear did not clear the reserved memory. +// vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); +// } +// } +// for (GLVolume *vol : vols) { +// vol->bounding_box = vol->indexed_vertex_array.bounding_box(); +// vol->indexed_vertex_array.shrink_to_fit(); +// } +// }); +// +// BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results"; +// // Remove empty volumes from the newly added volumes. +// volumes->volumes.erase( +// std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(), +// [](const GLVolume *volume) { return volume->empty(); }), +// volumes->volumes.end()); +// for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i) +// volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs); +// +// BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; +//} +// //void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs) //{ // // helper functions to select data in dependence of the extrusion view type diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 69530b84f2..ee65476573 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -611,6 +611,9 @@ public: //################################################################################################################## //################################################################################################################## + static void load_print_toolpaths(wxGLCanvas* canvas); + static void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& str_tool_colors); + static void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector& str_tool_colors); static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); // static void load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector& str_tool_colors, bool use_VBOs); //################################################################################################################## @@ -633,25 +636,29 @@ public: static void reset_warning_texture(); static unsigned int finalize_warning_texture(); - static void _load_print_toolpaths( - const Print *print, - GLVolumeCollection *volumes, - const std::vector &tool_colors, - bool use_VBOs); - - static void _load_print_object_toolpaths( - const PrintObject *print_object, - GLVolumeCollection *volumes, - const std::vector &tool_colors, - bool use_VBOs); - - static void _load_wipe_tower_toolpaths( - const Print *print, - GLVolumeCollection *volumes, - const std::vector &tool_colors_str, - bool use_VBOs); +//################################################################################################################## +// static void _load_print_toolpaths( +// const Print *print, +// GLVolumeCollection *volumes, +// const std::vector &tool_colors, +// bool use_VBOs); +// +// static void _load_print_object_toolpaths( +// const PrintObject *print_object, +// GLVolumeCollection *volumes, +// const std::vector &tool_colors, +// bool use_VBOs); +// +// static void _load_wipe_tower_toolpaths( +// const Print *print, +// GLVolumeCollection *volumes, +// const std::vector &tool_colors_str, +// bool use_VBOs); +//################################################################################################################## //################################################################################################################## + static void thick_lines_to_verts(const Lines& lines, const std::vector& widths, const std::vector& heights, bool closed, double top_z, GLVolume& volume); + static void thick_lines_to_verts(const Lines3& lines, const std::vector& widths, const std::vector& heights, bool closed, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 37f1d6b50d..a060797449 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -13,6 +13,11 @@ #include #include +#include +#include + +#include + #include #include @@ -1346,6 +1351,371 @@ void GLCanvas3D::set_toolpaths_range(double low, double high) m_volumes->set_range(low, high); } +void GLCanvas3D::load_print_toolpaths() +{ + if ((m_print == nullptr) || (m_volumes == nullptr)) + return; + + if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim)) + return; + + if (!m_print->has_skirt() && (m_print->config.brim_width.value == 0)) + return; + + const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish + + // number of skirt layers + size_t total_layer_count = 0; + for (const PrintObject* print_object : m_print->objects) + { + total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); + } + size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min(m_print->config.skirt_height.value, total_layer_count); + if ((skirt_height == 0) && (m_print->config.brim_width.value > 0)) + skirt_height = 1; + + // get first skirt_height layers (maybe this should be moved to a PrintObject method?) + const PrintObject* object0 = m_print->objects.front(); + std::vector print_zs; + print_zs.reserve(skirt_height * 2); + for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++i) + { + print_zs.push_back(float(object0->layers[i]->print_z)); + } + //FIXME why there are support layers? + for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++i) + { + print_zs.push_back(float(object0->support_layers[i]->print_z)); + } + sort_remove_duplicates(print_zs); + if (print_zs.size() > skirt_height) + print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); + + m_volumes->volumes.emplace_back(new GLVolume(color)); + GLVolume& volume = *m_volumes->volumes.back(); + for (size_t i = 0; i < skirt_height; ++i) { + volume.print_zs.push_back(print_zs[i]); + volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size()); + volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size()); + if (i == 0) + _3DScene::extrusionentity_to_verts(m_print->brim, print_zs[i], Point(0, 0), volume); + + _3DScene::extrusionentity_to_verts(m_print->skirt, print_zs[i], Point(0, 0), volume); + } + volume.bounding_box = volume.indexed_vertex_array.bounding_box(); + volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); +} + +void GLCanvas3D::load_print_object_toolpaths(const PrintObject& print_object, const std::vector& str_tool_colors) +{ + std::vector tool_colors = _parse_colors(str_tool_colors); + + struct Ctxt + { + const Points *shifted_copies; + std::vector layers; + bool has_perimeters; + bool has_infill; + bool has_support; + const std::vector* tool_colors; + + // Number of vertices (each vertex is 6x4=24 bytes long) + static const size_t alloc_size_max() { return 131072; } // 3.15MB + // static const size_t alloc_size_max () { return 65536; } // 1.57MB + // static const size_t alloc_size_max () { return 32768; } // 786kB + static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } + + static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow + static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish + static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish + + // For cloring by a tool, return a parsed color. + bool color_by_tool() const { return tool_colors != nullptr; } + size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } + const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } + int volume_idx(int extruder, int feature) const + { + return this->color_by_tool() ? std::min(this->number_tools() - 1, std::max(extruder - 1, 0)) : feature; + } + } ctxt; + + if (m_volumes == nullptr) + return; + + ctxt.shifted_copies = &print_object._shifted_copies; + + // order layers by print_z + ctxt.layers.reserve(print_object.layers.size() + print_object.support_layers.size()); + for (const Layer *layer : print_object.layers) + ctxt.layers.push_back(layer); + for (const Layer *layer : print_object.support_layers) + ctxt.layers.push_back(layer); + std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); + + // Maximum size of an allocation block: 32MB / sizeof(float) + ctxt.has_perimeters = print_object.state.is_done(posPerimeters); + ctxt.has_infill = print_object.state.is_done(posInfill); + ctxt.has_support = print_object.state.is_done(posSupportMaterial); + ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; + + BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; + + //FIXME Improve the heuristics for a grain size. + size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); + tbb::spin_mutex new_volume_mutex; + auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* { + auto *volume = new GLVolume(color); + new_volume_mutex.lock(); + volume->outside_printer_detection_enabled = false; + m_volumes->volumes.emplace_back(volume); + new_volume_mutex.unlock(); + return volume; + }; + const size_t volumes_cnt_initial = m_volumes->volumes.size(); + std::vector volumes_per_thread(ctxt.layers.size()); + tbb::parallel_for( + tbb::blocked_range(0, ctxt.layers.size(), grain_size), + [&ctxt, &new_volume](const tbb::blocked_range& range) { + std::vector vols; + if (ctxt.color_by_tool()) { + for (size_t i = 0; i < ctxt.number_tools(); ++i) + vols.emplace_back(new_volume(ctxt.color_tool(i))); + } + else + vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; + for (GLVolume *vol : vols) + vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); + for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) { + const Layer *layer = ctxt.layers[idx_layer]; + for (size_t i = 0; i < vols.size(); ++i) { + GLVolume &vol = *vols[i]; + if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) { + vol.print_zs.push_back(layer->print_z); + vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); + vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); + } + } + for (const Point © : *ctxt.shifted_copies) { + for (const LayerRegion *layerm : layer->regions) { + if (ctxt.has_perimeters) + _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, + *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]); + if (ctxt.has_infill) { + for (const ExtrusionEntity *ee : layerm->fills.entities) { + // fill represents infill extrusions of a single island. + const auto *fill = dynamic_cast(ee); + if (!fill->entities.empty()) + _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, + *vols[ctxt.volume_idx( + is_solid_infill(fill->entities.front()->role()) ? + layerm->region()->config.solid_infill_extruder : + layerm->region()->config.infill_extruder, + 1)]); + } + } + } + if (ctxt.has_support) { + const SupportLayer *support_layer = dynamic_cast(layer); + if (support_layer) { + for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) + _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, + *vols[ctxt.volume_idx( + (extrusion_entity->role() == erSupportMaterial) ? + support_layer->object()->config.support_material_extruder : + support_layer->object()->config.support_material_interface_extruder, + 2)]); + } + } + } + for (size_t i = 0; i < vols.size(); ++i) { + GLVolume &vol = *vols[i]; + if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { + // Store the vertex arrays and restart their containers, + vols[i] = new_volume(vol.color); + GLVolume &vol_new = *vols[i]; + // Assign the large pre-allocated buffers to the new GLVolume. + vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); + // Copy the content back to the old GLVolume. + vol.indexed_vertex_array = vol_new.indexed_vertex_array; + // Finalize a bounding box of the old GLVolume. + vol.bounding_box = vol.indexed_vertex_array.bounding_box(); + // Clear the buffers, but keep them pre-allocated. + vol_new.indexed_vertex_array.clear(); + // Just make sure that clear did not clear the reserved memory. + vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); + } + } + } + for (GLVolume *vol : vols) { + vol->bounding_box = vol->indexed_vertex_array.bounding_box(); + vol->indexed_vertex_array.shrink_to_fit(); + } + }); + + BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results"; + // Remove empty volumes from the newly added volumes. + m_volumes->volumes.erase( + std::remove_if(m_volumes->volumes.begin() + volumes_cnt_initial, m_volumes->volumes.end(), + [](const GLVolume *volume) { return volume->empty(); }), + m_volumes->volumes.end()); + for (size_t i = volumes_cnt_initial; i < m_volumes->volumes.size(); ++i) + m_volumes->volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); + + BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end"; +} + +void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector& str_tool_colors) +{ + if ((m_volumes == nullptr) || (m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty()) + return; + + if (!m_print->state.is_done(psWipeTower)) + return; + + std::vector tool_colors = _parse_colors(str_tool_colors); + + struct Ctxt + { + const Print *print; + const std::vector *tool_colors; + + // Number of vertices (each vertex is 6x4=24 bytes long) + static const size_t alloc_size_max() { return 131072; } // 3.15MB + static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } + + static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish + + // For cloring by a tool, return a parsed color. + bool color_by_tool() const { return tool_colors != nullptr; } + size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } + const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } + int volume_idx(int tool, int feature) const + { + return this->color_by_tool() ? std::min(this->number_tools() - 1, std::max(tool, 0)) : feature; + } + + const std::vector& tool_change(size_t idx) { + return priming.empty() ? + ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) : + ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]); + } + std::vector priming; + std::vector final; + } ctxt; + + ctxt.print = m_print; + ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; + if (m_print->m_wipe_tower_priming) + ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get()); + if (m_print->m_wipe_tower_final_purge) + ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get()); + + BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start"; + + //FIXME Improve the heuristics for a grain size. + size_t n_items = m_print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); + size_t grain_size = std::max(n_items / 128, size_t(1)); + tbb::spin_mutex new_volume_mutex; + auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* { + auto *volume = new GLVolume(color); + new_volume_mutex.lock(); + volume->outside_printer_detection_enabled = false; + m_volumes->volumes.emplace_back(volume); + new_volume_mutex.unlock(); + return volume; + }; + const size_t volumes_cnt_initial = m_volumes->volumes.size(); + std::vector volumes_per_thread(n_items); + tbb::parallel_for( + tbb::blocked_range(0, n_items, grain_size), + [&ctxt, &new_volume](const tbb::blocked_range& range) { + // Bounding box of this slab of a wipe tower. + std::vector vols; + if (ctxt.color_by_tool()) { + for (size_t i = 0; i < ctxt.number_tools(); ++i) + vols.emplace_back(new_volume(ctxt.color_tool(i))); + } + else + vols = { new_volume(ctxt.color_support()) }; + for (GLVolume *volume : vols) + volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); + for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) { + const std::vector &layer = ctxt.tool_change(idx_layer); + for (size_t i = 0; i < vols.size(); ++i) { + GLVolume &vol = *vols[i]; + if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { + vol.print_zs.push_back(layer.front().print_z); + vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); + vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); + } + } + for (const WipeTower::ToolChangeResult &extrusions : layer) { + for (size_t i = 1; i < extrusions.extrusions.size();) { + const WipeTower::Extrusion &e = extrusions.extrusions[i]; + if (e.width == 0.) { + ++i; + continue; + } + size_t j = i + 1; + if (ctxt.color_by_tool()) + for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++j); + else + for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++j); + size_t n_lines = j - i; + Lines lines; + std::vector widths; + std::vector heights; + lines.reserve(n_lines); + widths.reserve(n_lines); + heights.assign(n_lines, extrusions.layer_height); + for (; i < j; ++i) { + const WipeTower::Extrusion &e = extrusions.extrusions[i]; + assert(e.width > 0.f); + const WipeTower::Extrusion &e_prev = *(&e - 1); + lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y)); + widths.emplace_back(e.width); + } + _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, + *vols[ctxt.volume_idx(e.tool, 0)]); + } + } + } + for (size_t i = 0; i < vols.size(); ++i) { + GLVolume &vol = *vols[i]; + if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { + // Store the vertex arrays and restart their containers, + vols[i] = new_volume(vol.color); + GLVolume &vol_new = *vols[i]; + // Assign the large pre-allocated buffers to the new GLVolume. + vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); + // Copy the content back to the old GLVolume. + vol.indexed_vertex_array = vol_new.indexed_vertex_array; + // Finalize a bounding box of the old GLVolume. + vol.bounding_box = vol.indexed_vertex_array.bounding_box(); + // Clear the buffers, but keep them pre-allocated. + vol_new.indexed_vertex_array.clear(); + // Just make sure that clear did not clear the reserved memory. + vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); + } + } + for (GLVolume *vol : vols) { + vol->bounding_box = vol->indexed_vertex_array.bounding_box(); + vol->indexed_vertex_array.shrink_to_fit(); + } + }); + + BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results"; + // Remove empty volumes from the newly added volumes. + m_volumes->volumes.erase( + std::remove_if(m_volumes->volumes.begin() + volumes_cnt_initial, m_volumes->volumes.end(), + [](const GLVolume *volume) { return volume->empty(); }), + m_volumes->volumes.end()); + for (size_t i = volumes_cnt_initial; i < m_volumes->volumes.size(); ++i) + m_volumes->volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); + + BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; +} + void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors) { if ((m_canvas != nullptr) && (m_volumes != nullptr) && (m_print != nullptr)) @@ -2416,28 +2786,6 @@ static inline int hex_digit_to_int(const char c) (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; } -std::vector GLCanvas3D::_parse_colors(const std::vector& colors) -{ - std::vector output(colors.size() * 4, 1.0f); - for (size_t i = 0; i < colors.size(); ++i) { - const std::string& color = colors[i]; - const char* c = color.data() + 1; - if ((color.size() == 7) && (color.front() == '#')) - { - for (size_t j = 0; j < 3; ++j) - { - int digit1 = hex_digit_to_int(*c++); - int digit2 = hex_digit_to_int(*c++); - if ((digit1 == -1) || (digit2 == -1)) - break; - - output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.0f; - } - } - } - return output; -} - void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors) { // helper functions to select data in dependence of the extrusion view type @@ -3008,5 +3356,27 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe } } +std::vector GLCanvas3D::_parse_colors(const std::vector& colors) +{ + std::vector output(colors.size() * 4, 1.0f); + for (size_t i = 0; i < colors.size(); ++i) { + const std::string& color = colors[i]; + const char* c = color.data() + 1; + if ((color.size() == 7) && (color.front() == '#')) + { + for (size_t j = 0; j < 3; ++j) + { + int digit1 = hex_digit_to_int(*c++); + int digit2 = hex_digit_to_int(*c++); + if ((digit1 == -1) || (digit2 == -1)) + break; + + output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.0f; + } + } + } + return output; +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 69934a8221..4b2f82d85d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -411,6 +411,15 @@ public: std::vector get_current_print_zs(bool active_only) const; void set_toolpaths_range(double low, double high); + // Create 3D thick extrusion lines for a skirt and brim. + // Adds a new Slic3r::GUI::3DScene::Volume to volumes. + void load_print_toolpaths(); + // Create 3D thick extrusion lines for object forming extrusions. + // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, + // one for perimeters, one for infill and one for supports. + void load_print_object_toolpaths(const PrintObject& print_object, const std::vector& str_tool_colors); + // Create 3D thick extrusion lines for wipe tower extrusions + void load_wipe_tower_toolpaths(const std::vector& str_tool_colors); void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors); void register_on_viewport_changed_callback(void* callback); @@ -472,8 +481,6 @@ private: void _start_timer(); void _stop_timer(); - static std::vector _parse_colors(const std::vector& colors); - // generates gcode extrusion paths geometry void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors); // generates gcode travel paths geometry @@ -489,6 +496,8 @@ private: void _load_shells(); // sets gcode geometry visibility according to user selection void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data); + + static std::vector _parse_colors(const std::vector& colors); }; } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 850ccc6973..3f903b9b38 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -450,6 +450,30 @@ void GLCanvas3DManager::set_toolpaths_range(wxGLCanvas* canvas, double low, doub it->second->set_toolpaths_range(low, high); } +void GLCanvas3DManager::load_print_toolpaths(wxGLCanvas* canvas) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->load_print_toolpaths(); +} + +void GLCanvas3DManager::load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& tool_colors) +{ + if (print_object == nullptr) + return; + + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->load_print_object_toolpaths(*print_object, tool_colors); +} + +void GLCanvas3DManager::load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector& str_tool_colors) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->load_wipe_tower_toolpaths(str_tool_colors); +} + void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors) { if (preview_data == nullptr) diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 5a874de120..0d741d550b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -95,6 +95,9 @@ public: std::vector get_current_print_zs(wxGLCanvas* canvas, bool active_only) const; void set_toolpaths_range(wxGLCanvas* canvas, double low, double high); + void load_print_toolpaths(wxGLCanvas* canvas); + void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& tool_colors); + void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector& str_tool_colors); void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 8b454d3c3d..f6f5134d6b 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -511,31 +511,25 @@ reset_warning_texture() _3DScene::reset_warning_texture(); void -_load_print_toolpaths(print, volumes, tool_colors, use_VBOs) - Print *print; - GLVolumeCollection *volumes; - std::vector tool_colors; - int use_VBOs; +load_print_toolpaths(canvas) + SV *canvas; CODE: - _3DScene::_load_print_toolpaths(print, volumes, tool_colors, use_VBOs != 0); + _3DScene::load_print_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); void -_load_print_object_toolpaths(print_object, volumes, tool_colors, use_VBOs) - PrintObject *print_object; - GLVolumeCollection *volumes; +load_print_object_toolpaths(canvas, print_object, tool_colors) + SV *canvas; + PrintObject *print_object; std::vector tool_colors; - int use_VBOs; CODE: - _3DScene::_load_print_object_toolpaths(print_object, volumes, tool_colors, use_VBOs != 0); + _3DScene::load_print_object_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print_object, tool_colors); void -_load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs) - Print *print; - GLVolumeCollection *volumes; +load_wipe_tower_toolpaths(canvas, tool_colors) + SV *canvas; std::vector tool_colors; - int use_VBOs; CODE: - _3DScene::_load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs != 0); + _3DScene::load_wipe_tower_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), tool_colors); void load_gcode_preview(canvas, preview_data, str_tool_colors) From 40bb0b6f55c5702da34b801eb23ee891a06c723d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 5 Jun 2018 16:07:09 +0200 Subject: [PATCH 066/117] Fixed overflow in Polygon::area() --- xs/src/libslic3r/Polygon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp index 27f9a2ca1e..b5fd7e64f7 100644 --- a/xs/src/libslic3r/Polygon.cpp +++ b/xs/src/libslic3r/Polygon.cpp @@ -103,7 +103,7 @@ double Polygon::area() const double a = 0.; for (size_t i = 0, j = n - 1; i < n; ++i) { - a += double(points[j].x + points[i].x) * double(points[i].y - points[j].y); + a += ((double)points[j].x + (double)points[i].x) * ((double)points[i].y - (double)points[j].y); j = i; } return 0.5 * a; From c6e44509e09f0fac77ef3a50d302bc2bc192c00e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 6 Jun 2018 10:16:58 +0200 Subject: [PATCH 067/117] 3DScene load_object method moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 45 +++++++++-------------- lib/Slic3r/GUI/Plater/3D.pm | 9 ++++- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 12 +++--- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 35 ++++++++---------- xs/src/slic3r/GUI/3DScene.cpp | 10 +++++ xs/src/slic3r/GUI/3DScene.hpp | 3 ++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 31 ++++++++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 9 +++++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 18 +++++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 3 ++ xs/xsp/GUI_3DScene.xsp | 22 +++++++++++ 11 files changed, 142 insertions(+), 55 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index a55c4ab16b..b2240b7148 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -32,9 +32,7 @@ use Slic3r::Geometry qw(X Y); use Wx::GLCanvas qw(:all); use Slic3r::Geometry qw(PI); -# _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' #============================================================================================================================== __PACKAGE__->mk_accessors( qw( volumes @@ -2169,37 +2167,28 @@ sub new { return $self; } -sub load_object { - my ($self, $model, $print, $obj_idx, $instance_idxs) = @_; - #============================================================================================================================== - my $useVBOs = Slic3r::GUI::_3DScene::use_VBOs(); - $self->SetCurrent($self->GetContext) if $useVBOs; - +#sub load_object { +# my ($self, $model, $print, $obj_idx, $instance_idxs) = @_; +# # $self->SetCurrent($self->GetContext) if $useVBOs; -#============================================================================================================================== - - my $model_object; - if ($model->isa('Slic3r::Model::Object')) { - $model_object = $model; - $model = $model_object->model; - $obj_idx = 0; - } else { - $model_object = $model->get_object($obj_idx); - } - - $instance_idxs ||= [0..$#{$model_object->instances}]; -#============================================================================================================================== - my $volume_indices = $self->volumes->load_object( - $model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by, $useVBOs && Slic3r::GUI::_3DScene::is_shader_enabled($self)); +# +# my $model_object; +# if ($model->isa('Slic3r::Model::Object')) { +# $model_object = $model; +# $model = $model_object->model; +# $obj_idx = 0; +# } else { +# $model_object = $model->get_object($obj_idx); +# } +# +# $instance_idxs ||= [0..$#{$model_object->instances}]; # my $volume_indices = $self->volumes->load_object( # $model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by, # $self->UseVBOs); -#============================================================================================================================== - return @{$volume_indices}; -} - -#============================================================================================================================== +# return @{$volume_indices}; +#} +# ## Create 3D thick extrusion lines for a skirt and brim. ## Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes. #sub load_print_toolpaths { diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index d6b7acf2b3..4137d1458b 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -237,8 +237,13 @@ sub reload_scene { $self->{objects_volumes_idxs} = []; foreach my $obj_idx (0..$#{$self->{model}->objects}) { - my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx); - push(@{$self->{objects_volumes_idxs}}, \@volume_idxs); +#============================================================================================================================== + my $volume_idxs = Slic3r::GUI::_3DScene::load_model($self, $self->{model}, $obj_idx, [0]); + push(@{$self->{objects_volumes_idxs}}, \@{$volume_idxs}); + +# my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx); +# push(@{$self->{objects_volumes_idxs}}, \@volume_idxs); +#============================================================================================================================== } $self->update_volumes_selection; diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 84177dd0fd..56ca9d7383 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -115,11 +115,12 @@ sub new { my $canvas; if ($Slic3r::GUI::have_OpenGL) { $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); - $canvas->load_object($self->{model_object}, undef, undef, [0]); #============================================================================================================================== + Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); +# $canvas->load_object($self->{model_object}, undef, undef, [0]); # $canvas->set_auto_bed_shape; #============================================================================================================================== $canvas->SetSize([500,500]); @@ -261,14 +262,13 @@ sub _update { } #============================================================================================================================== - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); -# $self->{canvas}->reset_objects; -#============================================================================================================================== - $self->{canvas}->load_object($_, undef, undef, [0]) for @objects; -#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); + Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects; Slic3r::GUI::_3DScene::set_cutting_plane($self->{canvas}, $self->{cut_options}{z}, [@expolygons]); Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); Slic3r::GUI::_3DScene::render($self->{canvas}); +# $self->{canvas}->reset_objects; +# $self->{canvas}->load_object($_, undef, undef, [0]) for @objects; # $self->{canvas}->SetCuttingPlane( # $self->{cut_options}{z}, # [@expolygons], diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index ad9c4df94c..f7bfc37961 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -165,18 +165,16 @@ sub new { # convert scene volume to model object volume $self->reload_tree(($volume_idx == -1) ? undef : $canvas->volumes->[$volume_idx]->volume_idx); }); + Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]); + Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); + Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); + # $canvas->on_select(sub { # my ($volume_idx) = @_; # # convert scene volume to model object volume # $self->reload_tree(($volume_idx == -1) ? undef : $canvas->volumes->[$volume_idx]->volume_idx); # }); -#============================================================================================================================== - - $canvas->load_object($self->{model_object}, undef, undef, [0]); -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); - Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); - +# $canvas->load_object($self->{model_object}, undef, undef, [0]); # $canvas->set_auto_bed_shape; #============================================================================================================================== $canvas->SetSize([500,700]); @@ -514,14 +512,13 @@ sub _parts_changed { $self->reload_tree; if ($self->{canvas}) { #============================================================================================================================== - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); -# $self->{canvas}->reset_objects; -#============================================================================================================================== - $self->{canvas}->load_object($self->{model_object}); -#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); + Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas}); Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); Slic3r::GUI::_3DScene::render($self->{canvas}); +# $self->{canvas}->reset_objects; +# $self->{canvas}->load_object($self->{model_object}); # $self->{canvas}->zoom_to_volumes; # $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); # $self->{canvas}->Render; @@ -567,10 +564,11 @@ sub _update_canvas { if ($self->{canvas}) { #============================================================================================================================== - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); + Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); + Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); # $self->{canvas}->reset_objects; +# $self->{canvas}->load_object($self->{model_object}); #============================================================================================================================== - $self->{canvas}->load_object($self->{model_object}); # restore selection, if any if (my $itemData = $self->get_selection) { @@ -607,13 +605,12 @@ sub _update { my @objects = (); push @objects, $self->{model_object}; #============================================================================================================================== - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); -# $self->{canvas}->reset_objects; -#============================================================================================================================== - $self->{canvas}->load_object($_, undef, [0]) for @objects; -#============================================================================================================================== + Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); + Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects; Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); Slic3r::GUI::_3DScene::render($self->{canvas}); +# $self->{canvas}->reset_objects; +# $self->{canvas}->load_object($_, undef, [0]) for @objects; # $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); # $self->{canvas}->Render; #============================================================================================================================== diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 89b7d0a118..ec90011c6f 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2016,6 +2016,16 @@ static inline std::vector parse_colors(const std::vector &sc } //################################################################################################################## +std::vector _3DScene::load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs) +{ + return s_canvas_mgr.load_object(canvas, model_object, obj_idx, instance_idxs); +} + +std::vector _3DScene::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx, std::vector instance_idxs) +{ + return s_canvas_mgr.load_object(canvas, model, obj_idx, instance_idxs); +} + void _3DScene::load_print_toolpaths(wxGLCanvas* canvas) { s_canvas_mgr.load_print_toolpaths(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index ee65476573..832ebafc87 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -611,6 +611,9 @@ public: //################################################################################################################## //################################################################################################################## + static std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); + static std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx, std::vector instance_idxs); + static void load_print_toolpaths(wxGLCanvas* canvas); static void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& str_tool_colors); static void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector& str_tool_colors); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index a060797449..e1b70710f4 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -959,6 +959,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_moving_enabled(false) , m_shader_enabled(false) , m_multisample_allowed(false) + , m_color_by("volume") + , m_select_by("object") + , m_drag_by("instance") { if (m_canvas != nullptr) m_timer = new wxTimer(m_canvas); @@ -1351,6 +1354,34 @@ void GLCanvas3D::set_toolpaths_range(double low, double high) m_volumes->set_range(low, high); } +std::vector GLCanvas3D::load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs) +{ + if (m_volumes == nullptr) + return std::vector(); + + if (instance_idxs.empty()) + { + for (unsigned int i = 0; i < model_object.instances.size(); ++i) + { + instance_idxs.push_back(i); + } + } + + return m_volumes->load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_select_by, m_drag_by, m_use_VBOs && m_initialized); +} + +std::vector GLCanvas3D::load_object(const Model& model, int obj_idx, std::vector instance_idxs) +{ + if ((0 <= obj_idx) && (obj_idx < (int)model.objects.size())) + { + const ModelObject* model_object = model.objects[obj_idx]; + if (model_object != nullptr) + return load_object(*model_object, obj_idx, instance_idxs); + } + + return std::vector(); +} + void GLCanvas3D::load_print_toolpaths() { if ((m_print == nullptr) || (m_volumes == nullptr)) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 4b2f82d85d..c70cff360f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -25,6 +25,8 @@ class ExPolygon; class Print; class PrintObject; class GCodePreviewData; +class ModelObject; +class Model; namespace GUI { @@ -342,6 +344,10 @@ private: bool m_shader_enabled; bool m_multisample_allowed; + std::string m_color_by; + std::string m_select_by; + std::string m_drag_by; + GCodePreviewVolumeIndex m_gcode_preview_volume_index; PerlCallback m_on_viewport_changed_callback; @@ -411,6 +417,9 @@ public: std::vector get_current_print_zs(bool active_only) const; void set_toolpaths_range(double low, double high); + std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs); + std::vector load_object(const Model& model, int obj_idx, std::vector instance_idxs); + // Create 3D thick extrusion lines for a skirt and brim. // Adds a new Slic3r::GUI::3DScene::Volume to volumes. void load_print_toolpaths(); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 3f903b9b38..74949c5fe4 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -450,6 +450,24 @@ void GLCanvas3DManager::set_toolpaths_range(wxGLCanvas* canvas, double low, doub it->second->set_toolpaths_range(low, high); } +std::vector GLCanvas3DManager::load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs) +{ + if (model_object == nullptr) + return std::vector(); + + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->load_object(*model_object, obj_idx, instance_idxs) : std::vector(); +} + +std::vector GLCanvas3DManager::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx, std::vector instance_idxs) +{ + if (model == nullptr) + return std::vector(); + + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->load_object(*model, obj_idx, instance_idxs) : std::vector(); +} + void GLCanvas3DManager::load_print_toolpaths(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 0d741d550b..6ca22737ba 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -95,6 +95,9 @@ public: std::vector get_current_print_zs(wxGLCanvas* canvas, bool active_only) const; void set_toolpaths_range(wxGLCanvas* canvas, double low, double high); + std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); + std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx, std::vector instance_idxs); + void load_print_toolpaths(wxGLCanvas* canvas); void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& tool_colors); void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector& str_tool_colors); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index f6f5134d6b..bf8a6a67eb 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -510,6 +510,28 @@ reset_warning_texture() CODE: _3DScene::reset_warning_texture(); +std::vector +load_model_object(canvas, model_object, obj_idx, instance_idxs) + SV *canvas; + ModelObject *model_object; + int obj_idx; + std::vector instance_idxs; + CODE: + RETVAL = _3DScene::load_object((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), model_object, obj_idx, instance_idxs); + OUTPUT: + RETVAL + +std::vector +load_model(canvas, model, obj_idx, instance_idxs) + SV *canvas; + Model *model; + int obj_idx; + std::vector instance_idxs; + CODE: + RETVAL = _3DScene::load_object((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), model, obj_idx, instance_idxs); + OUTPUT: + RETVAL + void load_print_toolpaths(canvas) SV *canvas; From e79037c44d085e6fb845a19a9cc789d76103d7f6 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 6 Jun 2018 12:36:52 +0200 Subject: [PATCH 068/117] 3DScene member variables moved to c++ --- lib/Slic3r/GUI/3DScene.pm | 20 ++++++++++++-------- lib/Slic3r/GUI/Plater/3D.pm | 6 ++++-- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 8 +++----- xs/src/slic3r/GUI/3DScene.cpp | 15 +++++++++++++++ xs/src/slic3r/GUI/3DScene.hpp | 4 ++++ xs/src/slic3r/GUI/GLCanvas3D.cpp | 15 +++++++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 6 +++++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 21 +++++++++++++++++++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 4 ++++ xs/xsp/GUI_3DScene.xsp | 21 +++++++++++++++++++++ 10 files changed, 104 insertions(+), 16 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index b2240b7148..1995805f87 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -2150,19 +2150,23 @@ use List::Util qw(first min max); use Slic3r::Geometry qw(scale unscale epsilon); use Slic3r::Print::State ':steps'; -__PACKAGE__->mk_accessors(qw( - color_by - select_by - drag_by -)); +#=================================================================================================================================== +#__PACKAGE__->mk_accessors(qw( +# color_by +# select_by +# drag_by +#)); +#=================================================================================================================================== sub new { my $class = shift; my $self = $class->SUPER::new(@_); - $self->color_by('volume'); # object | volume - $self->select_by('object'); # object | volume | instance - $self->drag_by('instance'); # object | instance +#=================================================================================================================================== +# $self->color_by('volume'); # object | volume +# $self->select_by('object'); # object | volume | instance +# $self->drag_by('instance'); # object | instance +#=================================================================================================================================== return $self; } diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 4137d1458b..2a710b6f9f 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -22,11 +22,13 @@ sub new { #============================================================================================================================== Slic3r::GUI::_3DScene::enable_picking($self, 1); Slic3r::GUI::_3DScene::enable_moving($self, 1); + Slic3r::GUI::_3DScene::set_select_by($self, 'object'); + Slic3r::GUI::_3DScene::set_drag_by($self, 'instance'); # $self->enable_picking(1); # $self->enable_moving(1); +# $self->select_by('object'); +# $self->drag_by('instance'); #============================================================================================================================== - $self->select_by('object'); - $self->drag_by('instance'); $self->{objects} = $objects; $self->{model} = $model; diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index f7bfc37961..b0735c3498 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -155,11 +155,7 @@ sub new { $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); #============================================================================================================================== Slic3r::GUI::_3DScene::enable_picking($canvas, 1); -# $canvas->enable_picking(1); -#============================================================================================================================== - $canvas->select_by('volume'); - -#============================================================================================================================== + Slic3r::GUI::_3DScene::set_select_by($canvas, 'volume'); Slic3r::GUI::_3DScene::register_on_select_callback($canvas, sub { my ($volume_idx) = @_; # convert scene volume to model object volume @@ -169,6 +165,8 @@ sub new { Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); +# $canvas->enable_picking(1); +# $canvas->select_by('volume'); # $canvas->on_select(sub { # my ($volume_idx) = @_; # # convert scene volume to model object volume diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index ec90011c6f..5ee02a7803 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1857,6 +1857,21 @@ void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& return s_canvas_mgr.set_cutting_plane(canvas, z, polygons); } +void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value) +{ + return s_canvas_mgr.set_color_by(canvas, value); +} + +void _3DScene::set_select_by(wxGLCanvas* canvas, const std::string& value) +{ + return s_canvas_mgr.set_select_by(canvas, value); +} + +void _3DScene::set_drag_by(wxGLCanvas* canvas, const std::string& value) +{ + return s_canvas_mgr.set_drag_by(canvas, value); +} + bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) { return s_canvas_mgr.is_layers_editing_enabled(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 832ebafc87..6d94dd43b6 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -575,6 +575,10 @@ public: static void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); + static void set_color_by(wxGLCanvas* canvas, const std::string& value); + static void set_select_by(wxGLCanvas* canvas, const std::string& value); + static void set_drag_by(wxGLCanvas* canvas, const std::string& value); + static bool is_layers_editing_enabled(wxGLCanvas* canvas); static bool is_layers_editing_allowed(wxGLCanvas* canvas); static bool is_shader_enabled(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index e1b70710f4..783ae52f8c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1149,6 +1149,21 @@ void GLCanvas3D::set_cutting_plane(float z, const ExPolygons& polygons) m_cutting_plane.set(z, polygons); } +void GLCanvas3D::set_color_by(const std::string& value) +{ + m_color_by = value; +} + +void GLCanvas3D::set_select_by(const std::string& value) +{ + m_select_by = value; +} + +void GLCanvas3D::set_drag_by(const std::string& value) +{ + m_drag_by = value; +} + float GLCanvas3D::get_camera_zoom() const { return m_camera.zoom; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index c70cff360f..c8533f5147 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -386,7 +386,11 @@ public: void set_axes_length(float length); void set_cutting_plane(float z, const ExPolygons& polygons); - + + void set_color_by(const std::string& value); + void set_select_by(const std::string& value); + void set_drag_by(const std::string& value); + float get_camera_zoom() const; BoundingBoxf3 volumes_bounding_box() const; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 74949c5fe4..bfb11d9724 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -317,6 +317,27 @@ void GLCanvas3DManager::set_cutting_plane(wxGLCanvas* canvas, float z, const ExP it->second->set_cutting_plane(z, polygons); } +void GLCanvas3DManager::set_color_by(wxGLCanvas* canvas, const std::string& value) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_color_by(value); +} + +void GLCanvas3DManager::set_select_by(wxGLCanvas* canvas, const std::string& value) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_select_by(value); +} + +void GLCanvas3DManager::set_drag_by(wxGLCanvas* canvas, const std::string& value) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_drag_by(value); +} + bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const { CanvasesMap::const_iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 6ca22737ba..ba9d3d36b6 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -70,6 +70,10 @@ public: void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); + void set_color_by(wxGLCanvas* canvas, const std::string& value); + void set_select_by(wxGLCanvas* canvas, const std::string& value); + void set_drag_by(wxGLCanvas* canvas, const std::string& value); + bool is_layers_editing_enabled(wxGLCanvas* canvas) const; bool is_layers_editing_allowed(wxGLCanvas* canvas) const; bool is_shader_enabled(wxGLCanvas* canvas) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index bf8a6a67eb..8c03e48878 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -275,6 +275,27 @@ set_cutting_plane(canvas, z, polygons) CODE: _3DScene::set_cutting_plane((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), z, polygons); +void +set_color_by(canvas, value) + SV *canvas; + std::string value; + CODE: + _3DScene::set_color_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), value); + +void +set_select_by(canvas, value) + SV *canvas; + std::string value; + CODE: + _3DScene::set_select_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), value); + +void +set_drag_by(canvas, value) + SV *canvas; + std::string value; + CODE: + _3DScene::set_drag_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), value); + bool is_layers_editing_enabled(canvas) SV *canvas; From 8192580b5f2db16e8b45de70deccc9dd86d3763a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 6 Jun 2018 14:19:28 +0200 Subject: [PATCH 069/117] Removed DestroyGL method from 3DScene --- lib/Slic3r/GUI/3DScene.pm | 48 ++++++++++----------- lib/Slic3r/GUI/MainFrame.pm | 1 + lib/Slic3r/GUI/Plater/3D.pm | 4 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 57 +++++++++++++++++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 ++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 23 ++-------- 6 files changed, 90 insertions(+), 46 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 1995805f87..5f33b17abc 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -18,19 +18,19 @@ use warnings; use Wx qw(wxTheApp :timer :bitmap :icon :dialog); #============================================================================================================================== #use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER); -#============================================================================================================================== # must load OpenGL *before* Wx::GLCanvas use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); use base qw(Wx::GLCanvas Class::Accessor); -use Math::Trig qw(asin tan); -use List::Util qw(reduce min max first); #============================================================================================================================== -use Slic3r::Geometry qw(X Y); +#use Math::Trig qw(asin tan); +#use List::Util qw(reduce min max first); #use Slic3r::Geometry qw(X Y normalize scale unscale scaled_epsilon); #use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl JT_ROUND); #============================================================================================================================== use Wx::GLCanvas qw(:all); -use Slic3r::Geometry qw(PI); +#============================================================================================================================== +#use Slic3r::Geometry qw(PI); +#============================================================================================================================== # volumes: reference to vector of Slic3r::GUI::3DScene::Volume. #============================================================================================================================== @@ -259,12 +259,10 @@ sub new { sub Destroy { my ($self) = @_; -#============================================================================================================================== -# $self->{layer_height_edit_timer}->Stop; -#============================================================================================================================== - $self->DestroyGL; #============================================================================================================================== Slic3r::GUI::_3DScene::remove_canvas($self); +# $self->{layer_height_edit_timer}->Stop; +# $self->DestroyGL; #============================================================================================================================== return $self->SUPER::Destroy; } @@ -1247,13 +1245,11 @@ sub SetCurrent { # } # } #} -#=================================================================================================================================== - -sub DestroyGL { - my $self = shift; - if ($self->GetContext) { - $self->SetCurrent($self->GetContext); -#=================================================================================================================================== +# +#sub DestroyGL { +# my $self = shift; +# if ($self->GetContext) { +# $self->SetCurrent($self->GetContext); # if ($self->{plain_shader}) { # $self->{plain_shader}->release; # delete $self->{plain_shader}; @@ -1262,12 +1258,10 @@ sub DestroyGL { # $self->{layer_height_edit_shader}->release; # delete $self->{layer_height_edit_shader}; # } -#=================================================================================================================================== - $self->volumes->release_geometry; - } -} - -#============================================================================================================================== +# $self->volumes->release_geometry; +# } +#} +# #sub Render { # my ($self, $dc) = @_; # @@ -2145,10 +2139,12 @@ sub DestroyGL { package Slic3r::GUI::3DScene; use base qw(Slic3r::GUI::3DScene::Base); -use OpenGL qw(:glconstants :gluconstants :glufunctions); -use List::Util qw(first min max); -use Slic3r::Geometry qw(scale unscale epsilon); -use Slic3r::Print::State ':steps'; +#=================================================================================================================================== +#use OpenGL qw(:glconstants :gluconstants :glufunctions); +#use List::Util qw(first min max); +#use Slic3r::Geometry qw(scale unscale epsilon); +#use Slic3r::Print::State ':steps'; +#=================================================================================================================================== #=================================================================================================================================== #__PACKAGE__->mk_accessors(qw( diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index ebbbcf43eb..6a2246fa16 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -91,6 +91,7 @@ sub new { # but in rare cases it may not have been called yet. wxTheApp->{app_config}->save; #============================================================================================================================== + $self->{plater}->{print} = undef if($self->{plater}); Slic3r::GUI::_3DScene::remove_all_canvases(); #============================================================================================================================== # propagate event diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 2a710b6f9f..6641d45e5f 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -32,7 +32,9 @@ sub new { $self->{objects} = $objects; $self->{model} = $model; - $self->{print} = $print; +#============================================================================================================================== +# $self->{print} = $print; +#============================================================================================================================== $self->{config} = $config; #============================================================================================================================== Slic3r::GUI::_3DScene::set_print($self, $print); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 783ae52f8c..41741eef82 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -969,6 +969,12 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) GLCanvas3D::~GLCanvas3D() { + if (m_volumes != nullptr) + { + set_current(); + m_volumes->release_geometry(); + } + if (m_timer != nullptr) { delete m_timer; @@ -1836,6 +1842,57 @@ void GLCanvas3D::register_on_move_callback(void* callback) m_on_move_callback.register_callback(callback); } +void GLCanvas3D::bind_event_handlers() +{ + if (m_canvas != nullptr) + { + m_canvas->Bind(wxEVT_SIZE, &GLCanvas3D::on_size, this); + m_canvas->Bind(wxEVT_IDLE, &GLCanvas3D::on_idle, this); + m_canvas->Bind(wxEVT_CHAR, &GLCanvas3D::on_char, this); + m_canvas->Bind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this); + m_canvas->Bind(wxEVT_TIMER, &GLCanvas3D::on_timer, this); + m_canvas->Bind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_MIDDLE_UP, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_RIGHT_DOWN, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_RIGHT_UP, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_MOTION, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_ENTER_WINDOW, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_LEAVE_WINDOW, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_LEFT_DCLICK, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); + m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); + } +} + +void GLCanvas3D::unbind_event_handlers() +{ + if (m_canvas != nullptr) + { + m_canvas->GetEventHandler()->ProcessPendingEvents(); + m_canvas->Unbind(wxEVT_SIZE, &GLCanvas3D::on_size, this); + m_canvas->Unbind(wxEVT_IDLE, &GLCanvas3D::on_idle, this); + m_canvas->Unbind(wxEVT_CHAR, &GLCanvas3D::on_char, this); + m_canvas->Unbind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this); + m_canvas->Unbind(wxEVT_TIMER, &GLCanvas3D::on_timer, this); + m_canvas->Unbind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_MIDDLE_UP, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_RIGHT_DOWN, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_RIGHT_UP, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_MOTION, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_ENTER_WINDOW, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_LEAVE_WINDOW, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_LEFT_DCLICK, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); + } +} + void GLCanvas3D::on_size(wxSizeEvent& evt) { m_dirty = true; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index c8533f5147..7bd93a32eb 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -442,6 +442,9 @@ public: void register_on_model_update_callback(void* callback); void register_on_move_callback(void* callback); + void bind_event_handlers(); + void unbind_event_handlers(); + void on_size(wxSizeEvent& evt); void on_idle(wxIdleEvent& evt); void on_char(wxKeyEvent& evt); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index bfb11d9724..26c19e9fd3 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -138,25 +138,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) if (canvas3D == nullptr) return false; - canvas->Bind(wxEVT_SIZE, [canvas3D](wxSizeEvent& evt) { canvas3D->on_size(evt); }); - canvas->Bind(wxEVT_IDLE, [canvas3D](wxIdleEvent& evt) { canvas3D->on_idle(evt); }); - canvas->Bind(wxEVT_CHAR, [canvas3D](wxKeyEvent& evt) { canvas3D->on_char(evt); }); - canvas->Bind(wxEVT_MOUSEWHEEL, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse_wheel(evt); }); - canvas->Bind(wxEVT_TIMER, [canvas3D](wxTimerEvent& evt) { canvas3D->on_timer(evt); }); - canvas->Bind(wxEVT_LEFT_DOWN, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_LEFT_UP, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_MIDDLE_DOWN, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_MIDDLE_UP, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_RIGHT_DOWN, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_RIGHT_UP, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_MOTION, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_ENTER_WINDOW, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_LEAVE_WINDOW, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_LEFT_DCLICK, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_MIDDLE_DCLICK, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_RIGHT_DCLICK, [canvas3D](wxMouseEvent& evt) { canvas3D->on_mouse(evt); }); - canvas->Bind(wxEVT_PAINT, [canvas3D](wxPaintEvent& evt) { canvas3D->on_paint(evt); }); - + canvas3D->bind_event_handlers(); m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); std::cout << "canvas added: " << (void*)canvas << " (" << (void*)canvas3D << ")" << std::endl; @@ -170,6 +152,7 @@ bool GLCanvas3DManager::remove(wxGLCanvas* canvas) if (it == m_canvases.end()) return false; + it->second->unbind_event_handlers(); delete it->second; m_canvases.erase(it); @@ -183,6 +166,8 @@ void GLCanvas3DManager::remove_all() for (CanvasesMap::value_type& item : m_canvases) { std::cout << "canvas removed: " << (void*)item.second << std::endl; + + item.second->unbind_event_handlers(); delete item.second; } m_canvases.clear(); From 66b4620d9b79293c0e3a29df36b02735be93e977 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 6 Jun 2018 14:33:04 +0200 Subject: [PATCH 070/117] Fixed runtime error on Linux when removing canvases --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 41741eef82..bb5db86147 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1871,7 +1871,6 @@ void GLCanvas3D::unbind_event_handlers() { if (m_canvas != nullptr) { - m_canvas->GetEventHandler()->ProcessPendingEvents(); m_canvas->Unbind(wxEVT_SIZE, &GLCanvas3D::on_size, this); m_canvas->Unbind(wxEVT_IDLE, &GLCanvas3D::on_idle, this); m_canvas->Unbind(wxEVT_CHAR, &GLCanvas3D::on_char, this); From f6ef28beccd77b0c6b7eda593ae9297313e742c2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 6 Jun 2018 15:17:10 +0200 Subject: [PATCH 071/117] Removed method update_bed_size from 3D perl class --- lib/Slic3r/GUI/Plater.pm | 18 +++++++++++++----- lib/Slic3r/GUI/Plater/3D.pm | 10 +++++----- lib/Slic3r/GUI/Plater/3DPreview.pm | 7 +++---- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 309c2e5ed5..734eaaa954 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -408,14 +408,18 @@ sub new { $self->{canvas}->update_bed_size; if ($self->{canvas3D}) { - $self->{canvas3D}->update_bed_size; #============================================================================================================================== + Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape); Slic3r::GUI::_3DScene::zoom_to_bed($self->{canvas3D}); +# $self->{canvas3D}->update_bed_size; # $self->{canvas3D}->zoom_to_bed; #============================================================================================================================== } if ($self->{preview3D}) { - $self->{preview3D}->set_bed_shape($self->{config}->bed_shape); +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape); +# $self->{preview3D}->set_bed_shape($self->{config}->bed_shape); +#============================================================================================================================== } $self->update; @@ -1830,9 +1834,13 @@ sub on_config_change { $self->{config}->set($opt_key, $config->get($opt_key)); if ($opt_key eq 'bed_shape') { $self->{canvas}->update_bed_size; - $self->{canvas3D}->update_bed_size if $self->{canvas3D}; - $self->{preview3D}->set_bed_shape($self->{config}->bed_shape) - if $self->{preview3D}; +#============================================================================================================================== + Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D}; + Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; +# $self->{canvas3D}->update_bed_size if $self->{canvas3D}; +# $self->{preview3D}->set_bed_shape($self->{config}->bed_shape) +# if $self->{preview3D}; +#============================================================================================================================== $update_scheduled = 1; } elsif ($opt_key =~ '^wipe_tower' || $opt_key eq 'single_extruder_multi_material') { $update_scheduled = 1; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 6641d45e5f..6c6b564780 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -228,9 +228,10 @@ sub reload_scene { #============================================================================================================================== Slic3r::GUI::_3DScene::reset_volumes($self); + Slic3r::GUI::_3DScene::set_bed_shape($self, $self->{config}->bed_shape); # $self->reset_objects; +# $self->update_bed_size; #============================================================================================================================== - $self->update_bed_size; if (! $self->IsShown && ! $force) { $self->{reload_delayed} = 1; @@ -306,13 +307,12 @@ sub reload_scene { } } -sub update_bed_size { - my ($self) = @_; #============================================================================================================================== - Slic3r::GUI::_3DScene::set_bed_shape($self, $self->{config}->bed_shape); +#sub update_bed_size { +# my ($self) = @_; # $self->set_bed_shape($self->{config}->bed_shape); +#} #============================================================================================================================== -} # Called by the Platter wxNotebook when this page is activated. sub OnActivate { diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 7b713b46fc..55198eeb6a 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -546,13 +546,12 @@ sub set_z_idx_high } } -sub set_bed_shape { - my ($self, $bed_shape) = @_; #============================================================================================================================== - Slic3r::GUI::_3DScene::set_bed_shape($self->canvas, $bed_shape); +#sub set_bed_shape { +# my ($self, $bed_shape) = @_; # $self->canvas->set_bed_shape($bed_shape); +#} #============================================================================================================================== -} sub set_number_extruders { my ($self, $number_extruders) = @_; From ce6a23ef3bc0e0caefdc96efc002a9a98339d50f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 6 Jun 2018 15:19:06 +0200 Subject: [PATCH 072/117] Repair by the netfabb service: Implemented progress dialog and cancelation. --- xs/src/slic3r/Utils/FixModelByWin10.cpp | 201 ++++++++++++++++-------- xs/src/slic3r/Utils/FixModelByWin10.hpp | 2 - 2 files changed, 132 insertions(+), 71 deletions(-) diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp index 27a79f84eb..556035a5b1 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.cpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp @@ -6,10 +6,19 @@ #include "FixModelByWin10.hpp" -#include -#include +#include +#include #include +#include +#include +#include #include + +#include +#include +#include + +#include // for ComPtr #include // from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ @@ -17,16 +26,13 @@ #include #include -#include -#include -#include - #include "libslic3r/Model.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/Format/3mf.hpp" #include "../GUI/GUI.hpp" #include "../GUI/PresetBundle.hpp" +#include #include extern "C"{ @@ -105,8 +111,11 @@ static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast(pinst)); } +// To be called often to test whether to cancel the operation. +typedef std::function ThrowOnCancelFn; + template -static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr &asyncAction, int blocking_tick_ms = 300) +static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr &asyncAction, ThrowOnCancelFn throw_on_cancel, int blocking_tick_ms = 100) { Microsoft::WRL::ComPtr asyncInfo; asyncAction.As(&asyncInfo); @@ -118,6 +127,7 @@ static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr &asyncActio asyncInfo->get_Status(&status); if (status != AsyncStatus::Started) return status; + throw_on_cancel(); ::Sleep(blocking_tick_ms); } } @@ -125,7 +135,8 @@ static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr &asyncActio static HRESULT winrt_open_file_stream( const std::wstring &path, ABI::Windows::Storage::FileAccessMode mode, - ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream) + ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream, + ThrowOnCancelFn throw_on_cancel) { // Get the file factory. Microsoft::WRL::ComPtr fileFactory; @@ -142,7 +153,7 @@ static HRESULT winrt_open_file_stream( (*s_WindowsDeleteString)(hstr_path); // Wait until the file gets open, get the actual file. - AsyncStatus status = winrt_async_await(fileOpenAsync); + AsyncStatus status = winrt_async_await(fileOpenAsync, throw_on_cancel); Microsoft::WRL::ComPtr storageFile; if (status == AsyncStatus::Completed) { hr = fileOpenAsync->GetResults(storageFile.GetAddressOf()); @@ -159,7 +170,7 @@ static HRESULT winrt_open_file_stream( hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf()); if (FAILED(hr)) return hr; - status = winrt_async_await(fileStreamAsync); + status = winrt_async_await(fileStreamAsync, throw_on_cancel); if (status == AsyncStatus::Completed) { hr = fileStreamAsync->GetResults(fileStream); } else { @@ -189,21 +200,23 @@ bool is_windows10() return false; } -bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst) -{ - if (! is_windows10()) { - return false; - } +// Progress function, to be called regularly to update the progress. +typedef std::function ProgressFn; - if (! winrt_load_runtime_object_library()) { - printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n"); - return false; - } +void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst, ProgressFn on_progress, ThrowOnCancelFn throw_on_cancel) +{ + if (! is_windows10()) + throw std::runtime_error("fix_model_by_win10_sdk called on non Windows 10 system"); + + if (! winrt_load_runtime_object_library()) + throw std::runtime_error("Failed to initialize the WinRT library."); HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED); { + on_progress(L("Exporting the source model"), 20); + Microsoft::WRL::ComPtr fileStream; - hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); + hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf(), throw_on_cancel); Microsoft::WRL::ComPtr printing3d3mfpackage; hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf()); @@ -211,30 +224,29 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path Microsoft::WRL::ComPtr> modelAsync; hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf()); - AsyncStatus status = winrt_async_await(modelAsync); + AsyncStatus status = winrt_async_await(modelAsync, throw_on_cancel); Microsoft::WRL::ComPtr model; - if (status == AsyncStatus::Completed) { + if (status == AsyncStatus::Completed) hr = modelAsync->GetResults(model.GetAddressOf()); - } else { - printf("Failed loading the input model. Exiting.\n"); - return false; - } + else + throw std::runtime_error(L("Failed loading the input model.")); Microsoft::WRL::ComPtr> meshes; hr = model->get_Meshes(meshes.GetAddressOf()); unsigned num_meshes = 0; hr = meshes->get_Size(&num_meshes); + on_progress(L("Repairing the model by the Netfabb service"), 40); + Microsoft::WRL::ComPtr repairAsync; hr = model->RepairAsync(repairAsync.GetAddressOf()); - status = winrt_async_await(repairAsync); - if (status != AsyncStatus::Completed) { - printf("Mesh repair failed. Exiting.\n"); - return false; - } - printf("Mesh repair finished successfully.\n"); + status = winrt_async_await(repairAsync, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Mesh repair failed.")); repairAsync->GetResults(); + on_progress(L("Loading the repaired model"), 60); + // Verify the number of meshes returned after the repair action. meshes.Reset(); hr = model->get_Meshes(meshes.GetAddressOf()); @@ -243,20 +255,16 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path // Save model to this class' Printing3D3MFPackage. Microsoft::WRL::ComPtr saveToPackageAsync; hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf()); - status = winrt_async_await(saveToPackageAsync); - if (status != AsyncStatus::Completed) { - printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return false; - } + status = winrt_async_await(saveToPackageAsync, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); hr = saveToPackageAsync->GetResults(); Microsoft::WRL::ComPtr> generatorStreamAsync; hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf()); - status = winrt_async_await(generatorStreamAsync); - if (status != AsyncStatus::Completed) { - printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return false; - } + status = winrt_async_await(generatorStreamAsync, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); Microsoft::WRL::ComPtr generatorStream; hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); @@ -286,11 +294,9 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path Microsoft::WRL::ComPtr> asyncRead; for (;;) { hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf()); - status = winrt_async_await(asyncRead); - if (status != AsyncStatus::Completed) { - printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return false; - } + status = winrt_async_await(asyncRead, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); hr = buffer->get_Length(&length); if (length == 0) break; @@ -300,38 +306,95 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path // Here all the COM objects will be released through the ComPtr destructors. } (*s_RoUninitialize)(); - return true; } +class RepairCanceledException : public std::exception { +public: + const char* what() const throw() { return "Model repair has been canceled"; } +}; + void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result) { - enum { PROGRESS_RANGE = 1000 }; + std::mutex mutex; + std::condition_variable condition; + std::unique_lock lock(mutex); + struct Progress { + std::string message; + int percent = 0; + bool updated = false; + } progress; + std::atomic canceled = false; + std::atomic finished = false; + + // Open a progress dialog. wxProgressDialog progress_dialog( _(L("Model fixing")), _(L("Exporting model...")), - PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); - progress_dialog.Pulse(); - + 100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); // Executing the calculation in a background thread, so that the COM context could be created with its own threading model. // (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context). - auto worker = std::thread([&model_object, &print, &result, &progress_dialog](){ - boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - path_src += ".3mf"; - Model model; - model.add_object(model_object); - bool res = Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast(&print), false); - model.clear_objects(); - model.clear_materials(); - - boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - path_dst += ".3mf"; - res = fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string()); - boost::filesystem::remove(path_src); - PresetBundle bundle; - res = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result); - boost::filesystem::remove(path_dst); + bool success = false; + auto on_progress = [&mutex, &condition, &progress](const char *msg, unsigned prcnt) { + std::lock_guard lk(mutex); + progress.message = msg; + progress.percent = prcnt; + progress.updated = true; + condition.notify_all(); + }; + auto worker_thread = boost::thread([&model_object, &print, &result, on_progress, &success, &canceled, &finished]() { + try { + on_progress(L("Exporting the source model"), 0); + boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_src += ".3mf"; + Model model; + model.add_object(model_object); + if (! Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast(&print), false)) { + boost::filesystem::remove(path_src); + throw std::runtime_error(L("Export of a temporary 3mf file failed")); + } + model.clear_objects(); + model.clear_materials(); + boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_dst += ".3mf"; + fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string(), on_progress, + [&canceled]() { if (canceled) throw RepairCanceledException(); }); + boost::filesystem::remove(path_src); + PresetBundle bundle; + on_progress(L("Loading the repaired model"), 80); + bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result); + boost::filesystem::remove(path_dst); + if (! loaded) + throw std::runtime_error(L("Import of the repaired 3mf file failed")); + success = true; + finished = true; + on_progress(L("Model repair finished"), 100); + } catch (RepairCanceledException &ex) { + canceled = true; + finished = true; + on_progress(L("Model repair canceled"), 100); + } catch (std::exception &ex) { + success = false; + finished = true; + on_progress(ex.what(), 100); + } }); - worker.join(); + while (! finished) { + condition.wait_for(lock, std::chrono::milliseconds(500), [&progress]{ return progress.updated; }); + if (! progress_dialog.Update(progress.percent, _(progress.message))) + canceled = true; + progress.updated = false; + } + + if (canceled) { + // Nothing to show. + } else if (success) { + wxMessageDialog dlg(nullptr, _(L("Model repaired successfully")), _(L("Model Repair by the Netfabb service")), wxICON_INFORMATION | wxOK_DEFAULT); + dlg.ShowModal(); + } else { + wxMessageDialog dlg(nullptr, _(L("Model repair failed: \n")) + _(progress.message), _(L("Model Repair by the Netfabb service")), wxICON_ERROR | wxOK_DEFAULT); + dlg.ShowModal(); + } + worker_thread.join(); } } // namespace Slic3r diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp index 299c9b75bf..c148a6970d 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.hpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp @@ -12,13 +12,11 @@ class Print; #ifdef HAS_WIN10SDK extern bool is_windows10(); -extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst); extern void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result); #else /* HAS_WIN10SDK */ inline bool is_windows10() { return false; } -inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; } inline void fix_model_by_win10_sdk_gui(const ModelObject &, const Print &, Model &) {} #endif /* HAS_WIN10SDK */ From ff864078400b73075826d189b75c8e57b4c5693e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 7 Jun 2018 09:22:19 +0200 Subject: [PATCH 073/117] Key down and char event handlers of class 3D moved to c++ --- lib/Slic3r/GUI/Plater.pm | 21 ++-- lib/Slic3r/GUI/Plater/3D.pm | 159 +++++++++++++----------- xs/src/slic3r/GUI/3DScene.cpp | 39 +++++- xs/src/slic3r/GUI/3DScene.hpp | 9 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 91 +++++++++++++- xs/src/slic3r/GUI/GLCanvas3D.hpp | 17 ++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 53 +++++++- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 9 +- xs/xsp/GUI_3DScene.xsp | 55 +++++++- 9 files changed, 357 insertions(+), 96 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 734eaaa954..d44a4d3937 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -120,16 +120,23 @@ sub new { #============================================================================================================================== Slic3r::GUI::_3DScene::register_on_double_click_callback($self->{canvas3D}, $on_double_click); Slic3r::GUI::_3DScene::register_on_right_click_callback($self->{canvas3D}, sub { $on_right_click->($self->{canvas3D}, @_); }); + Slic3r::GUI::_3DScene::register_on_arrange_callback($self->{canvas3D}, sub { $self->arrange }); + Slic3r::GUI::_3DScene::register_on_rotate_object_left_callback($self->{canvas3D}, sub { $self->rotate(-45, Z, 'relative') }); + Slic3r::GUI::_3DScene::register_on_rotate_object_right_callback($self->{canvas3D}, sub { $self->rotate( 45, Z, 'relative') }); + Slic3r::GUI::_3DScene::register_on_scale_object_uniformly_callback($self->{canvas3D}, sub { $self->changescale(undef) }); + Slic3r::GUI::_3DScene::register_on_increase_objects_callback($self->{canvas3D}, sub { $self->increase() }); + Slic3r::GUI::_3DScene::register_on_decrease_objects_callback($self->{canvas3D}, sub { $self->decrease() }); + Slic3r::GUI::_3DScene::register_on_remove_object_callback($self->{canvas3D}, sub { $self->remove() }); # $self->{canvas3D}->set_on_double_click($on_double_click); # $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); +# $self->{canvas3D}->set_on_arrange(sub { $self->arrange }); +# $self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') }); +# $self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') }); +# $self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) }); +# $self->{canvas3D}->set_on_increase_objects(sub { $self->increase() }); +# $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() }); +# $self->{canvas3D}->set_on_remove_object(sub { $self->remove() }); #============================================================================================================================== - $self->{canvas3D}->set_on_arrange(sub { $self->arrange }); - $self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') }); - $self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') }); - $self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) }); - $self->{canvas3D}->set_on_increase_objects(sub { $self->increase() }); - $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() }); - $self->{canvas3D}->set_on_remove_object(sub { $self->remove() }); $self->{canvas3D}->set_on_instances_moved($on_instances_moved); $self->{canvas3D}->set_on_enable_action_buttons($enable_action_buttons); #=================================================================================================================================== diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 6c6b564780..da3bfc6176 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -5,14 +5,21 @@ use utf8; use List::Util qw(); use Wx qw(:misc :pen :brush :sizer :font :cursor :keycode wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR); +#============================================================================================================================== +#use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR); +#============================================================================================================================== use base qw(Slic3r::GUI::3DScene Class::Accessor); use Wx::Locale gettext => 'L'; +#============================================================================================================================== __PACKAGE__->mk_accessors(qw( - on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly + on_rotate_object_left on_rotate_object_right on_scale_object_uniformly on_remove_object on_increase_objects on_decrease_objects on_enable_action_buttons)); +#__PACKAGE__->mk_accessors(qw( +# on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly +# on_remove_object on_increase_objects on_decrease_objects on_enable_action_buttons)); +#============================================================================================================================== sub new { my $class = shift; @@ -94,43 +101,45 @@ sub new { if $wipe_tower_moved && $self->{on_wipe_tower_moved}; }); - EVT_KEY_DOWN($self, sub { - my ($s, $event) = @_; - if ($event->HasModifiers) { - $event->Skip; - } else { - my $key = $event->GetKeyCode; - if ($key == WXK_DELETE) { - $self->on_remove_object->() if $self->on_remove_object; - } else { - $event->Skip; - } - } - }); - - EVT_CHAR($self, sub { - my ($s, $event) = @_; - if ($event->HasModifiers) { - $event->Skip; - } else { - my $key = $event->GetKeyCode; - if ($key == ord('a')) { - $self->on_arrange->() if $self->on_arrange; - } elsif ($key == ord('l')) { - $self->on_rotate_object_left->() if $self->on_rotate_object_left; - } elsif ($key == ord('r')) { - $self->on_rotate_object_right->() if $self->on_rotate_object_right; - } elsif ($key == ord('s')) { - $self->on_scale_object_uniformly->() if $self->on_scale_object_uniformly; - } elsif ($key == ord('+')) { - $self->on_increase_objects->() if $self->on_increase_objects; - } elsif ($key == ord('-')) { - $self->on_decrease_objects->() if $self->on_decrease_objects; - } else { - $event->Skip; - } - } - }); +#============================================================================================================================== +# EVT_KEY_DOWN($self, sub { +# my ($s, $event) = @_; +# if ($event->HasModifiers) { +# $event->Skip; +# } else { +# my $key = $event->GetKeyCode; +# if ($key == WXK_DELETE) { +# $self->on_remove_object->() if $self->on_remove_object; +# } else { +# $event->Skip; +# } +# } +# }); +# +# EVT_CHAR($self, sub { +# my ($s, $event) = @_; +# if ($event->HasModifiers) { +# $event->Skip; +# } else { +# my $key = $event->GetKeyCode; +# if ($key == ord('a')) { +# $self->on_arrange->() if $self->on_arrange; +# } elsif ($key == ord('l')) { +# $self->on_rotate_object_left->() if $self->on_rotate_object_left; +# } elsif ($key == ord('r')) { +# $self->on_rotate_object_right->() if $self->on_rotate_object_right; +# } elsif ($key == ord('s')) { +# $self->on_scale_object_uniformly->() if $self->on_scale_object_uniformly; +# } elsif ($key == ord('+')) { +# $self->on_increase_objects->() if $self->on_increase_objects; +# } elsif ($key == ord('-')) { +# $self->on_decrease_objects->() if $self->on_decrease_objects; +# } else { +# $event->Skip; +# } +# } +# }); +#============================================================================================================================== return $self; } @@ -150,43 +159,43 @@ sub set_on_select_object { # my ($self, $cb) = @_; # $self->on_right_click($cb); #} +# +#sub set_on_arrange { +# my ($self, $cb) = @_; +# $self->on_arrange($cb); +#} +# +#sub set_on_rotate_object_left { +# my ($self, $cb) = @_; +# $self->on_rotate_object_left($cb); +#} +# +#sub set_on_rotate_object_right { +# my ($self, $cb) = @_; +# $self->on_rotate_object_right($cb); +#} +# +#sub set_on_scale_object_uniformly { +# my ($self, $cb) = @_; +# $self->on_scale_object_uniformly($cb); +#} +# +#sub set_on_increase_objects { +# my ($self, $cb) = @_; +# $self->on_increase_objects($cb); +#} +# +#sub set_on_decrease_objects { +# my ($self, $cb) = @_; +# $self->on_decrease_objects($cb); +#} +# +#sub set_on_remove_object { +# my ($self, $cb) = @_; +# $self->on_remove_object($cb); +#} #============================================================================================================================== -sub set_on_arrange { - my ($self, $cb) = @_; - $self->on_arrange($cb); -} - -sub set_on_rotate_object_left { - my ($self, $cb) = @_; - $self->on_rotate_object_left($cb); -} - -sub set_on_rotate_object_right { - my ($self, $cb) = @_; - $self->on_rotate_object_right($cb); -} - -sub set_on_scale_object_uniformly { - my ($self, $cb) = @_; - $self->on_scale_object_uniformly($cb); -} - -sub set_on_increase_objects { - my ($self, $cb) = @_; - $self->on_increase_objects($cb); -} - -sub set_on_decrease_objects { - my ($self, $cb) = @_; - $self->on_decrease_objects($cb); -} - -sub set_on_remove_object { - my ($self, $cb) = @_; - $self->on_remove_object($cb); -} - sub set_on_instances_moved { my ($self, $cb) = @_; $self->{on_instances_moved} = $cb; @@ -243,7 +252,7 @@ sub reload_scene { $self->{objects_volumes_idxs} = []; foreach my $obj_idx (0..$#{$self->{model}->objects}) { #============================================================================================================================== - my $volume_idxs = Slic3r::GUI::_3DScene::load_model($self, $self->{model}, $obj_idx, [0]); + my $volume_idxs = Slic3r::GUI::_3DScene::load_model($self, $self->{model}, $obj_idx); push(@{$self->{objects_volumes_idxs}}, \@{$volume_idxs}); # my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 5ee02a7803..79d4cba257 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1997,6 +1997,41 @@ void _3DScene::register_on_move_callback(wxGLCanvas* canvas, void* callback) s_canvas_mgr.register_on_move_callback(canvas, callback); } +void _3DScene::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_remove_object_callback(canvas, callback); +} + +void _3DScene::register_on_arrange_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_arrange_callback(canvas, callback); +} + +void _3DScene::register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_rotate_object_left_callback(canvas, callback); +} + +void _3DScene::register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_rotate_object_right_callback(canvas, callback); +} + +void _3DScene::register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_scale_object_uniformly_callback(canvas, callback); +} + +void _3DScene::register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_increase_objects_callback(canvas, callback); +} + +void _3DScene::register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_decrease_objects_callback(canvas, callback); +} + //void _3DScene::_glew_init() //{ // glewInit(); @@ -2036,9 +2071,9 @@ std::vector _3DScene::load_object(wxGLCanvas* canvas, const ModelObject* mo return s_canvas_mgr.load_object(canvas, model_object, obj_idx, instance_idxs); } -std::vector _3DScene::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx, std::vector instance_idxs) +std::vector _3DScene::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx) { - return s_canvas_mgr.load_object(canvas, model, obj_idx, instance_idxs); + return s_canvas_mgr.load_object(canvas, model, obj_idx); } void _3DScene::load_print_toolpaths(wxGLCanvas* canvas) diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 6d94dd43b6..38cb82ccb6 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -610,13 +610,20 @@ public: static void register_on_select_callback(wxGLCanvas* canvas, void* callback); static void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); static void register_on_move_callback(wxGLCanvas* canvas, void* callback); + static void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback); + static void register_on_arrange_callback(wxGLCanvas* canvas, void* callback); + static void register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback); + static void register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback); + static void register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback); + static void register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback); + static void register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback); // static void _glew_init(); //################################################################################################################## //################################################################################################################## static std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); - static std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx, std::vector instance_idxs); + static std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); static void load_print_toolpaths(wxGLCanvas* canvas); static void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& str_tool_colors); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index bb5db86147..d0baa6563f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1391,13 +1391,13 @@ std::vector GLCanvas3D::load_object(const ModelObject& model_object, int ob return m_volumes->load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_select_by, m_drag_by, m_use_VBOs && m_initialized); } -std::vector GLCanvas3D::load_object(const Model& model, int obj_idx, std::vector instance_idxs) +std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) { if ((0 <= obj_idx) && (obj_idx < (int)model.objects.size())) { const ModelObject* model_object = model.objects[obj_idx]; if (model_object != nullptr) - return load_object(*model_object, obj_idx, instance_idxs); + return load_object(*model_object, obj_idx, std::vector()); } return std::vector(); @@ -1842,6 +1842,48 @@ void GLCanvas3D::register_on_move_callback(void* callback) m_on_move_callback.register_callback(callback); } +void GLCanvas3D::register_on_remove_object_callback(void* callback) +{ + if (callback != nullptr) + m_on_remove_object_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_arrange_callback(void* callback) +{ + if (callback != nullptr) + m_on_arrange_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_rotate_object_left_callback(void* callback) +{ + if (callback != nullptr) + m_on_rotate_object_left_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_rotate_object_right_callback(void* callback) +{ + if (callback != nullptr) + m_on_rotate_object_right_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_scale_object_uniformly_callback(void* callback) +{ + if (callback != nullptr) + m_on_scale_object_uniformly_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_increase_objects_callback(void* callback) +{ + if (callback != nullptr) + m_on_increase_objects_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_decrease_objects_callback(void* callback) +{ + if (callback != nullptr) + m_on_decrease_objects_callback.register_callback(callback); +} + void GLCanvas3D::bind_event_handlers() { if (m_canvas != nullptr) @@ -1864,6 +1906,7 @@ void GLCanvas3D::bind_event_handlers() m_canvas->Bind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Bind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); + m_canvas->Bind(wxEVT_KEY_DOWN, &GLCanvas3D::on_key_down, this); } } @@ -1889,6 +1932,7 @@ void GLCanvas3D::unbind_event_handlers() m_canvas->Unbind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Unbind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); + m_canvas->Unbind(wxEVT_KEY_DOWN, &GLCanvas3D::on_key_down, this); } } @@ -1927,13 +1971,33 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) // text input switch (keyCode) { + // key + + case 43: { m_on_increase_objects_callback.call(); break; } + // key - + case 45: { m_on_decrease_objects_callback.call(); break; } + // key A/a + case 65: + case 97: { m_on_arrange_callback.call(); break; } // key B/b case 66: case 98: { zoom_to_bed(); break; } + // key L/l + case 76: + case 108: { m_on_rotate_object_left_callback.call(); break; } + // key R/r + case 82: + case 114: { m_on_rotate_object_right_callback.call(); break; } + // key S/s + case 83: + case 115: { m_on_scale_object_uniformly_callback.call(); break; } // key Z/z case 90: case 122: { zoom_to_volumes(); break; } - default: { evt.Skip(); break; } + default: + { + evt.Skip(); + break; + } } } } @@ -2243,6 +2307,20 @@ void GLCanvas3D::on_paint(wxPaintEvent& evt) render(); } +void GLCanvas3D::on_key_down(wxKeyEvent& evt) +{ + if (evt.HasModifiers()) + evt.Skip(); + else + { + int key = evt.GetKeyCode(); + if (key == WXK_DELETE) + m_on_remove_object_callback.call(); + else + evt.Skip(); + } +} + Size GLCanvas3D::get_canvas_size() const { int w = 0; @@ -2434,6 +2512,13 @@ void GLCanvas3D::_deregister_callbacks() m_on_select_callback.deregister_callback(); m_on_model_update_callback.deregister_callback(); m_on_move_callback.deregister_callback(); + m_on_remove_object_callback.deregister_callback(); + m_on_arrange_callback.deregister_callback(); + m_on_rotate_object_left_callback.deregister_callback(); + m_on_rotate_object_right_callback.deregister_callback(); + m_on_scale_object_uniformly_callback.deregister_callback(); + m_on_increase_objects_callback.deregister_callback(); + m_on_decrease_objects_callback.deregister_callback(); } void GLCanvas3D::_mark_volumes_for_layer_height() const diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 7bd93a32eb..dde51a2773 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -356,6 +356,13 @@ private: PerlCallback m_on_select_callback; PerlCallback m_on_model_update_callback; PerlCallback m_on_move_callback; + PerlCallback m_on_remove_object_callback; + PerlCallback m_on_arrange_callback; + PerlCallback m_on_rotate_object_left_callback; + PerlCallback m_on_rotate_object_right_callback; + PerlCallback m_on_scale_object_uniformly_callback; + PerlCallback m_on_increase_objects_callback; + PerlCallback m_on_decrease_objects_callback; public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); @@ -422,7 +429,7 @@ public: void set_toolpaths_range(double low, double high); std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs); - std::vector load_object(const Model& model, int obj_idx, std::vector instance_idxs); + std::vector load_object(const Model& model, int obj_idx); // Create 3D thick extrusion lines for a skirt and brim. // Adds a new Slic3r::GUI::3DScene::Volume to volumes. @@ -441,6 +448,13 @@ public: void register_on_select_callback(void* callback); void register_on_model_update_callback(void* callback); void register_on_move_callback(void* callback); + void register_on_remove_object_callback(void* callback); + void register_on_arrange_callback(void* callback); + void register_on_rotate_object_left_callback(void* callback); + void register_on_rotate_object_right_callback(void* callback); + void register_on_scale_object_uniformly_callback(void* callback); + void register_on_increase_objects_callback(void* callback); + void register_on_decrease_objects_callback(void* callback); void bind_event_handlers(); void unbind_event_handlers(); @@ -452,6 +466,7 @@ public: void on_timer(wxTimerEvent& evt); void on_mouse(wxMouseEvent& evt); void on_paint(wxPaintEvent& evt); + void on_key_down(wxKeyEvent& evt); Size get_canvas_size() const; Point get_local_mouse_position() const; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 26c19e9fd3..a957d8b9a7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -465,13 +465,13 @@ std::vector GLCanvas3DManager::load_object(wxGLCanvas* canvas, const ModelO return (it != m_canvases.end()) ? it->second->load_object(*model_object, obj_idx, instance_idxs) : std::vector(); } -std::vector GLCanvas3DManager::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx, std::vector instance_idxs) +std::vector GLCanvas3DManager::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx) { if (model == nullptr) return std::vector(); CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->load_object(*model, obj_idx, instance_idxs) : std::vector(); + return (it != m_canvases.end()) ? it->second->load_object(*model, obj_idx) : std::vector(); } void GLCanvas3DManager::load_print_toolpaths(wxGLCanvas* canvas) @@ -550,6 +550,55 @@ void GLCanvas3DManager::register_on_move_callback(wxGLCanvas* canvas, void* call it->second->register_on_move_callback(callback); } +void GLCanvas3DManager::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_remove_object_callback(callback); +} + +void GLCanvas3DManager::register_on_arrange_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_arrange_callback(callback); +} + +void GLCanvas3DManager::register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_rotate_object_left_callback(callback); +} + +void GLCanvas3DManager::register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_rotate_object_right_callback(callback); +} + +void GLCanvas3DManager::register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_scale_object_uniformly_callback(callback); +} + +void GLCanvas3DManager::register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_increase_objects_callback(callback); +} + +void GLCanvas3DManager::register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_decrease_objects_callback(callback); +} + GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index ba9d3d36b6..50d282cc9f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -100,7 +100,7 @@ public: void set_toolpaths_range(wxGLCanvas* canvas, double low, double high); std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); - std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx, std::vector instance_idxs); + std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); void load_print_toolpaths(wxGLCanvas* canvas); void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& tool_colors); @@ -113,6 +113,13 @@ public: void register_on_select_callback(wxGLCanvas* canvas, void* callback); void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); void register_on_move_callback(wxGLCanvas* canvas, void* callback); + void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback); + void register_on_arrange_callback(wxGLCanvas* canvas, void* callback); + void register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback); + void register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback); + void register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback); + void register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback); + void register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 8c03e48878..b70d49b762 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -472,8 +472,56 @@ register_on_move_callback(canvas, callback) SV *callback; CODE: _3DScene::register_on_move_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_remove_object_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_remove_object_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_arrange_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_arrange_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_rotate_object_left_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_rotate_object_left_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_rotate_object_right_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_rotate_object_right_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); - +void +register_on_scale_object_uniformly_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_scale_object_uniformly_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_increase_objects_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_increase_objects_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_decrease_objects_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_decrease_objects_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + unsigned int finalize_legend_texture() CODE: @@ -543,13 +591,12 @@ load_model_object(canvas, model_object, obj_idx, instance_idxs) RETVAL std::vector -load_model(canvas, model, obj_idx, instance_idxs) +load_model(canvas, model, obj_idx) SV *canvas; Model *model; int obj_idx; - std::vector instance_idxs; CODE: - RETVAL = _3DScene::load_object((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), model, obj_idx, instance_idxs); + RETVAL = _3DScene::load_object((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), model, obj_idx); OUTPUT: RETVAL From a8500d6bae6769811610589ccafe9051ff3bb947 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 7 Jun 2018 11:18:28 +0200 Subject: [PATCH 074/117] class 3D callbacks moved to c++ --- lib/Slic3r/GUI/Plater.pm | 21 +++-- lib/Slic3r/GUI/Plater/3D.pm | 107 ++++++++++++------------ xs/src/libslic3r/Utils.hpp | 1 + xs/src/libslic3r/utils.cpp | 18 ++++ xs/src/slic3r/GUI/3DScene.cpp | 25 ++++-- xs/src/slic3r/GUI/3DScene.hpp | 5 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 78 +++++++++++++++-- xs/src/slic3r/GUI/GLCanvas3D.hpp | 12 ++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 35 ++++++-- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 5 +- xs/xsp/GUI_3DScene.xsp | 35 ++++++-- 11 files changed, 250 insertions(+), 92 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d44a4d3937..82249bfd4f 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -126,7 +126,8 @@ sub new { Slic3r::GUI::_3DScene::register_on_scale_object_uniformly_callback($self->{canvas3D}, sub { $self->changescale(undef) }); Slic3r::GUI::_3DScene::register_on_increase_objects_callback($self->{canvas3D}, sub { $self->increase() }); Slic3r::GUI::_3DScene::register_on_decrease_objects_callback($self->{canvas3D}, sub { $self->decrease() }); - Slic3r::GUI::_3DScene::register_on_remove_object_callback($self->{canvas3D}, sub { $self->remove() }); + Slic3r::GUI::_3DScene::register_on_remove_object_callback($self->{canvas3D}, sub { $self->remove() }); + Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved); # $self->{canvas3D}->set_on_double_click($on_double_click); # $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); # $self->{canvas3D}->set_on_arrange(sub { $self->arrange }); @@ -136,21 +137,29 @@ sub new { # $self->{canvas3D}->set_on_increase_objects(sub { $self->increase() }); # $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() }); # $self->{canvas3D}->set_on_remove_object(sub { $self->remove() }); +# $self->{canvas3D}->set_on_instances_moved($on_instances_moved); #============================================================================================================================== - $self->{canvas3D}->set_on_instances_moved($on_instances_moved); $self->{canvas3D}->set_on_enable_action_buttons($enable_action_buttons); #=================================================================================================================================== Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); # $self->{canvas3D}->use_plain_shader(1); #=================================================================================================================================== - $self->{canvas3D}->set_on_wipe_tower_moved(sub { - my ($new_pos_3f) = @_; + Slic3r::GUI::_3DScene::register_on_wipe_tower_moved_callback($self->{canvas3D}, sub { + my ($x, $y) = @_; my $cfg = Slic3r::Config->new; - $cfg->set('wipe_tower_x', $new_pos_3f->x); - $cfg->set('wipe_tower_y', $new_pos_3f->y); + $cfg->set('wipe_tower_x', $x); + $cfg->set('wipe_tower_y', $y); $self->GetFrame->{options_tabs}{print}->load_config($cfg); }); + +# $self->{canvas3D}->set_on_wipe_tower_moved(sub { +# my ($new_pos_3f) = @_; +# my $cfg = Slic3r::Config->new; +# $cfg->set('wipe_tower_x', $new_pos_3f->x); +# $cfg->set('wipe_tower_y', $new_pos_3f->y); +# $self->GetFrame->{options_tabs}{print}->load_config($cfg); +# }); #============================================================================================================================== Slic3r::GUI::_3DScene::register_on_model_update_callback($self->{canvas3D}, sub { if (wxTheApp->{app_config}->get("background_processing")) { diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index da3bfc6176..28bc6490c7 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -13,9 +13,7 @@ use base qw(Slic3r::GUI::3DScene Class::Accessor); use Wx::Locale gettext => 'L'; #============================================================================================================================== -__PACKAGE__->mk_accessors(qw( - on_rotate_object_left on_rotate_object_right on_scale_object_uniformly - on_remove_object on_increase_objects on_decrease_objects on_enable_action_buttons)); +__PACKAGE__->mk_accessors(qw(on_enable_action_buttons)); #__PACKAGE__->mk_accessors(qw( # on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly # on_remove_object on_increase_objects on_decrease_objects on_enable_action_buttons)); @@ -44,12 +42,15 @@ sub new { #============================================================================================================================== $self->{config} = $config; #============================================================================================================================== + Slic3r::GUI::_3DScene::set_model($self, $model); Slic3r::GUI::_3DScene::set_print($self, $print); Slic3r::GUI::_3DScene::set_config($self, $config); #============================================================================================================================== $self->{on_select_object} = sub {}; - $self->{on_instances_moved} = sub {}; - $self->{on_wipe_tower_moved} = sub {}; +#============================================================================================================================== +# $self->{on_instances_moved} = sub {}; +# $self->{on_wipe_tower_moved} = sub {}; +#============================================================================================================================== $self->{objects_volumes_idxs} = []; @@ -64,44 +65,39 @@ sub new { # $self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx) # if ($self->{on_select_object}); # }); -#============================================================================================================================== - -#============================================================================================================================== - Slic3r::GUI::_3DScene::register_on_move_callback($self, sub { +# # $self->on_move(sub { -#============================================================================================================================== - my @volume_idxs = @_; - my %done = (); # prevent moving instances twice - my $object_moved; - my $wipe_tower_moved; - foreach my $volume_idx (@volume_idxs) { - my $volume = $self->volumes->[$volume_idx]; - my $obj_idx = $volume->object_idx; - my $instance_idx = $volume->instance_idx; - next if $done{"${obj_idx}_${instance_idx}"}; - $done{"${obj_idx}_${instance_idx}"} = 1; - if ($obj_idx < 1000) { - # Move a regular object. - my $model_object = $self->{model}->get_object($obj_idx); - $model_object - ->instances->[$instance_idx] - ->offset - ->translate($volume->origin->x, $volume->origin->y); #)) - $model_object->invalidate_bounding_box; - $object_moved = 1; - } elsif ($obj_idx == 1000) { - # Move a wipe tower proxy. - $wipe_tower_moved = $volume->origin; - } - } - - $self->{on_instances_moved}->() - if $object_moved && $self->{on_instances_moved}; - $self->{on_wipe_tower_moved}->($wipe_tower_moved) - if $wipe_tower_moved && $self->{on_wipe_tower_moved}; - }); - -#============================================================================================================================== +# my @volume_idxs = @_; +# my %done = (); # prevent moving instances twice +# my $object_moved; +# my $wipe_tower_moved; +# foreach my $volume_idx (@volume_idxs) { +# my $volume = $self->volumes->[$volume_idx]; +# my $obj_idx = $volume->object_idx; +# my $instance_idx = $volume->instance_idx; +# next if $done{"${obj_idx}_${instance_idx}"}; +# $done{"${obj_idx}_${instance_idx}"} = 1; +# if ($obj_idx < 1000) { +# # Move a regular object. +# my $model_object = $self->{model}->get_object($obj_idx); +# $model_object +# ->instances->[$instance_idx] +# ->offset +# ->translate($volume->origin->x, $volume->origin->y); #)) +# $model_object->invalidate_bounding_box; +# $object_moved = 1; +# } elsif ($obj_idx == 1000) { +# # Move a wipe tower proxy. +# $wipe_tower_moved = $volume->origin; +# } +# } +# +# $self->{on_instances_moved}->() +# if $object_moved && $self->{on_instances_moved}; +# $self->{on_wipe_tower_moved}->($wipe_tower_moved) +# if $wipe_tower_moved && $self->{on_wipe_tower_moved}; +# }); +# # EVT_KEY_DOWN($self, sub { # my ($s, $event) = @_; # if ($event->HasModifiers) { @@ -194,19 +190,17 @@ sub set_on_select_object { # my ($self, $cb) = @_; # $self->on_remove_object($cb); #} -#============================================================================================================================== - -sub set_on_instances_moved { - my ($self, $cb) = @_; - $self->{on_instances_moved} = $cb; -} - -sub set_on_wipe_tower_moved { - my ($self, $cb) = @_; - $self->{on_wipe_tower_moved} = $cb; -} - -#============================================================================================================================== +# +#sub set_on_instances_moved { +# my ($self, $cb) = @_; +# $self->{on_instances_moved} = $cb; +#} +# +#sub set_on_wipe_tower_moved { +# my ($self, $cb) = @_; +# $self->{on_wipe_tower_moved} = $cb; +#} +# #sub set_on_model_update { # my ($self, $cb) = @_; # $self->on_model_update($cb); @@ -216,6 +210,9 @@ sub set_on_wipe_tower_moved { sub set_on_enable_action_buttons { my ($self, $cb) = @_; $self->on_enable_action_buttons($cb); +#============================================================================================================================== + Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self, $cb); +#============================================================================================================================== } sub update_volumes_selection { diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 45e83b1b8d..913f91ccf4 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -96,6 +96,7 @@ public: void call(int i) const; void call(int i, int j) const; void call(const std::vector& ints) const; + void call(double x, double y) const; // void call(); // void call(int i); // void call(int i, int j); diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 883f5e753d..017e2f1b31 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -266,6 +266,24 @@ void PerlCallback::call(const std::vector& ints) const LEAVE; } +//############################################################################################################## +void PerlCallback::call(double x, double y) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(x))); + XPUSHs(sv_2mortal(newSVnv(y))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} +//############################################################################################################## + #ifdef WIN32 #ifndef NOMINMAX # define NOMINMAX diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 79d4cba257..2d341996e7 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1832,6 +1832,11 @@ void _3DScene::set_print(wxGLCanvas* canvas, Print* print) s_canvas_mgr.set_print(canvas, print); } +void _3DScene::set_model(wxGLCanvas* canvas, Model* model) +{ + s_canvas_mgr.set_model(canvas, model); +} + void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { return s_canvas_mgr.set_bed_shape(canvas, shape); @@ -1992,11 +1997,6 @@ void _3DScene::register_on_model_update_callback(wxGLCanvas* canvas, void* callb s_canvas_mgr.register_on_model_update_callback(canvas, callback); } -void _3DScene::register_on_move_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_move_callback(canvas, callback); -} - void _3DScene::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback) { s_canvas_mgr.register_on_remove_object_callback(canvas, callback); @@ -2032,6 +2032,21 @@ void _3DScene::register_on_decrease_objects_callback(wxGLCanvas* canvas, void* c s_canvas_mgr.register_on_decrease_objects_callback(canvas, callback); } +void _3DScene::register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_instance_moved_callback(canvas, callback); +} + +void _3DScene::register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_wipe_tower_moved_callback(canvas, callback); +} + +void _3DScene::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_enable_action_buttons_callback(canvas, callback); +} + //void _3DScene::_glew_init() //{ // glewInit(); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 38cb82ccb6..7c4d8b3e9e 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -565,6 +565,7 @@ public: static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); static void set_print(wxGLCanvas* canvas, Print* print); + static void set_model(wxGLCanvas* canvas, Model* model); static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); static void set_auto_bed_shape(wxGLCanvas* canvas); @@ -609,7 +610,6 @@ public: static void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); static void register_on_select_callback(wxGLCanvas* canvas, void* callback); static void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); - static void register_on_move_callback(wxGLCanvas* canvas, void* callback); static void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback); static void register_on_arrange_callback(wxGLCanvas* canvas, void* callback); static void register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback); @@ -617,6 +617,9 @@ public: static void register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback); static void register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback); static void register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback); + static void register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback); + static void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); + static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); // static void _glew_init(); //################################################################################################################## diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index d0baa6563f..f969e1d46d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -947,6 +947,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_volumes(nullptr) , m_config(nullptr) , m_print(nullptr) + , m_model(nullptr) , m_dirty(true) , m_initialized(false) , m_use_VBOs(false) @@ -1116,6 +1117,11 @@ void GLCanvas3D::set_print(Print* print) m_print = print; } +void GLCanvas3D::set_model(Model* model) +{ + m_model = model; +} + void GLCanvas3D::set_bed_shape(const Pointfs& shape) { m_bed.set_shape(shape); @@ -1836,12 +1842,6 @@ void GLCanvas3D::register_on_model_update_callback(void* callback) m_on_model_update_callback.register_callback(callback); } -void GLCanvas3D::register_on_move_callback(void* callback) -{ - if (callback != nullptr) - m_on_move_callback.register_callback(callback); -} - void GLCanvas3D::register_on_remove_object_callback(void* callback) { if (callback != nullptr) @@ -1884,6 +1884,24 @@ void GLCanvas3D::register_on_decrease_objects_callback(void* callback) m_on_decrease_objects_callback.register_callback(callback); } +void GLCanvas3D::register_on_instance_moved_callback(void* callback) +{ + if (callback != nullptr) + m_on_instance_moved_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_wipe_tower_moved_callback(void* callback) +{ + if (callback != nullptr) + m_on_wipe_tower_moved_callback.register_callback(callback); +} + +void GLCanvas3D::register_on_enable_action_buttons_callback(void* callback) +{ + if (callback != nullptr) + m_on_enable_action_buttons_callback.register_callback(callback); +} + void GLCanvas3D::bind_event_handlers() { if (m_canvas != nullptr) @@ -2283,7 +2301,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } - m_on_move_callback.call(volume_idxs); + _on_move(volume_idxs); } m_mouse.drag.volume_idx = -1; @@ -2511,7 +2529,6 @@ void GLCanvas3D::_deregister_callbacks() m_on_right_click_callback.deregister_callback(); m_on_select_callback.deregister_callback(); m_on_model_update_callback.deregister_callback(); - m_on_move_callback.deregister_callback(); m_on_remove_object_callback.deregister_callback(); m_on_arrange_callback.deregister_callback(); m_on_rotate_object_left_callback.deregister_callback(); @@ -2519,6 +2536,9 @@ void GLCanvas3D::_deregister_callbacks() m_on_scale_object_uniformly_callback.deregister_callback(); m_on_increase_objects_callback.deregister_callback(); m_on_decrease_objects_callback.deregister_callback(); + m_on_instance_moved_callback.deregister_callback(); + m_on_wipe_tower_moved_callback.deregister_callback(); + m_on_enable_action_buttons_callback.deregister_callback(); } void GLCanvas3D::_mark_volumes_for_layer_height() const @@ -3543,6 +3563,48 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe } } +void GLCanvas3D::_on_move(const std::vector& volume_idxs) +{ + if ((m_model == nullptr) || (m_volumes == nullptr)) + return; + + std::set done; // prevent moving instances twice + bool object_moved = false; + Pointf3 wipe_tower_origin(0.0, 0.0, 0.0); + for (int volume_idx : volume_idxs) + { + GLVolume* volume = m_volumes->volumes[volume_idx]; + int obj_idx = volume->object_idx(); + int instance_idx = volume->instance_idx(); + + // prevent moving instances twice + char done_id[64]; + ::sprintf(done_id, "%d_%d", obj_idx, instance_idx); + if (done.find(done_id) != done.end()) + continue; + + done.insert(done_id); + + if (obj_idx < 1000) + { + // Move a regular object. + ModelObject* model_object = m_model->objects[obj_idx]; + model_object->instances[instance_idx]->offset.translate(volume->origin.x, volume->origin.y); + model_object->invalidate_bounding_box(); + object_moved = true; + } + else if (obj_idx == 1000) + // Move a wipe tower proxy. + wipe_tower_origin = volume->origin; + } + + if (object_moved) + m_on_instance_moved_callback.call(); + + if (wipe_tower_origin != Pointf3(0.0, 0.0, 0.0)) + m_on_wipe_tower_moved_callback.call(wipe_tower_origin.x, wipe_tower_origin.y); +} + std::vector GLCanvas3D::_parse_colors(const std::vector& colors) { std::vector output(colors.size() * 4, 1.0f); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index dde51a2773..e18b80345c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -330,6 +330,7 @@ private: GLVolumeCollection* m_volumes; DynamicPrintConfig* m_config; Print* m_print; + Model* m_model; bool m_dirty; bool m_initialized; @@ -355,7 +356,6 @@ private: PerlCallback m_on_right_click_callback; PerlCallback m_on_select_callback; PerlCallback m_on_model_update_callback; - PerlCallback m_on_move_callback; PerlCallback m_on_remove_object_callback; PerlCallback m_on_arrange_callback; PerlCallback m_on_rotate_object_left_callback; @@ -363,6 +363,9 @@ private: PerlCallback m_on_scale_object_uniformly_callback; PerlCallback m_on_increase_objects_callback; PerlCallback m_on_decrease_objects_callback; + PerlCallback m_on_instance_moved_callback; + PerlCallback m_on_wipe_tower_moved_callback; + PerlCallback m_on_enable_action_buttons_callback; public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); @@ -381,6 +384,7 @@ public: void set_config(DynamicPrintConfig* config); void set_print(Print* print); + void set_model(Model* model); // Set the bed shape to a single closed 2D polygon(array of two element arrays), // triangulate the bed and store the triangles into m_bed.m_triangles, @@ -447,7 +451,6 @@ public: void register_on_right_click_callback(void* callback); void register_on_select_callback(void* callback); void register_on_model_update_callback(void* callback); - void register_on_move_callback(void* callback); void register_on_remove_object_callback(void* callback); void register_on_arrange_callback(void* callback); void register_on_rotate_object_left_callback(void* callback); @@ -455,6 +458,9 @@ public: void register_on_scale_object_uniformly_callback(void* callback); void register_on_increase_objects_callback(void* callback); void register_on_decrease_objects_callback(void* callback); + void register_on_instance_moved_callback(void* callback); + void register_on_wipe_tower_moved_callback(void* callback); + void register_on_enable_action_buttons_callback(void* callback); void bind_event_handlers(); void unbind_event_handlers(); @@ -528,6 +534,8 @@ private: // sets gcode geometry visibility according to user selection void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data); + void _on_move(const std::vector& volume_idxs); + static std::vector _parse_colors(const std::vector& colors); }; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index a957d8b9a7..34d62927ab 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -268,6 +268,13 @@ void GLCanvas3DManager::set_print(wxGLCanvas* canvas, Print* print) it->second->set_print(print); } +void GLCanvas3DManager::set_model(wxGLCanvas* canvas, Model* model) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_model(model); +} + void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -543,13 +550,6 @@ void GLCanvas3DManager::register_on_model_update_callback(wxGLCanvas* canvas, vo it->second->register_on_model_update_callback(callback); } -void GLCanvas3DManager::register_on_move_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_move_callback(callback); -} - void GLCanvas3DManager::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -599,6 +599,27 @@ void GLCanvas3DManager::register_on_decrease_objects_callback(wxGLCanvas* canvas it->second->register_on_decrease_objects_callback(callback); } +void GLCanvas3DManager::register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_instance_moved_callback(callback); +} + +void GLCanvas3DManager::register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_wipe_tower_moved_callback(callback); +} + +void GLCanvas3DManager::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_enable_action_buttons_callback(callback); +} + GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 50d282cc9f..f012b7703b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -60,6 +60,7 @@ public: void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); void set_print(wxGLCanvas* canvas, Print* print); + void set_model(wxGLCanvas* canvas, Model* model); void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); void set_auto_bed_shape(wxGLCanvas* canvas); @@ -112,7 +113,6 @@ public: void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); void register_on_select_callback(wxGLCanvas* canvas, void* callback); void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); - void register_on_move_callback(wxGLCanvas* canvas, void* callback); void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback); void register_on_arrange_callback(wxGLCanvas* canvas, void* callback); void register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback); @@ -120,6 +120,9 @@ public: void register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback); void register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback); void register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback); + void register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback); + void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); + void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index b70d49b762..0635cea31d 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -239,6 +239,13 @@ set_print(canvas, print) CODE: _3DScene::set_print((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print); +void +set_model(canvas, model) + SV *canvas; + Model *model; + CODE: + _3DScene::set_model((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), model); + void set_bed_shape(canvas, shape) SV *canvas; @@ -466,13 +473,6 @@ register_on_model_update_callback(canvas, callback) CODE: _3DScene::register_on_model_update_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); -void -register_on_move_callback(canvas, callback) - SV *canvas; - SV *callback; - CODE: - _3DScene::register_on_move_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); - void register_on_remove_object_callback(canvas, callback) SV *canvas; @@ -522,6 +522,27 @@ register_on_decrease_objects_callback(canvas, callback) CODE: _3DScene::register_on_decrease_objects_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_instance_moved_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_instance_moved_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_wipe_tower_moved_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_wipe_tower_moved_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + +void +register_on_enable_action_buttons_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_enable_action_buttons_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + unsigned int finalize_legend_texture() CODE: From 766d1d52a9c602c61f9bec66edb05162244e9c0b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 7 Jun 2018 16:13:32 +0200 Subject: [PATCH 075/117] Fixed import of model rotated clockwise from 3mf --- xs/src/libslic3r/Format/3mf.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp index 0467962c3d..2c32db1a67 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/xs/src/libslic3r/Format/3mf.cpp @@ -1271,6 +1271,7 @@ namespace Slic3r { if ((std::abs(sx - sy) > 0.00001) || (std::abs(sx - sz) > 0.00001)) return; +#if 0 // use quaternions // rotations (extracted using quaternion) double inv_sx = 1.0 / sx; double inv_sy = 1.0 / sy; @@ -1331,6 +1332,25 @@ namespace Slic3r { if (angle_z < 0.0) angle_z += 2.0 * PI; } +#else // use eigen library + double inv_sx = 1.0 / sx; + double inv_sy = 1.0 / sy; + double inv_sz = 1.0 / sz; + + Eigen::Matrix3d m3x3; + m3x3 << (double)matrix(0, 0) * inv_sx, (double)matrix(0, 1) * inv_sy, (double)matrix(0, 2) * inv_sz, + (double)matrix(1, 0) * inv_sx, (double)matrix(1, 1) * inv_sy, (double)matrix(1, 2) * inv_sz, + (double)matrix(2, 0) * inv_sx, (double)matrix(2, 1) * inv_sy, (double)matrix(2, 2) * inv_sz; + + Eigen::AngleAxisd rotation; + rotation.fromRotationMatrix(m3x3); + + // invalid rotation axis, we currently handle only rotations around Z axis + if ((rotation.angle() != 0.0) && (rotation.axis() != Eigen::Vector3d::UnitZ()) && (rotation.axis() != -Eigen::Vector3d::UnitZ())) + return; + + double angle_z = (rotation.axis() == Eigen::Vector3d::UnitZ()) ? rotation.angle() : -rotation.angle(); +#endif instance.offset.x = offset_x; instance.offset.y = offset_y; From bf2fd5457822d6774fc4ddb1c8954474c67fb775 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Jun 2018 09:40:00 +0200 Subject: [PATCH 076/117] reload_scene method of 3D class moved to c++ --- lib/Slic3r/GUI/Plater.pm | 92 ++++++++---- lib/Slic3r/GUI/Plater/3D.pm | 187 ++++++++++-------------- xs/src/libslic3r/Utils.hpp | 1 + xs/src/libslic3r/utils.cpp | 5 + xs/src/slic3r/GUI/3DScene.cpp | 20 +++ xs/src/slic3r/GUI/3DScene.hpp | 7 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 105 +++++++++++++ xs/src/slic3r/GUI/GLCanvas3D.hpp | 12 ++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 27 ++++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 7 + xs/xsp/GUI_3DScene.xsp | 29 ++++ 11 files changed, 355 insertions(+), 137 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 82249bfd4f..9dad798071 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -128,23 +128,10 @@ sub new { Slic3r::GUI::_3DScene::register_on_decrease_objects_callback($self->{canvas3D}, sub { $self->decrease() }); Slic3r::GUI::_3DScene::register_on_remove_object_callback($self->{canvas3D}, sub { $self->remove() }); Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved); -# $self->{canvas3D}->set_on_double_click($on_double_click); -# $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); -# $self->{canvas3D}->set_on_arrange(sub { $self->arrange }); -# $self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') }); -# $self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') }); -# $self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) }); -# $self->{canvas3D}->set_on_increase_objects(sub { $self->increase() }); -# $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() }); -# $self->{canvas3D}->set_on_remove_object(sub { $self->remove() }); -# $self->{canvas3D}->set_on_instances_moved($on_instances_moved); -#============================================================================================================================== - $self->{canvas3D}->set_on_enable_action_buttons($enable_action_buttons); -#=================================================================================================================================== + Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); -# $self->{canvas3D}->use_plain_shader(1); -#=================================================================================================================================== + Slic3r::GUI::_3DScene::register_on_wipe_tower_moved_callback($self->{canvas3D}, sub { my ($x, $y) = @_; my $cfg = Slic3r::Config->new; @@ -153,14 +140,6 @@ sub new { $self->GetFrame->{options_tabs}{print}->load_config($cfg); }); -# $self->{canvas3D}->set_on_wipe_tower_moved(sub { -# my ($new_pos_3f) = @_; -# my $cfg = Slic3r::Config->new; -# $cfg->set('wipe_tower_x', $new_pos_3f->x); -# $cfg->set('wipe_tower_y', $new_pos_3f->y); -# $self->GetFrame->{options_tabs}{print}->load_config($cfg); -# }); -#============================================================================================================================== Slic3r::GUI::_3DScene::register_on_model_update_callback($self->{canvas3D}, sub { if (wxTheApp->{app_config}->get("background_processing")) { $self->schedule_background_process; @@ -172,6 +151,27 @@ sub new { Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); +# $self->{canvas3D}->set_on_double_click($on_double_click); +# $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); +# $self->{canvas3D}->set_on_arrange(sub { $self->arrange }); +# $self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') }); +# $self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') }); +# $self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) }); +# $self->{canvas3D}->set_on_increase_objects(sub { $self->increase() }); +# $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() }); +# $self->{canvas3D}->set_on_remove_object(sub { $self->remove() }); +# $self->{canvas3D}->set_on_instances_moved($on_instances_moved); +# $self->{canvas3D}->set_on_enable_action_buttons($enable_action_buttons); +# $self->{canvas3D}->use_plain_shader(1); +# +# $self->{canvas3D}->set_on_wipe_tower_moved(sub { +# my ($new_pos_3f) = @_; +# my $cfg = Slic3r::Config->new; +# $cfg->set('wipe_tower_x', $new_pos_3f->x); +# $cfg->set('wipe_tower_y', $new_pos_3f->y); +# $self->GetFrame->{options_tabs}{print}->load_config($cfg); +# }); +# # $self->{canvas3D}->set_on_model_update(sub { # if (wxTheApp->{app_config}->get("background_processing")) { # $self->schedule_background_process; @@ -229,7 +229,20 @@ sub new { #============================================================================================================================== } +#============================================================================================================================== + if ($preview == $self->{canvas3D}) { + if (Slic3r::GUI::_3DScene::is_reload_delayed($self->{canvas3D})) { + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); + } + } + else { +#============================================================================================================================== $preview->OnActivate if $preview->can('OnActivate'); +#============================================================================================================================== + } +#============================================================================================================================== }); # toolbar for object manipulation @@ -1795,7 +1808,12 @@ sub update { } $self->{canvas}->reload_scene if $self->{canvas}; - $self->{canvas3D}->reload_scene if $self->{canvas3D}; +#============================================================================================================================== + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0); +# $self->{canvas3D}->reload_scene if $self->{canvas3D}; +#============================================================================================================================== $self->{preview3D}->reset_gcode_preview_data if $self->{preview3D}; $self->{preview3D}->reload_print if $self->{preview3D}; } @@ -1933,14 +1951,29 @@ sub list_item_selected { my $obj_idx = $event->GetIndex; $self->select_object($obj_idx); $self->{canvas}->Refresh; - $self->{canvas3D}->update_volumes_selection if $self->{canvas3D}; #============================================================================================================================== - Slic3r::GUI::_3DScene::render($self->{canvas3D}) if $self->{canvas3D}; + if ($self->{canvas3D}) { + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::render($self->{canvas3D}); + } +# $self->{canvas3D}->update_volumes_selection if $self->{canvas3D}; # $self->{canvas3D}->Render if $self->{canvas3D}; #============================================================================================================================== undef $self->{_lecursor}; } +#============================================================================================================================== +sub collect_selections { + my ($self) = @_; + my $selections = []; + foreach my $o (@{$self->{objects}}) { + push(@$selections, $o->selected); + } + return $selections; +} +#============================================================================================================================== + sub list_item_activated { my ($self, $event, $obj_idx) = @_; @@ -2036,7 +2069,12 @@ sub object_settings_dialog { $self->{print}->reload_object($obj_idx); $self->schedule_background_process; $self->{canvas}->reload_scene if $self->{canvas}; - $self->{canvas3D}->reload_scene if $self->{canvas3D}; +#============================================================================================================================== + my $selections = $self->collect_selections; + Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); + Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0); +# $self->{canvas3D}->reload_scene if $self->{canvas3D}; +#============================================================================================================================== } else { $self->resume_background_process; } diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 28bc6490c7..fdd55cdd8f 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -13,7 +13,6 @@ use base qw(Slic3r::GUI::3DScene Class::Accessor); use Wx::Locale gettext => 'L'; #============================================================================================================================== -__PACKAGE__->mk_accessors(qw(on_enable_action_buttons)); #__PACKAGE__->mk_accessors(qw( # on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly # on_remove_object on_increase_objects on_decrease_objects on_enable_action_buttons)); @@ -33,9 +32,9 @@ sub new { # $self->enable_moving(1); # $self->select_by('object'); # $self->drag_by('instance'); +# +# $self->{objects} = $objects; #============================================================================================================================== - - $self->{objects} = $objects; $self->{model} = $model; #============================================================================================================================== # $self->{print} = $print; @@ -50,9 +49,9 @@ sub new { #============================================================================================================================== # $self->{on_instances_moved} = sub {}; # $self->{on_wipe_tower_moved} = sub {}; +# +# $self->{objects_volumes_idxs} = []; #============================================================================================================================== - - $self->{objects_volumes_idxs} = []; #============================================================================================================================== Slic3r::GUI::_3DScene::register_on_select_callback($self, sub { @@ -205,125 +204,93 @@ sub set_on_select_object { # my ($self, $cb) = @_; # $self->on_model_update($cb); #} -#============================================================================================================================== - -sub set_on_enable_action_buttons { - my ($self, $cb) = @_; - $self->on_enable_action_buttons($cb); -#============================================================================================================================== - Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self, $cb); -#============================================================================================================================== -} - -sub update_volumes_selection { - my ($self) = @_; - - foreach my $obj_idx (0..$#{$self->{model}->objects}) { - if ($self->{objects}[$obj_idx]->selected) { - my $volume_idxs = $self->{objects_volumes_idxs}->[$obj_idx]; -#============================================================================================================================== - Slic3r::GUI::_3DScene::select_volume($self, $_) for @{$volume_idxs}; +# +#sub set_on_enable_action_buttons { +# my ($self, $cb) = @_; +# $self->on_enable_action_buttons($cb); +#} +# +#sub update_volumes_selection { +# my ($self) = @_; +# +# foreach my $obj_idx (0..$#{$self->{model}->objects}) { +# if ($self->{objects}[$obj_idx]->selected) { +# my $volume_idxs = $self->{objects_volumes_idxs}->[$obj_idx]; # $self->select_volume($_) for @{$volume_idxs}; -#============================================================================================================================== - } - } -} - -sub reload_scene { - my ($self, $force) = @_; - -#============================================================================================================================== - Slic3r::GUI::_3DScene::reset_volumes($self); - Slic3r::GUI::_3DScene::set_bed_shape($self, $self->{config}->bed_shape); +# } +# } +#} +# +#sub reload_scene { +# my ($self, $force) = @_; +# # $self->reset_objects; # $self->update_bed_size; -#============================================================================================================================== - - if (! $self->IsShown && ! $force) { - $self->{reload_delayed} = 1; - return; - } - - $self->{reload_delayed} = 0; - - $self->{objects_volumes_idxs} = []; - foreach my $obj_idx (0..$#{$self->{model}->objects}) { -#============================================================================================================================== - my $volume_idxs = Slic3r::GUI::_3DScene::load_model($self, $self->{model}, $obj_idx); - push(@{$self->{objects_volumes_idxs}}, \@{$volume_idxs}); - +# +# if (! $self->IsShown && ! $force) { +# $self->{reload_delayed} = 1; +# return; +# } +# +# $self->{reload_delayed} = 0; +# +# $self->{objects_volumes_idxs} = []; +# foreach my $obj_idx (0..$#{$self->{model}->objects}) { # my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx); # push(@{$self->{objects_volumes_idxs}}, \@volume_idxs); -#============================================================================================================================== - } - - $self->update_volumes_selection; - - if (defined $self->{config}->nozzle_diameter) { - # Should the wipe tower be visualized? - my $extruders_count = scalar @{ $self->{config}->nozzle_diameter }; - # Height of a print. - my $height = $self->{model}->bounding_box->z_max; - # Show at least a slab. - $height = 10 if $height < 10; - if ($extruders_count > 1 && $self->{config}->single_extruder_multi_material && $self->{config}->wipe_tower && - ! $self->{config}->complete_objects) { - $self->volumes->load_wipe_tower_preview(1000, - $self->{config}->wipe_tower_x, $self->{config}->wipe_tower_y, $self->{config}->wipe_tower_width, - #$self->{config}->wipe_tower_per_color_wipe# 15 * ($extruders_count - 1), # this is just a hack when the config parameter became obsolete - 15 * ($extruders_count - 1), -#============================================================================================================================== - $self->{model}->bounding_box->z_max, $self->{config}->wipe_tower_rotation_angle, Slic3r::GUI::_3DScene::use_VBOs()); +# } +# +# $self->update_volumes_selection; +# +# if (defined $self->{config}->nozzle_diameter) { +# # Should the wipe tower be visualized? +# my $extruders_count = scalar @{ $self->{config}->nozzle_diameter }; +# # Height of a print. +# my $height = $self->{model}->bounding_box->z_max; +# # Show at least a slab. +# $height = 10 if $height < 10; +# if ($extruders_count > 1 && $self->{config}->single_extruder_multi_material && $self->{config}->wipe_tower && +# ! $self->{config}->complete_objects) { +# $self->volumes->load_wipe_tower_preview(1000, +# $self->{config}->wipe_tower_x, $self->{config}->wipe_tower_y, $self->{config}->wipe_tower_width, +# #$self->{config}->wipe_tower_per_color_wipe# 15 * ($extruders_count - 1), # this is just a hack when the config parameter became obsolete +# 15 * ($extruders_count - 1), # $self->{model}->bounding_box->z_max, $self->{config}->wipe_tower_rotation_angle, $self->UseVBOs); -#============================================================================================================================== - } - } - -#============================================================================================================================== - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self); +# } +# } +# # $self->update_volumes_colors_by_extruder($self->{config}); -#============================================================================================================================== - - # checks for geometry outside the print volume to render it accordingly - if (scalar @{$self->volumes} > 0) - { - my $contained = $self->volumes->check_outside_state($self->{config}); - if (!$contained) { -#============================================================================================================================== - Slic3r::GUI::_3DScene::enable_warning_texture($self, 1); +# +# # checks for geometry outside the print volume to render it accordingly +# if (scalar @{$self->volumes} > 0) +# { +# my $contained = $self->volumes->check_outside_state($self->{config}); +# if (!$contained) { # $self->set_warning_enabled(1); -#============================================================================================================================== - Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume")); - $self->on_enable_action_buttons->(0) if ($self->on_enable_action_buttons); - } else { -#============================================================================================================================== - Slic3r::GUI::_3DScene::enable_warning_texture($self, 0); +# Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume")); +# $self->on_enable_action_buttons->(0) if ($self->on_enable_action_buttons); +# } else { # $self->set_warning_enabled(0); -#============================================================================================================================== - $self->volumes->reset_outside_state(); - Slic3r::GUI::_3DScene::reset_warning_texture(); - $self->on_enable_action_buttons->(scalar @{$self->{model}->objects} > 0) if ($self->on_enable_action_buttons); - } - } else { -#============================================================================================================================== - Slic3r::GUI::_3DScene::enable_warning_texture($self, 0); +# $self->volumes->reset_outside_state(); +# Slic3r::GUI::_3DScene::reset_warning_texture(); +# $self->on_enable_action_buttons->(scalar @{$self->{model}->objects} > 0) if ($self->on_enable_action_buttons); +# } +# } else { # $self->set_warning_enabled(0); -#============================================================================================================================== - Slic3r::GUI::_3DScene::reset_warning_texture(); - } -} - -#============================================================================================================================== +# Slic3r::GUI::_3DScene::reset_warning_texture(); +# } +#} +# #sub update_bed_size { # my ($self) = @_; # $self->set_bed_shape($self->{config}->bed_shape); #} +# +## Called by the Platter wxNotebook when this page is activated. +#sub OnActivate { +# my ($self) = @_; +# $self->reload_scene(1) if ($self->{reload_delayed}); +#} #============================================================================================================================== -# Called by the Platter wxNotebook when this page is activated. -sub OnActivate { - my ($self) = @_; - $self->reload_scene(1) if ($self->{reload_delayed}); -} - 1; diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 913f91ccf4..472b0478f4 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -97,6 +97,7 @@ public: void call(int i, int j) const; void call(const std::vector& ints) const; void call(double x, double y) const; + void call(bool b) const; // void call(); // void call(int i); // void call(int i, int j); diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 017e2f1b31..7209d86f6b 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -282,6 +282,11 @@ void PerlCallback::call(double x, double y) const FREETMPS; LEAVE; } + +void PerlCallback::call(bool b) const +{ + call(b ? 1 : 0); +} //############################################################################################################## #ifdef WIN32 diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 2d341996e7..e773ec6eb2 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1822,6 +1822,16 @@ void _3DScene::select_volume(wxGLCanvas* canvas, unsigned int id) s_canvas_mgr.select_volume(canvas, id); } +void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections) +{ + s_canvas_mgr.update_volumes_selection(canvas, selections); +} + +void _3DScene::set_objects_selections(wxGLCanvas* canvas, const std::vector& selections) +{ + s_canvas_mgr.set_objects_selections(canvas, selections); +} + void _3DScene::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) { s_canvas_mgr.set_config(canvas, config); @@ -1892,6 +1902,11 @@ bool _3DScene::is_shader_enabled(wxGLCanvas* canvas) return s_canvas_mgr.is_shader_enabled(canvas); } +bool _3DScene::is_reload_delayed(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_reload_delayed(canvas); +} + void _3DScene::enable_layers_editing(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_layers_editing(canvas, enable); @@ -2091,6 +2106,11 @@ std::vector _3DScene::load_object(wxGLCanvas* canvas, const Model* model, i return s_canvas_mgr.load_object(canvas, model, obj_idx); } +void _3DScene::reload_scene(wxGLCanvas* canvas, bool force) +{ + s_canvas_mgr.reload_scene(canvas, force); +} + void _3DScene::load_print_toolpaths(wxGLCanvas* canvas) { s_canvas_mgr.load_print_toolpaths(canvas); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 7c4d8b3e9e..a66130a3b1 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -562,6 +562,9 @@ public: static void reset_volumes(wxGLCanvas* canvas); static void deselect_volumes(wxGLCanvas* canvas); static void select_volume(wxGLCanvas* canvas, unsigned int id); + static void update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections); + + static void set_objects_selections(wxGLCanvas* canvas, const std::vector& selections); static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); static void set_print(wxGLCanvas* canvas, Print* print); @@ -584,6 +587,8 @@ public: static bool is_layers_editing_allowed(wxGLCanvas* canvas); static bool is_shader_enabled(wxGLCanvas* canvas); + static bool is_reload_delayed(wxGLCanvas* canvas); + static void enable_layers_editing(wxGLCanvas* canvas, bool enable); static void enable_warning_texture(wxGLCanvas* canvas, bool enable); static void enable_legend_texture(wxGLCanvas* canvas, bool enable); @@ -628,6 +633,8 @@ public: static std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); static std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); + static void reload_scene(wxGLCanvas* canvas, bool force); + static void load_print_toolpaths(wxGLCanvas* canvas); static void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& str_tool_colors); static void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector& str_tool_colors); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index f969e1d46d..7452686276 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -2,6 +2,7 @@ #include "../../slic3r/GUI/3DScene.hpp" #include "../../slic3r/GUI/GLShader.hpp" +#include "../../slic3r/GUI/GUI.hpp" #include "../../libslic3r/ClipperUtils.hpp" #include "../../libslic3r/PrintConfig.hpp" #include "../../libslic3r/Print.hpp" @@ -963,6 +964,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_color_by("volume") , m_select_by("object") , m_drag_by("instance") + , m_reload_delayed(false) { if (m_canvas != nullptr) m_timer = new wxTimer(m_canvas); @@ -1107,6 +1109,29 @@ void GLCanvas3D::select_volume(unsigned int id) } } +void GLCanvas3D::update_volumes_selection(const std::vector& selections) +{ + if (m_model == nullptr) + return; + + for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) + { + if (selections[obj_idx] == 1) + { + const std::vector& volume_idxs = m_objects_volumes_idxs[obj_idx]; + for (int v : volume_idxs) + { + select_volume(v); + } + } + } +} + +void GLCanvas3D::set_objects_selections(const std::vector& selections) +{ + m_objects_selections = selections; +} + void GLCanvas3D::set_config(DynamicPrintConfig* config) { m_config = config; @@ -1210,6 +1235,11 @@ bool GLCanvas3D::is_shader_enabled() const return m_shader_enabled; } +bool GLCanvas3D::is_reload_delayed() const +{ + return m_reload_delayed; +} + void GLCanvas3D::enable_layers_editing(bool enable) { m_layers_editing.set_enabled(enable); @@ -1409,6 +1439,81 @@ std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) return std::vector(); } +void GLCanvas3D::reload_scene(bool force) +{ + if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr) || (m_volumes == nullptr)) + return; + + reset_volumes(); + set_bed_shape(dynamic_cast(m_config->option("bed_shape"))->values); + + if (!m_canvas->IsShown() && !force) + { + m_reload_delayed = true; + return; + } + + m_reload_delayed = false; + + m_objects_volumes_idxs.clear(); + + for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) + { + m_objects_volumes_idxs.push_back(load_object(*m_model, obj_idx)); + } + + update_volumes_selection(m_objects_selections); + + if (m_config->has("nozzle_diameter")) + { + // Should the wipe tower be visualized ? + unsigned int extruders_count = (unsigned int)dynamic_cast(m_config->option("nozzle_diameter"))->values.size(); + + bool semm = dynamic_cast(m_config->option("single_extruder_multi_material"))->value; + bool wt = dynamic_cast(m_config->option("wipe_tower"))->value; + bool co = dynamic_cast(m_config->option("complete_objects"))->value; + + if ((extruders_count > 1) && semm && wt && !co) + { + // Height of a print (Show at least a slab) + coordf_t height = std::max(m_model->bounding_box().max.z, 10.0); + + float x = dynamic_cast(m_config->option("wipe_tower_x"))->value; + float y = dynamic_cast(m_config->option("wipe_tower_y"))->value; + float w = dynamic_cast(m_config->option("wipe_tower_width"))->value; + float a = dynamic_cast(m_config->option("wipe_tower_rotation_angle"))->value; + + m_volumes->load_wipe_tower_preview(1000, x, y, w, 15.0f * (float)(extruders_count - 1), (float)height, a, m_use_VBOs && m_initialized); + } + } + + update_volumes_colors_by_extruder(); + + // checks for geometry outside the print volume to render it accordingly + if (!m_volumes->empty()) + { + bool contained = m_volumes->check_outside_state(m_config); + if (!contained) + { + enable_warning_texture(true); + _3DScene::generate_warning_texture(L("Detected object outside print volume")); + m_on_enable_action_buttons_callback.call(false); + } + else + { + enable_warning_texture(false); + m_volumes->reset_outside_state(); + _3DScene::reset_warning_texture(); + m_on_enable_action_buttons_callback.call(!m_model->objects.empty()); + } + } + else + { + enable_warning_texture(false); + _3DScene::reset_warning_texture(); + } +} + void GLCanvas3D::load_print_toolpaths() { if ((m_print == nullptr) || (m_volumes == nullptr)) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index e18b80345c..49d06c135d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -14,6 +14,7 @@ class wxKeyEvent; class wxMouseEvent; class wxTimerEvent; class wxPaintEvent; +class wxActivateEvent; namespace Slic3r { @@ -349,6 +350,10 @@ private: std::string m_select_by; std::string m_drag_by; + bool m_reload_delayed; + std::vector> m_objects_volumes_idxs; + std::vector m_objects_selections; + GCodePreviewVolumeIndex m_gcode_preview_volume_index; PerlCallback m_on_viewport_changed_callback; @@ -381,6 +386,9 @@ public: void reset_volumes(); void deselect_volumes(); void select_volume(unsigned int id); + void update_volumes_selection(const std::vector& selections); + + void set_objects_selections(const std::vector& selections); void set_config(DynamicPrintConfig* config); void set_print(Print* print); @@ -410,6 +418,8 @@ public: bool is_layers_editing_allowed() const; bool is_shader_enabled() const; + bool is_reload_delayed() const; + void enable_layers_editing(bool enable); void enable_warning_texture(bool enable); void enable_legend_texture(bool enable); @@ -435,6 +445,8 @@ public: std::vector load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs); std::vector load_object(const Model& model, int obj_idx); + void reload_scene(bool force); + // Create 3D thick extrusion lines for a skirt and brim. // Adds a new Slic3r::GUI::3DScene::Volume to volumes. void load_print_toolpaths(); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 34d62927ab..cd4549bd83 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -254,6 +254,20 @@ void GLCanvas3DManager::select_volume(wxGLCanvas* canvas, unsigned int id) it->second->select_volume(id); } +void GLCanvas3DManager::update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->update_volumes_selection(selections); +} + +void GLCanvas3DManager::set_objects_selections(wxGLCanvas* canvas, const std::vector& selections) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_objects_selections(selections); +} + void GLCanvas3DManager::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -348,6 +362,12 @@ bool GLCanvas3DManager::is_shader_enabled(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_shader_enabled() : false; } +bool GLCanvas3DManager::is_reload_delayed(wxGLCanvas* canvas) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->is_reload_delayed() : false; +} + void GLCanvas3DManager::enable_layers_editing(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); @@ -481,6 +501,13 @@ std::vector GLCanvas3DManager::load_object(wxGLCanvas* canvas, const Model* return (it != m_canvases.end()) ? it->second->load_object(*model, obj_idx) : std::vector(); } +void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->reload_scene(force); +} + void GLCanvas3DManager::load_print_toolpaths(wxGLCanvas* canvas) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index f012b7703b..d0765932a4 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -57,6 +57,9 @@ public: void reset_volumes(wxGLCanvas* canvas); void deselect_volumes(wxGLCanvas* canvas); void select_volume(wxGLCanvas* canvas, unsigned int id); + void update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections); + + void set_objects_selections(wxGLCanvas* canvas, const std::vector& selections); void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); void set_print(wxGLCanvas* canvas, Print* print); @@ -79,6 +82,8 @@ public: bool is_layers_editing_allowed(wxGLCanvas* canvas) const; bool is_shader_enabled(wxGLCanvas* canvas) const; + bool is_reload_delayed(wxGLCanvas* canvas) const; + void enable_layers_editing(wxGLCanvas* canvas, bool enable); void enable_warning_texture(wxGLCanvas* canvas, bool enable); void enable_legend_texture(wxGLCanvas* canvas, bool enable); @@ -103,6 +108,8 @@ public: std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); + void reload_scene(wxGLCanvas* canvas, bool force); + void load_print_toolpaths(wxGLCanvas* canvas); void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& tool_colors); void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector& str_tool_colors); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 0635cea31d..46c4381fd8 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -225,6 +225,20 @@ select_volume(canvas, id) CODE: _3DScene::select_volume((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id); +void +update_volumes_selection(canvas, selections) + SV *canvas; + std::vector selections; + CODE: + _3DScene::update_volumes_selection((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), selections); + +void +set_objects_selections(canvas, selections) + SV *canvas; + std::vector selections; + CODE: + _3DScene::set_objects_selections((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), selections); + void set_config(canvas, config) SV *canvas; @@ -327,6 +341,14 @@ is_shader_enabled(canvas) OUTPUT: RETVAL +bool +is_reload_delayed(canvas) + SV *canvas; + CODE: + RETVAL = _3DScene::is_reload_delayed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL + void enable_layers_editing(canvas, enable) SV *canvas; @@ -621,6 +643,13 @@ load_model(canvas, model, obj_idx) OUTPUT: RETVAL +void +reload_scene(canvas, force) + SV *canvas; + bool force; + CODE: + _3DScene::reload_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), force); + void load_print_toolpaths(canvas) SV *canvas; From 96d9879d7209bb709bd3e8b3d50b5ba292d7a3d8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 8 Jun 2018 11:37:07 +0200 Subject: [PATCH 077/117] class 3D on_select callback moved to c++ --- lib/Slic3r/GUI/Plater.pm | 8 +++-- lib/Slic3r/GUI/Plater/3D.pm | 41 +++++++++---------------- xs/src/slic3r/GUI/3DScene.cpp | 4 +-- xs/src/slic3r/GUI/3DScene.hpp | 2 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 22 +++++++++---- xs/src/slic3r/GUI/GLCanvas3D.hpp | 5 +-- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 4 +-- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 +- xs/xsp/GUI_3DScene.xsp | 4 +-- 9 files changed, 47 insertions(+), 45 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 9dad798071..f74bc7d829 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -78,7 +78,10 @@ sub new { my $on_select_object = sub { my ($obj_idx) = @_; # Ignore the special objects (the wipe tower proxy and such). - $self->select_object((defined($obj_idx) && $obj_idx < 1000) ? $obj_idx : undef); +#============================================================================================================================== + $self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef); +# $self->select_object((defined($obj_idx) && $obj_idx < 1000) ? $obj_idx : undef); +#============================================================================================================================== }; my $on_double_click = sub { $self->object_settings_dialog if $self->selected_object; @@ -116,8 +119,8 @@ sub new { if ($Slic3r::GUI::have_OpenGL) { $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config}); $self->{preview_notebook}->AddPage($self->{canvas3D}, L('3D')); - $self->{canvas3D}->set_on_select_object($on_select_object); #============================================================================================================================== + Slic3r::GUI::_3DScene::register_on_select_object_callback($self->{canvas3D}, $on_select_object); Slic3r::GUI::_3DScene::register_on_double_click_callback($self->{canvas3D}, $on_double_click); Slic3r::GUI::_3DScene::register_on_right_click_callback($self->{canvas3D}, sub { $on_right_click->($self->{canvas3D}, @_); }); Slic3r::GUI::_3DScene::register_on_arrange_callback($self->{canvas3D}, sub { $self->arrange }); @@ -151,6 +154,7 @@ sub new { Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); +# $self->{canvas3D}->set_on_select_object($on_select_object); # $self->{canvas3D}->set_on_double_click($on_double_click); # $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); # $self->{canvas3D}->set_on_arrange(sub { $self->arrange }); diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index fdd55cdd8f..7f83e0f577 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -10,9 +10,9 @@ use Wx qw(:misc :pen :brush :sizer :font :cursor :keycode wxTAB_TRAVERSAL); #============================================================================================================================== use base qw(Slic3r::GUI::3DScene Class::Accessor); -use Wx::Locale gettext => 'L'; - #============================================================================================================================== +#use Wx::Locale gettext => 'L'; +# #__PACKAGE__->mk_accessors(qw( # on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly # on_remove_object on_increase_objects on_decrease_objects on_enable_action_buttons)); @@ -28,37 +28,24 @@ sub new { Slic3r::GUI::_3DScene::enable_moving($self, 1); Slic3r::GUI::_3DScene::set_select_by($self, 'object'); Slic3r::GUI::_3DScene::set_drag_by($self, 'instance'); + Slic3r::GUI::_3DScene::set_model($self, $model); + Slic3r::GUI::_3DScene::set_print($self, $print); + Slic3r::GUI::_3DScene::set_config($self, $config); # $self->enable_picking(1); # $self->enable_moving(1); # $self->select_by('object'); # $self->drag_by('instance'); # # $self->{objects} = $objects; -#============================================================================================================================== - $self->{model} = $model; -#============================================================================================================================== +# $self->{model} = $model; # $self->{print} = $print; -#============================================================================================================================== - $self->{config} = $config; -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_model($self, $model); - Slic3r::GUI::_3DScene::set_print($self, $print); - Slic3r::GUI::_3DScene::set_config($self, $config); -#============================================================================================================================== - $self->{on_select_object} = sub {}; -#============================================================================================================================== +# $self->{config} = $config; +# $self->{on_select_object} = sub {}; # $self->{on_instances_moved} = sub {}; # $self->{on_wipe_tower_moved} = sub {}; # # $self->{objects_volumes_idxs} = []; -#============================================================================================================================== - -#============================================================================================================================== - Slic3r::GUI::_3DScene::register_on_select_callback($self, sub { - my ($volume_idx) = @_; - $self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx) - if ($self->{on_select_object}); - }); +# # $self->on_select(sub { # my ($volume_idx) = @_; # $self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx) @@ -139,12 +126,12 @@ sub new { return $self; } -sub set_on_select_object { - my ($self, $cb) = @_; - $self->{on_select_object} = $cb; -} - #============================================================================================================================== +#sub set_on_select_object { +# my ($self, $cb) = @_; +# $self->{on_select_object} = $cb; +#} +# #sub set_on_double_click { # my ($self, $cb) = @_; # $self->on_double_click($cb); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index e773ec6eb2..21204b0fff 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2002,9 +2002,9 @@ void _3DScene::register_on_right_click_callback(wxGLCanvas* canvas, void* callba s_canvas_mgr.register_on_right_click_callback(canvas, callback); } -void _3DScene::register_on_select_callback(wxGLCanvas* canvas, void* callback) +void _3DScene::register_on_select_object_callback(wxGLCanvas* canvas, void* callback) { - s_canvas_mgr.register_on_select_callback(canvas, callback); + s_canvas_mgr.register_on_select_object_callback(canvas, callback); } void _3DScene::register_on_model_update_callback(wxGLCanvas* canvas, void* callback) diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index a66130a3b1..8e82849e84 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -613,7 +613,7 @@ public: static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); static void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); static void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); - static void register_on_select_callback(wxGLCanvas* canvas, void* callback); + static void register_on_select_object_callback(wxGLCanvas* canvas, void* callback); static void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); static void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback); static void register_on_arrange_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 7452686276..34df13a54b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1935,10 +1935,10 @@ void GLCanvas3D::register_on_right_click_callback(void* callback) m_on_right_click_callback.register_callback(callback); } -void GLCanvas3D::register_on_select_callback(void* callback) +void GLCanvas3D::register_on_select_object_callback(void* callback) { if (callback != nullptr) - m_on_select_callback.register_callback(callback); + m_on_select_object_callback.register_callback(callback); } void GLCanvas3D::register_on_model_update_callback(void* callback) @@ -2255,7 +2255,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } // propagate event through callback - m_on_select_callback.call(volume_idx); + if (m_picking_enabled) + _on_select(volume_idx); // 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. @@ -2632,7 +2633,7 @@ void GLCanvas3D::_deregister_callbacks() m_on_viewport_changed_callback.deregister_callback(); m_on_double_click_callback.deregister_callback(); m_on_right_click_callback.deregister_callback(); - m_on_select_callback.deregister_callback(); + m_on_select_object_callback.deregister_callback(); m_on_model_update_callback.deregister_callback(); m_on_remove_object_callback.deregister_callback(); m_on_arrange_callback.deregister_callback(); @@ -3710,10 +3711,19 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) m_on_wipe_tower_moved_callback.call(wipe_tower_origin.x, wipe_tower_origin.y); } +void GLCanvas3D::_on_select(int volume_idx) +{ + if ((m_volumes != nullptr) && (volume_idx < (int)m_volumes->volumes.size())) + m_on_select_object_callback.call((volume_idx == -1) ? -1 : m_volumes->volumes[volume_idx]->object_idx()); +} + std::vector GLCanvas3D::_parse_colors(const std::vector& colors) { + static const float INV_255 = 1.0f / 255.0f; + std::vector output(colors.size() * 4, 1.0f); - for (size_t i = 0; i < colors.size(); ++i) { + for (size_t i = 0; i < colors.size(); ++i) + { const std::string& color = colors[i]; const char* c = color.data() + 1; if ((color.size() == 7) && (color.front() == '#')) @@ -3725,7 +3735,7 @@ std::vector GLCanvas3D::_parse_colors(const std::vector& col if ((digit1 == -1) || (digit2 == -1)) break; - output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.0f; + output[i * 4 + j] = float(digit1 * 16 + digit2) * INV_255; } } } diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 49d06c135d..b8c64228b7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -359,7 +359,7 @@ private: PerlCallback m_on_viewport_changed_callback; PerlCallback m_on_double_click_callback; PerlCallback m_on_right_click_callback; - PerlCallback m_on_select_callback; + PerlCallback m_on_select_object_callback; PerlCallback m_on_model_update_callback; PerlCallback m_on_remove_object_callback; PerlCallback m_on_arrange_callback; @@ -461,7 +461,7 @@ public: void register_on_viewport_changed_callback(void* callback); void register_on_double_click_callback(void* callback); void register_on_right_click_callback(void* callback); - void register_on_select_callback(void* callback); + void register_on_select_object_callback(void* callback); void register_on_model_update_callback(void* callback); void register_on_remove_object_callback(void* callback); void register_on_arrange_callback(void* callback); @@ -547,6 +547,7 @@ private: void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data); void _on_move(const std::vector& volume_idxs); + void _on_select(int volume_idx); static std::vector _parse_colors(const std::vector& colors); }; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index cd4549bd83..e01f85396f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -563,11 +563,11 @@ void GLCanvas3DManager::register_on_right_click_callback(wxGLCanvas* canvas, voi it->second->register_on_right_click_callback(callback); } -void GLCanvas3DManager::register_on_select_callback(wxGLCanvas* canvas, void* callback) +void GLCanvas3DManager::register_on_select_object_callback(wxGLCanvas* canvas, void* callback) { CanvasesMap::iterator it = _get_canvas(canvas); if (it != m_canvases.end()) - it->second->register_on_select_callback(callback); + it->second->register_on_select_object_callback(callback); } void GLCanvas3DManager::register_on_model_update_callback(wxGLCanvas* canvas, void* callback) diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index d0765932a4..76f8ffcb5f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -118,7 +118,7 @@ public: void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); - void register_on_select_callback(wxGLCanvas* canvas, void* callback); + void register_on_select_object_callback(wxGLCanvas* canvas, void* callback); void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback); void register_on_arrange_callback(wxGLCanvas* canvas, void* callback); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 46c4381fd8..f8d22843e7 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -482,11 +482,11 @@ register_on_right_click_callback(canvas, callback) _3DScene::register_on_right_click_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); void -register_on_select_callback(canvas, callback) +register_on_select_object_callback(canvas, callback) SV *canvas; SV *callback; CODE: - _3DScene::register_on_select_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + _3DScene::register_on_select_object_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); void register_on_model_update_callback(canvas, callback) From 7b1187992cb5df7b5898f9abf33cb367af714d8c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 11 Jun 2018 10:46:32 +0200 Subject: [PATCH 078/117] Added bed texture for Prusa printers --- resources/icons/bed/mk2_bottom.png | Bin 0 -> 27212 bytes resources/icons/bed/mk2_top.png | Bin 0 -> 102340 bytes resources/icons/bed/mk3_bottom.png | Bin 0 -> 174128 bytes resources/icons/bed/mk3_top.png | Bin 0 -> 85263 bytes xs/src/slic3r/GUI/GLCanvas3D.cpp | 547 ++++++++++++++++++++--------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 68 +++- 6 files changed, 441 insertions(+), 174 deletions(-) create mode 100644 resources/icons/bed/mk2_bottom.png create mode 100644 resources/icons/bed/mk2_top.png create mode 100644 resources/icons/bed/mk3_bottom.png create mode 100644 resources/icons/bed/mk3_top.png diff --git a/resources/icons/bed/mk2_bottom.png b/resources/icons/bed/mk2_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..d06de22eb3599b80b131415919bbf77b99b1ff89 GIT binary patch literal 27212 zcmY(rdpy(c|3AK&L*-B?XC}wW`AnD&!W@ena?TvfX*tGdUOJG&G&vtC78P=4Ij&+$ zPMOMiWJN^I=j3;-_xJP1@7B$2+jGx7x3c^7xSy^PFIyUO9uYbMfj~HsCWh7!2ot!= z1UbYCZq_3|?}8gv4>My!$N}Te)7H{-aO7~X$+Zv&L{XCQ1C4xc5DpHqg(593v3-L< zIdx?YHQpycAmR|D;rXkPqYJGO+28HMX7_bOOJ#9JYPkcX#wlwO>)}jrmgbFW&MGjqW{Z%C(ENmQ1a_5!$@2+9rTdY)?dhy|cy}iE! zjfBbLwqL&JmwKPU81xTXh1uIaIlDy*>EEI4v7f-xnDdV&h|I*D6#szY6R@b$VoR19 z<*kBn$d0g29jiKtcIJCn@vS4gA42b{=9P}E*+t9fgr`Ewcw$64wtpKg^kh7-O{`Yq zOqOoY(QVT`q5D2@$BA^sy@I}XcTKD7%}T2zNfJxB%4(AFwsPM!hS0TCPhrO@G1-KP zj{iKEH>e!&v4mMM)2{suoH?rSC%Gw(c$`?=CuZ@tG$&<#mJ7Oz#X9d%*PvvHYB`x|>Jk>SErtsR7cS@cN4N+FhB0kg#ekYKfJ) z-^COa_oK^m(hCFPc0#Q|T-#hz71GECAynqiq|qfDtPdJnnj&&SVf59*V9b%_2+kwr zxjppfs~wj}`XsH#k*($W0Uky?4k)xIT()T{VbO7;K??xX*?qE z3yaI4x3FuELg7nRby|~9cw$0OC^}CoRJ<)Q;bP*ZOq^ubzR4}j(p(yDOL|n-dNsmM zf0)U03l&a#c5z2nt!)T)u?wN-8F)#f9BS;#N+$iy9C>$wQKp>Q@|uQQU!0_vEB9UV;h@s!YE0cHcKy zkmKLMbStOL&p!}CgG)@}6EkS0r_Nj}3hlzEFM6L|z)*B%RL|)`wGCYe?(zD<>fR3C z^(R8BP(84=vruI}E8_kcxxefVE%C$gcrkW8__jTAvfVsyO`bL(pFZaqt;VhL#5bs+ zPB(qwrpxTvDf(45O#ZLc;haS*&IYEfXd{}fG`~7LcW+!fYXPOyljYi#GJ-fGH6g@3 zWa?;-_&y*{t3j(u&`!@~kmTarg4BjBeO5H%2NTn0M5aQJWvtbaW0YoIUh<(}q2Qot zLM(SuV}5Vb?1m6u(A@61RU+noOSEs0_sV9;1KyZ%HcVmunoGyDg3prsO0!VSks(*U zpeF?ifk_{za2!rbW}H3FN{YP2$G1d`QrBr~Pt*SD z6t~Blw5grccGsZ>Rc&_$ zU(8?`ztxT_YMXcvCwp?v&Owh^d`?!nK+>;*V2>P?nKMu@D@59xjy`U(T3Nn*_}ZqJ zw4e3>?FMp4dN0zxrgZ|ZXwk5f>=_Nt$?{+G>)NDA&As!?(~=#FwytUA#J@Ia*oo=d zEIc#k)us6)&wnX*)#Y@K=95DIB}XK0;%0+M)Xf^4mekl7HEuf=UDLXc@3UyQZ5+n_ zgSHxSafE9JHC2+ZJ#oT+z;jS;Yji36Kph+NGab@AI3wQYN7`41`4bRJKCzo9k;#%Qa_0j`%b9h8O3VOG?3W1N zI19`M)dF+Z^D}o_M=5O&vrBa9jB7_GtB@~zT(6E?KZ%7}Z;J8LW7SKv<~p9`^ocR0 zt$!*ea3ggq7E)Bm*?*m!hje$Ck?mJw54@&E@UNHt`(hkQqCKs^x113X`3$R8OOQQt zf42VDc`X&|t*us)R@}|F_Jo8jvr6`_Yb^cjsfDK>4In zG_KfQ$yzVE#8W;Bf22;G-uSVt&BS59`y!*jCt>eOYW7qSVptYYMwr-J37j zUv|+gNFK)He;thR%LIl9cX_R*J{X29f&p$1RP-jPPl+=V5r(V4x}OinQ3T z{i5E;SA7OwIwP7xh$A=OOSj36_PjLf{NpU$Mj2b!dhQV;kRXGoNp7-{Q?;3tfJjUi z;{zWiI7zpjr_vru+JDt38F{b(ZD2}A$~_XC`cK0Ae!878e@0QpH@C;kXg=jf*KMdO z?)3y?NExp`YZcUiEkE<~Z@($Xa>_%9_7!*pjDmIPLLe?9r)A&iRl$hF4kF%P43a7n zAgp*idJwsJ1L-eZh4A~P!k*9WJZWU`AE!$E|mF9;u)7YbR*nU2DwW+nbE0H9a}4FF8WnxK!XZ;v_wn>j_^nZD_Qib^=gm z`Y+u=`pAy8=#0>Wr3;?;uXS~82AVW9T2g<2r?d3?@UEHuYdT${uV5j|xi!KW zUO}~lc9YGOG5lfy4M6{TO=}E(6I`YE(TzX;hUS@vn^?B5APwcqT)m$_1`bDG^xDYq zsvN_e44c*-3T7iqH|d2J8BaHlp-OoX)E|h=3(d9kXy0?FQrSKmftNPSh8=E?DfV`S z@3X}^Ws!m?OU>4Z6Xz;E*|WDLwWt3^c4YZ9!BghgqNe$J+jyU*zXni&b^yF*Z@_6O zs$>U9cAS!PyaPw8uvhXWB0w?OK~E?tn6V7Zl}QE4izz^od0(%5{36hQ6C5j2!C8M$omP;&VjG?qrI6E)6O$jIK^oE$LtFyIlXtm>KWtEOt;v zaAWDKI;5=M78SNS*Cq;KKSFOARA72~d_RjRPuc?FmXweMYXtxF3O1wxPXoAlLxj4- z%%IWPX$QXunUJnc8Qm%RGtX$7&i=qnbPWt4wy?QFjT`RTM3=xqWX33dQx1)k2t!Zu zRyFl^U|@*s>ee^054y>p7a=WIs5s*e0>RicN@n@}j}3mNn@Ke<-M9_CepGW3t*FjF z-qDOUcgH;MQ5Qm9l@+l}`6F?DRj;mfnaX0XyEhXk4c}~ZE8*Vg*&l3i zwr1t?ufl~k{8A3PU!d-XEujt{MaIw?j=l}md@v9<>^jvkY<<5J;8y|ZP! zdqT)r7+t}`Fd(&?BUmArxJ~>dRpQiXvmdfXdX86AK!1MS>fPQC<3~2t4BMn-4i4=%*tN1hVQ6`|{t8(x&0zb5DESc^* zjS9FP`ixMPv}m_7{&DhEFWSA85G1d{|9NJOf~A-_y7{eH@3eox9Cdej`ToJ!hlvVI z`Tk#sxYR_H^Hr0|w9rI?@W;van(|iTIVL13=gcK4Z~CH3W(7gY1@FJK=^33TU6Ag- z2H@v%$}hU6mm)nDT|sE=(u5cIFEx2azkmM0a}*O5o|?A)zDqO6vLT|HFqj^Ic-Fl( zd;@tW*MBXSYdc$XvQcVG*M{1+Af!h1w4YL(+A~v)?N?p2gxvgi<8;zS*G5zY;amB8 z2;MF$Ji;?VCed=6cyfgHitphYCrtj&xY+S*eYdlpX2QBQ$=zqPnt0f?IS7lN6ZYJBVk=Re;l2l4Hvv!&x$?RDX zka2HRQ~o{I4>&NL1(J@lpJq&F?Q6uNy)Q`*PA7-tuwZ#}(Qqzz1IGnHb_hGvKoUNq)sm`=X)qH-t+3zuF*Ny7y(>8 z!0}pY5VL2p&K$>l7F2LhqqB*Z<92Whb^XS~zArolkc=3_)bER1^dm|2{ZQqSe&*NU zs+NewPiozzlPjtrI3`nm-9V~?yn_1{!C-c^9xz|FP&RYIJ@yRI0LA6~`} zv-;_l)u~+8zRXZj<^1D%{9i^}44pQEjxgqPnflOab9UI?G zl{M248Cx2LY_Qt4Pm_>rv#z!9J#4*Z2^Wr z(sbC%uIcqWs()_%i((}&d0j!Hx-j&KecXZzJN%t!zt6#YYv4;hfAC6DZ9{&2%gn}*R zUO6{PdrG3>+4(+c(8sZV*j5HM07b~_Qs0z-D}5H;+* zo5vc_tznC?>NIE-WVu6=&zijN>+GZe(W_>&%HFXffiuD2r6U2K2G0SOE{kp)aWD~F zf(X#x{rEmui(2G0v1p&I>5CD5s-k$`5}~iBh`7_ic#+4VZN5s$(yePv7VMD0B{}=a zLmvbNk0#xOGbt-N)1bk5DRZ~Zq9w(_#rPHJq7X{4AW0PaHk3*Xej^~newscEU{4l6 zHRTiz>(@X8s-Rnpr6oI+t^^ZOZgKhf&P(qrdEGosx*Nl;XYWCu@dEWOg3?}qBVRJsIs$`+7=+4Qar+S*zQ4_#PJ6ivFZ zcBh!-)$UgFO>W87=kUar%6`cjB`%zLgAT*@_C9a$JUqVVN1eJprM&zsAzRTjErD23 zaR{Hv^aPg>?-zZV`Z^&ps7N2XW!!X_vTC=}!dR@c@9i6SvgPt>$ZO4yPbb;=u`*&wwg5cLOUZga218Ac03E_Jla zjsh%Brjvjk{_%Y~)~%>jaH=nXVV$u=iob!|%QoI-uTs-#J8Ia$riMaSb5MME0*oY| zV|)z9kNT&tpPdS;PiM+{)9K!0Y9~GsSZm*hp4-&iV(LTvresig?V-t^E0ZOsmp;<- z&h+ctO-b)}XRkef{6w4e+w#Y>=A-||w9(a?`j?&eFIkh{EEqClRb0_;;03{=IB#VS z3KK~?QMfWKd4&tBQkW1}tCG5O-nWqjJ15iPxaH9NDLXcypm6&}2!nDbm9e}OJhuYa zZnC`zknX?vAqZyoWcCzZLX5hLrR;Cm)I|s|-}3S3xqH7hqfaIZ4{8ATCLI~HtQPCg zg`PFY4SXmHkVsy?gxI$U>w5Bjl}5Ionfs$?f+*0w?)%A3ylvyvTnYN{-cOxDiVBUF zuDY7mm~`oY4Qu=3B@P;4)FGSYx8`oBm$opgmA*K^&mH0?DT+3yzV(Fr;1?$*%}WoTlhxv1 zM+?HbKkK*`tw(qp;C+L{1X#S|z8S(Ze3iy1_dJ4{&J^-`_z<^+kO-~_@isMk&3bf> zSt{k*cOC7oaoc@`@sdAOl6Q_EU874 zhT8!(t#Z1@CZp3A^`%E>ydSx|OL~QCLhcea!0BIp-1ax5!llPr%3%m^%2tHT*qFSZ zSxqmFL*q)^7OYaS^PoTLw)K<|G7rDjpXncsH{#K~rBS6aLaFwQE`z>g!9mX|&&7emzUMPEkM9II{{r|1 zXAsB6QxoRzS|l0M?>%6!Od{+n&ecV@|E-PxAQMRd^~j?Ka>SW2)Y8fKz<>fTvj5+0 zkN6a-mn<-Qh%sKbb0fjrl+`ufJ_AAJccie#s{l@|o!H#V=S>bRhNOL79?*Vv+j-oZ zT@AG>x5QzabjMJ9ZZ+WnrrnWRMWIGgffg1REY*cP-#(C7PL?=Cj(Y+#Y8Hovy(?@Q zcCD=X^ny)b<0a>eZTa!-H0hpk;aS+b+JD>M7yNMXyW3Jq`UTJHD98DzQ+ zkbgL7A6dU6+U?WyTeHrR@P5{CS<(I{mp;M`vvR{ucvrGj)A4DDY;x!u{V;OI_0DUS0U5=BowKGdgCq4MZ+BmWK z)j*3=1V%er9=IG8lQ$}uwMdegqIWMGcn4&&g+GoJafz!iS}KrV+3X~EYO?#wAyUgp zPmH=|1E+XNrRkV_Mfxo#7_Y#fHE=tVfqhj>2}-zOajLDg2jsjV}Jw;=749^ef>Uf@2_ zs3_T%_v5?cL80BP5f@}Yp8(7w7KBM7=T!4Q3qJ#(&G ziOy4ADvD(daCJ0Fcpy~Td>-P`UB+>(X~K+q$jz^ikA?AF^qP{3bXEI~b6<%ZUb`l| zmo}ZAPaQlU7_g<5yN6|4G{R@+Suh^ib0xzoKXQIkVgp00k~d7^E8?2v+To$U)nvKd zSf+~HlS4+3HFLtyH<8yg^1?lw3YEy~_OW%4;Ahp!YKtA=k!=ElZnweUyjL>0_sNYF z=*JQH_5afOAd2ebYF~Lm55xoN?o3QZ>*YerD;R;+gn`U?F`hUHFR(u1n06SbqYD_44aY#?|1(Oyly4-mF?_c{Vd3TLv(WSe1_rLyI*}D&QS`WaU@HRI3F!HOD(AW9y<={ zG0C+^UFsPpR(~h&bf5(-7mT50c#2odt-pmZqjb8iu9g^Us56^UM-2YHa)R(mM(ngoaE~zNvYd6Sg1`iIJ-+drNp37^dhm7DDi-&AH{&H@|6eIDdka%N%`$ z$#+qe?Deugo3-y0TBVQ>XvHSOlRlze#W{S%Ba7F6&l$;Ank7+kccoAP@!R~y)`d=* zueCvH);1znuQcK~df-KS=HP=X4}UdLe{-DFupP}mG5MG%XexhXu#+O1*cm1loY0a! zY?r~}WoJ<6i_v;mrXekclb#_D%B>W<&HOHkjVE0$ek%AVR3$jv@Q)SJfc=k!;rIZU z(jlZ-$!Yh@Qv9ZI2M~WLu*Uxp+J}->`~TMkAYly0Gk?-U4KeZqG(~qC!|@#75H~}N z)BsH+SXly*qmdVb^!xfahPd-TDtzw<$_4Q8l0WBs~ayD6>$ZpQxncB`Z zT*k8XOlK$2;~ZslXJ9>SL7T)THy7nBPta3L6&(bs6u_gi>1fh3_AC^Ikt;cKG`|ks zcozZPKElJ8tqfP{tb5S0c6JgIX+4YvRG}r2j9UET3!a>7Mxw4<7tx*Z1W7F<1`Yu! zGPk$lee(LXfO=p)w@t4WKtjl!J{`g#HzFVy$)`}$CUR(c+A2ncVNVN`6Mmtq1vZEd z1OXISEI1URlJ^EUZZO`ZP@OCQbSbK3T9q%Fp?>K_?q@>;p8^AF<~W6pW;#bj<2FEo z-2?gd-KXhv_}N&&DIsKK!Xc^sB8IR0`y#iy*AhdAe?L51^Hwk`b@K4?oY$8-tjm1~ zkvhRP9W{su9_`$P*>C>QAi&l(@t7pV8RKvqH`q#VaI=z?%d4Hs|2g^J(tp`;p#?OI4k{KXOAxP)kIv{b9m z_TuX97WCKX<&$IBJ?UO4%Y`!O0?uTqVJFC@X_Wc&d@wWkF32T9Z{H`tA$HQ+f+Qi} z9BZYI>T^0v`@xkn34x7FHh3}f^%Ya(EVLL|v>-HNW9W|Bn)15xZkeP0iKz)%}l< zB;{q1o!cu6%-lz<84`0fp3#kf{ku@#bZNHhkI?SbXPedSY^4F9!M zBe}aHMN$2%g_3hrFEJ(oHapp&A>h>7nF(1geE5=Bc6a{u1sPuCjk=G!p3!BDs>Dyq zI*0-^ef9wD*GXTbM9jVmVf13(J$?^qxBH7hPpkQ4Z6V#FJ8T{L>}B1Tdil^g--d8%|g>O3LzkLo#8 z`9gtsE6lwIatIDU~ZBZ0^(RR2xk8vYfI_FnOg z8(XUXG7BMsjMu|gK6tShj3_eGQ1*DAJ;VO91UVeUY4f`l0O ztqdy6dNxs*VJ6W}WTC&Ql0!cI3v8}lpQ-*|6g&z|+f#Xdv{e^n{^UB;eb+_f!QOy& zcrpr1ZGqYv&l0ZTIuOY?F=GjIv0rx@&#HNg&vl%H@Y|y-ffRbhw+oD0Ny(zk@s0Y) zJ>_TDt~z83qRcD5*Yr&sAs#<)V+sVuEgKf@`c@H(-SITgy&i-ZpJu^L3O?svIxL3>_ zN9bZ0k7m-M(EI6US0&spPJc_eG9`~Jl^)SOQl7C+q|OTZ%WFo+jO4Mt#)k?BSk=4{ z)|Q!T@r299RTGe{Y(yh!A-G1IoU}q(h-Dw9njg7#k0*XiD!?p%kl!o{r(nMeoSv1MyGBdGHs0;_q3@ny6QL-UM%m)`VuHl^Ios#y%$}? zCMETGCPy?%zGc#X$+aJhAF&@Ec>&vGU6l9ae1SuZwdn2zA5(&bVszV@A3kGsf*rjY z+umP&-BLp31@FM6x7lqT&-iERLHGccelU_4OichafJ$aW4q8(%2O>Bo@Gt1({t>GV zf(@=3$nsF1qee44OUq49lktLhl-^g^@Pm<}H~e-cG$e!YWf#7)hk}T-|2zOk;?FM+ zKwt_$Mm!2i2omE=q%y^=XZNn=ZT{~90&4CTUhx8x1tgCVmtz<{jKewzMgZwV*DN3D z3K07a99RgMsO+%rpx4CCh*Zc$6oEne{~<2&2OzLyVDN$r1jH$_A1+uOfgpc&`X&CcpvH zSCgnSRWe;Oxe`AW%|)`z%;2d~Bja2m>7ijoIDRuBt5q~~m|f43Dh&ol#!ih45^{K0 z)=xj}#N_^U192a_I$Xi@fPYK0cEBd+GUwty1GrfYC zhb886NWRvXrHne2>RvjjmDz zHA2ZDcvfHb*b|xhD+Qm8+n45h0P+jBX=O8I9H7OWKSiq8BEehtze={qgn++Z_D{MX zX#kI@WhRXzu5eaWd@tPTm~N_0?gpRtiHnDBS#KG(p48NV9)V%hRKhhMi>$cgTQ6~+ znfpqARlIJ4|BBq@Mqb3bHk8kwak10Ii3c1-Mmg5B-b`Oik$&xXQ*#M~(B2~>U3IrZ zYFfLc7Lg6NZE9Mzq+TDtq4^}+f9WuoUM5Xoj75Oz(L+L(%oxTFI6~5k9|9w4TFbgq zFJ!h_;A!{;Ix2m!09aCgX`a#1(u-ydx1)S*=W!7I7Gj-;+vloB4 z$A;3jdA8p}ICzZDUF?B6CnUo*ruC#5vd%*m^s5h zkwqvRMX=;P#=Rtm3xd(?Vky~TmjY{J)MumyqwS%ekHpkZHE#=B8E!peZ%`?9o+_DD z1wp}jbanW*k8>BPx_UZz}VL=e^HiiMS z;;Yxk2k}l@QSeVeQPwYl91wJmO!dZbz)S`6D)NL+vPx@qw>+f9Q5h7B7!X(d`p>@^ zC6B%PpOI6o9AY4ZVl8WMgk%T4Yh?OO1E8<#zo68^s5LMe2P?LalDss^Xvimo5T%jj zT;&NN`rTjK4WO`KuD5#(i+)$1ruy4h4f|%{Bvf2~A!s9l?s+{Q!)P?XzlZ{X)vh>a zK{AR+Nxw(w^!O|=s1c$(F=O&$OBPni#KqLl`qwJVSiESlzmPdqYVWr82yGxC+e$bi z0fm=IkIC;2RI55OG7}G)2XQFNJx}W_TnwLteIPQ7{<86^VWhn z@ZWIZ?v@}juI4%fv1K6{W8q9~7bY~4K{T?&^i}cOU91bK74OX>ySahyE9aG8eEoNR zzksbTJ=C@)ZS>?^t``?Rx8*>&%9gqYs)%O+Gi*g}mV6}ogV`ahe0uJ35pD>1mR-QC zN{ik3u$~k3En(uG&eSdBEaW|MQ!yj-JYL^)vOsaJ42kp=ifkd&Bdd8yu~R+x2vtlz zvzVNpjR$54!i7AG7ny918{qBHdEC-&Pu-BV@>X_SU0lN^61Lk6sh0pPg!-q2MihN& zK2LS9&byAbM@Z?Xc`h-?VSV{(Bl4^j)e9e}qv(-|w!{*Wq$?~lPE&0F?aS~Fa-g2* z(p&;0&*=0KP`Z2UzvO{z#s|>S{FlNQWgEvF zfD9D4161>vZ;lKOfq$*YMh+`?Bp;^P==EX_=9- zw@|V(p-S7?F^9xoF#*Nb;`v4{7NvU04>v5LUc|OLp!G;~qT_;+w$5kYjP{-r?ZBhl zpTNz>RnOhM!_Fl=*#D*n$S~K=H}I6TV!?A)zCpBrB+?G=fsh&euB0I2LDDmonENc} z0`ck-Ll4BV@-vJhos)IK%EB;*+^1N_V|+ojmYv!IBK}HWJwK5oF&`9b6+neH^o_t- zzP;jH8_ z#vEigHbjB5J9U!}R*3>3ru4B{2&e-kKKGiL2facMP zIs26lM}8xJ5n@tkMt(KYRa#0OogUE6DoB8&t*as{lb4-U3lqq4QYcmrwsX`Bl0DV{ zzuv+-%$?2VoxY#n)xgiambmF{)~t5Tq(N&43NKAx?BzFT)(M{hh2hYwv0wKzl|s)g z^n%v3Pc$&@V-^ep7Ys4|TO^`q|9K-Zyc{&( zovoIvOjmc@*kb1G)Byu=ldfE4LmfeWTvDz&PaVYCA`!1)GE$-@`FC-r=&FUDA-VuO zVC8?wqsvQjuvXP&+YU$COP$ESJ*A2iHWl$2Zg!x~NJT$x8)EjdMLO})qnU=Y z1tx90oVldy1>J3~CBw}>>K=u}5uB2~2}_HnsY}dt%1uDiLSGWXa?U(I{?0slNM=k} zPWM!frUBkRsF{$24?t*)(4ONrrN%I~z_c+rt28nLdNlf=!Eyu1FFW?_2#Y6^Y>8c6$-nwBu%@2Cd-{Hsd642MRrDJ{yZ~e0Y zI~CD&hiI9Ia02}&v`%cyVj9gR>&f^kHtLrf(G0Pu0RW1Ijk zfQ(D{7Xm8y1KkB7J6tV47E~$x|Eg~CQ47T?sX+i_@`Q!rOAi@m93BF?%zBEEpi!BF zIhA4KC%BFE?t`xFg^w$~=64id|0)n+ea+nWyXHHuXcfPUf2GZ(lTq%@tknW!$gWlb zokuJ$?gg8^+ELJpNk~9WxkUPJNtpi0(%ce4=UinD7_GcBiSb)&qt5tBHVAj3He$-+0;Jo%NNGBz zSnSW5!+uFGo~{m&X#3(id4vNgbfb1g!B&k+41}Rj4BN6D($G*Xqwe!pqsIkzzwyNp z$6X$d_6O&Ug;!vlU(}&qa5@c;e^r|wc;p1|%eFmNe_W+uyP<#PKmQl!@8rLzycY1f z6GhG(4^En>%^>e!T#|h%j@K-_g#G2z_`ti^Q~jgE(;LoJ@wDBsLcMkutvyn5hE7e) zxas|eLwZ-I?j6EkhUxwpprxM2Xmh-1f?)~2B&Gv64fLng0yaKOzhQPuPGGAkGNzv3 zUU_L4?S$<&?r_GpYLG?~wj+yhYuqdF#LaM69zLc;o?%r6F==P}ui?W{jTX_yah0-L zbQbQFF2m?({8$TA09cl96Sgr$tudsggl)s3UN$@mtq>dm>MBX}X#M4THIn%mH%sx9 zsEAv6jZEyK2}?>+qhm)oE_Iah1{k&GZvT=xk&|-P-O6PFE;@g41FXcgpCnO(i~Gau zh$v_C#_wd;1j>U(-j59jdmo=+V<|q!qkY7?dn(c^k$u^13U}jk;cy;EJ?bxK>b@>E znbHY%r0CR#RE045Fmqg3FbEmmUeXadT7|r#|AC3rRjRv!w2tSUq~$PpV;99?L8mQ6 zMOHWKHV}uD2;I#lm#aXZQHoher-en5j%-~w=;yzKyQ>mo*NEYHYA-Iv-#CIe3&!og zuwm)&P?`2Wx@p%7e4XUkr(k)w3yj780op_mb=Fb|a~L%1&?*ZTqrRzk zM~K}_Dhyu;i+;PAuesm0yzGT_RREp&EjluHFx7Z)sQ$|XAQDyTegbOB@)E2RTS#E! zi0G16hD{+Ruk=%iPg0Vetq=tUCs+0_SL1*;c~)6&E<$p;IA%P@5)rTFhmmvd&x#F< zh;(K})zhwk%IpA0zoX-n#D;hh6B_m~tl5^EA*y4phCQ;Qd1n^;r1s8I=eV}DzcBLS zX@A^0s^N}}3vK3_`|IwQ1+c}YV1XXWMt;s4@O!gG&EoILJ$l2QG3e3XVGD%N92<3) zT}Y#6H=eUT^cN0pC*)|JxT0QN{IR3Wk(&9?{Yv_Q4SVyKp-q#fBE3@9Cu2ROMoWp) zLtr>+ZvXLJHK^GAE)bkcn1z+i|82iAS=Qq% z+ToSKjmjWciIk}Sry;YH^Tpx)$Kj3koq;g8FZJtg`)nMHq$aKZ=>p*r@4*+tpDyxq z<*RK!QZMcMC_wm4&k75;7U@?Y%67K|s*qI4ygvul)z(3GSR)@3Ry^`#s(BSwjbj$_ z+5FShC*CzaxAXq8)dRt$)iuQ7Zg3mdWykw4J&+a+e8fAN(m5*9($cXNoD}Q)s={`! z8T(7BSLvHo@%XUsI2l`OrYIal4Kn+3f1a)v97hP5pVl2sPB*&k!{neex66kr)4XM@ z5iTZo+VroTFr6y@^=5yNe9}sMz|7z0^WRRoGb0=M-s2vH2JGy!%|}2+Gmld{@ph#j zkI$V3Wpm30Eob{bYW}oG{!1K4eyOpR(<3x#d^jipfbspybhWE)=dNco4%yIX$|$l2 zb){ZHg2BHw*R`pbRi`jAlLD@irvBJBsFk24ySM$pGg=sMnTDNl&*)}ph63HY?3Hm4 zI0g1?9c_8;xSmzL?>2@Mu%f-n24qXoPkYdH;)q)OH_vDkqsZ!PG;ilL@e?Jxiw_UerM4qu$75gCH>1OSaSn@#YzO&Bl3?o3Xb>ik7 zmDC7`ycp1kpx>?m`l*aqPLTP_0;8`6zm?O@`Kinvc&2$Ng6E)9qM*{so<->l&?LX< z0j6c;eSJl;!(A|%t%NT6v>}{;lC*qD+R%Zx#zZ|k-*2F1MW*kh1_F(`-{S{=dH=IjK+g9;3}bUCt5V9-YbL#TmOu|`H4 zwyZG+h(Wnz_I6S{qaT3KHh>_R3Y69Y-Mprit&BeLoPLYAozn$MHKKB{-nZOpTK8WB!>lhsxBsfp2J5}9A!M(}W_CvI|O>J0YMZyg#Q?hUE>-nnpb^9ZuhsG}a= z$69#QpT(7tL}{R_gtnQ3>o|+cu-C-PNJoY*vz|7yt+3?!c+#CC+q9+;vcQJO8lD;O zxLJ}!@v=?K8!?0rWp$?O5pS(1LYeA1nrFM@_(_x8- zKzWqD$x7?1?+N!hhDW$C(p?nG&MlMa{MVgZ`-zvL%ZGa(7TlgPv4!OGac`Lkv4VE) z!5WbdIWY5;-1$8C`SIZdhqU#_3ENH}nTQVw18JORWkdR{!^__~G$*;av!r(;jShdb zlG%;4G5iw)ysNCcdl`oCAmELf0}m@wblFLoT#LalEh{0rE|7{PrzaBWHLng)aWV}% znF*00MK~>{S*D<#Q|g_RrR0Qm5S*xjtTX)!PRpocj5nr*i70smQqC4DD-npykH;m0 z@>;`fSQYQK!!!|NW%%bx5w6+H`OfKD^3}$>T4uXSUYEA)ke|aL9}QmM-E)25e#{{K z>-i;aKa_9z$cIWKJo@LH9!ZvJysiDFw^(0UQ4%x{HM$U~VNc;hkKbhepyzURSZ)%{ zL&d{y+%|V?ND}u-%$Z_ti!l@%td6wgv zSO5RxSIw6$flH#4Sp7O*x@x&gO#U3eJ{~_ed-v<&w(2>GM?2_pa@>zPDHbCs4yF+! ziilveWYB@Ne#s!mTW4%5do{BEQ&ZO<7n;0so-?>Ijn&6l{tPRsT)#AhvBMkKLA5lU_%AJ#L*SCe{FAFetg(FKE!_jWl_MyynzvUv z$H<~ivFWUgm``(j?T4lE{{#+8K+J-YYDtSxcM{X z(Sz8ebJXs*A&v3qG)1V9TYgDVQmRNZ8T4z zf}f(_B@LEj)3b1gygJ9RL*+^mO7YjGIFMPH#19TCzZK{;r~OWxh54t43M$udIQ6=^ zLB7KFq$>PY3xDih`$dsUmmy*opZ69c<+iT4mlB>4whLjFN;+QthlLl{eIDzyUZmP0 zs~x&^Q>LSzFCv6N=jJ9=Bp=#f+9dMfqt_mm1t}|}Ro~{oQXW_g9}``=fx$_obk9?9 z*F0`CT*6dJiNUl*4~dDIb2hqn+uw1~SHIRh_Iat6LYQ9r~qb4%?3E z;lI-{+|V4lDrmqTKY1b%A$Hah0)Z<)-E8bAhp`FU7o3VED`0Ir+($hZZA{Mca6hrA zYvkOvHtdleapLq(POy^Q)rNDsDHX}Trvg9QVHGQf# zrvrS`bU}XoTpqt38=HhyxMv%tJ2R@}opQ<1q<7+*sL&53Dl^BY_kf_3-((n{UGphN$oFr|e&%<2`TP9_hpJJaF88pX?!k!X88edst7*-xB;2iqf4y-~{Qg3j z+WD!3SDi3aaxU}(#h2^Jvrkmw89H@K>USkfj04^p?FmFM$OJ_Nvd*_85mY7~cT)ox zn?el#RZvefqbQ;2B#TBr#*Qt3FrnJ1XIlG=V1v)P)Ps$5p?|&E7Z^!1$E}1W*+>E5 zvFfR4HX^9cRGG|`fd{}@l2x}70_%c3_}rBGFS5|^m5$9DZ;|7jU-o@Dwk@%Y&84_{ zG$uk$@+i{fu)BZ&ONqp8RIKAj^oiN;fbV-hr8hT$LAiZm>ICUG!pavhh6$EQp>(cw z-ljO*Z5)L{!XcF`n@oFgO7; zl7U28s9II{VOi#UmT@`1Gu4uCX+Mpm2d7kz{My0yA)V2wOwC{`p}I*rh}H|C$+9~{ zZU@P^vPAl-39<}ZEtS`dMItB7^M`S#(E7RVL0rUR#B0E$Mf5?z#%1$99i26j7rp@> zk=m~fT-@i067wp+@TKoTf=dWXrn&KhB)ZfkYfPE^Yq7M@Q)n(%CBJ|PkKcxv@uMPX zk8SH@vHFQ`{Kbe@sSSiENK*4;X~uRc0e$;1p*V3R=Ik+i+apila$yNh-DeI2@!^jr z?`#uaT;2<_rg}+#D>*r*3ljP){~%}T7hu7H@S^1Z>?E)MvyLPLp23DIZhr2d4LBF;g6n|a+ZMrOe zC3J8kGM;dIY8Fu7%(s{ELIFgZM_yLcV6cJe`fBBi547KcmREbe6yVJ7|1Y6`#B0v6 z5%Tv`a75lxz?MGT?U_<<;ekkS@S8S;6`GaLw%yJijhZDF7!f~Y0Csd7oKJs`QyBg7 zakiK=S>XOhW3_tmphAD>-EZ~fl<%>BeA!kl!T)Lj0YaJj!%_s*@OHMod2ti!6Q`gG+zS+#Z;vRqCCO6j(wemoh zZ5~_hCmpbl+po~+otyQf6C@MEmY39qtSX{LbcA8cqEo)JKSd3zKpK*mu9TeKWWDq| zQ9kg86{OuFel7iBMOIa0t%<=jS{=a?2RXP$67~s82e68NxisSb*rxVV*Rzw$(TnvH{|1}luO;r(V}F+( zXJ$uslQ#1j4trTOrY&donEd)@hZf3Nle**3o&^mg`JRp3(F9U``R)^g38__1hBIQ8 z-J^Z+tSAg=bI%Woi4(?4H7hbLXlKZtv zjr+L%Zt#S9Ac1a9J&{y!GQQ^eadmAaA#}WZXY!0hd&a+AdRGl=|M$I6iyK#7y7s}8 z^Wk8?v;dfYuH2c1Dpu2dTa%;h_iZu*@;<3_qm{?GhXh{g*Nj1$gJ0d#QB#B{PK?c0@4OtW0-pWu}5PjiJb-rhd z_Lo}0FPn9rlEI26-9zC!hFt4JkaEh`vA?fJAVIi-gk;xP!(~C1q5o4Bao7s{}4wv{v z_^ywti!4cN>8Rj%WN{#aD%~oSH_5lh=VM@GB*}}a)5Y!G8co3Q80mEqaa??6;OC*} zw)tF6cd)0clf?7KYA;KI{IolU?~NvRHpL+^Lm?L=YER7NL?Oy=LM(PYt-}2w_Ngt2 zNU{KoEDgq3c6@ujmk8b=cE=7fCC$PT@1>AgqcWiU8rEv>jNaDCT+NA%`>u08$e({u zR&%Y@PyEbcNJ=}D41s-fU$KZHbMj^b0#r|yov-aFCcZs^8m?5^H1yD&M(?g@g2L>@ z&2krXFi1WK)*1{sO)hmL>G&|@UH>a-WJmH~HQoq@eLpTJoSLN?>XQrzt=~lLQyO}G zGE=W8uXZe-IILfPXD+;wL{6)@toD6nzIOs#mk8C0VlRHo!J|J^Hzfyq#g=z3x)=0# z32su3Bx{l~AG1h8oXy8M!scbqj`zxfo`0?fD&OvZMk-xus67i=6y<4&+^tFDJaR8M zOMf+3WIW>U20efAwFU|aRfVKmHkF%&hWQJ2%+NPfRy~5Q_9|$S(pXwYEiNtpZhnMd z8fvU&ttPjkWp|zwq4x1Uri)hLqtc)DH!DxE1{1=oqDyM;*G`Kaciua=Sfo!ro(cA> z?hPZ8c8{W-zhiHEr?iEMF8FM%lzwiP zgj+S(OqzQX54qZOSGJI?eeU!TRqP#(wbPN{8w6D*FK8Ja71)qQp~YUR?8$ zA;Rl515W;d1-0#U;5j_Klc@IWD8i9yu-*uqk-nEB_r*3(qBKHU33=m%yV`FHn8 z_S@1n&>gF@_Im|h-xfe$C~m)T-q89{&`X}?K_+9H(ZM7mD|cpo3N8=Bs*jrn-#GOc z1RT7(P~n(0-=eR8`8fE=pE$C{8QKf2z5?%H?pd`8xMxAu(!j{6wn`lVu<&PvlW zB9VHE7lFP}Bw`lw*#6~Z`Gs8QIe2?f!gC!{Je0DMl<`_HBnM)aU(FI`vqaPsr<{Wr zrV1=zuljlwx0!m}BUwU!S20tR1f=9$@IZ{Ic%JQaCp_QF^H;*dpJr98zWWj9y7F7i z`0o)wMgn-160e3mWTFVL3GnV{cl3JcT775wZ%42jL3!fY*##M3ArfikW_#QUtQsaK zmIbSN3MdyV>PDBvGX!|B{^$S7D9NGlK5TKmd@#%o0_%BH>X2&-Ba3L zb&#ape1CQDzz;&G6;1;n7dIgtkJSb$tFCUlIlJVtW5gFv^15qgS{AZ0f;k#U8gu6o zp{o@MbDef}dY5qovDMukL{D(I@g1#cIKKh`j5`zzNY z7Mjxj=?dw}*u>m9DZIV)5ji@TEc!!`NEe;9+ND%M)c)ID&9LBn%(efzd@P9XXc0Xm z)o4Av_P1tNGNW>_li+F@kPtjS`hcgwHF9`DUo1?+De_y;(&a)r(Me+Ey+JabH9J?- z?>2}pS5-=<2fq@5Tzo%+*FK3Xx%S&4Mbo5*g!Wem2+@vesmM|r(I5qjRgX#hbt@M`;0+m6Flzjv|7 z7P64$xMzg!;DEby#8D6=RrM|Xv9siV?u;-~ima`ZVwOUE9ZORu6Z~hrEV_m10V{UTIpYqGjXyP!5 zp#r<xi;r_||EKOcj_5zMY^l7LdZF{zzBEASa1eSf z%_{AYzf*DjTJgl;NtMFKU6;oVPQ1;-=q=Q~siETCpwjHPvH&$ZLl4p~A!$qF+rjtf z

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 literal 0 HcmV?d00001 diff --git a/resources/icons/bed/mk2_top.png b/resources/icons/bed/mk2_top.png new file mode 100644 index 0000000000000000000000000000000000000000..142050c3a26328237307af611062de60bf1fe3ab GIT binary patch literal 102340 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 literal 0 HcmV?d00001 diff --git a/resources/icons/bed/mk3_top.png b/resources/icons/bed/mk3_top.png new file mode 100644 index 0000000000000000000000000000000000000000..0403c2f05cbc11d814e42ec407996ee046751cd5 GIT binary patch literal 85263 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≧eD;nEh|q= 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áOFscC z8$~tm@OChr_OmaDqe{V-ob#Y_O!XrOKT7Lv}WoJjnoG zB(*~lNT5in9!xxu6QxMHXrtwJySVhco4Hee5N8xcDgy!=bh+{SaY6#sPvQ9vNgP^Q zf}gj817W8Ld{#uf_f5*F@)m%Wc5|^Ii20Km<1h|^R73&T1hp|zIs!PagI-fv1yz$A z0%V6sZ;@O0RN0^$ce7{eUYzfl_H#2rpZWekfB&zsv4jM4laI9bCP?hz!-65?!oqQw z0wPx+;eB%Pe4csd!K)d^h7(KA{lyN?mZAe^S2=E~F#G!)RS2_J#-@Eo+HY4}Mz$6kdZ<3Jqh*-RzQrrR+ z2xo&rAWXR6FEf;_Gj1rYG;;i-sz>vcqWQR6w9AG;Owy@a0rM-hVNFPNe>Ik%;t(Jv z5a|A&7^MxL`Z@tm5Db}fVDKU0vM9CC7BBF2xja5*F-JORVr3+kj75r^W#H<>S~LH8Lc zD2~*PEIG~~QHXad=i9n}-i(~Go=m*h9M>`4x#&?#%gj66R9o~&Py~T>;_(v!dc-Lm)dk)=u@q&3VHwl3$RN235&&OP+) zcK+v$W7y`G0yDh=t#nHiHkikPH2bq;PQx&9QVoB8#@nc%2t)Gl;lqM}&;Gjjq^GAN zR77}PaY(ruc=zvVeppbrlXO!fWh#+h_`J_L^{1EAbGX}Reb74s@o}R+t1m2wXrf@E z2{MhRXM`uKGO@F9#xd(6Lo52 zas##ceCp}ycs#v1Uz!r|I{aHV|A6G_yASB%g7o%0U*_9*3XT#hv{&dCh7#5qpUzp5 zDCsSPkMxxFGRUoYVSeq|Ic&34QhSH8(ioMqK~?5NUC?^rbZ8)a#!Q`Iq}8TouYV+j zpFN;?-N<)m8Nt@H%KrV|A_J(5%J+xx3a+qMi-uZ1Ki(SpvHslQxsl>+qFJA~L=rTY zyETzQ2-`1W@d9bWzr%L|6jo=3*;aBCRKlicemX=D2()F%9p5OJ$|#lFP((hzA6|;J zJ|gj2a|IJ$jC4tM)z3o6e%SIy^>I<|lWMkR4ab#g{XgN=mSmZS-G?>;%s1sY7EK~v zlRg@A{t^m)eTS4SuX^0Y@B`mJnbb;_s(4;HCBi+Jt@=YqM&+SQuuz4$>v)a>Wg=dT z17@-T3RAFN3vPAW+kQsQDgD&HVFO#}3F%)G>u^4oK`Zxleek2GTRQcdc606z($VqF z`Ru8ZgTF@PYg*;9zjDx81UW?BrF@2+`1=(-4yqCc^VPLyZFxIe%AGSYOd;|weqt8C zk*xGl=4mhaNqF|#cl)6m6{U2Gy|NhRG@nvtX*_j?9JN<=PmrvRyoU74-;aJa=I7nN z1Jx#?sctfdRj#^>4JRvX!Qv)u}h= zMu`CkoBIzmCd1_d)$<>QX4)GVp{j0n#WM>STdoKx|6^hkH0w#+h-aGVBJ`j9K2h{! zMBp6J$R~JzC8j(zhJ&Elo#ne%2qLqoTqVB!^IzvaOv}PH|IHC^fj_n;7Oe_MS9^Ub@-=)(+>gkE15d=b< z3`dD~#iWRtlQ!D}OJ17t#89P&;I-ZFBNxytZNz1BY_0y`Nku&IG*dK*ZFSppy}jQR zGoE-RBPx7w;a%V-VhdNak4(%fwm}%m9I(RPoUS?7<2gv@d8psB@Nb$ zrRnOJhh3xxbJtm(SXCd>xf^Kxm-qeI_cK>r%5-l+iVY7*8UiAM+YX5Milf{-dYNZp z{4xTFMBkroj_KtqWvR*5B4!PyRIxoxI~is*5y=AWO#yDKTTa`R!W+Nq)|W>egt5YE z(mIIr1C@+?!$f`Rh7+k^!lf>AJSy*@L8^KB${kOpE!JGi%s zA!$q3wyRHriLNZ-y}RvpB@YhimR7RyqIQyquKwT0*|ntG#r4L+&ey4#E=}F>%2Kx2 zs)*uRca9f9yTz`WI%@^gMR6~=5~Tyi8XVdbY8&* zK3`4kjxy+v*31^D4^p8Rv=)YLl9;i{DWQ&UXJ{@6}oDWSe z+Yl@=Jm`QqW1#fK&jTSgb!$NtN;16pg4)-LiT2+LN6Pr*9$HI=FbUD637{%&B=vVN zPai%^S%~4Uw-rW_VrceF^n27B)I}mJl=sdlk3-U&>&1;xN9p4BV8 zw$g-6);?CK`84f@Ujs5oRbc7@slCke7!(Lbkd4=*Jc5d>)A?7?Vnd@{vQ(eS%PS9E zq1)@XV2#EGamsU(FXk1g}%% z@;{Q`hjjP=@sE?6?d|EB<38i-IGTWerofC#2{cZ%5O*?6?pupBfg}FEtV>DM2V)bg z=b%RbW;-6M-GscfMc)W%I_Ei*XtEWY2SAAhv$BR+Hyq%% zfg>Cv6a*Pw^Eo<3K5s!H{W%Omjz2m3hqoO>I$JEWMrM$r5P$x9eJ;`p>IX=Gv*|J6 z&;D>oEejUecrN1L9|KeJeZ02E>rA#FxQFDM2~Gto-$`C2^BBFc-)sJ!$1>Tlmyz*_ zHAeL@)eJ*r{|{k2m(YG1_!ryw|B?pd@9()yP?T)R~<-iSG zSBNoca%0Ssi0^p=gf`54Esw|$9I9A`(9?z2w6CvD$ecTPJ~r9cOz(puQV!JXD24_k zckeF4c%~h=jtAgP-|isrDkHqcddMn*aY4jsz9kg&J|3{M6-d{;LeUS3ZZET!czkp{9 z)iyy`1B^KzJbZ{3&!qGXp3es;xZSWH7exn|NeYZOJPnEa55yvP3-#+F<#$~5@mfdwC30ABeA zYD+~G2q(F!`n`mY(N4W{o-XX}0u})J-CkJ)uv=Izf=VGA$rbFAbfT-<@mD z125Rzy(LmoQgR-P+v{FH9X~0nN-*Ce${PfKw3Sg6Ntw?^txv=Csfme0ATtoa87AwL z<-Sv@U&l7_*?0&?^Gm$Z=b!Kh_u*RA^uN3ss6iTa9yKGwr_1XXZBNxmb^+6I7`1#P zh991G=mI?}F84o*nwpx$+7&w3D!XJt@^+LZ`Ga^mHEP`6L}o;+dZNX5vlOb{_z5eS zkB{It%`h=bsy|!RGW*IFwEa>Pi5Y91=2B1QV3u78>Ob&(qCv*`5FIURXUF!%bE}Jp zhm1ov5E#MB0KQtbpjmKQ8tN*Nk^Uzbp;{nM+5)09-dmp6|(^jC^?^vYPW<Knc`))?+eC=P~*SXS<0fj{h2BD23)ONV6OZ%)!TRtEwIC6ztl04&z30Q z=F005*v-MGgjwPh)zw4_NgSrFFSNDaL3@IpnfX?O)7;}kHmzT^wi>el;W_Z;8eLbA z=X9{CLnbsd)M#DL&rj@kz2nxng~-RIrKbAUPdQctsY{7EMyBFl{mytmk~c`d&aSGq z9I2oRXa5ac(2JeD=eJELu#0u*|1rHadHnnr!<&5~tRVMqn-ekvi|UbU@_64?5sdN} zA0Hp&=XX!X-WH$+E&XWPA=E@mOH1#ax=G}_c=wtHu{~9Vh77GO`>0>M_6Gksbp&IO zOZW*nOZMv3SS^94m&BRYbAUmwx-r}Fm|_1INf)af6~b=T1E1R8?tVz0nWgbVbc zvd=@x=8#ElV>oYfk0s1(#}1k`(iLu^mN36_4yASBhez&k+;y4MP)!yKU=N z4w^z5D+6%MaiN(>Sg;K#{H`wYR*`2R!;ju+uE`cOguq;Jiz+-avNMI(+UCxZaG_sr z>;>C`U0pDPoP~vjOKR!@eAeJpjl-P>X#?*desX0dCMTQ2(}w(9u1gM-lFyUo2g{v8 z&{|sY^4ywOTl3OaR}bz8xy74_WIb?HvRgD8c6>cNJdA<-50s0Vu3FeoDQ`9T@qrNe zw)?GkaWT>0zlTRh+*bYM+SHk34zWy+-pfDMdxSkFu^G=Jvw?mmHGy1wTwJpblXG0- zW%2bFJ6#od4AG9mf~4EEOSgp_1t^&mKeqcyc>dK)NK8DtY8|%uv7Yrh3bH+Krq|ZQ zLzCAJU)%SaaG4CG@|W-V%X~VyWkdUFo4czfI2g4*Lo}IK@l_lg>Ie#6-Jy%)%?E#H zyBIPcbzQQxCKN>CBs0(YQ@Q{Vr#De7NkcA9nQ82fknW~QdHu>>yESQD&J z$i<e&lWK_=VL`V(=z=^!IG7VYShS*!Bx3ACKkgfCQPucuSnq4tMc?&j!@$ZitlzPHd z@YYILi=#hUHuEK^V_(}fx9Z$8w!^gXLlni$=rq4{O#0RG>eOlV&mT;Nh@mvW=o3Gv z(ONieH=c*HZ^VsmTG+-1iU!FBS800hcihq&f{RL``bJhqS;6ZsRQRf_cz6(V{|N)0HvNgr5_B*1hS1~;mp}Uj(+RD+8W^On zUj0bS;$vcZhvL?ncmev;LAfkYN2OgKVOGqprxa=XLAeFF!CEGmTEKyP&*#tQ9XUC< zkCLw?{tRY>LlfETzwPM=e!H6KUxiAPUCuDRSRJoTq`R#hc%Ks|(xrh2*DDbFGAp`Z zBzTTx;IlHWr#p)n>u^!@mY3W4^Fx1?@@xsSf|EOO#?H28`NwKWePrq-9X6L|W^Ow& zkM~q^wj*uA4) z3piA${|T2D7q>ql5dYz=t*QA2s9hJ4n48L&At*Ojd#{J{<%lt({Vt-182h0LH5Evs z@Zm2~ObLdv2u~nQDW=I;AZ@tHT(+wwE8rXp0pi_~CS%2Fxc6HjZLzSh+-8^Bln`tT zd=um8V)%xHl+*`LlTq845IyMIx5w`$s-e)`7%wA3K8>0=Mn=Ys=^AEp+6QtYMX#&i zNP^E}r#Gx*No;tCY|po*nKGp(B)s|Y#<(dej*;YP} z%O5gGg=(X`57SH1*?84Oh+K>qeaS1JiLU~;eZ{J9Jo^QljL;~s@b#^9FGnv2lo zuDRJN9)T9-9sLwpRIlytP&8w>2jj~$LQ(n@@-)=WBpn1U%wFo2mW=QRbke6!Sj%c_ zN#GsV`b5wA2~**dkdfuJv`{g~g{2B*s`x-ofOrTuCqMs(v&ro$#5?OIh{=tfTS;^Q zk&pMcC7kBtiWIQa4g5VW{&O6rm%$v#s<4J%gMYIcRp=D$$G9MTjtGA_yK6CmTG$Th zA$m4X=?woW`~-^a*lZBmWZN>nwgKG^h!?vJkdcrosNpC#Bt2U0E65&CRU^+dn4`VhptTt3cWEJf+9t8)(*7 z*$mT5|2;&XhK%~y$mo;5AKL%t0?;Tj5knYsfzI)>?A(B>b5rO>=Ne-7d7@wj@($S* z6TX<+f@fzD^bdOSnBq}6$I0*l04u_vbmz_ddeB8V^~IBYqQWqIcGlpdB9rnyaW`Xx z{d^~c=2Iw(8+;K+fDuSGWS_VQ%7qiFDqZgdD|u-o9)S;sey~YshUT?) zL&;@=fS+UNlL=fG5DK_#+^~&(YQs!MJluoVEKh%hgMKs&4YW7@54dA_+~R?A|PXO1zdWcU%=OAwmDJ|(f!fxRzf?q z(nP$9M33*FVC1{6A}nE#VU%Zk)+Yn>)Z2f*J^vsv1B^s#u2kf2Ew%?iJZVRsA%I1T zP&CN`w263T-fmw2Hxb-mSyrY2!IB?205bY0 zqyUuS;^L6So3_h+mHn+q!LVFpRDKqdH#VkOu2<7lR{wj+#iTtDg+uAH!mgW027G5Z z?)#9&CblvgEP9B}kD!=4Sc#S#*jZ`a6xQSy>Rj%+ zMWgrG*svj>Lb{bUMdYl9u*VQ%Az;1D8xAQ(D>v?cl~vOY8#4`$kSX*I`u)afV=fYa zfJUvprT|=naTGg}MsuFrx`c~>o93BQmlRYy2^BM>?oh~R-s65)w$`}_h4*JqVv~`}-9;0xgS^vEgswaJ54{bESX#IR?@4%1UfZOrYe=rN&E><UjTtyYNYjszl*z@+dgmymVi7g1TGp-(LK@_gma;t zO{a3RL>urg=zs6y{CG12vY^tK@Jd+=K(}QWH_I3pP!-ip!Q8D1)_gyk??vb4<-r0Y zvvz`^jvj>n^5VImU23Q|bn6``&@=~Ac;9D^LIUf8qWM>)=@Va-Hb7O-1Gqy;8D3n> z!eQJ-g=_-Am|OQ~l2=KA>&mD|nfk|%AB(X3TD|+A!YwX7ei*>LX#0re%FfWBE_VB> zhsY*ZXT@9QhLj?O*1a~V)zuzLHxr%|-D`Tcj~QR)-6PW;1=5qg<`nfNGY%SKFI3&W zC&(?Wn*EhKMUhS}E_t(wB(TGV@0$(Z$hR*=KTdP&u8`o8K=tqsHT7CZPs%vX61d}g6jMzYWN??V#JRsmF& z`yh)Un4dpjWepAZ56YSqD3%J>Q^q<-z!+xT2n%8CcYuN9HD^l~gwy!#OT*3JMtmtN zhn~kRe^=OSzC%kp^c-sqi!DgBaGz5Z?t7?QJQLK7(3My$NH)s->3g`^11{HOy8rHf z6rZa33|TGWSstAw7B{Vdzw(DjlSJ(#A|LEp4&#O|vE*}FJ?89dVo87RijKIcm9lC* z%U_GvJZgKyXxQpY3T$h>Hb5!aZ|->I1U*tCc?)fr9Kd4%H%QGg4{Q<%$Avn3vXyBd zgOjQ{DGb%Erc9Ww4`!4H@ZXEXrBy*}zytbKSgHPbePhE0GE?$f~qM%ETk5F z`Qh7W7%4lKNjpN@L#=6I8`N^Sf!5Iibcla|ti7A%#eiIm%-#;MW}-9oAnZUwqcU};W4*2>w&67~(|0}3}^;pVca zZlabOWRFFCaRg*QjdX1l&*P7MtArM!o;;$=?_$`l@Ga zjS0EiCY)?zbSCU5EG2}MK84x8SiIR-Sxv=pFz(2aa+Se3)vdO`67kr$Q!6Z({8An7 zNbd9G3WRejj7IOxf@D8CgVs+c5Pz$>Y~a+TTOxfcPx^T`RD=v|kUa^c-m$TxWhIgy zKl-@k3XE{a)8N1RuO{YC37Xz&~V-uK7p?yV_@S(F=*oqf* zGTUtD|L!Bz)0cp6U^FxY{qaP0CEQ0s-(2G{iwp{&*38i0cr&nO#_1nNS)_LuqJQ-8~=+?OPD>a^amV$0O11D zgRlzVC@uvLdOLNz@lba)9Lq|VZ8s2k~Yxg;#>_` z9$H9O%^>mjR!n8w3PnEbAzumg57sIUVRyds753Tof_r^xpD>^)R;0(>`-N*v zFGmDakSCw?>o%+*&{<%S`Sr}*49fUj;_~wFxE!v^Wtlg7ADBUb8$_z*cKPq%qp-VG zdq98zt31>>F|n~7XKhe^!y>#eKPEEUK>hy&JDZqgXRetHIWj2I{S!^mKrl_FpZQLKbX@AR!$lfx|ZF z(sxkKcox2R;y~a1Q1}-7#`{vOGBb$ExODSP?$%I2FF~g?_%!5E``%&IiDFULxKl_6 zBBUb_UVHI)a};?gpGDI|!p-5He|0=8doV0Z%Z)IBk1n)6eX;j-sQDp`dw@?zZM|w+ zg1ocvDj%WOS?@6YSkn1dHcBd=?Kf^FToDAB`ZZmC8E zgo=i9m&3#WpVOf67yBaP?O^Ux4-sm+iSkfDq&ct)vN6{zI52dJ){ib#r7m5~#6MBy zd7W(umLt2&qQ(ooj@t!xT;ZhE1{K}DON*~q*MDoq*_NiBGJ9p0oY{P1*FB8*s|U{^ z@bbi9HLQDEj#)YVb_x*b-a~BVcu^lg>H{@#z4V&+1$->Mj(1SYZH}goS?~;RhRohT z`<#)r7=71})Piyo%8hh=W}VX8uN_09qt^{-C&gR_oOKdrv51)#KQ+wf-awmaaq|6O9SuCA zKAtoIa$MYpng8!K-r-Wv>xY%!zg$G_v=Ghw7(?IAZsGqoqWX{oSa?I2YNENUaFbWM@G1G+l3xw zAF~w1>_ov}MSe|VJHOK{))XhR8kEb6A)`V5ZwXzl^mG5H2Y$F1`)u(<(Bf z*<>$LUO_>x=Wj>>jI4UwoaOYKi%%}EFZRk6BB6HUIV6Ia>mW;?=ijj!6@Bv*;$^5nA)K@hK;fHTVPJKR zF@-KoCn%VVp^BEF0P9r`R^qxye-r^)JEo|G05SinWJ~;1lM}oNetsZ);5{@4eLD+l z>(P#2GjsDQ-xE8r<6lpLyVBIqp4~s=A|Ct}oT_BD#v@AFm!*bxq^KxSuz^-`uBXha zRq)Tm?UP#FUV*{lh~;BS)_a5CExrsO`$KqmURETH_&sPGtUWaOz4KBrQ!M2K*`^9c zRX`wH(_1M;RzX9J(2E&J6C7kTZ;iSOYY?;R-|1396T$L;CpY zfkP+s`daLBYXcnma_ilDuj3e8fvWh-E*dIddN#H&uT>S!n(5qJ`tzH)W-sf38ffpI z@n1mq-DbR$v=CejykVYk`U>xyxOWOkJUD2ASqXf{`*xv=JP-a;lstwBFM8(;V8vfG z)+#W&xd)fvWpjqW#Upn1?~lG-62;j52dIr3-g^YCBn^NP6a-u>Lik=f;R#$_2LSv8 zWP=9dA7ncK>YES-x&DvbXfWejZaG$cP`v7?UF^vleq6m8wc4YVq}t|dcc-G0uPIUg zRmsHdM){3C6CZ45#2?{gvAVP+Oo30@>=I|I#1lu*033-$_!G*g0qv8QJ+U4B@z!&8 zY&;IE;M;bbFsj7+dU=Smf{YnlHoxeMg6l>%^WRE0O{Brogi4x5RTw_YTDTsxqyZ55 z@{@Yrz$zVcS=M`8isjJ=-JMstF()w$p-{BeAX2TVVaim#AOZDl3*z1A<`0lE`R*G4 zvbN7MG7I++T&Ca9jqJZxSI23**pqj0ZkW-NekCt2UkSao)wMOBtG-U3cd3^bsZn=vcxqVBURNq`+nf7IR)?Js>%3kz%QU8!c9S#AVC(! zLpO}SpZ%FMydo4BxF_%z zOtREJ+dlq_7p$V^w-v7GLLq-16OodD`i#yqGzX91)S&U+VnPyf2M{NO z({4By1A4yb@ja{KlN<0qk@r)`zsqVUb6`aC4R#?6e~YqI=>n`XQLkZC)a`o>-52wJ z`%4mUx1e2-Ur>OYYsLEeX9&A9(LIcscCbo$xCbVir`{B}n0|2hBCKHyEH(GV^Xj@j zHy0H~RVY`kXU=S%U)NtTc?ai#i_DQn#5yUh3FzXwn}1dP0x`w>4rNsUEylr^~^y*kt|_|0~Md z*+)vVD9QGjKqXb#cMr{Fp%V8^IBx|ItQG|WXaJQaT_rSdwr3hthO1~FGjf`M;O)V*byn4R6U1`l{Ric(Kw@U)?Cfj=)TaD)V^VOM zg+pvm79%!nc~UA(I#8eey@&7yHK~Ke5CSs@8{Pf=`Tjcq;O_C+1VR?N&&b$mO>H2o z?6o`FBPxIlGdrRta>E zdwddy-4CWJKOj#mK^ir>l5g@xu4Y3-A}1pRdJ-4%oMe^Qq4pfC47CbTJRXwrmFr zns7LP?N&ASB_(MwFffXZT1Ak#1(47(!0qEN?l4~d#B5CV-lggr!`L^Q{Nyz~qhial z`vdQP_nUq1;M2A z@mB)cpN$^c<$49vsp{%>d%pyJ-TT#rqRA~+n6Zk1I9ekZ+)90|n|APqU2x>Znm&z$ zr0ja0{Y3)=!}Vg>y|$+a?;wj$C$GKv0dFh5d&98jU1<>hc(Nn0zGF>eT@I3qH(;@l ziamg}ozQz&Jz4Iw;adhCTF1@x>DTNn%pBby?XP{g5d%p|0UQz2SH*)6h1QufX&3YD3RGBQ$9Mn-O# zp%8x0>+}8p@5g;V9^K?P=epkS*ZcW;j@LzZrSOa+N|MX>1OJxB3+PrRLC*;&J6kpQ zHsP_c)jeMi9831Un2SymMAuj=XmG+^gED!#$U^Scs@Kvh+#nYBkq^~Cmhw)BU?9Bz zV9s;!EvwKzw$zNHbSr!bp@!cszy9(R7X)uo3tX0C}gN2O zK4l!wtVKUue?#rUXStqRyTdNcYx%PM@}0XP=a~Tj9unR~yn2A5-Q^-owB-YRYpcrm(xkCx z)3w$Y7E+&|*{?@jXYse12g~I(SxHH|xPN_Dx=#rlxGslN72;h0;7ms!K=En7rRx}S z_H#ZKM+@O}v%gYxdx&B)dTotWIawnw`*6)&W%1D5`1jE{UVkn{HSrjeN3tnV>CsvQxSN7yI=3vt zS;Zf~$>^(fpKk!$)qds|myc$)zc35>giSWy^StrOE)NJ!4F3_YiFWVV^XHcaYM6*V z0BZH+hcKEP4q{JU8G?jg+ui7>WCvlN{gA4kum!1sR?@E9Sk^NV%C>&}iAtEjaJ>5h zSizrF&9XGOdMYUjt^gdr&w4Gbl6H%V`p7@FW*Mb2b3End;!@{i)QNZt)_}mF2T*7o zzdRG?;^q!dObo@(T=-Vbi_-JDNAm0wJ#mS|2HV}-+@Awd+r%y>l6kG}4R7I->8ECu zZOvblh)xMN)-&ANx*GAQTjvieijIVgOop*rsggK`c}zZ1ujzkvb9_>kmmTOXapXnv zrvdt-kUDGNCUm@*s8{MkcxelJ0nYPP7U7G`^u>!x7A>_tJOEy zzinBpuY&Kn*DZQQ^S-}7?&2}Q`a9uOW5z1Q@<{6jc|K>)Y*ScE)_HZR4V0Eoio}fv zrLDKFB{gK;@|zdE>RV;_F5*0|RJDi%(5rH*vf9IG2dv%TCA zyLoM$`XjbP-A&#a!)8li6%432VASLwy+=6aOs|sN`h;+8e9CiQ)0Vqi-E;WZBmcPdM$Nw0 zIujB@XZ&B|lh^h72eJ+jp-;g+@pf{e^tkrC&M*8~fP#SmMy`_>k|HDEsy zkG8P%%wXJ{Lo|YBdGU5b+NiQ%1KmfqM&Czoqkvr|_0;dz9aFwd`CHTGgq1bfLMUUN zMH6r8n77c@#SYNRy=hL>=@z_0r?e;HCi`2qAx^2W%c|Cz&T{TjpUcd^g_&eiwf0U3 z@lRDRncgewySFdaZ}WctYEBc9h&-Lmg$oySGTv+c-Uht}ZiG4?ig?JQ<<%W;d-);` z-AmEDs>>O#d@b?1_>7i>Q-9RcttNstlZ#)X+Fhtev#4j@es=w6-Nck1l~Zl9yKRW( z&#bzK&Hhx*l~swES;^i%79}=rfiMd? zWOxWJ(QqO87(F@u{iErhuQZ%UB;MP&Yx~e5eXQVT_QWFg?yEuEF3B(Se%_pU*{bWE zOyOjNErEwSxhuMR7ET2AN`o?Anv)Z`pvr{mO98xxldNv2gn9A^7xXeWi%BQV(B0(A zhodFc14T>chC3wI-SUmo&E~LTW7<%W+fXnFSaKv{JrLF$p7D8e>u0ooV*;2)8lg?9puSW531k7Z}8wZTX&N8 zM5FfJ-nf0kUrqH79uC-tfVN4pWRb>%7@jjJ0iN4*)2ByTH=m@f(&rgpaLqo_|KC)9 zS8;63BYQH(ha>b2bDykTk`yC!o|+gvak`<@CiMLP^Yg23C$}3sNn@uUnkf6|7|`s% zKqAgSa8h(6qS|&B*u=i>Ls8i3+htycwj{}e$p`1(?1NrGiAt(tJches?9oX9c4pXR z;FMbWbqg>s*G#%I$Mn7I6iD_`kS0^ z+*wqW9!J)F%Y2LKDBDGn6Qyg=5^P|K%Rf+uNpWWI zzAQZnpl@ve{>7*~j@70~uS?OmfK0 zt~jQ9iw*Zy-!(jj+42EAUT5ZhexzGD&JbM_d-PtJtB4ksO{0@r&dxQRqK(W&#t}N{ zZ%i!vbWd|hX{`k{ok=^d6d3oOLMLq1Zy`E7Wlk9}{@EKqqc5aM+enAc1WID2s}RW2 z^h1aK^t$U97#xN7!<)AI;26wykk& zX4*#;aY?fz-Ff%uT`HHx`O>&|k{q^8JCY=}mO7~)N!4MDpvC{8{+1m5-gr&cTyG7u z9w%dR`M<8oSW_5V7&RD&);G-WuXv4bU=1WzB$CNwWFYnQE&&*unUq5aJR&9~^)$=zv$GHVI`75RUl%t+f?pOf3$9gppc|SyKl0QaI5fql-^Q3qChRKp;T$ zLFn<;)pn42s8nhj)cCZF3$e=FcI4zlMk&}R8D?)&>vUeQG&1r9%svB$OYmsu zfms%!0|4W?15pV^jVd5t_^jvlGcdCgZSs$6SPR@c_0?e*Cy`(tu1DXb^#dm?7gl&J z%=Q+-ZU3W|Ey^#G4||&7)d{7aFlwJGBT^cKBLWH)`jnlv!ny>@ftJ($z*I-(M(Z16 z;=~t#f&*b%!#&`Wv1+*9ifsa;5XlG0gMPHp+-C_7APj^b6G5u3Sc4UkN9=-B1+`ut zjLL!ePUGPBKIlc-ro8ZM*uNn6HV;Fa&`(s`3i~i43Gh4N_(v}IYOqI>;aU&w>%|;r zk`Gw9btvHWzCQs>?7lp+Cz|x!gJ=bOfi#Hx1tRU&nHJr&djFVj+i`A~@>zo6N{U%i z6?x^I<{gDV9c`)kf3xNGf3T#bvguar|;h6AMckQ9nW%JawEu$aOkh zNX1o{L$VbM6SsN^IKl@{vrb0OLYn;h2l4pu)e0~NPiN6if@`PTT3z_+qV)p-vop}F z;?%rVIaKf6zkg}r*NLDP7aPKMSQI;M;_w3`B&sMW8ZMSPEJlH<(W3fowE#)#Mm&d) z6OufKB)m^oukPh2B-uig3GUwqz5ran-ZO+{*T~wsrR{H^>UPQ9yT^*?eJ~umJ>K%q z-nsv}?$b%0nsD2=@I!h!)RQF6za49V7$2yS{>}L)UJ#xG*`;rU&Eatds?%swm~oiuRGF5b z>^m(|;`G!~Kj@A3u0ZWtcbWGWMaG>)er;$U>a@GD5boJxmVYTqGj+^5#v$T;Woy*g zh3ps~k?;*Wzx@#wPv5vHe*2E5*?MDeT$k{WU`QUDF&i-8c&0zhq^~`Gx0PQxPpTsR z@Y2NwtC2X}3EACezu&3>M^O86p(BadW@BZsiM zfGC7?XYDb}tNLsl9MueNvww;Nj;&%^7h9s&EGvs&75Jedv_YUV7-8H}hjSBz3JZz2 zX_$k)4YaucdyvmRPV&9M;0GtM=aLWKZWq)fskru&&h;Gv0Z8(JZG)iVlO))H9**KM zf@|c%3-VFewBvxdQv-11RvH8(eN&<(k*1Y zT1WDpOgi$bT+=C(OW*z5XmNs1w|fh7PW0UTO}UAgYnzkOV~x~YCT|`{{`HME7tVgx zE8wSS^55_!zxxKBfwSwlb6ggqe&eza;!-QAUse$!Tr<<~oYc@t@Gmi<0mp4UctUlC z_oy|gS0{Qvj54|&HSQ{~IaJ<@d#kNv2t9AQ;MU6hn=c^Uqbm)1a1R4cE+INYj2k2f zEjkkDplY}XlRCx(M)DwJ-UbrG^1-TxsD?&^1D!Zj7D5-r5dtH@Pt-7<{Ti21{I$AA z=}lp2sfE3TL_$fagK)DUx2s7Z6T?Mc&tAGg#dnK9AAefxMg%cvb566vZ<^hCB|(A+ zeTqBK->+&l^!){D>|sn@bZ;EHJcu5g=u7)At6qX9!jOmQUP@S8+#Q%qHKBha-?L83 zXl@EC3T|3GhWGHz81mGS_s>QTq`jbu`R>2k0i)ZvCZ{KZdta7rM1Xa{?K->U{q3<~ zgWKxvjqmO2J3YGi;Qhy*m9lDE61r8p>DMcx^fgutldHXvviWQ7Z}d1B53<60cTulp zev8bLx2j=Sy--}iljub7a1_ppKWb*aU90gY+;BLkMj7n206R|4;A5IB8g-`_YD}NJ zRJHms5^sUAp1^_0k8&v&B= zHP5AujEv?E3>s&~^*7mc@O+e#WO(d%btpN7t z1T}9#)J_K1UGe{+!0;Xf&io zF*nqqpVv>Ah(5+2^{-a95kUO`TF2@QlAq+eAGpJ_^i%fwT_*tGWfr{Rt5&y%ijmL{ zGl7=#+jl33o)a>08(g7K3}-$~ULV(Yp?^`y&>mTdR=6|UcpW}2JFV^Q&q_!YSgfgO zX-A@F7w0mUVZ{Ct1}8uIIA#@o?yNGOfS9lyUW>(=q58iPBtEwT`BqWjCj z>m_qx(Q4!d;r9pTL})Yw`}^*91Fx#GW{1Pj9jL~}rxhNa)y*9!OYFI@SIOnO5Kfx2 z!wWy$b$?7O8!ki)4V|^Z7@Oqg2qYMNhM`DRpQs0KsWMEDl>mLIEfMFajt$iXo@wS7 z7Rtl4F|w`?2!j-FZ_#(i`PdVUn_L} zrix<*d>YErX}|=M55iv1w>A=)URbY^y2mw)c)+1*#_k7XVi!^g)W$?0wYz2Wgcb<{vp(?9ZJ$gV!m(dl3=`KD3imB#exZ=zjo|o%wYJ z#2)Nbut#e^xefzXC0t_g*F7!yaar|aj_K|ey$O`0xApa&Ow-hI6}(fdjL|iQ_XMeh zR#465N;-Wkju(QI1yRYs#r$@t0I|6&+%x>Y_@;%$7`Q-grC{~!H5@Gaf)<7_=RSSJ zyRRw|AOvy_9J54Xu==0pVR2w8l-?rew~Od-fn%ZHeTfx4_F_VaoTE%dq z5?fvH%0k>4*X-$K4k`nsRk1sEt?cDWm)7Ja?Tuj45pX-A^}oOs(MKTAe}_1)Z?3^` zH}k5`kS7q*Q3@6#X_9dr?9x&N?rB)Wb8GLLnqD_G;vEn+;{`rGK0AoOgv)KRa_uH_ ze>M&mBJfQl2>;?iJB4V)aW7PP*P-lW-*>SwNvA}I`KB)mY&~$D6K$9Sos{{uV`}_r zlN#Ou!VzNfC=&z}!F6y54nZvffcOYkmwvnwD};y4;dyL&FFb8Vhe8>&I(*{9iOW6` ze!^i-p0uz^jd$hVMuQn-X7XWvxZpM3{>m*|^17ME4gndlpoIr15_L=gGv{V^&JB(H z;LZ&wa+5uIPneUsOlsPC_9)pyZe_yWj_HFD1>V11kIj!YzSdUth@ZN_oWV9KQA4@- zs5iz?bAG+ele7n#AGC1G=Wl#}W#d33v~KQ#`ySq?x6p~`NFcYjtF8TWzx-b(E6ETZ zUwHf~E#>c}$YFjPhG%WA+WMPnU^}+K8I7AZLkip!9j&P;DJl#-;C?rdzU3Xq;toWf zYOq`dqeNElKPRA;9_IPRaGP&_bQ#h}zisEepz+Ut%ZW4G!5`UPlCgXnci zug{2J;{p(?7`57Rdt;=Gj~c(OCAgi5+IAb{u2JaE>pHcYY!_c)$dT*hA!7E z1DXDO#?`1EZI@WR@2g5&x1?{3|2Fg<>-@w&z$n>eRmr6;PQoR*8-2fhet+yn5faX% zcxFYcCOnfkt#Z7t@{p+D7>bTRn|?=@k*JyKeQfF$HAMmTzgV7|C#IPvdyiQdJXU4G z_5=D#MvsM};0yf@d^(YzCfFUqhL4cV&pz}O>(Sj~38g0DZgCL0#{2h&v6J20-NXMc z{L_{%A`@v*@s|uz^BQ)u8aRS!zbWsm%Si}{!P)t~$rZeX;VDuQ75p^ear8yOShXILo10qBgiL%4>de-|Bk)JO%SaM_cpqgAIwyTDtr> zpyxDNpBy|fWtnHDDzb}E6d;L7`XAP|w&YOogux3KK`W-RqEaRPMa+$b)PyxXyu z=xBM;I5BR`^}NEvX<&+uzGEx?g)mc1e`OK)bRA>yzRV6DfZ0#e3MP ze4hyq!VIJ@BJ_IwXYy@Zzpf$ZRS&|4)FZxMEwi&rfT4SnF^m2XlO3?abD%2QA`q{%p?5#Wh@Pei}w)>2bb z$A5#2c~{oSBJ12qnNH;QxutyCRbW8O8qnd z{^G2CP~Q)DIepNX4q_EU3DO1zkaO-;S@Jah6%7XgG1i^A4$w#&T$~ZizmN~`2&Qc~ zw&hm{tyv-~>7-lZ*U1?nLcfQWn~H%ING}l1V(is-9{b>Vpj-M)M501`;_CZR^VqNJ=WovY@l>UY>*h-AC# zG@W=Nz`)CP%c?@oZc!m8S-+^>N@u&8+DD`G&thqv36j<8s8TjZizs22)`)~eJLG$lVHqNlcMM{{`f3!E`LFt%v zJ#23QdNbmiKL?C2D+ zPJh&guKTr~oXItcI6gSR6w&hII8?wJrBuqoaDvK*hNt20H#|7bA9GL$j+IF_f>1sQ zbJvM_BD{}oI4UN_4ihpkPl16%82s?MeL@2F)Tjdc7VKQFCAfA`&P*Oz9j zY1{rJ9U#Q@!|z9qLw>!EM~#I&dNf`ljXLh&_5IZ~7j-=G<1W;F{*x~*%q+FQk4UVu z7F%^fw?@cVAlvA`*J)TS`|rzE*!sLi0E?TLVV`gPrfG3MQjG9U5qF2(<`Cp8l*>UJ z{R96_VQ>NFmP+H{YrCu!8GsR(EI(Og?8N zQo8C=Vkr*~z=fXT#`7x(p$P1u@#OJ}>vBDJXts+J^0dBA1oZ~GbM~@p1yP;SzQyl8 z_FU4Q$4t$hnOYEMDOFUR-gahZcTa@gPotXe$uAnxX^JzQI;YNLU;b@$(W|TaHkB^@ z{tM@C_kcC&OCv{(5Tu8b#IYlT1HhRB9|98|`+@q@L|^@PSylV^4{Z8shv!UB(>uoJ z{{rV0K?KC%`|!BEG3p(o=N6Ot7!Dk$55aXcUz}e0uP?fz`IQ2_ZNIV85<` z&xweJ5u`jM@LJ-@f`5(%3;6z@WRh^ulMn0yW^wPJ(<}0cX=kz!_tm?_I+X5wP3{N( zSVocCryZHo8O1z1Xs;xG%$9VhKh~yIavJ&l?A|GB00z*RR^(Re<^xf%DFJi22XZ?oybvcB=0V2J*Y6s6fo1a4sYO#1)gpBziX zOeEMD3#uqhdym{WvZk}1Ij8f|j@pvbGzJ+vAD-;$bWXavoy>7*j5qDNYIz%uKA2A_ zK4#v`zs)T)U3*}ChUN*qfwrsSlg8nO1qn%9B>1ssU>q!b()v*G=vBk)=v-fQ@#v!x zMVIV^)M;^-eWM!xoFXr>m&nMEg!4QKh$0_O>&0^ww6CWz%tW_niiW&>?ZskWL2f=^ z?zF6)_Uj{MpH0~aaTrx?p4MWxC75NhB~&x!C|hHc;X2$w z7J0tsF3Iww{i?frtk~s>OuCu?pEy>jp)DWJb5z?clX5Z+& zKmSPHX%$DsvYx>&8R|Wm55H+|;N(5IEPYPXReVa>Z7#fXWrvA3BJDtI!7TaJPf`(C z-oJkfbd;lJW_)uuysPLqfX^{7=02*P0@b5IngMh{;D&`%Mnjy{MnGoPlh)K3$CqEj zBO+1;M6!MD#YEiqty7Itm!qk=YD$}Dc6c0X6k)1je_3nII&L)Pw5dk-%qXkS-(rIf zi=cIX7hYS0)e4zx5Khf_zk4Eh#sKFm@HeP~dtr27rHJzirfzvLTe8v=GSkr6jDn>` zT~)&3^YoS!64SIghY389mP#uozuK16o>zCCVzGs07Th!%&hy;Q&?l6~?|Bcgboo1<{q!GJ=8Uu> z7spZ`_8sA>GGW^Yb=H~VhPKNmpR8%`t&Nr=u1t|Mmbfh6rUp7=8dEfFueoXaiLn;Z zF}veV{nQ+V+G;7YyZu!Cwj75J3-U&4w(?dE+o0`j&L3gh%}3UKs`t#5%PRWQ zQOdZ>j-kEM=_W&dlp-m;fI)LMZikX(^?Rosq>gx`SyrDG6jULR)-UTG(J;|VW6;>4 zOqbl`cIao6ZH3^6!SE&}v%GBGPG-624c`web}C)<&9g6I%i_^;s>s>IMvba*QF-*H zJn=crIW#K%P?5;YZ4rYUR+Ak2f9a0O*++yMb_G1ous5-OXr-+0=J($4SW)`in<(y| zcfGGSzDdkFc7JpB&gV2fxh|Ox5|^GVy5BY8PBMwmigQ@V3BHlWe!MpGm*F2$QL!zJ z&OBU@E+$uBb6ogJ3^_=*P@v{+!pC=-6QX3d=eifSz3=`YC&c8rCxdY&W<-M_K~Rgn zfBr>iR^yPVZmgi26S=lImMZ0;D`U8s_QtM}f}MNrVMO|tk4k>3UHT5m{xZ5B*L@cm zmAv>XjZI&?-iyccX5enw>gl|PMl`Wv&P+PRD|DCD{9Yz_?<)CnUZqnsvmisj%|^^W z*R?O!D_U3L5^1P~H+4>-PVIuf^rKML=a$Mtu6o+iqZ5@}E;C&_t0Yeg?P1$a)r?=? zomi0e({lT4u0hL+{(t3NY?24v8-fm#ggHzoCo{OHZDF*D; zW)XY*B9^y3_0>3-syP(p*FPGk5NvQg4n}v}V)U@!`(nNb3H=JwDkE z)fT#*dD^oPVPm%g65=e&S8K@<%r(?QK~+?^U%X(oEFJ$~FUD17wF& z)HDnOAIp9DtWJM4C7SH~@T_nkg^BU-u}zy;``f>U#P?LCf40}V!b#q-J-zMsNixZ} zantTvrn9rW7JI5TKk0AmE-jy82xr{cRTV3=S~PHA8!fbhlX8K5-z+WX*m5@Wh2#JQ zUGd9Ou_Mw{pL;#+kFqvdQRu?{&KA7ok)ZrMWHWBVL6V`m>C=1^71T2X1Q|K>*;*g9 zTPZ|sbJc1oj}p?UkJV51rCgF?tl2lux+(whru>`1UdpcW9;WYu`pLq&mrhEUTFdXK z-@#dFLEbBKtA{&%x$=lzn2^iOEAbmMpVc?+QRC4fxBN2xa9_KGv6xEl;+A%6y|Q#V zU)ru|w`i`n#*OwACo(@vc(s{WM$EAWb^7q9944tjMmiI5vKLFaieo9`vgw_hbt+DZ zpKlV;x<6JhVJANFsbMYQ$CmW{z4gLEsqb#<`INgdMNxgtikc!$`U-3R7vlO=`K?(3 zW;tsRK+B*=CHVZoV6T=O{YIiTr}Wz+ZLFbLDltr+iCkSv(wv$ zJ)K&_D4M)~w~2MMpjiFZ&c)xgoLj8!+52yk>~ovHA9W>wZ7$%yGxGHmR$*hQ`W5x2 z`4^v}IW6j*Z%v*S3zCyKw|%kv#KLg5z+IjTXYGoXjr7-V6=$i?jH>6&vzBpvdu2>I zGlnV=X(8Vuyi=Vuz{TcVX;ekusvRriR~x%^yb;UoHFfL8mTZ*-H6qToDWB6ca83P) z#}zQ{m(U#Ou$A)>W`R4Pv8Q$PV1(E{kBB8#M$#MWjgOD7i)>_kV=cSvJE_`lwKC-5 ztVg#Bx90yu3ICt#g!&f1_}*|{zsEUwL0mTieG^^lMfrLR3aY-&<>wM1Hg+q0@NR2q zSqoRX;~7l!e6t#@j@6t8(&k zsPG-11+i?O^hysg-s|mf3R=14-P-465g4(nZt$zPfgqfa%V`#u_LVl3e4QYXA{*1< z))NwmoWRdZky|Xv-%&<8^L63u5DhUoYFVKhqJ%$Qocg3AD!z%8gaH=~jg73E$BrsE zJqmcX^kwX_2=Fo}Vl-gw?~b0_ep(Bd76c(-vYbX*g717a9yoxV%d^-AhPQ%262R;1Nqi4!sV}A|pdB>xgyA$r&%o@t= z9;TEpn4g0*#$m`Qh^ANhG~1?eIi&H$5An4}?G(!~b-akC1duTRtt2;s7Hw(tAvM~C z=$ER9`Jlv9BTU(feH$R(%Ve_8gRT4Lv+B;%FTcv8H=dj7BM7Y7^-^6tre-wr57l3mT2LhA|>HYr5!ypt_{x?EYKS5Y#-vM0r8h)(C$ z`DSwx2ghrs7qN}a{>+jOD}TQ*_k8~{Py3;Pen|Yzg51=NI?2jz()Fok`6l*yMinMk zN@ZQiCCA)48%vVO$7(ZT{>4>W6*~6lhor?tTB%if*|V1vl<5rbwraKx%&GrvHKX5= z@vwq6oU_%Wy-Fd1E{vHzoi5gnZ9UaTluQ>Vlo9MkuMf|i>>VdtW6?78wD|6jcPIZc zP_qAPS5G%{R`H>H^hw7kw3GRtNrse(bO9rU+7q5+{w!0Gjp6hPD_a!=`#bNPKNYn9 zPG?oxGY`Ya7%D@wW!XUax<$qqyQ_CB8M!&Eaw;!mk0fNg31;6W!h4qHW6Tg1&Mdq~ zE}M?B??053PW5=R{FiRJWXX1=U_7Z;(29*$?F4I*!SRW7Dk-C#?wFX5K8LF|^YvgJ zQud1lnqbp(j^6)%kEIN2bObYf9sg?AuWRJ_ksLbxD+SB06SC>7K7!Lx=N>!u8$F(Q zaGArt)R!XP>z?L(+X3mg@d7I7BM_cS*)hucrqT z+w~pl)c$pTzP@AINZ^l{DL?3X$vf^nUs^n@#c?%!QG(zLA0zT;}eGs{E^SKT*K zi`{2Y`tYgbj*aONq>T8gAkQ_Y9{u_CKf&YQ)+zg^Otx4n z=Cj+Aoy(HfCL}$!i`HE9?`q=M?n*wgb=B)(jgens zz&zMcW*sSP%Vi1Pxh&pbQ!}%gl51Zlu5y^rc=p<2%t*w-ezPCFW|DtH-X4gfbOfmi zZ6K64?HTcF=aaDBXuG@jBwjh5+3^tMGzbdK%nn~+mKOR>iPF+tK36w}Rr6jBFrfLk zHW}1+ZTDJg{arS$*sb{I=8vUcc{lTKZY*vEi2xRu1Nk`k)jED#I<2(+d0zSFK>h7p zmp#+Ig%&SK#0VHAr*=jJ0INX@G2Bb}IaPe=lTMznerAUT1XLs)2n#1N>UHO*_$2A2 z`n=6k`|Ep*D`u}aFOFZcIro^WJ{N>Hp>triEsTA-HrIbgUwSW8eHULxrInIoxyl`oS;8S=$CEyY;rjHXJi4 v2;WP1`X2QOI;AyF=)_j9Kg literal 0 HcmV?d00001 diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 34df13a54b..f68e58d7d8 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -3,6 +3,7 @@ #include "../../slic3r/GUI/3DScene.hpp" #include "../../slic3r/GUI/GLShader.hpp" #include "../../slic3r/GUI/GUI.hpp" +#include "../../slic3r/GUI/PresetBundle.hpp" #include "../../libslic3r/ClipperUtils.hpp" #include "../../libslic3r/PrintConfig.hpp" #include "../../libslic3r/Print.hpp" @@ -18,10 +19,15 @@ #include #include +#include #include #include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include "SVG.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + static const float TRACKBALLSIZE = 0.8f; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; static const float GROUND_Z = -0.02f; @@ -41,25 +47,66 @@ static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; namespace Slic3r { namespace GUI { -bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z) +bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords) { - m_data.clear(); + m_vertices.clear(); + m_tex_coords.clear(); - unsigned int size = 9 * (unsigned int)triangles.size(); - if (size == 0) + unsigned int v_size = 9 * (unsigned int)triangles.size(); + unsigned int t_size = 6 * (unsigned int)triangles.size(); + if (v_size == 0) return false; - m_data = std::vector(size, 0.0f); + m_vertices = std::vector(v_size, 0.0f); + if (generate_tex_coords) + m_tex_coords = std::vector(t_size, 0.0f); - unsigned int coord = 0; + float min_x = (float)unscale(triangles[0].points[0].x); + float min_y = (float)unscale(triangles[0].points[0].y); + float max_x = min_x; + float max_y = min_y; + + unsigned int v_coord = 0; + unsigned int t_coord = 0; for (const Polygon& t : triangles) { for (unsigned int v = 0; v < 3; ++v) { const Point& p = t.points[v]; - m_data[coord++] = (float)unscale(p.x); - m_data[coord++] = (float)unscale(p.y); - m_data[coord++] = z; + float x = (float)unscale(p.x); + float y = (float)unscale(p.y); + + m_vertices[v_coord++] = x; + m_vertices[v_coord++] = y; + m_vertices[v_coord++] = z; + + if (generate_tex_coords) + { + m_tex_coords[t_coord++] = x; + m_tex_coords[t_coord++] = y; + + min_x = std::min(min_x, x); + max_x = std::max(max_x, x); + min_y = std::min(min_y, y); + max_y = std::max(max_y, y); + } + } + } + + if (generate_tex_coords) + { + float size_x = max_x - min_x; + float size_y = max_y - min_y; + + if ((size_x != 0.0f) && (size_y != 0.0f)) + { + float inv_size_x = 1.0f / size_x; + float inv_size_y = -1.0f / size_y; + for (unsigned int i = 0; i < m_tex_coords.size(); i += 2) + { + m_tex_coords[i] *= inv_size_x; + m_tex_coords[i + 1] *= inv_size_y; + } } } @@ -68,36 +115,42 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z) bool GeometryBuffer::set_from_lines(const Lines& lines, float z) { - m_data.clear(); + m_vertices.clear(); + m_tex_coords.clear(); unsigned int size = 6 * (unsigned int)lines.size(); if (size == 0) return false; - m_data = std::vector(size, 0.0f); + m_vertices = std::vector(size, 0.0f); unsigned int coord = 0; for (const Line& l : lines) { - m_data[coord++] = (float)unscale(l.a.x); - m_data[coord++] = (float)unscale(l.a.y); - m_data[coord++] = z; - m_data[coord++] = (float)unscale(l.b.x); - m_data[coord++] = (float)unscale(l.b.y); - m_data[coord++] = z; + m_vertices[coord++] = (float)unscale(l.a.x); + m_vertices[coord++] = (float)unscale(l.a.y); + m_vertices[coord++] = z; + m_vertices[coord++] = (float)unscale(l.b.x); + m_vertices[coord++] = (float)unscale(l.b.y); + m_vertices[coord++] = z; } return true; } -const float* GeometryBuffer::get_data() const +const float* GeometryBuffer::get_vertices() const { - return m_data.data(); + return m_vertices.data(); } -unsigned int GeometryBuffer::get_data_size() const +const float* GeometryBuffer::get_tex_coords() const { - return (unsigned int)m_data.size(); + return m_tex_coords.data(); +} + +unsigned int GeometryBuffer::get_vertices_count() const +{ + return (unsigned int)m_vertices.size() / 3; } Size::Size() @@ -188,6 +241,106 @@ void Rect::set_bottom(float bottom) m_bottom = bottom; } +GLCanvas3D::GLTextureData::GLTextureData() + : m_id(0) + , m_width(0) + , m_height(0) + , m_source("") +{ +} + +GLCanvas3D::GLTextureData::~GLTextureData() +{ + reset(); +} + +bool GLCanvas3D::GLTextureData::load_from_file(const std::string& filename) +{ + reset(); + + // Load a PNG with an alpha channel. + wxImage image; + if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) + { + reset(); + return false; + } + + m_width = image.GetWidth(); + m_height = image.GetHeight(); + int n_pixels = m_width * m_height; + + if (n_pixels <= 0) + { + reset(); + return false; + } + + // Get RGB & alpha raw data from wxImage, pack them into an array. + unsigned char* img_rgb = image.GetData(); + if (img_rgb == nullptr) + { + reset(); + return false; + } + + unsigned char* img_alpha = image.GetAlpha(); + + std::vector data(n_pixels * 4, 0); + for (int i = 0; i < n_pixels; ++i) + { + int data_id = i * 4; + int img_id = i * 3; + data[data_id + 0] = img_rgb[img_id + 0]; + data[data_id + 1] = img_rgb[img_id + 1]; + data[data_id + 2] = img_rgb[img_id + 2]; + data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; + } + + // sends data to gpu + ::glGenTextures(1, &m_id); + ::glBindTexture(GL_TEXTURE_2D, m_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(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + ::glBindTexture(GL_TEXTURE_2D, 0); + + m_source = filename; + return true; +} + +void GLCanvas3D::GLTextureData::reset() +{ + if (m_id != 0) + ::glDeleteTextures(1, &m_id); + + m_id = 0; + m_width = 0; + m_height = 0; + m_source = ""; +} + +unsigned int GLCanvas3D::GLTextureData::get_id() const +{ + return m_id; +} + +int GLCanvas3D::GLTextureData::get_width() const +{ + return m_width; +} + +int GLCanvas3D::GLTextureData::get_height() const +{ + return m_height; +} + +const std::string& GLCanvas3D::GLTextureData::get_source() const +{ + return m_source; +} + GLCanvas3D::Camera::Camera() : type(Ortho) , zoom(1.0f) @@ -222,6 +375,11 @@ void GLCanvas3D::Camera::set_theta(float theta) m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta); } +GLCanvas3D::Bed::Bed() + : m_type(Custom) +{ +} + const Pointfs& GLCanvas3D::Bed::get_shape() const { return m_shape; @@ -230,6 +388,7 @@ const Pointfs& GLCanvas3D::Bed::get_shape() const void GLCanvas3D::Bed::set_shape(const Pointfs& shape) { m_shape = shape; + m_type = _detect_type(); _calc_bounding_box(); @@ -244,7 +403,7 @@ void GLCanvas3D::Bed::set_shape(const Pointfs& shape) const BoundingBox& bed_bbox = poly.contour.bounding_box(); _calc_gridlines(poly, bed_bbox); - m_polygon = offset_ex(poly.contour, bed_bbox.radius() * 1.7, jtRound, scale_(0.5))[0].contour; + m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour; } const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const @@ -262,39 +421,26 @@ Point GLCanvas3D::Bed::point_projection(const Point& point) const return m_polygon.point_projection(point); } -void GLCanvas3D::Bed::render() const +void GLCanvas3D::Bed::render(float theta) const { - unsigned int triangles_vcount = m_triangles.get_data_size() / 3; - if (triangles_vcount > 0) + switch (m_type) { - ::glDisable(GL_LIGHTING); - ::glDisable(GL_DEPTH_TEST); - - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - ::glEnableClientState(GL_VERTEX_ARRAY); - - ::glColor4f(0.8f, 0.6f, 0.5f, 0.4f); - ::glNormal3d(0.0f, 0.0f, 1.0f); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_data()); - ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount); - - // we need depth test for grid, otherwise it would disappear when looking - // the object from below - glEnable(GL_DEPTH_TEST); - - // draw grid - unsigned int gridlines_vcount = m_gridlines.get_data_size() / 3; - - ::glLineWidth(3.0f); - ::glColor4f(0.2f, 0.2f, 0.2f, 0.4f); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_data()); - ::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount); - - ::glDisableClientState(GL_VERTEX_ARRAY); - - ::glDisable(GL_BLEND); + case MK2: + { + _render_mk2(theta); + break; + } + case MK3: + { + _render_mk3(theta); + break; + } + default: + case Custom: + { + _render_custom(); + break; + } } } @@ -312,7 +458,7 @@ void GLCanvas3D::Bed::_calc_triangles(const ExPolygon& poly) Polygons triangles; poly.triangulate(&triangles); - if (!m_triangles.set_from_triangles(triangles, GROUND_Z)) + if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom)) printf("Unable to create bed triangles\n"); } @@ -345,6 +491,166 @@ void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& printf("Unable to create bed grid lines\n"); } +GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const +{ + EType type = Custom; + + const PresetBundle* bundle = get_preset_bundle(); + if (bundle != nullptr) + { + const Preset& curr = bundle->printers.get_selected_preset(); + if (curr.config.has("bed_shape") && _are_equal(m_shape, dynamic_cast(curr.config.option("bed_shape"))->values)) + { + if ((curr.vendor != nullptr) && (curr.vendor->name == "Prusa Research")) + { + if (boost::contains(curr.name, "MK2")) + type = MK2; + else if (boost::contains(curr.name, "MK3")) + type = MK3; + } + } + } + + return type; +} + +void GLCanvas3D::Bed::_render_mk2(float theta) const +{ + std::string filename = resources_dir() + "/icons/bed/mk2_top.png"; + if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) + { + if (!m_top_texture.load_from_file(filename)) + { + _render_custom(); + return; + } + } + + filename = resources_dir() + "/icons/bed/mk2_bottom.png"; + if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename)) + { + if (!m_bottom_texture.load_from_file(filename)) + { + _render_custom(); + return; + } + } + + _render_prusa(theta); +} + +void GLCanvas3D::Bed::_render_mk3(float theta) const +{ + std::string filename = resources_dir() + "/icons/bed/mk3_top.png"; + if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) + { + if (!m_top_texture.load_from_file(filename)) + { + _render_custom(); + return; + } + } + + filename = resources_dir() + "/icons/bed/mk3_bottom.png"; + if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename)) + { + if (!m_bottom_texture.load_from_file(filename)) + { + _render_custom(); + return; + } + } + + _render_prusa(theta); +} + +void GLCanvas3D::Bed::_render_prusa(float theta) const +{ + unsigned int triangles_vcount = m_triangles.get_vertices_count(); + if (triangles_vcount > 0) + { + ::glEnable(GL_DEPTH_TEST); + + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ::glEnable(GL_TEXTURE_2D); + + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + if (theta > 90.0f) + ::glFrontFace(GL_CW); + + ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + ::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id()); + ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()); + ::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords()); + ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount); + + if (theta > 90.0f) + ::glFrontFace(GL_CCW); + + ::glBindTexture(GL_TEXTURE_2D, 0); + ::glDisableClientState(GL_TEXTURE_COORD_ARRAY); + ::glDisableClientState(GL_VERTEX_ARRAY); + + ::glDisable(GL_TEXTURE_2D); + + ::glDisable(GL_BLEND); + } +} + +void GLCanvas3D::Bed::_render_custom() const +{ + m_top_texture.reset(); + m_bottom_texture.reset(); + + unsigned int triangles_vcount = m_triangles.get_vertices_count(); + if (triangles_vcount > 0) + { + ::glEnable(GL_LIGHTING); + ::glEnable(GL_DEPTH_TEST); + + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ::glEnableClientState(GL_VERTEX_ARRAY); + + ::glColor4f(0.8f, 0.6f, 0.5f, 0.4f); + ::glNormal3d(0.0f, 0.0f, 1.0f); + ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()); + ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount); + + // draw grid + unsigned int gridlines_vcount = m_gridlines.get_vertices_count(); + + ::glLineWidth(3.0f); + ::glColor4f(0.2f, 0.2f, 0.2f, 0.4f); + ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices()); + ::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount); + + ::glDisableClientState(GL_VERTEX_ARRAY); + + ::glDisable(GL_BLEND); + } +} + +bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2) +{ + if (bed_1.size() != bed_2.size()) + return false; + + for (unsigned int i = 0; i < (unsigned int)bed_1.size(); ++i) + { + if (bed_1[i] != bed_2[i]) + return false; + } + + return true; +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLCanvas3D::Axes::Axes() : length(0.0f) { @@ -431,11 +737,11 @@ void GLCanvas3D::CuttingPlane::_render_contour() const if (m_z >= 0.0f) { - unsigned int lines_vcount = m_lines.get_data_size() / 3; + unsigned int lines_vcount = m_lines.get_vertices_count(); ::glLineWidth(2.0f); ::glColor3f(0.0f, 0.0f, 0.0f); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_lines.get_data()); + ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_lines.get_vertices()); ::glDrawArrays(GL_LINES, 0, (GLsizei)lines_vcount); } @@ -515,20 +821,6 @@ void GLCanvas3D::Shader::_reset() } } -GLCanvas3D::LayersEditing::GLTextureData::GLTextureData() - : id(0) - , width(0) - , height(0) -{ -} - -GLCanvas3D::LayersEditing::GLTextureData::GLTextureData(unsigned int id, int width, int height) - : id(id) - , width(width) - , height(height) -{ -} - GLCanvas3D::LayersEditing::LayersEditing() : m_use_legacy_opengl(false) , m_enabled(false) @@ -544,18 +836,6 @@ GLCanvas3D::LayersEditing::LayersEditing() GLCanvas3D::LayersEditing::~LayersEditing() { - if (m_tooltip_texture.id != 0) - { - ::glDeleteTextures(1, &m_tooltip_texture.id); - m_tooltip_texture = GLTextureData(); - } - - if (m_reset_texture.id != 0) - { - ::glDeleteTextures(1, &m_reset_texture.id); - m_reset_texture = GLTextureData(); - } - if (m_z_texture_id != 0) { ::glDeleteTextures(1, &m_z_texture_id); @@ -626,9 +906,9 @@ void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObje _render_profile(print_object, bar_rect); // Revert the matrices. - glPopMatrix(); + ::glPopMatrix(); - glEnable(GL_DEPTH_TEST); + ::glEnable(GL_DEPTH_TEST); } int GLCanvas3D::LayersEditing::get_shader_program_id() const @@ -730,10 +1010,10 @@ bool GLCanvas3D::LayersEditing::_is_initialized() const void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const { - if (m_tooltip_texture.id == 0) + if (m_tooltip_texture.get_id() == 0) { - m_tooltip_texture = _load_texture_from_file("variable_layer_height_tooltip.png"); - if (m_tooltip_texture.id == 0) + std::string filename = resources_dir() + "/icons/variable_layer_height_tooltip.png"; + if (!m_tooltip_texture.load_from_file(filename)) return; } @@ -744,24 +1024,24 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas float bar_left = bar_rect.get_left(); float reset_bottom = reset_rect.get_bottom(); - float l = bar_left - (float)m_tooltip_texture.width * inv_zoom - gap; + float l = bar_left - (float)m_tooltip_texture.get_width() * inv_zoom - gap; float r = bar_left - gap; - float t = reset_bottom + (float)m_tooltip_texture.height * inv_zoom + gap; + float t = reset_bottom + (float)m_tooltip_texture.get_height() * inv_zoom + gap; float b = reset_bottom + gap; - canvas.render_texture(m_tooltip_texture.id, l, r, b, t); + canvas.render_texture(m_tooltip_texture.get_id(), l, r, b, t); } void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const { - if (m_reset_texture.id == 0) + if (m_reset_texture.get_id() == 0) { - m_reset_texture = _load_texture_from_file("variable_layer_height_reset.png"); - if (m_reset_texture.id == 0) + std::string filename = resources_dir() + "/icons/variable_layer_height_reset.png"; + if (!m_reset_texture.load_from_file(filename)) return; } - canvas.render_texture(m_reset_texture.id, reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); + canvas.render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); } void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const @@ -858,53 +1138,6 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, } } -GLCanvas3D::LayersEditing::GLTextureData GLCanvas3D::LayersEditing::_load_texture_from_file(const std::string& filename) -{ - const std::string& path = resources_dir() + "/icons/"; - - // Load a PNG with an alpha channel. - wxImage image; - if (!image.LoadFile(path + filename, wxBITMAP_TYPE_PNG)) - return GLTextureData(); - - int width = image.GetWidth(); - int height = image.GetHeight(); - int n_pixels = width * height; - - if (n_pixels <= 0) - return GLTextureData(); - - // Get RGB & alpha raw data from wxImage, pack them into an array. - unsigned char* img_rgb = image.GetData(); - if (img_rgb == nullptr) - return GLTextureData(); - - unsigned char* img_alpha = image.GetAlpha(); - - std::vector data(n_pixels * 4, 0); - for (int i = 0; i < n_pixels; ++i) - { - int data_id = i * 4; - int img_id = i * 3; - data[data_id + 0] = img_rgb[img_id + 0]; - data[data_id + 1] = img_rgb[img_id + 1]; - data[data_id + 2] = img_rgb[img_id + 2]; - data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; - } - - // sends data to gpu - GLuint tex_id; - ::glGenTextures(1, &tex_id); - ::glBindTexture(GL_TEXTURE_2D, tex_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(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)width, (GLsizei)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); - ::glBindTexture(GL_TEXTURE_2D, 0); - - return GLTextureData((unsigned int)tex_id, width, height); -} - const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); const Pointf3 GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX); @@ -1016,16 +1249,16 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) ::glEnable(GL_LIGHT1); // light from camera - GLfloat specular[4] = { 0.3f, 0.3f, 0.3f, 1.0f }; - ::glLightfv(GL_LIGHT1, GL_SPECULAR, specular); - GLfloat diffuse[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - ::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse); + GLfloat specular_cam[4] = { 0.3f, 0.3f, 0.3f, 1.0f }; + ::glLightfv(GL_LIGHT1, GL_SPECULAR, specular_cam); + GLfloat diffuse_cam[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; + ::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse_cam); // light from above - GLfloat specular1[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - ::glLightfv(GL_LIGHT0, GL_SPECULAR, specular1); - GLfloat diffuse1[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; - ::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse1); + GLfloat specular_top[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; + ::glLightfv(GL_LIGHT0, GL_SPECULAR, specular_top); + GLfloat diffuse_top[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; + ::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_top); // Enables Smooth Color Shading; try GL_FLAT for (lack of) fun. ::glShadeModel(GL_SMOOTH); @@ -1357,16 +1590,16 @@ void GLCanvas3D::render() _camera_tranform(); - GLfloat position[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; - ::glLightfv(GL_LIGHT1, GL_POSITION, position); - GLfloat position1[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; - ::glLightfv(GL_LIGHT0, GL_POSITION, position1); + GLfloat position_cam[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; + ::glLightfv(GL_LIGHT1, GL_POSITION, position_cam); + GLfloat position_top[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; + ::glLightfv(GL_LIGHT0, GL_POSITION, position_top); _picking_pass(); _render_background(); - _render_bed(); - _render_axes(); _render_objects(); + _render_bed(m_camera.get_theta()); + _render_axes(); _render_cutting_plane(); _render_warning_texture(); _render_legend_texture(); @@ -2781,9 +3014,9 @@ void GLCanvas3D::_render_background() const ::glPopMatrix(); } -void GLCanvas3D::_render_bed() const +void GLCanvas3D::_render_bed(float theta) const { - m_bed.render(); + m_bed.render(theta); } void GLCanvas3D::_render_axes() const diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index b8c64228b7..8917fb9eb0 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -33,14 +33,17 @@ namespace GUI { class GeometryBuffer { - std::vector m_data; + std::vector m_vertices; + std::vector m_tex_coords; public: - bool set_from_triangles(const Polygons& triangles, float z); + bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords); bool set_from_lines(const Lines& lines, float z); - const float* get_data() const; - unsigned int get_data_size() const; + const float* get_vertices() const; + const float* get_tex_coords() const; + + unsigned int get_vertices_count() const; }; class Size @@ -112,6 +115,27 @@ class GLCanvas3D void reset() { first_volumes.clear(); } }; + struct GLTextureData + { + private: + unsigned int m_id; + int m_width; + int m_height; + std::string m_source; + + public: + GLTextureData(); + ~GLTextureData(); + + bool load_from_file(const std::string& filename); + void reset(); + + unsigned int get_id() const; + int get_width() const; + int get_height() const; + const std::string& get_source() const; + }; + public: struct Camera { @@ -143,13 +167,28 @@ public: class Bed { + public: + enum EType : unsigned char + { + MK2, + MK3, + Custom, + Num_Types + }; + + private: + EType m_type; Pointfs m_shape; BoundingBoxf3 m_bounding_box; Polygon m_polygon; GeometryBuffer m_triangles; GeometryBuffer m_gridlines; + mutable GLTextureData m_top_texture; + mutable GLTextureData m_bottom_texture; public: + Bed(); + const Pointfs& get_shape() const; void set_shape(const Pointfs& shape); @@ -157,12 +196,18 @@ public: bool contains(const Point& point) const; Point point_projection(const Point& point) const; - void render() const; + void render(float theta) const; private: void _calc_bounding_box(); void _calc_triangles(const ExPolygon& poly); void _calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); + EType _detect_type() const; + void _render_mk2(float theta) const; + void _render_mk3(float theta) const; + void _render_prusa(float theta) const; + void _render_custom() const; + static bool _are_equal(const Pointfs& bed_1, const Pointfs& bed_2); }; struct Axes @@ -227,16 +272,6 @@ public: }; private: - struct GLTextureData - { - unsigned int id; - int width; - int height; - - GLTextureData(); - GLTextureData(unsigned int id, int width, int height); - }; - bool m_use_legacy_opengl; bool m_enabled; Shader m_shader; @@ -284,7 +319,6 @@ public: void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const; void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; - static GLTextureData _load_texture_from_file(const std::string& filename); }; struct Mouse @@ -507,7 +541,7 @@ private: void _camera_tranform() const; void _picking_pass() const; void _render_background() const; - void _render_bed() const; + void _render_bed(float theta) const; void _render_axes() const; void _render_objects() const; void _render_cutting_plane() const; From 7b4870d1cbe8ea009093981dcc35961c8ecb4205 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 11 Jun 2018 11:40:11 +0200 Subject: [PATCH 079/117] Fixed selection and rendering in object settings dialog --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 5 +-- xs/src/slic3r/GUI/GLCanvas3D.cpp | 49 ++++++++++++++++++----- xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 ++ 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index b0735c3498..135d752332 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -156,10 +156,9 @@ sub new { #============================================================================================================================== Slic3r::GUI::_3DScene::enable_picking($canvas, 1); Slic3r::GUI::_3DScene::set_select_by($canvas, 'volume'); - Slic3r::GUI::_3DScene::register_on_select_callback($canvas, sub { + Slic3r::GUI::_3DScene::register_on_select_object_callback($canvas, sub { my ($volume_idx) = @_; - # convert scene volume to model object volume - $self->reload_tree(($volume_idx == -1) ? undef : $canvas->volumes->[$volume_idx]->volume_idx); + $self->reload_tree($volume_idx); }); Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]); Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index f68e58d7d8..e5f352bf76 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -380,6 +380,16 @@ GLCanvas3D::Bed::Bed() { } +bool GLCanvas3D::Bed::is_prusa() const +{ + return (m_type == MK2) || (m_type == MK3); +} + +bool GLCanvas3D::Bed::is_custom() const +{ + return m_type == Custom; +} + const Pointfs& GLCanvas3D::Bed::get_shape() const { return m_shape; @@ -610,7 +620,7 @@ void GLCanvas3D::Bed::_render_custom() const if (triangles_vcount > 0) { ::glEnable(GL_LIGHTING); - ::glEnable(GL_DEPTH_TEST); + ::glDisable(GL_DEPTH_TEST); ::glEnable(GL_BLEND); ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -625,6 +635,8 @@ void GLCanvas3D::Bed::_render_custom() const // draw grid unsigned int gridlines_vcount = m_gridlines.get_vertices_count(); + // we need depth test for grid, otherwise it would disappear when looking the object from below + ::glEnable(GL_DEPTH_TEST); ::glLineWidth(3.0f); ::glColor4f(0.2f, 0.2f, 0.2f, 0.4f); ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices()); @@ -1595,11 +1607,24 @@ void GLCanvas3D::render() GLfloat position_top[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; ::glLightfv(GL_LIGHT0, GL_POSITION, position_top); + float theta = m_camera.get_theta(); + bool is_custom_bed = m_bed.is_custom(); + _picking_pass(); _render_background(); + // untextured bed needs to be rendered before objects + if (is_custom_bed) + { + _render_bed(theta); + _render_axes(); + } _render_objects(); - _render_bed(m_camera.get_theta()); - _render_axes(); + // textured bed needs to be rendered after objects + if (!is_custom_bed) + { + _render_bed(theta); + _render_axes(); + } _render_cutting_plane(); _render_warning_texture(); _render_legend_texture(); @@ -2393,11 +2418,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) // Don't allow to zoom too far outside the scene. float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box()); if (zoom_min > 0.0f) - { - zoom_min *= 0.4f; - if (zoom < zoom_min) - zoom = zoom_min; - } + zoom = std::max(zoom, zoom_min * 0.8f); m_camera.zoom = zoom; m_on_viewport_changed_callback.call(); @@ -3946,8 +3967,18 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) void GLCanvas3D::_on_select(int volume_idx) { + int id = -1; if ((m_volumes != nullptr) && (volume_idx < (int)m_volumes->volumes.size())) - m_on_select_object_callback.call((volume_idx == -1) ? -1 : m_volumes->volumes[volume_idx]->object_idx()); + { + if (volume_idx != -1) + { + if (m_select_by == "volume") + id = m_volumes->volumes[volume_idx]->volume_idx(); + else if (m_select_by == "object") + id = m_volumes->volumes[volume_idx]->object_idx(); + } + } + m_on_select_object_callback.call(id); } std::vector GLCanvas3D::_parse_colors(const std::vector& colors) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 8917fb9eb0..dde96ff417 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -189,6 +189,9 @@ public: public: Bed(); + bool is_prusa() const; + bool is_custom() const; + const Pointfs& get_shape() const; void set_shape(const Pointfs& shape); From 085110c4d987ee46f2c4d982469677f41aba3dd3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 11 Jun 2018 13:48:02 +0200 Subject: [PATCH 080/117] Removed 3DScene volumes from perl --- lib/Slic3r/GUI/3DScene.pm | 19 +++----- lib/Slic3r/GUI/Plater.pm | 5 ++- lib/Slic3r/GUI/Plater/3DPreview.pm | 3 +- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 25 ++++++++--- xs/src/slic3r/GUI/3DScene.cpp | 19 +++++++- xs/src/slic3r/GUI/3DScene.hpp | 5 ++- xs/src/slic3r/GUI/GLCanvas3D.cpp | 55 +++++++++++++++++++---- xs/src/slic3r/GUI/GLCanvas3D.hpp | 5 ++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 25 +++++++++-- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 5 ++- xs/xsp/GUI_3DScene.xsp | 38 +++++++++++++--- 11 files changed, 160 insertions(+), 44 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 5f33b17abc..e40beda26a 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -34,9 +34,6 @@ use Wx::GLCanvas qw(:all); # volumes: reference to vector of Slic3r::GUI::3DScene::Volume. #============================================================================================================================== -__PACKAGE__->mk_accessors( qw( - volumes - ) ); #__PACKAGE__->mk_accessors( qw(_quat _dirty init # enable_picking # enable_moving @@ -165,17 +162,11 @@ sub new { # $self->use_plain_shader(0); # $self->_apply_zoom_to_volumes_filter(0); # $self->_mouse_dragging(0); -#============================================================================================================================== - - # Collection of GLVolume objects - $self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new); -#============================================================================================================================== - Slic3r::GUI::_3DScene::set_volumes($self, $self->volumes); - Slic3r::GUI::_3DScene::reset_volumes($self); -#============================================================================================================================== - - # 3D point in model space -#============================================================================================================================== +# +# # Collection of GLVolume objects +# $self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new); +# +# # 3D point in model space # $self->_camera_type('ortho'); ## $self->_camera_type('perspective'); # $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index f74bc7d829..85d9426fb4 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -2109,7 +2109,10 @@ sub object_list_changed { } my $export_in_progress = $self->{export_gcode_output_file} || $self->{send_gcode_file}; - my $model_fits = $self->{canvas3D} ? $self->{canvas3D}->volumes->check_outside_state($self->{config}) : 1; +#============================================================================================================================== + my $model_fits = $self->{canvas3D} ? Slic3r::GUI::_3DScene::check_volumes_outside_state($self->{canvas3D}, $self->{config}) : 1; +# my $model_fits = $self->{canvas3D} ? $self->{canvas3D}->volumes->check_outside_state($self->{config}) : 1; +#============================================================================================================================== my $method = ($have_objects && ! $export_in_progress && $model_fits) ? 'Enable' : 'Disable'; $self->{"btn_$_"}->$method for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode); diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 55198eeb6a..86a862c758 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -398,10 +398,11 @@ sub load_print { # $self->canvas->reset_legend_texture(); #============================================================================================================================== } else { - $self->{force_sliders_full_range} = (scalar(@{$self->canvas->volumes}) == 0); #============================================================================================================================== + $self->{force_sliders_full_range} = (Slic3r::GUI::_3DScene::get_volumes_count($self->canvas) == 0); Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print); Slic3r::GUI::_3DScene::load_gcode_preview($self->canvas, $self->gcode_preview_data, \@colors); +# $self->{force_sliders_full_range} = (scalar(@{$self->canvas->volumes}) == 0); # $self->canvas->load_gcode_preview($self->print, $self->gcode_preview_data, \@colors); #============================================================================================================================== $self->show_hide_ui_elements('full'); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 135d752332..546ec5445f 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -280,7 +280,10 @@ sub selection_changed { # deselect all meshes if ($self->{canvas}) { - $_->set_selected(0) for @{$self->{canvas}->volumes}; +#============================================================================================================================== + Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas}); +# $_->set_selected(0) for @{$self->{canvas}->volumes}; +#============================================================================================================================== } # disable things as if nothing is selected @@ -308,7 +311,10 @@ sub selection_changed { if ($itemData->{type} eq 'volume') { # select volume in 3D preview if ($self->{canvas}) { - $self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id}); +# $self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1); +#============================================================================================================================== } $self->{btn_delete}->Enable; $self->{btn_split}->Enable; @@ -450,7 +456,10 @@ sub on_btn_move_up { if ($itemData && $itemData->{type} eq 'volume') { my $volume_id = $itemData->{volume_id}; if ($self->{model_object}->move_volume_up($volume_id)) { - $self->{canvas}->volumes->move_volume_up($volume_id); +#============================================================================================================================== + Slic3r::GUI::_3DScene::move_volume_up($self->{canvas}, $volume_id); +# $self->{canvas}->volumes->move_volume_up($volume_id); +#============================================================================================================================== $self->{parts_changed} = 1; $self->reload_tree($volume_id - 1); } @@ -463,7 +472,10 @@ sub on_btn_move_down { if ($itemData && $itemData->{type} eq 'volume') { my $volume_id = $itemData->{volume_id}; if ($self->{model_object}->move_volume_down($volume_id)) { - $self->{canvas}->volumes->move_volume_down($volume_id); +#============================================================================================================================== + Slic3r::GUI::_3DScene::move_volume_down($self->{canvas}, $volume_id); +# $self->{canvas}->volumes->move_volume_down($volume_id); +#============================================================================================================================== $self->{parts_changed} = 1; $self->reload_tree($volume_id + 1); } @@ -570,7 +582,10 @@ sub _update_canvas { # restore selection, if any if (my $itemData = $self->get_selection) { if ($itemData->{type} eq 'volume') { - $self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1); +#============================================================================================================================== + Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id}); +# $self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1); +#============================================================================================================================== } } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 21204b0fff..4f95f4ba6e 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1802,9 +1802,9 @@ bool _3DScene::is_shown_on_screen(wxGLCanvas* canvas) return s_canvas_mgr.is_shown_on_screen(canvas); } -void _3DScene::set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes) +unsigned int _3DScene::get_volumes_count(wxGLCanvas* canvas) { - s_canvas_mgr.set_volumes(canvas, volumes); + return s_canvas_mgr.get_volumes_count(canvas); } void _3DScene::reset_volumes(wxGLCanvas* canvas) @@ -1827,6 +1827,21 @@ void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections) { s_canvas_mgr.set_objects_selections(canvas, selections); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 8e82849e84..1f179b0091 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -558,11 +558,14 @@ public: static bool is_shown_on_screen(wxGLCanvas* canvas); - static void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); + static unsigned int get_volumes_count(wxGLCanvas* canvas); static void reset_volumes(wxGLCanvas* canvas); static void deselect_volumes(wxGLCanvas* canvas); static void select_volume(wxGLCanvas* canvas, unsigned int id); static void update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections); + static bool check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config); + static bool move_volume_up(wxGLCanvas* canvas, unsigned int id); + static bool move_volume_down(wxGLCanvas* canvas, unsigned int id); static void set_objects_selections(wxGLCanvas* canvas, const std::vector& selections); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index e5f352bf76..7d39d97d3e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -24,10 +24,6 @@ #include #include -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -#include "SVG.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - static const float TRACKBALLSIZE = 0.8f; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; static const float GROUND_Z = -0.02f; @@ -661,7 +657,6 @@ bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2) return true; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLCanvas3D::Axes::Axes() : length(0.0f) @@ -1212,15 +1207,18 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_reload_delayed(false) { if (m_canvas != nullptr) + { m_timer = new wxTimer(m_canvas); + m_volumes = new GLVolumeCollection; + } } GLCanvas3D::~GLCanvas3D() { if (m_volumes != nullptr) { - set_current(); - m_volumes->release_geometry(); + reset_volumes(); + delete m_volumes; } if (m_timer != nullptr) @@ -1317,9 +1315,9 @@ bool GLCanvas3D::is_shown_on_screen() const return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; } -void GLCanvas3D::set_volumes(GLVolumeCollection* volumes) +unsigned int GLCanvas3D::get_volumes_count() const { - m_volumes = volumes; + return (m_volumes != nullptr) ? (unsigned int)m_volumes->volumes.size() : 0; } void GLCanvas3D::reset_volumes() @@ -1372,6 +1370,45 @@ void GLCanvas3D::update_volumes_selection(const std::vector& selections) } } +bool GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const +{ + return (m_volumes != nullptr) ? m_volumes->check_outside_state(config) : false; +} + +bool GLCanvas3D::move_volume_up(unsigned int id) +{ + if (m_volumes != nullptr) + { + if ((id > 0) && (id < (unsigned int)m_volumes->volumes.size())) + { + std::swap(m_volumes->volumes[id - 1], m_volumes->volumes[id]); + std::swap(m_volumes->volumes[id - 1]->composite_id, m_volumes->volumes[id]->composite_id); + std::swap(m_volumes->volumes[id - 1]->select_group_id, m_volumes->volumes[id]->select_group_id); + std::swap(m_volumes->volumes[id - 1]->drag_group_id, m_volumes->volumes[id]->drag_group_id); + return true; + } + } + + return false; +} + +bool GLCanvas3D::move_volume_down(unsigned int id) +{ + if (m_volumes != nullptr) + { + if ((id >= 0) && (id + 1 < (unsigned int)m_volumes->volumes.size())) + { + std::swap(m_volumes->volumes[id + 1], m_volumes->volumes[id]); + std::swap(m_volumes->volumes[id + 1]->composite_id, m_volumes->volumes[id]->composite_id); + std::swap(m_volumes->volumes[id + 1]->select_group_id, m_volumes->volumes[id]->select_group_id); + std::swap(m_volumes->volumes[id + 1]->drag_group_id, m_volumes->volumes[id]->drag_group_id); + return true; + } + } + + return false; +} + void GLCanvas3D::set_objects_selections(const std::vector& selections) { m_objects_selections = selections; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index dde96ff417..2bdf9e692f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -419,11 +419,14 @@ public: bool is_shown_on_screen() const; - void set_volumes(GLVolumeCollection* volumes); + unsigned int get_volumes_count() const; void reset_volumes(); void deselect_volumes(); void select_volume(unsigned int id); void update_volumes_selection(const std::vector& selections); + bool check_volumes_outside_state(const DynamicPrintConfig* config) const; + bool move_volume_up(unsigned int id); + bool move_volume_down(unsigned int id); void set_objects_selections(const std::vector& selections); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index e01f85396f..54bd54ba83 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -226,11 +226,10 @@ bool GLCanvas3DManager::is_shown_on_screen(wxGLCanvas* canvas) const return (it != m_canvases.end()) ? it->second->is_shown_on_screen() : false; } -void GLCanvas3DManager::set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes) +unsigned int GLCanvas3DManager::get_volumes_count(wxGLCanvas* canvas) const { - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_volumes(volumes); + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->get_volumes_count() : 0; } void GLCanvas3DManager::reset_volumes(wxGLCanvas* canvas) @@ -261,6 +260,24 @@ void GLCanvas3DManager::update_volumes_selection(wxGLCanvas* canvas, const std:: it->second->update_volumes_selection(selections); } +bool GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->check_volumes_outside_state(config) : false; +} + +bool GLCanvas3DManager::move_volume_up(wxGLCanvas* canvas, unsigned int id) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->move_volume_up(id) : false; +} + +bool GLCanvas3DManager::move_volume_down(wxGLCanvas* canvas, unsigned int id) +{ + CanvasesMap::const_iterator it = _get_canvas(canvas); + return (it != m_canvases.end()) ? it->second->move_volume_down(id) : false; +} + void GLCanvas3DManager::set_objects_selections(wxGLCanvas* canvas, const std::vector& selections) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 76f8ffcb5f..435993acd9 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -53,11 +53,14 @@ public: bool is_shown_on_screen(wxGLCanvas* canvas) const; - void set_volumes(wxGLCanvas* canvas, GLVolumeCollection* volumes); + unsigned int get_volumes_count(wxGLCanvas* canvas) const; void reset_volumes(wxGLCanvas* canvas); void deselect_volumes(wxGLCanvas* canvas); void select_volume(wxGLCanvas* canvas, unsigned int id); void update_volumes_selection(wxGLCanvas* canvas, const std::vector& selections); + bool check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const; + bool move_volume_up(wxGLCanvas* canvas, unsigned int id); + bool move_volume_down(wxGLCanvas* canvas, unsigned int id); void set_objects_selections(wxGLCanvas* canvas, const std::vector& selections); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index f8d22843e7..8deb6419d2 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -199,12 +199,13 @@ is_shown_on_screen(canvas) OUTPUT: RETVAL -void -set_volumes(canvas, volumes) - SV *canvas; - GLVolumeCollection *volumes; +unsigned int +get_volumes_count(canvas) + SV *canvas; CODE: - _3DScene::set_volumes((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), volumes); + RETVAL = _3DScene::get_volumes_count((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); + OUTPUT: + RETVAL void reset_volumes(canvas) @@ -232,6 +233,33 @@ update_volumes_selection(canvas, selections) CODE: _3DScene::update_volumes_selection((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), selections); +bool +check_volumes_outside_state(canvas, config) + SV *canvas; + DynamicPrintConfig *config; + CODE: + RETVAL = _3DScene::check_volumes_outside_state((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), config); + OUTPUT: + RETVAL + +bool +move_volume_up(canvas, id) + SV *canvas; + unsigned int id; + CODE: + RETVAL = _3DScene::move_volume_up((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id); + OUTPUT: + RETVAL + +bool +move_volume_down(canvas, id) + SV *canvas; + unsigned int id; + CODE: + RETVAL = _3DScene::move_volume_down((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), id); + OUTPUT: + RETVAL + void set_objects_selections(canvas, selections) SV *canvas; From a02ea39525d2378d4b3be204f8068d956d3ed26e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 11 Jun 2018 15:13:13 +0200 Subject: [PATCH 081/117] GLCanvas3D volumes as a stack variable --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 290 +++++++++++------------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 17 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 1 + xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 18 +- 4 files changed, 147 insertions(+), 179 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 7d39d97d3e..0d234a7649 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1185,7 +1185,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) , m_timer(nullptr) - , m_volumes(nullptr) , m_config(nullptr) , m_print(nullptr) , m_model(nullptr) @@ -1207,19 +1206,12 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_reload_delayed(false) { if (m_canvas != nullptr) - { m_timer = new wxTimer(m_canvas); - m_volumes = new GLVolumeCollection; - } } GLCanvas3D::~GLCanvas3D() { - if (m_volumes != nullptr) - { - reset_volumes(); - delete m_volumes; - } + reset_volumes(); if (m_timer != nullptr) { @@ -1291,8 +1283,8 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) // on linux the gl context is not valid until the canvas is not shown on screen // we defer the geometry finalization of volumes until the first call to render() - if ((m_volumes != nullptr) && !m_volumes->empty()) - m_volumes->finalize_geometry(m_use_VBOs); + if (!m_volumes.empty()) + m_volumes.finalize_geometry(m_use_VBOs); m_initialized = true; @@ -1317,36 +1309,33 @@ bool GLCanvas3D::is_shown_on_screen() const unsigned int GLCanvas3D::get_volumes_count() const { - return (m_volumes != nullptr) ? (unsigned int)m_volumes->volumes.size() : 0; + return (unsigned int)m_volumes.volumes.size(); } void GLCanvas3D::reset_volumes() { - if (set_current() && (m_volumes != nullptr)) + if (set_current()) { - m_volumes->release_geometry(); - m_volumes->clear(); + m_volumes.release_geometry(); + m_volumes.clear(); m_dirty = true; } } void GLCanvas3D::deselect_volumes() { - if (m_volumes != nullptr) + for (GLVolume* vol : m_volumes.volumes) { - for (GLVolume* vol : m_volumes->volumes) - { - if (vol != nullptr) - vol->selected = false; - } + if (vol != nullptr) + vol->selected = false; } } void GLCanvas3D::select_volume(unsigned int id) { - if ((m_volumes != nullptr) && (id < (unsigned int)m_volumes->volumes.size())) + if (id < (unsigned int)m_volumes.volumes.size()) { - GLVolume* vol = m_volumes->volumes[id]; + GLVolume* vol = m_volumes.volumes[id]; if (vol != nullptr) vol->selected = true; } @@ -1372,38 +1361,32 @@ void GLCanvas3D::update_volumes_selection(const std::vector& selections) bool GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const { - return (m_volumes != nullptr) ? m_volumes->check_outside_state(config) : false; + return m_volumes.check_outside_state(config); } bool GLCanvas3D::move_volume_up(unsigned int id) { - if (m_volumes != nullptr) + if ((id > 0) && (id < (unsigned int)m_volumes.volumes.size())) { - if ((id > 0) && (id < (unsigned int)m_volumes->volumes.size())) - { - std::swap(m_volumes->volumes[id - 1], m_volumes->volumes[id]); - std::swap(m_volumes->volumes[id - 1]->composite_id, m_volumes->volumes[id]->composite_id); - std::swap(m_volumes->volumes[id - 1]->select_group_id, m_volumes->volumes[id]->select_group_id); - std::swap(m_volumes->volumes[id - 1]->drag_group_id, m_volumes->volumes[id]->drag_group_id); - return true; - } + std::swap(m_volumes.volumes[id - 1], m_volumes.volumes[id]); + std::swap(m_volumes.volumes[id - 1]->composite_id, m_volumes.volumes[id]->composite_id); + std::swap(m_volumes.volumes[id - 1]->select_group_id, m_volumes.volumes[id]->select_group_id); + std::swap(m_volumes.volumes[id - 1]->drag_group_id, m_volumes.volumes[id]->drag_group_id); + return true; } - + return false; } bool GLCanvas3D::move_volume_down(unsigned int id) { - if (m_volumes != nullptr) + if ((id >= 0) && (id + 1 < (unsigned int)m_volumes.volumes.size())) { - if ((id >= 0) && (id + 1 < (unsigned int)m_volumes->volumes.size())) - { - std::swap(m_volumes->volumes[id + 1], m_volumes->volumes[id]); - std::swap(m_volumes->volumes[id + 1]->composite_id, m_volumes->volumes[id]->composite_id); - std::swap(m_volumes->volumes[id + 1]->select_group_id, m_volumes->volumes[id]->select_group_id); - std::swap(m_volumes->volumes[id + 1]->drag_group_id, m_volumes->volumes[id]->drag_group_id); - return true; - } + std::swap(m_volumes.volumes[id + 1], m_volumes.volumes[id]); + std::swap(m_volumes.volumes[id + 1]->composite_id, m_volumes.volumes[id]->composite_id); + std::swap(m_volumes.volumes[id + 1]->select_group_id, m_volumes.volumes[id]->select_group_id); + std::swap(m_volumes.volumes[id + 1]->drag_group_id, m_volumes.volumes[id]->drag_group_id); + return true; } return false; @@ -1491,13 +1474,10 @@ float GLCanvas3D::get_camera_zoom() const BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const { BoundingBoxf3 bb; - if (m_volumes != nullptr) + for (const GLVolume* volume : m_volumes.volumes) { - for (const GLVolume* volume : m_volumes->volumes) - { - if (!m_apply_zoom_to_volumes_filter || ((volume != nullptr) && volume->zoom_to_volumes)) - bb.merge(volume->transformed_bounding_box()); - } + if (!m_apply_zoom_to_volumes_filter || ((volume != nullptr) && volume->zoom_to_volumes)) + bb.merge(volume->transformed_bounding_box()); } return bb; } @@ -1616,10 +1596,8 @@ void GLCanvas3D::set_viewport_from_scene(const GLCanvas3D& other) void GLCanvas3D::update_volumes_colors_by_extruder() { - if ((m_volumes == nullptr) || (m_config == nullptr)) - return; - - m_volumes->update_colors_by_extruder(m_config); + if (m_config != nullptr) + m_volumes.update_colors_by_extruder(m_config); } void GLCanvas3D::render() @@ -1697,20 +1675,16 @@ void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, fl std::vector GLCanvas3D::get_current_print_zs(bool active_only) const { - return (m_volumes != nullptr) ? m_volumes->get_current_print_zs(active_only) : std::vector(); + return m_volumes.get_current_print_zs(active_only); } void GLCanvas3D::set_toolpaths_range(double low, double high) { - if (m_volumes != nullptr) - m_volumes->set_range(low, high); + m_volumes.set_range(low, high); } std::vector GLCanvas3D::load_object(const ModelObject& model_object, int obj_idx, std::vector instance_idxs) { - if (m_volumes == nullptr) - return std::vector(); - if (instance_idxs.empty()) { for (unsigned int i = 0; i < model_object.instances.size(); ++i) @@ -1718,8 +1692,7 @@ std::vector GLCanvas3D::load_object(const ModelObject& model_object, int ob instance_idxs.push_back(i); } } - - return m_volumes->load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_select_by, m_drag_by, m_use_VBOs && m_initialized); + return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_select_by, m_drag_by, m_use_VBOs && m_initialized); } std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) @@ -1736,7 +1709,7 @@ std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) void GLCanvas3D::reload_scene(bool force) { - if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr) || (m_volumes == nullptr)) + if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) return; reset_volumes(); @@ -1778,16 +1751,16 @@ void GLCanvas3D::reload_scene(bool force) float w = dynamic_cast(m_config->option("wipe_tower_width"))->value; float a = dynamic_cast(m_config->option("wipe_tower_rotation_angle"))->value; - m_volumes->load_wipe_tower_preview(1000, x, y, w, 15.0f * (float)(extruders_count - 1), (float)height, a, m_use_VBOs && m_initialized); + m_volumes.load_wipe_tower_preview(1000, x, y, w, 15.0f * (float)(extruders_count - 1), (float)height, a, m_use_VBOs && m_initialized); } } update_volumes_colors_by_extruder(); // checks for geometry outside the print volume to render it accordingly - if (!m_volumes->empty()) + if (!m_volumes.empty()) { - bool contained = m_volumes->check_outside_state(m_config); + bool contained = m_volumes.check_outside_state(m_config); if (!contained) { enable_warning_texture(true); @@ -1797,7 +1770,7 @@ void GLCanvas3D::reload_scene(bool force) else { enable_warning_texture(false); - m_volumes->reset_outside_state(); + m_volumes.reset_outside_state(); _3DScene::reset_warning_texture(); m_on_enable_action_buttons_callback.call(!m_model->objects.empty()); } @@ -1811,7 +1784,7 @@ void GLCanvas3D::reload_scene(bool force) void GLCanvas3D::load_print_toolpaths() { - if ((m_print == nullptr) || (m_volumes == nullptr)) + if (m_print == nullptr) return; if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim)) @@ -1849,8 +1822,8 @@ void GLCanvas3D::load_print_toolpaths() if (print_zs.size() > skirt_height) print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); - m_volumes->volumes.emplace_back(new GLVolume(color)); - GLVolume& volume = *m_volumes->volumes.back(); + m_volumes.volumes.emplace_back(new GLVolume(color)); + GLVolume& volume = *m_volumes.volumes.back(); for (size_t i = 0; i < skirt_height; ++i) { volume.print_zs.push_back(print_zs[i]); volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size()); @@ -1896,9 +1869,6 @@ void GLCanvas3D::load_print_object_toolpaths(const PrintObject& print_object, co return this->color_by_tool() ? std::min(this->number_tools() - 1, std::max(extruder - 1, 0)) : feature; } } ctxt; - - if (m_volumes == nullptr) - return; ctxt.shifted_copies = &print_object._shifted_copies; @@ -1925,11 +1895,11 @@ void GLCanvas3D::load_print_object_toolpaths(const PrintObject& print_object, co auto *volume = new GLVolume(color); new_volume_mutex.lock(); volume->outside_printer_detection_enabled = false; - m_volumes->volumes.emplace_back(volume); + m_volumes.volumes.emplace_back(volume); new_volume_mutex.unlock(); return volume; }; - const size_t volumes_cnt_initial = m_volumes->volumes.size(); + const size_t volumes_cnt_initial = m_volumes.volumes.size(); std::vector volumes_per_thread(ctxt.layers.size()); tbb::parallel_for( tbb::blocked_range(0, ctxt.layers.size(), grain_size), @@ -2012,19 +1982,19 @@ void GLCanvas3D::load_print_object_toolpaths(const PrintObject& print_object, co BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results"; // Remove empty volumes from the newly added volumes. - m_volumes->volumes.erase( - std::remove_if(m_volumes->volumes.begin() + volumes_cnt_initial, m_volumes->volumes.end(), + m_volumes.volumes.erase( + std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), [](const GLVolume *volume) { return volume->empty(); }), - m_volumes->volumes.end()); - for (size_t i = volumes_cnt_initial; i < m_volumes->volumes.size(); ++i) - m_volumes->volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); + m_volumes.volumes.end()); + for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) + m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end"; } void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector& str_tool_colors) { - if ((m_volumes == nullptr) || (m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty()) + if ((m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty()) return; if (!m_print->state.is_done(psWipeTower)) @@ -2078,11 +2048,11 @@ void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector& str_t auto *volume = new GLVolume(color); new_volume_mutex.lock(); volume->outside_printer_detection_enabled = false; - m_volumes->volumes.emplace_back(volume); + m_volumes.volumes.emplace_back(volume); new_volume_mutex.unlock(); return volume; }; - const size_t volumes_cnt_initial = m_volumes->volumes.size(); + const size_t volumes_cnt_initial = m_volumes.volumes.size(); std::vector volumes_per_thread(n_items); tbb::parallel_for( tbb::blocked_range(0, n_items, grain_size), @@ -2164,25 +2134,25 @@ void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector& str_t BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results"; // Remove empty volumes from the newly added volumes. - m_volumes->volumes.erase( - std::remove_if(m_volumes->volumes.begin() + volumes_cnt_initial, m_volumes->volumes.end(), + m_volumes.volumes.erase( + std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), [](const GLVolume *volume) { return volume->empty(); }), - m_volumes->volumes.end()); - for (size_t i = volumes_cnt_initial; i < m_volumes->volumes.size(); ++i) - m_volumes->volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); + m_volumes.volumes.end()); + for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) + m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; } void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors) { - if ((m_canvas != nullptr) && (m_volumes != nullptr) && (m_print != nullptr)) + if ((m_canvas != nullptr) && (m_print != nullptr)) { // ensures that the proper context is selected if (!set_current()) return; - if (m_volumes->empty()) + if (m_volumes.empty()) { std::vector tool_colors = _parse_colors(str_tool_colors); @@ -2193,17 +2163,16 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const _load_gcode_retractions(preview_data); _load_gcode_unretractions(preview_data); - if (m_volumes->empty()) + if (m_volumes.empty()) _3DScene::reset_legend_texture(); else { _3DScene::generate_legend_texture(preview_data, tool_colors); // removes empty volumes - m_volumes->volumes.erase(std::remove_if(m_volumes->volumes.begin(), m_volumes->volumes.end(), - [](const GLVolume* volume) { return volume->print_zs.empty(); }), - m_volumes->volumes.end()); - + m_volumes.volumes.erase(std::remove_if(m_volumes.volumes.begin(), m_volumes.volumes.end(), + [](const GLVolume* volume) { return volume->print_zs.empty(); }), m_volumes.volumes.end()); + _load_shells(); } } @@ -2473,9 +2442,6 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt) void GLCanvas3D::on_mouse(wxMouseEvent& evt) { - if (m_volumes == nullptr) - return; - Point pos(evt.GetX(), evt.GetY()); int selected_object_idx = (is_layers_editing_enabled() && (m_print != nullptr)) ? _get_layers_editing_first_selected_object_id(m_print->objects.size()) : -1; @@ -2531,10 +2497,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (volume_idx != -1) { - int group_id = m_volumes->volumes[volume_idx]->select_group_id; + int group_id = m_volumes.volumes[volume_idx]->select_group_id; if (group_id != -1) { - for (GLVolume* vol : m_volumes->volumes) + for (GLVolume* vol : m_volumes.volumes) { if ((vol != nullptr) && (vol->select_group_id == group_id)) vol->selected = true; @@ -2558,7 +2524,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (evt.LeftDown() && m_moving_enabled) { // Only accept the initial position, if it is inside the volume bounding box. - BoundingBoxf3 volume_bbox = m_volumes->volumes[volume_idx]->transformed_bounding_box(); + BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box(); volume_bbox.offset(1.0); if (volume_bbox.contains(pos3d)) { @@ -2573,7 +2539,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.RightDown()) { // if right clicking on volume, propagate event through callback - if (m_volumes->volumes[volume_idx]->hover) + if (m_volumes.volumes[volume_idx]->hover) m_on_right_click_callback.call(pos.x, pos.y); } } @@ -2602,14 +2568,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Calculate the translation vector. Vectorf3 vector = m_mouse.drag.start_position_3D.vector_to(cur_pos); // Get the volume being dragged. - GLVolume* volume = m_volumes->volumes[m_mouse.drag.volume_idx]; + GLVolume* volume = m_volumes.volumes[m_mouse.drag.volume_idx]; // Get all volumes belonging to the same group, if any. std::vector volumes; if (volume->drag_group_id == -1) volumes.push_back(volume); else { - for (GLVolume* v : m_volumes->volumes) + for (GLVolume* v : m_volumes.volumes) { if ((v != nullptr) && (v->drag_group_id == volume->drag_group_id)) volumes.push_back(v); @@ -2686,14 +2652,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // get all volumes belonging to the same group, if any std::vector volume_idxs; int vol_id = m_mouse.drag.volume_idx; - int group_id = m_volumes->volumes[vol_id]->drag_group_id; + int group_id = m_volumes.volumes[vol_id]->drag_group_id; if (group_id == -1) volume_idxs.push_back(vol_id); else { - for (int i = 0; i < m_volumes->volumes.size(); ++i) + for (int i = 0; i < m_volumes.volumes.size(); ++i) { - if (m_volumes->volumes[i]->drag_group_id == group_id) + if (m_volumes.volumes[i]->drag_group_id == group_id) volume_idxs.push_back(i); } } @@ -2940,10 +2906,10 @@ void GLCanvas3D::_deregister_callbacks() void GLCanvas3D::_mark_volumes_for_layer_height() const { - if ((m_volumes == nullptr) || (m_print == nullptr)) + if (m_print == nullptr) return; - for (GLVolume* vol : m_volumes->volumes) + for (GLVolume* vol : m_volumes.volumes) { int object_id = int(vol->select_group_id / 1000000); int shader_id = m_layers_editing.get_shader_program_id(); @@ -2986,7 +2952,7 @@ void GLCanvas3D::_picking_pass() const { const Pointf& pos = m_mouse.position; - if (m_picking_enabled && !m_mouse.dragging && (pos != Pointf(DBL_MAX, DBL_MAX)) && (m_volumes != nullptr)) + if (m_picking_enabled && !m_mouse.dragging && (pos != Pointf(DBL_MAX, DBL_MAX))) { // Render the object for picking. // FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing. @@ -3017,19 +2983,19 @@ void GLCanvas3D::_picking_pass() const m_hover_volume_id = -1; - for (GLVolume* vol : m_volumes->volumes) + for (GLVolume* vol : m_volumes.volumes) { vol->hover = false; } - if (volume_id < m_volumes->volumes.size()) + if (volume_id < m_volumes.volumes.size()) { m_hover_volume_id = volume_id; - m_volumes->volumes[volume_id]->hover = true; - int group_id = m_volumes->volumes[volume_id]->select_group_id; + m_volumes.volumes[volume_id]->hover = true; + int group_id = m_volumes.volumes[volume_id]->select_group_id; if (group_id != -1) { - for (GLVolume* vol : m_volumes->volumes) + for (GLVolume* vol : m_volumes.volumes) { if (vol->select_group_id == group_id) vol->hover = true; @@ -3084,7 +3050,8 @@ void GLCanvas3D::_render_axes() const void GLCanvas3D::_render_objects() const { - if ((m_volumes == nullptr) || m_volumes->empty()) + if (m_volumes.empty()) + return; ::glEnable(GL_LIGHTING); @@ -3100,15 +3067,15 @@ void GLCanvas3D::_render_objects() const if (m_config != nullptr) { const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(); - m_volumes->set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height")); - m_volumes->check_outside_state(m_config); + m_volumes.set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height")); + m_volumes.check_outside_state(m_config); } // do not cull backfaces to show broken geometry, if any ::glDisable(GL_CULL_FACE); } m_shader.start_using(); - m_volumes->render_VBOs(); + m_volumes.render_VBOs(); m_shader.stop_using(); if (m_picking_enabled) @@ -3120,7 +3087,7 @@ void GLCanvas3D::_render_objects() const if (m_picking_enabled) ::glDisable(GL_CULL_FACE); - m_volumes->render_legacy(); + m_volumes.render_legacy(); if (m_picking_enabled) ::glEnable(GL_CULL_FACE); @@ -3199,12 +3166,12 @@ void GLCanvas3D::_render_legend_texture() const void GLCanvas3D::_render_layer_editing_overlay() const { - if ((m_volumes == nullptr) || (m_print == nullptr)) + if (m_print == nullptr) return; GLVolume* volume = nullptr; - for (GLVolume* vol : m_volumes->volumes) + for (GLVolume* vol : m_volumes.volumes) { if ((vol != nullptr) && vol->selected && vol->has_layer_height_texture()) { @@ -3233,9 +3200,6 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const { static const float INV_255 = 1.0f / 255.0f; - if (m_volumes == nullptr) - return; - if (fake_colors) ::glDisable(GL_LIGHTING); else @@ -3251,7 +3215,7 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const ::glEnableClientState(GL_NORMAL_ARRAY); unsigned int volume_id = 0; - for (GLVolume* vol : m_volumes->volumes) + for (GLVolume* vol : m_volumes.volumes) { if (fake_colors) { @@ -3285,7 +3249,7 @@ float GLCanvas3D::_get_layers_editing_cursor_z_relative() const int GLCanvas3D::_get_layers_editing_first_selected_object_id(unsigned int objects_count) const { - return (m_volumes != nullptr) ? m_layers_editing.get_first_selected_object_id(*m_volumes, objects_count) : -1; + return m_layers_editing.get_first_selected_object_id(m_volumes, objects_count); } void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) @@ -3294,7 +3258,7 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) if (object_idx_selected == -1) return; - if ((m_volumes == nullptr) || (m_print == nullptr)) + if (m_print == nullptr) return; PrintObject* selected_obj = m_print->get_object(object_idx_selected); @@ -3329,7 +3293,7 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) } } - m_volumes->volumes[volume_idx]->generate_layer_height_texture(selected_obj, 1); + m_volumes.volumes[volume_idx]->generate_layer_height_texture(selected_obj, 1); _refresh_if_shown_on_screen(); // Automatic action on mouse down with the same coordinate. @@ -3469,7 +3433,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat }; typedef std::vector FiltersList; - size_t initial_volumes_count = m_volumes->volumes.size(); + size_t initial_volumes_count = m_volumes.volumes.size(); // detects filters FiltersList filters; @@ -3491,27 +3455,27 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat // creates a new volume for each filter for (Filter& filter : filters) { - m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)m_volumes->volumes.size()); + m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)m_volumes.volumes.size()); GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba); if (volume != nullptr) { filter.volume = volume; - m_volumes->volumes.emplace_back(volume); + m_volumes.volumes.emplace_back(volume); } else { // an error occourred - restore to previous state and return m_gcode_preview_volume_index.first_volumes.pop_back(); - if (initial_volumes_count != m_volumes->volumes.size()) + if (initial_volumes_count != m_volumes.volumes.size()) { - std::vector::iterator begin = m_volumes->volumes.begin() + initial_volumes_count; - std::vector::iterator end = m_volumes->volumes.end(); + std::vector::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; + std::vector::iterator end = m_volumes.volumes.end(); for (std::vector::iterator it = begin; it < end; ++it) { GLVolume* volume = *it; delete volume; } - m_volumes->volumes.erase(begin, end); + m_volumes.volumes.erase(begin, end); return; } } @@ -3536,11 +3500,11 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat } // finalize volumes and sends geometry to gpu - if (m_volumes->volumes.size() > initial_volumes_count) + if (m_volumes.volumes.size() > initial_volumes_count) { - for (size_t i = initial_volumes_count; i < m_volumes->volumes.size(); ++i) + for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i) { - GLVolume* volume = m_volumes->volumes[i]; + GLVolume* volume = m_volumes.volumes[i]; volume->bounding_box = volume->indexed_vertex_array.bounding_box(); volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); } @@ -3549,7 +3513,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors) { - size_t initial_volumes_count = m_volumes->volumes.size(); + size_t initial_volumes_count = m_volumes.volumes.size(); m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count); bool res = true; @@ -3575,27 +3539,27 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, if (!res) { // an error occourred - restore to previous state and return - if (initial_volumes_count != m_volumes->volumes.size()) + if (initial_volumes_count != m_volumes.volumes.size()) { - std::vector::iterator begin = m_volumes->volumes.begin() + initial_volumes_count; - std::vector::iterator end = m_volumes->volumes.end(); + std::vector::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; + std::vector::iterator end = m_volumes.volumes.end(); for (std::vector::iterator it = begin; it < end; ++it) { GLVolume* volume = *it; delete volume; } - m_volumes->volumes.erase(begin, end); + m_volumes.volumes.erase(begin, end); } return; } // finalize volumes and sends geometry to gpu - if (m_volumes->volumes.size() > initial_volumes_count) + if (m_volumes.volumes.size() > initial_volumes_count) { - for (size_t i = initial_volumes_count; i < m_volumes->volumes.size(); ++i) + for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i) { - GLVolume* volume = m_volumes->volumes[i]; + GLVolume* volume = m_volumes.volumes[i]; volume->bounding_box = volume->indexed_vertex_array.bounding_box(); volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); } @@ -3647,7 +3611,7 @@ bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data) else { type.volume = volume; - m_volumes->volumes.emplace_back(volume); + m_volumes.volumes.emplace_back(volume); } } @@ -3713,7 +3677,7 @@ bool GLCanvas3D::_travel_paths_by_feedrate(const GCodePreviewData& preview_data) else { feedrate.volume = volume; - m_volumes->volumes.emplace_back(volume); + m_volumes.volumes.emplace_back(volume); } } @@ -3779,7 +3743,7 @@ bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, con else { tool.volume = volume; - m_volumes->volumes.emplace_back(volume); + m_volumes.volumes.emplace_back(volume); } } @@ -3802,7 +3766,7 @@ bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, con void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data) { - m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)m_volumes->volumes.size()); + m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)m_volumes.volumes.size()); // nothing to render, return if (preview_data.retraction.positions.empty()) @@ -3811,7 +3775,7 @@ void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data) GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba); if (volume != nullptr) { - m_volumes->volumes.emplace_back(volume); + m_volumes.volumes.emplace_back(volume); GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions); std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); @@ -3833,7 +3797,7 @@ void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data) void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data) { - m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)m_volumes->volumes.size()); + m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)m_volumes.volumes.size()); // nothing to render, return if (preview_data.unretraction.positions.empty()) @@ -3842,7 +3806,7 @@ void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data) GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba); if (volume != nullptr) { - m_volumes->volumes.emplace_back(volume); + m_volumes.volumes.emplace_back(volume); GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions); std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); @@ -3864,7 +3828,7 @@ void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data) void GLCanvas3D::_load_shells() { - size_t initial_volumes_count = m_volumes->volumes.size(); + size_t initial_volumes_count = m_volumes.volumes.size(); m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count); if (m_print->objects.empty()) @@ -3885,7 +3849,7 @@ void GLCanvas3D::_load_shells() for (ModelInstance* instance : model_obj->instances) { - m_volumes->load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs && m_initialized); + m_volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs && m_initialized); } ++object_id; @@ -3897,7 +3861,7 @@ void GLCanvas3D::_load_shells() unsigned int extruders_count = config.nozzle_diameter.size(); if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) { const float width_per_extruder = 15.0f; // a simple workaround after wipe_tower_per_color_wipe got obsolete - m_volumes->load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, m_use_VBOs && m_initialized); + m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, m_use_VBOs && m_initialized); } } @@ -3906,8 +3870,8 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size(); for (unsigned int i = 0; i < size; ++i) { - std::vector::iterator begin = m_volumes->volumes.begin() + m_gcode_preview_volume_index.first_volumes[i].id; - std::vector::iterator end = (i + 1 < size) ? m_volumes->volumes.begin() + m_gcode_preview_volume_index.first_volumes[i + 1].id : m_volumes->volumes.end(); + std::vector::iterator begin = m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i].id; + std::vector::iterator end = (i + 1 < size) ? m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i + 1].id : m_volumes.volumes.end(); for (std::vector::iterator it = begin; it != end; ++it) { @@ -3962,7 +3926,7 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe void GLCanvas3D::_on_move(const std::vector& volume_idxs) { - if ((m_model == nullptr) || (m_volumes == nullptr)) + if (m_model == nullptr) return; std::set done; // prevent moving instances twice @@ -3970,7 +3934,7 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) Pointf3 wipe_tower_origin(0.0, 0.0, 0.0); for (int volume_idx : volume_idxs) { - GLVolume* volume = m_volumes->volumes[volume_idx]; + GLVolume* volume = m_volumes.volumes[volume_idx]; int obj_idx = volume->object_idx(); int instance_idx = volume->instance_idx(); @@ -4005,14 +3969,14 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) void GLCanvas3D::_on_select(int volume_idx) { int id = -1; - if ((m_volumes != nullptr) && (volume_idx < (int)m_volumes->volumes.size())) + if (volume_idx < (int)m_volumes.volumes.size()) { if (volume_idx != -1) { if (m_select_by == "volume") - id = m_volumes->volumes[volume_idx]->volume_idx(); + id = m_volumes.volumes[volume_idx]->volume_idx(); else if (m_select_by == "object") - id = m_volumes->volumes[volume_idx]->object_idx(); + id = m_volumes.volumes[volume_idx]->object_idx(); } } m_on_select_object_callback.call(id); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 2bdf9e692f..30f4cb37f0 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -1,12 +1,8 @@ #ifndef slic3r_GLCanvas3D_hpp_ #define slic3r_GLCanvas3D_hpp_ -#include "../../libslic3r/BoundingBox.hpp" -#include "../../libslic3r/Utils.hpp" -#include "../../libslic3r/ExPolygon.hpp" +#include "../../slic3r/GUI/3DScene.hpp" -class wxGLCanvas; -class wxGLContext; class wxTimer; class wxSizeEvent; class wxIdleEvent; @@ -14,20 +10,11 @@ class wxKeyEvent; class wxMouseEvent; class wxTimerEvent; class wxPaintEvent; -class wxActivateEvent; namespace Slic3r { -class GLVolumeCollection; -class GLVolume; -class DynamicPrintConfig; class GLShader; class ExPolygon; -class Print; -class PrintObject; -class GCodePreviewData; -class ModelObject; -class Model; namespace GUI { @@ -365,7 +352,7 @@ private: Shader m_shader; Mouse m_mouse; - GLVolumeCollection* m_volumes; + mutable GLVolumeCollection m_volumes; DynamicPrintConfig* m_config; Print* m_print; Model* m_model; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 54bd54ba83..fe0b64d0e7 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -1,6 +1,7 @@ #include "GLCanvas3DManager.hpp" #include "../../slic3r/GUI/GUI.hpp" #include "../../slic3r/GUI/AppConfig.hpp" +#include "../../slic3r/GUI/GLCanvas3D.hpp" #include diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 435993acd9..9de44256aa 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -1,13 +1,29 @@ #ifndef slic3r_GLCanvas3DManager_hpp_ #define slic3r_GLCanvas3DManager_hpp_ -#include "../../slic3r/GUI/GLCanvas3D.hpp" +#include "../../libslic3r/BoundingBox.hpp" #include +#include + +class wxGLCanvas; +class wxGLContext; namespace Slic3r { + +class DynamicPrintConfig; +class Print; +class Model; +class ExPolygon; +typedef std::vector ExPolygons; +class ModelObject; +class PrintObject; +class GCodePreviewData; + namespace GUI { +class GLCanvas3D; + class GLCanvas3DManager { struct GLInfo From 44220530cbdfd9781b604c70c70aebd62ba1e366 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 11 Jun 2018 15:49:04 +0200 Subject: [PATCH 082/117] Use a single gl context created in c++ --- lib/Slic3r/GUI/3DScene.pm | 26 ++++++++++++------------- xs/src/slic3r/GUI/3DScene.cpp | 4 ++-- xs/src/slic3r/GUI/3DScene.hpp | 2 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 2 +- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 23 +++++++++++++++++++--- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 4 +++- xs/xsp/GUI_3DScene.xsp | 7 +++---- 7 files changed, 42 insertions(+), 26 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index e40beda26a..11ca0611d8 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -145,7 +145,7 @@ sub new { #============================================================================================================================== #============================================================================================================================== - Slic3r::GUI::_3DScene::add_canvas($self, $self->GetContext); + Slic3r::GUI::_3DScene::add_canvas($self); Slic3r::GUI::_3DScene::allow_multisample($self, $can_multisample); # my $context = $self->GetContext; # $self->SetCurrent($context); @@ -1078,19 +1078,17 @@ sub Destroy { # my @projected = gluUnProject_p($x, $y, $z, @mview, @proj, @viewport); # return Slic3r::Pointf3->new(@projected); #} -#============================================================================================================================== - -sub GetContext { - my ($self) = @_; - return $self->{context} ||= Wx::GLContext->new($self); -} - -sub SetCurrent { - my ($self, $context) = @_; - return $self->SUPER::SetCurrent($context); -} - -#============================================================================================================================== +# +#sub GetContext { +# my ($self) = @_; +# return $self->{context} ||= Wx::GLContext->new($self); +#} +# +#sub SetCurrent { +# my ($self, $context) = @_; +# return $self->SUPER::SetCurrent($context); +#} +# #sub UseVBOs { # my ($self) = @_; # diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 4f95f4ba6e..dc40118b50 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1773,10 +1773,10 @@ bool _3DScene::use_VBOs() return s_canvas_mgr.use_VBOs(); } -bool _3DScene::add_canvas(wxGLCanvas* canvas, wxGLContext* context) +bool _3DScene::add_canvas(wxGLCanvas* canvas) { std::cout << "_3DScene::add_canvas()" << std::endl; - return s_canvas_mgr.add(canvas, context); + return s_canvas_mgr.add(canvas); } bool _3DScene::remove_canvas(wxGLCanvas* canvas) diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 1f179b0091..9685be64da 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -550,7 +550,7 @@ public: static std::string get_gl_info(bool format_as_html, bool extensions); static bool use_VBOs(); - static bool add_canvas(wxGLCanvas* canvas, wxGLContext* context); + static bool add_canvas(wxGLCanvas* canvas); static bool remove_canvas(wxGLCanvas* canvas); static void remove_all_canvases(); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 0d234a7649..51e03a949c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1348,7 +1348,7 @@ void GLCanvas3D::update_volumes_selection(const std::vector& selections) for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) { - if (selections[obj_idx] == 1) + if ((selections[obj_idx] == 1) && (obj_idx < (unsigned int)m_objects_volumes_idxs.size())) { const std::vector& volume_idxs = m_objects_volumes_idxs[obj_idx]; for (int v : volume_idxs) diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index fe0b64d0e7..298c781434 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -124,18 +124,35 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten } GLCanvas3DManager::GLCanvas3DManager() - : m_gl_initialized(false) + : m_context(nullptr) + , m_gl_initialized(false) , m_use_legacy_opengl(false) , m_use_VBOs(false) { } -bool GLCanvas3DManager::add(wxGLCanvas* canvas, wxGLContext* context) +GLCanvas3DManager::~GLCanvas3DManager() { + if (m_context != nullptr) + delete m_context; +} + +bool GLCanvas3DManager::add(wxGLCanvas* canvas) +{ + if (canvas == nullptr) + return false; + if (_get_canvas(canvas) != m_canvases.end()) return false; - GLCanvas3D* canvas3D = new GLCanvas3D(canvas, context); + if (m_context == nullptr) + { + m_context = new wxGLContext(canvas); + if (m_context == nullptr) + return false; + } + + GLCanvas3D* canvas3D = new GLCanvas3D(canvas, m_context); if (canvas3D == nullptr) return false; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 9de44256aa..f5187d3d39 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -43,6 +43,7 @@ class GLCanvas3DManager typedef std::map CanvasesMap; + wxGLContext* m_context; CanvasesMap m_canvases; GLInfo m_gl_info; bool m_gl_initialized; @@ -51,8 +52,9 @@ class GLCanvas3DManager public: GLCanvas3DManager(); + ~GLCanvas3DManager(); - bool add(wxGLCanvas* canvas, wxGLContext* context); + bool add(wxGLCanvas* canvas); bool remove(wxGLCanvas* canvas); void remove_all(); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 8deb6419d2..91200eb2dd 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -168,13 +168,12 @@ use_VBOs() RETVAL = _3DScene::use_VBOs(); OUTPUT: RETVAL - + bool -add_canvas(canvas, context) +add_canvas(canvas) SV *canvas; - SV *context; CODE: - RETVAL = _3DScene::add_canvas((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (wxGLContext*)wxPli_sv_2_object(aTHX_ context, "Wx::GLContext")); + RETVAL = _3DScene::add_canvas((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); OUTPUT: RETVAL From af3d07bb056c3666973d17b7665c4600121661ec Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 12 Jun 2018 09:18:25 +0200 Subject: [PATCH 083/117] Attempt to workaround bug in wxWidgets IsShownOnScreen() method --- lib/Slic3r/GUI/Plater.pm | 5 +++++ xs/src/slic3r/GUI/3DScene.cpp | 4 ++-- xs/src/slic3r/GUI/3DScene.hpp | 2 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 16 +++++++++++----- xs/src/slic3r/GUI/GLCanvas3D.hpp | 6 +++++- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 7 ++++--- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 +- xs/xsp/GUI_3DScene.xsp | 11 +++++------ 8 files changed, 34 insertions(+), 19 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 85d9426fb4..18545db3e8 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -202,6 +202,7 @@ sub new { if ($Slic3r::GUI::have_OpenGL) { $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); #============================================================================================================================== + Slic3r::GUI::_3DScene::set_active($self->{preview3D}->canvas, 0); Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); }); # $self->{preview3D}->canvas->on_viewport_changed(sub { # $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); @@ -222,6 +223,8 @@ sub new { if ($preview == $self->{preview3D}) { #============================================================================================================================== + Slic3r::GUI::_3DScene::set_active($self->{preview3D}->canvas, 1); + Slic3r::GUI::_3DScene::set_active($self->{canvas3D}, 0); Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1); # $self->{preview3D}->canvas->set_legend_enabled(1); #============================================================================================================================== @@ -235,6 +238,8 @@ sub new { #============================================================================================================================== if ($preview == $self->{canvas3D}) { + Slic3r::GUI::_3DScene::set_active($self->{canvas3D}, 1); + Slic3r::GUI::_3DScene::set_active($self->{preview3D}->canvas, 0); if (Slic3r::GUI::_3DScene::is_reload_delayed($self->{canvas3D})) { my $selections = $self->collect_selections; Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index dc40118b50..046974ba93 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1797,9 +1797,9 @@ bool _3DScene::init(wxGLCanvas* canvas) return s_canvas_mgr.init(canvas); } -bool _3DScene::is_shown_on_screen(wxGLCanvas* canvas) +void _3DScene::set_active(wxGLCanvas* canvas, bool active) { - return s_canvas_mgr.is_shown_on_screen(canvas); + s_canvas_mgr.set_active(canvas, active); } unsigned int _3DScene::get_volumes_count(wxGLCanvas* canvas) diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 9685be64da..2d043953df 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -556,7 +556,7 @@ public: static bool init(wxGLCanvas* canvas); - static bool is_shown_on_screen(wxGLCanvas* canvas); + static void set_active(wxGLCanvas* canvas, bool active); static unsigned int get_volumes_count(wxGLCanvas* canvas); static void reset_volumes(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 51e03a949c..b032ce3530 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1189,6 +1189,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) , m_print(nullptr) , m_model(nullptr) , m_dirty(true) + , m_active(true) , m_initialized(false) , m_use_VBOs(false) , m_force_zoom_to_bed_enabled(false) @@ -1302,9 +1303,9 @@ bool GLCanvas3D::set_current() return false; } -bool GLCanvas3D::is_shown_on_screen() const +void GLCanvas3D::set_active(bool active) { - return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; + m_active = active; } unsigned int GLCanvas3D::get_volumes_count() const @@ -1605,7 +1606,7 @@ void GLCanvas3D::render() if (m_canvas == nullptr) return; - if (!is_shown_on_screen()) + if (!_is_shown_on_screen()) return; // ensures that the proper context is selected and that this canvas is initialized @@ -2722,6 +2723,11 @@ Point GLCanvas3D::get_local_mouse_position() const return Point(mouse_pos.x, mouse_pos.y); } +bool GLCanvas3D::_is_shown_on_screen() const +{ + return (m_canvas != nullptr) ? m_active && m_canvas->IsShownOnScreen() : false; +} + void GLCanvas3D::_force_zoom_to_bed() { zoom_to_bed(); @@ -2927,7 +2933,7 @@ void GLCanvas3D::_mark_volumes_for_layer_height() const void GLCanvas3D::_refresh_if_shown_on_screen() { - if (is_shown_on_screen()) + if (_is_shown_on_screen()) { const Size& cnv_size = get_canvas_size(); _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); @@ -2942,7 +2948,7 @@ void GLCanvas3D::_camera_tranform() const ::glLoadIdentity(); ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch - ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw + ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw Pointf3 neg_target = m_camera.target.negative(); ::glTranslatef((GLfloat)neg_target.x, (GLfloat)neg_target.y, (GLfloat)neg_target.z); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 30f4cb37f0..d694db4e29 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -358,6 +358,9 @@ private: Model* m_model; bool m_dirty; + // the active member has been introduced to overcome a bug in wxWidgets method IsShownOnScreen() which always return true + // when a window is inside a wxNotebook + bool m_active; bool m_initialized; bool m_use_VBOs; bool m_force_zoom_to_bed_enabled; @@ -404,7 +407,7 @@ public: bool set_current(); - bool is_shown_on_screen() const; + void set_active(bool active); unsigned int get_volumes_count() const; void reset_volumes(); @@ -517,6 +520,7 @@ public: Point get_local_mouse_position() const; private: + bool _is_shown_on_screen() const; void _force_zoom_to_bed(); void _resize(unsigned int w, unsigned int h); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 298c781434..b021e65a8e 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -238,10 +238,11 @@ bool GLCanvas3DManager::init(wxGLCanvas* canvas) return false; } -bool GLCanvas3DManager::is_shown_on_screen(wxGLCanvas* canvas) const +void GLCanvas3DManager::set_active(wxGLCanvas* canvas, bool active) { - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_shown_on_screen() : false; + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->set_active(active); } unsigned int GLCanvas3DManager::get_volumes_count(wxGLCanvas* canvas) const diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index f5187d3d39..741c8e29b6 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -69,7 +69,7 @@ public: bool init(wxGLCanvas* canvas); - bool is_shown_on_screen(wxGLCanvas* canvas) const; + void set_active(wxGLCanvas* canvas, bool active); unsigned int get_volumes_count(wxGLCanvas* canvas) const; void reset_volumes(wxGLCanvas* canvas); diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 91200eb2dd..c7f3670fcb 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -190,13 +190,12 @@ remove_all_canvases() CODE: _3DScene::remove_all_canvases(); -bool -is_shown_on_screen(canvas) - SV *canvas; +void +set_active(canvas, active) + SV *canvas; + bool active; CODE: - RETVAL = _3DScene::is_shown_on_screen((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas")); - OUTPUT: - RETVAL + _3DScene::set_active((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), active); unsigned int get_volumes_count(canvas) From 0faaef76e82f36c0db3d706156a36f5aa0b4fcd0 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 12 Jun 2018 12:18:16 +0200 Subject: [PATCH 084/117] C++ code cleanup --- lib/Slic3r/GUI/3DScene.pm | 3 +- xs/src/libslic3r/BoundingBox.cpp | 2 - xs/src/libslic3r/BoundingBox.hpp | 2 - xs/src/libslic3r/Print.hpp | 2 - xs/src/libslic3r/PrintObject.cpp | 4 - xs/src/libslic3r/Utils.hpp | 6 - xs/src/libslic3r/utils.cpp | 21 - xs/src/slic3r/GUI/3DScene.cpp | 1020 ------------------------------ xs/src/slic3r/GUI/3DScene.hpp | 91 --- xs/src/slic3r/GUI/GLShader.cpp | 7 - xs/src/slic3r/GUI/GLShader.hpp | 4 +- 11 files changed, 2 insertions(+), 1160 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 11ca0611d8..157e7229c9 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1255,8 +1255,7 @@ sub Destroy { # my ($self, $dc) = @_; # # # prevent calling SetCurrent() when window is not shown yet -# return unless Slic3r::GUI::_3DScene::is_shown_on_screen($self); -## return unless $self->IsShownOnScreen; +# return unless $self->IsShownOnScreen; # return unless my $context = $self->GetContext; # $self->SetCurrent($context); # $self->InitGL; diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp index e23accbde2..ceb968a50a 100644 --- a/xs/src/libslic3r/BoundingBox.cpp +++ b/xs/src/libslic3r/BoundingBox.cpp @@ -222,7 +222,6 @@ BoundingBox3Base::center() const } template Pointf3 BoundingBox3Base::center() const; -//######################################################################################################################################33 template coordf_t BoundingBox3Base::max_size() const { @@ -230,7 +229,6 @@ BoundingBox3Base::max_size() const return std::max(s.x, std::max(s.y, s.z)); } template coordf_t BoundingBox3Base::max_size() const; -//######################################################################################################################################33 // Align a coordinate to a grid. The coordinate may be negative, // the aligned value will never be bigger than the original one. diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index 7ce24f3a40..5de94aa9c7 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -94,9 +94,7 @@ public: void translate(const Pointf3 &pos) { this->translate(pos.x, pos.y, pos.z); } void offset(coordf_t delta); PointClass center() const; -//######################################################################################################################################33 coordf_t max_size() const; -//######################################################################################################################################33 bool contains(const PointClass &point) const { return BoundingBoxBase::contains(point) && point.z >= this->min.z && point.z <= this->max.z; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 8b6b3773f8..86c15b6799 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -184,9 +184,7 @@ public: void reset_layer_height_profile(); -//############################################################################################################################################ void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action); -//############################################################################################################################################ // Collect the slicing parameters, to be used by variable layer thickness algorithm, // by the interactive layer height editor and by the printing process itself. diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 9d0fe03fbd..ba0876a851 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -4,9 +4,7 @@ #include "Geometry.hpp" #include "SupportMaterial.hpp" #include "Surface.hpp" -//############################################################################################################################################ #include "Slicing.hpp" -//############################################################################################################################################ #include #include @@ -1964,7 +1962,6 @@ void PrintObject::reset_layer_height_profile() this->model_object()->layer_height_profile_valid = false; } -//############################################################################################################################################ void PrintObject::adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) { update_layer_height_profile(_model_object->layer_height_profile); @@ -1972,6 +1969,5 @@ void PrintObject::adjust_layer_height_profile(coordf_t z, coordf_t layer_thickne _model_object->layer_height_profile_valid = true; layer_height_profile_valid = false; } -//############################################################################################################################################ } // namespace Slic3r diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 472b0478f4..05eaf282f1 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -91,18 +91,12 @@ public: ~PerlCallback() { this->deregister_callback(); } void register_callback(void *sv); void deregister_callback(); -//############################################################################################################## void call() const; void call(int i) const; void call(int i, int j) const; void call(const std::vector& ints) const; void call(double x, double y) const; void call(bool b) const; -// void call(); -// void call(int i); -// void call(int i, int j); -//// void call(const std::vector &ints); -//############################################################################################################## private: void *m_callback; }; diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 7209d86f6b..83c45b1906 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -184,10 +184,7 @@ void PerlCallback::deregister_callback() } } -//############################################################################################################## void PerlCallback::call() const -//void PerlCallback::call() -//############################################################################################################## { if (! m_callback) return; @@ -201,10 +198,7 @@ void PerlCallback::call() const LEAVE; } -//############################################################################################################## void PerlCallback::call(int i) const -//void PerlCallback::call(int i) -//############################################################################################################## { if (! m_callback) return; @@ -219,10 +213,7 @@ void PerlCallback::call(int i) const LEAVE; } -//############################################################################################################## void PerlCallback::call(int i, int j) const -//void PerlCallback::call(int i, int j) -//############################################################################################################## { if (! m_callback) return; @@ -238,10 +229,7 @@ void PerlCallback::call(int i, int j) const LEAVE; } -//############################################################################################################## void PerlCallback::call(const std::vector& ints) const -//void PerlCallback::call(const std::vector &ints) -//############################################################################################################## { if (! m_callback) return; @@ -249,24 +237,16 @@ void PerlCallback::call(const std::vector& ints) const ENTER; SAVETMPS; PUSHMARK(SP); -//############################################################################################################## for (int i : ints) { XPUSHs(sv_2mortal(newSViv(i))); } - -// AV* av = newAV(); -// for (int i : ints) -// av_push(av, newSViv(i)); -// XPUSHs(av); -//############################################################################################################## PUTBACK; perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); FREETMPS; LEAVE; } -//############################################################################################################## void PerlCallback::call(double x, double y) const { if (!m_callback) @@ -287,7 +267,6 @@ void PerlCallback::call(bool b) const { call(b ? 1 : 0); } -//############################################################################################################## #ifdef WIN32 #ifndef NOMINMAX diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 046974ba93..9a11699a28 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1348,10 +1348,7 @@ static void point_to_indexed_vertex_array(const Point3& point, volume.push_triangle(idxs[0], idxs[3], idxs[4]); } -//################################################################################################################## void _3DScene::thick_lines_to_verts( -//static void thick_lines_to_verts( -//################################################################################################################## const Lines &lines, const std::vector &widths, const std::vector &heights, @@ -1362,10 +1359,7 @@ void _3DScene::thick_lines_to_verts( thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, top_z, volume.indexed_vertex_array); } -//################################################################################################################## void _3DScene::thick_lines_to_verts(const Lines3& lines, -//static void thick_lines_to_verts(const Lines3& lines, -//################################################################################################################## const std::vector& widths, const std::vector& heights, bool closed, @@ -1383,10 +1377,7 @@ static void thick_point_to_verts(const Point3& point, } // Fill in the qverts and tverts with quads and triangles for the extrusion_path. -//################################################################################################################## void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) -//static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) -//################################################################################################################## { Lines lines = extrusion_path.polyline.lines(); std::vector widths(lines.size(), extrusion_path.width); @@ -1395,10 +1386,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, flo } // Fill in the qverts and tverts with quads and triangles for the extrusion_path. -//################################################################################################################## void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) -//static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) -//################################################################################################################## { Polyline polyline = extrusion_path.polyline; polyline.remove_duplicate_points(); @@ -1410,10 +1398,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, flo } // Fill in the qverts and tverts with quads and triangles for the extrusion_loop. -//################################################################################################################## void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) -//static inline void extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) -//################################################################################################################## { Lines lines; std::vector widths; @@ -1431,10 +1416,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, flo } // Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path. -//################################################################################################################## void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) -//static inline void extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) -//################################################################################################################## { Lines lines; std::vector widths; @@ -1451,23 +1433,13 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_mult thick_lines_to_verts(lines, widths, heights, false, print_z, volume); } -//################################################################################################################## -//static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume); -//################################################################################################################## - -//################################################################################################################## void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) -//static inline void extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) -//################################################################################################################## { for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities) extrusionentity_to_verts(extrusion_entity, print_z, copy, volume); } -//################################################################################################################## void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) -//static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) -//################################################################################################################## { if (extrusion_entity != nullptr) { auto *extrusion_path = dynamic_cast(extrusion_entity); @@ -1494,10 +1466,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, } } -//################################################################################################################## void _3DScene::polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume) -//static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume) -//################################################################################################################## { Lines3 lines = polyline.lines(); std::vector widths(lines.size(), width); @@ -1505,22 +1474,14 @@ void _3DScene::polyline3_to_verts(const Polyline3& polyline, double width, doubl thick_lines_to_verts(lines, widths, heights, false, volume); } -//################################################################################################################## void _3DScene::point3_to_verts(const Point3& point, double width, double height, GLVolume& volume) -//static void point3_to_verts(const Point3& point, double width, double height, GLVolume& volume) -//################################################################################################################## { thick_point_to_verts(point, width, height, volume); } -//################################################################################################################## -//_3DScene::GCodePreviewVolumeIndex _3DScene::s_gcode_preview_volume_index; -//################################################################################################################## _3DScene::LegendTexture _3DScene::s_legend_texture; _3DScene::WarningTexture _3DScene::s_warning_texture; -//################################################################################################################## GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; -//################################################################################################################## unsigned int _3DScene::TextureBase::finalize() { @@ -1757,7 +1718,6 @@ bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, con return true; } -//################################################################################################################## void _3DScene::init_gl() { s_canvas_mgr.init_gl(); @@ -2077,12 +2037,6 @@ void _3DScene::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, vo s_canvas_mgr.register_on_enable_action_buttons_callback(canvas, callback); } -//void _3DScene::_glew_init() -//{ -// glewInit(); -//} -//################################################################################################################## - static inline int hex_digit_to_int(const char c) { return @@ -2110,7 +2064,6 @@ static inline std::vector parse_colors(const std::vector &sc return output; } -//################################################################################################################## std::vector _3DScene::load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs) { return s_canvas_mgr.load_object(canvas, model_object, obj_idx, instance_idxs); @@ -2146,47 +2099,10 @@ void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* pr s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors); } -//void _3DScene::load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector& str_tool_colors, bool use_VBOs) -//{ -// if ((preview_data == nullptr) || (volumes == nullptr)) -// return; -// -// if (volumes->empty()) -// { -// std::vector tool_colors = parse_colors(str_tool_colors); -// -// s_gcode_preview_volume_index.reset(); -// -// _load_gcode_extrusion_paths(*preview_data, *volumes, tool_colors, use_VBOs); -// _load_gcode_travel_paths(*preview_data, *volumes, tool_colors, use_VBOs); -// _load_gcode_retractions(*preview_data, *volumes, use_VBOs); -// _load_gcode_unretractions(*preview_data, *volumes, use_VBOs); -// -// if (volumes->empty()) -// reset_legend_texture(); -// else -// { -// _generate_legend_texture(*preview_data, tool_colors); -// -// // removes empty volumes -// volumes->volumes.erase(std::remove_if(volumes->volumes.begin(), volumes->volumes.end(), -// [](const GLVolume *volume) { return volume->print_zs.empty(); }), -// volumes->volumes.end()); -// -// _load_shells(*print, *volumes, use_VBOs); -// } -// } -// -// _update_gcode_volumes_visibility(*preview_data, *volumes); -//} -//################################################################################################################## - -//################################################################################################################## void _3DScene::generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors) { s_legend_texture.generate(preview_data, tool_colors); } -//################################################################################################################## unsigned int _3DScene::get_legend_texture_width() { @@ -2233,940 +2149,4 @@ unsigned int _3DScene::finalize_warning_texture() return s_warning_texture.finalize(); } -//################################################################################################################## -//// Create 3D thick extrusion lines for a skirt and brim. -//// Adds a new Slic3r::GUI::3DScene::Volume to volumes. -//void _3DScene::_load_print_toolpaths( -// const Print *print, -// GLVolumeCollection *volumes, -// const std::vector &tool_colors, -// bool use_VBOs) -//{ -// if (!print->has_skirt() && print->config.brim_width.value == 0) -// return; -// -// const float color[] = { 0.5f, 1.0f, 0.5f, 1.f }; // greenish -// -// // number of skirt layers -// size_t total_layer_count = 0; -// for (const PrintObject *print_object : print->objects) -// total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); -// size_t skirt_height = print->has_infinite_skirt() ? -// total_layer_count : -// std::min(print->config.skirt_height.value, total_layer_count); -// if (skirt_height == 0 && print->config.brim_width.value > 0) -// skirt_height = 1; -// -// // get first skirt_height layers (maybe this should be moved to a PrintObject method?) -// const PrintObject *object0 = print->objects.front(); -// std::vector print_zs; -// print_zs.reserve(skirt_height * 2); -// for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++ i) -// print_zs.push_back(float(object0->layers[i]->print_z)); -// //FIXME why there are support layers? -// for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++ i) -// print_zs.push_back(float(object0->support_layers[i]->print_z)); -// sort_remove_duplicates(print_zs); -// if (print_zs.size() > skirt_height) -// print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); -// -// volumes->volumes.emplace_back(new GLVolume(color)); -// GLVolume &volume = *volumes->volumes.back(); -// for (size_t i = 0; i < skirt_height; ++ i) { -// volume.print_zs.push_back(print_zs[i]); -// volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size()); -// volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size()); -// if (i == 0) -// extrusionentity_to_verts(print->brim, print_zs[i], Point(0, 0), volume); -// extrusionentity_to_verts(print->skirt, print_zs[i], Point(0, 0), volume); -// } -// volume.bounding_box = volume.indexed_vertex_array.bounding_box(); -// volume.indexed_vertex_array.finalize_geometry(use_VBOs); -//} -// -//// Create 3D thick extrusion lines for object forming extrusions. -//// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, -//// one for perimeters, one for infill and one for supports. -//void _3DScene::_load_print_object_toolpaths( -// const PrintObject *print_object, -// GLVolumeCollection *volumes, -// const std::vector &tool_colors_str, -// bool use_VBOs) -//{ -// std::vector tool_colors = parse_colors(tool_colors_str); -// -// struct Ctxt -// { -// const Points *shifted_copies; -// std::vector layers; -// bool has_perimeters; -// bool has_infill; -// bool has_support; -// const std::vector* tool_colors; -// -// // Number of vertices (each vertex is 6x4=24 bytes long) -// static const size_t alloc_size_max () { return 131072; } // 3.15MB -//// static const size_t alloc_size_max () { return 65536; } // 1.57MB -//// static const size_t alloc_size_max () { return 32768; } // 786kB -// static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } -// -// static const float* color_perimeters () { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow -// static const float* color_infill () { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish -// static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish -// -// // For cloring by a tool, return a parsed color. -// bool color_by_tool() const { return tool_colors != nullptr; } -// size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } -// const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } -// int volume_idx(int extruder, int feature) const -// { return this->color_by_tool() ? std::min(this->number_tools() - 1, std::max(extruder - 1, 0)) : feature; } -// } ctxt; -// -// ctxt.shifted_copies = &print_object->_shifted_copies; -// -// // order layers by print_z -// ctxt.layers.reserve(print_object->layers.size() + print_object->support_layers.size()); -// for (const Layer *layer : print_object->layers) -// ctxt.layers.push_back(layer); -// for (const Layer *layer : print_object->support_layers) -// ctxt.layers.push_back(layer); -// std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); -// -// // Maximum size of an allocation block: 32MB / sizeof(float) -// ctxt.has_perimeters = print_object->state.is_done(posPerimeters); -// ctxt.has_infill = print_object->state.is_done(posInfill); -// ctxt.has_support = print_object->state.is_done(posSupportMaterial); -// ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; -// -// BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; -// -// //FIXME Improve the heuristics for a grain size. -// size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); -// tbb::spin_mutex new_volume_mutex; -// auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { -// auto *volume = new GLVolume(color); -// new_volume_mutex.lock(); -// volume->outside_printer_detection_enabled = false; -// volumes->volumes.emplace_back(volume); -// new_volume_mutex.unlock(); -// return volume; -// }; -// const size_t volumes_cnt_initial = volumes->volumes.size(); -// std::vector volumes_per_thread(ctxt.layers.size()); -// tbb::parallel_for( -// tbb::blocked_range(0, ctxt.layers.size(), grain_size), -// [&ctxt, &new_volume](const tbb::blocked_range& range) { -// std::vector vols; -// if (ctxt.color_by_tool()) { -// for (size_t i = 0; i < ctxt.number_tools(); ++ i) -// vols.emplace_back(new_volume(ctxt.color_tool(i))); -// } else -// vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; -// for (GLVolume *vol : vols) -// vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); -// for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { -// const Layer *layer = ctxt.layers[idx_layer]; -// for (size_t i = 0; i < vols.size(); ++ i) { -// GLVolume &vol = *vols[i]; -// if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) { -// vol.print_zs.push_back(layer->print_z); -// vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); -// vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); -// } -// } -// for (const Point ©: *ctxt.shifted_copies) { -// for (const LayerRegion *layerm : layer->regions) { -// if (ctxt.has_perimeters) -// extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, -// *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]); -// if (ctxt.has_infill) { -// for (const ExtrusionEntity *ee : layerm->fills.entities) { -// // fill represents infill extrusions of a single island. -// const auto *fill = dynamic_cast(ee); -// if (! fill->entities.empty()) -// extrusionentity_to_verts(*fill, float(layer->print_z), copy, -// *vols[ctxt.volume_idx( -// is_solid_infill(fill->entities.front()->role()) ? -// layerm->region()->config.solid_infill_extruder : -// layerm->region()->config.infill_extruder, -// 1)]); -// } -// } -// } -// if (ctxt.has_support) { -// const SupportLayer *support_layer = dynamic_cast(layer); -// if (support_layer) { -// for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) -// extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, -// *vols[ctxt.volume_idx( -// (extrusion_entity->role() == erSupportMaterial) ? -// support_layer->object()->config.support_material_extruder : -// support_layer->object()->config.support_material_interface_extruder, -// 2)]); -// } -// } -// } -// for (size_t i = 0; i < vols.size(); ++ i) { -// GLVolume &vol = *vols[i]; -// if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { -// // Store the vertex arrays and restart their containers, -// vols[i] = new_volume(vol.color); -// GLVolume &vol_new = *vols[i]; -// // Assign the large pre-allocated buffers to the new GLVolume. -// vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); -// // Copy the content back to the old GLVolume. -// vol.indexed_vertex_array = vol_new.indexed_vertex_array; -// // Finalize a bounding box of the old GLVolume. -// vol.bounding_box = vol.indexed_vertex_array.bounding_box(); -// // Clear the buffers, but keep them pre-allocated. -// vol_new.indexed_vertex_array.clear(); -// // Just make sure that clear did not clear the reserved memory. -// vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); -// } -// } -// } -// for (GLVolume *vol : vols) { -// vol->bounding_box = vol->indexed_vertex_array.bounding_box(); -// vol->indexed_vertex_array.shrink_to_fit(); -// } -// }); -// -// BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results"; -// // Remove empty volumes from the newly added volumes. -// volumes->volumes.erase( -// std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(), -// [](const GLVolume *volume) { return volume->empty(); }), -// volumes->volumes.end()); -// for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i) -// volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs); -// -// BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end"; -//} -// -//void _3DScene::_load_wipe_tower_toolpaths( -// const Print *print, -// GLVolumeCollection *volumes, -// const std::vector &tool_colors_str, -// bool use_VBOs) -//{ -// if (print->m_wipe_tower_tool_changes.empty()) -// return; -// -// std::vector tool_colors = parse_colors(tool_colors_str); -// -// struct Ctxt -// { -// const Print *print; -// const std::vector *tool_colors; -// -// // Number of vertices (each vertex is 6x4=24 bytes long) -// static const size_t alloc_size_max () { return 131072; } // 3.15MB -// static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } -// -// static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish -// -// // For cloring by a tool, return a parsed color. -// bool color_by_tool() const { return tool_colors != nullptr; } -// size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } -// const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } -// int volume_idx(int tool, int feature) const -// { return this->color_by_tool() ? std::min(this->number_tools() - 1, std::max(tool, 0)) : feature; } -// -// const std::vector& tool_change(size_t idx) { -// return priming.empty() ? -// ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) : -// ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]); -// } -// std::vector priming; -// std::vector final; -// } ctxt; -// -// ctxt.print = print; -// ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; -// if (print->m_wipe_tower_priming) -// ctxt.priming.emplace_back(*print->m_wipe_tower_priming.get()); -// if (print->m_wipe_tower_final_purge) -// ctxt.final.emplace_back(*print->m_wipe_tower_final_purge.get()); -// -// BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start"; -// -// //FIXME Improve the heuristics for a grain size. -// size_t n_items = print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); -// size_t grain_size = std::max(n_items / 128, size_t(1)); -// tbb::spin_mutex new_volume_mutex; -// auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { -// auto *volume = new GLVolume(color); -// new_volume_mutex.lock(); -// volume->outside_printer_detection_enabled = false; -// volumes->volumes.emplace_back(volume); -// new_volume_mutex.unlock(); -// return volume; -// }; -// const size_t volumes_cnt_initial = volumes->volumes.size(); -// std::vector volumes_per_thread(n_items); -// tbb::parallel_for( -// tbb::blocked_range(0, n_items, grain_size), -// [&ctxt, &new_volume](const tbb::blocked_range& range) { -// // Bounding box of this slab of a wipe tower. -// std::vector vols; -// if (ctxt.color_by_tool()) { -// for (size_t i = 0; i < ctxt.number_tools(); ++ i) -// vols.emplace_back(new_volume(ctxt.color_tool(i))); -// } else -// vols = { new_volume(ctxt.color_support()) }; -// for (GLVolume *volume : vols) -// volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); -// for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { -// const std::vector &layer = ctxt.tool_change(idx_layer); -// for (size_t i = 0; i < vols.size(); ++ i) { -// GLVolume &vol = *vols[i]; -// if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { -// vol.print_zs.push_back(layer.front().print_z); -// vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); -// vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); -// } -// } -// for (const WipeTower::ToolChangeResult &extrusions : layer) { -// for (size_t i = 1; i < extrusions.extrusions.size();) { -// const WipeTower::Extrusion &e = extrusions.extrusions[i]; -// if (e.width == 0.) { -// ++ i; -// continue; -// } -// size_t j = i + 1; -// if (ctxt.color_by_tool()) -// for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++ j) ; -// else -// for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++ j) ; -// size_t n_lines = j - i; -// Lines lines; -// std::vector widths; -// std::vector heights; -// lines.reserve(n_lines); -// widths.reserve(n_lines); -// heights.assign(n_lines, extrusions.layer_height); -// for (; i < j; ++ i) { -// const WipeTower::Extrusion &e = extrusions.extrusions[i]; -// assert(e.width > 0.f); -// const WipeTower::Extrusion &e_prev = *(&e - 1); -// lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y)); -// widths.emplace_back(e.width); -// } -// thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, -// *vols[ctxt.volume_idx(e.tool, 0)]); -// } -// } -// } -// for (size_t i = 0; i < vols.size(); ++ i) { -// GLVolume &vol = *vols[i]; -// if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { -// // Store the vertex arrays and restart their containers, -// vols[i] = new_volume(vol.color); -// GLVolume &vol_new = *vols[i]; -// // Assign the large pre-allocated buffers to the new GLVolume. -// vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); -// // Copy the content back to the old GLVolume. -// vol.indexed_vertex_array = vol_new.indexed_vertex_array; -// // Finalize a bounding box of the old GLVolume. -// vol.bounding_box = vol.indexed_vertex_array.bounding_box(); -// // Clear the buffers, but keep them pre-allocated. -// vol_new.indexed_vertex_array.clear(); -// // Just make sure that clear did not clear the reserved memory. -// vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); -// } -// } -// for (GLVolume *vol : vols) { -// vol->bounding_box = vol->indexed_vertex_array.bounding_box(); -// vol->indexed_vertex_array.shrink_to_fit(); -// } -// }); -// -// BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results"; -// // Remove empty volumes from the newly added volumes. -// volumes->volumes.erase( -// std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(), -// [](const GLVolume *volume) { return volume->empty(); }), -// volumes->volumes.end()); -// for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i) -// volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs); -// -// BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; -//} -// -//void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs) -//{ -// // helper functions to select data in dependence of the extrusion view type -// struct Helper -// { -// static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path) -// { -// switch (type) -// { -// case GCodePreviewData::Extrusion::FeatureType: -// return (float)path.role(); -// case GCodePreviewData::Extrusion::Height: -// return path.height; -// case GCodePreviewData::Extrusion::Width: -// return path.width; -// case GCodePreviewData::Extrusion::Feedrate: -// return path.feedrate; -// case GCodePreviewData::Extrusion::VolumetricRate: -// return path.feedrate * (float)path.mm3_per_mm; -// case GCodePreviewData::Extrusion::Tool: -// return (float)path.extruder_id; -// } -// -// return 0.0f; -// } -// -// static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector& tool_colors, float value) -// { -// switch (data.extrusion.view_type) -// { -// case GCodePreviewData::Extrusion::FeatureType: -// return data.get_extrusion_role_color((ExtrusionRole)(int)value); -// case GCodePreviewData::Extrusion::Height: -// return data.get_height_color(value); -// case GCodePreviewData::Extrusion::Width: -// return data.get_width_color(value); -// case GCodePreviewData::Extrusion::Feedrate: -// return data.get_feedrate_color(value); -// case GCodePreviewData::Extrusion::VolumetricRate: -// return data.get_volumetric_rate_color(value); -// case GCodePreviewData::Extrusion::Tool: -// { -// GCodePreviewData::Color color; -// ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float)); -// return color; -// } -// } -// -// return GCodePreviewData::Color::Dummy; -// } -// }; -// -// // Helper structure for filters -// struct Filter -// { -// float value; -// ExtrusionRole role; -// GLVolume* volume; -// -// Filter(float value, ExtrusionRole role) -// : value(value) -// , role(role) -// , volume(nullptr) -// { -// } -// -// bool operator == (const Filter& other) const -// { -// if (value != other.value) -// return false; -// -// if (role != other.role) -// return false; -// -// return true; -// } -// }; -// -// typedef std::vector FiltersList; -// size_t initial_volumes_count = volumes.volumes.size(); -// -// // detects filters -// FiltersList filters; -// for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) -// { -// for (const ExtrusionPath& path : layer.paths) -// { -// ExtrusionRole role = path.role(); -// float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); -// if (std::find(filters.begin(), filters.end(), Filter(path_filter, role)) == filters.end()) -// filters.emplace_back(path_filter, role); -// } -// } -// -// // nothing to render, return -// if (filters.empty()) -// return; -// -// // creates a new volume for each filter -// for (Filter& filter : filters) -// { -// s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)volumes.volumes.size()); -// GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba); -// if (volume != nullptr) -// { -// filter.volume = volume; -// volumes.volumes.emplace_back(volume); -// } -// else -// { -// // an error occourred - restore to previous state and return -// s_gcode_preview_volume_index.first_volumes.pop_back(); -// if (initial_volumes_count != volumes.volumes.size()) -// { -// std::vector::iterator begin = volumes.volumes.begin() + initial_volumes_count; -// std::vector::iterator end = volumes.volumes.end(); -// for (std::vector::iterator it = begin; it < end; ++it) -// { -// GLVolume* volume = *it; -// delete volume; -// } -// volumes.volumes.erase(begin, end); -// return; -// } -// } -// } -// -// // populates volumes -// for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) -// { -// for (const ExtrusionPath& path : layer.paths) -// { -// float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); -// FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role())); -// if (filter != filters.end()) -// { -// filter->volume->print_zs.push_back(layer.z); -// filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.quad_indices.size()); -// filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.triangle_indices.size()); -// -// extrusionentity_to_verts(path, layer.z, *filter->volume); -// } -// } -// } -// -// // finalize volumes and sends geometry to gpu -// if (volumes.volumes.size() > initial_volumes_count) -// { -// for (size_t i = initial_volumes_count; i < volumes.volumes.size(); ++i) -// { -// GLVolume* volume = volumes.volumes[i]; -// volume->bounding_box = volume->indexed_vertex_array.bounding_box(); -// volume->indexed_vertex_array.finalize_geometry(use_VBOs); -// } -// } -//} -// -//void _3DScene::_load_gcode_travel_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs) -//{ -// size_t initial_volumes_count = volumes.volumes.size(); -// s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count); -// -// bool res = true; -// switch (preview_data.extrusion.view_type) -// { -// case GCodePreviewData::Extrusion::Feedrate: -// { -// res = _travel_paths_by_feedrate(preview_data, volumes); -// break; -// } -// case GCodePreviewData::Extrusion::Tool: -// { -// res = _travel_paths_by_tool(preview_data, volumes, tool_colors); -// break; -// } -// default: -// { -// res = _travel_paths_by_type(preview_data, volumes); -// break; -// } -// } -// -// if (!res) -// { -// // an error occourred - restore to previous state and return -// if (initial_volumes_count != volumes.volumes.size()) -// { -// std::vector::iterator begin = volumes.volumes.begin() + initial_volumes_count; -// std::vector::iterator end = volumes.volumes.end(); -// for (std::vector::iterator it = begin; it < end; ++it) -// { -// GLVolume* volume = *it; -// delete volume; -// } -// volumes.volumes.erase(begin, end); -// } -// -// return; -// } -// -// // finalize volumes and sends geometry to gpu -// if (volumes.volumes.size() > initial_volumes_count) -// { -// for (size_t i = initial_volumes_count; i < volumes.volumes.size(); ++i) -// { -// GLVolume* volume = volumes.volumes[i]; -// volume->bounding_box = volume->indexed_vertex_array.bounding_box(); -// volume->indexed_vertex_array.finalize_geometry(use_VBOs); -// } -// } -//} -// -//bool _3DScene::_travel_paths_by_type(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) -//{ -// // Helper structure for types -// struct Type -// { -// GCodePreviewData::Travel::EType value; -// GLVolume* volume; -// -// explicit Type(GCodePreviewData::Travel::EType value) -// : value(value) -// , volume(nullptr) -// { -// } -// -// bool operator == (const Type& other) const -// { -// return value == other.value; -// } -// }; -// -// typedef std::vector TypesList; -// -// // colors travels by travel type -// -// // detects types -// TypesList types; -// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) -// { -// if (std::find(types.begin(), types.end(), Type(polyline.type)) == types.end()) -// types.emplace_back(polyline.type); -// } -// -// // nothing to render, return -// if (types.empty()) -// return true; -// -// // creates a new volume for each type -// for (Type& type : types) -// { -// GLVolume* volume = new GLVolume(preview_data.travel.type_colors[type.value].rgba); -// if (volume == nullptr) -// return false; -// else -// { -// type.volume = volume; -// volumes.volumes.emplace_back(volume); -// } -// } -// -// // populates volumes -// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) -// { -// TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type)); -// if (type != types.end()) -// { -// type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); -// type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size()); -// type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size()); -// -// polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *type->volume); -// } -// } -// -// return true; -//} -// -//bool _3DScene::_travel_paths_by_feedrate(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) -//{ -// // Helper structure for feedrate -// struct Feedrate -// { -// float value; -// GLVolume* volume; -// -// explicit Feedrate(float value) -// : value(value) -// , volume(nullptr) -// { -// } -// -// bool operator == (const Feedrate& other) const -// { -// return value == other.value; -// } -// }; -// -// typedef std::vector FeedratesList; -// -// // colors travels by feedrate -// -// // detects feedrates -// FeedratesList feedrates; -// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) -// { -// if (std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)) == feedrates.end()) -// feedrates.emplace_back(polyline.feedrate); -// } -// -// // nothing to render, return -// if (feedrates.empty()) -// return true; -// -// // creates a new volume for each feedrate -// for (Feedrate& feedrate : feedrates) -// { -// GLVolume* volume = new GLVolume(preview_data.get_feedrate_color(feedrate.value).rgba); -// if (volume == nullptr) -// return false; -// else -// { -// feedrate.volume = volume; -// volumes.volumes.emplace_back(volume); -// } -// } -// -// // populates volumes -// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) -// { -// FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)); -// if (feedrate != feedrates.end()) -// { -// feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); -// feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size()); -// feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size()); -// -// polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *feedrate->volume); -// } -// } -// -// return true; -//} -// -//bool _3DScene::_travel_paths_by_tool(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors) -//{ -// // Helper structure for tool -// struct Tool -// { -// unsigned int value; -// GLVolume* volume; -// -// explicit Tool(unsigned int value) -// : value(value) -// , volume(nullptr) -// { -// } -// -// bool operator == (const Tool& other) const -// { -// return value == other.value; -// } -// }; -// -// typedef std::vector ToolsList; -// -// // colors travels by tool -// -// // detects tools -// ToolsList tools; -// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) -// { -// if (std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)) == tools.end()) -// tools.emplace_back(polyline.extruder_id); -// } -// -// // nothing to render, return -// if (tools.empty()) -// return true; -// -// // creates a new volume for each tool -// for (Tool& tool : tools) -// { -// GLVolume* volume = new GLVolume(tool_colors.data() + tool.value * 4); -// if (volume == nullptr) -// return false; -// else -// { -// tool.volume = volume; -// volumes.volumes.emplace_back(volume); -// } -// } -// -// // populates volumes -// for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) -// { -// ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)); -// if (tool != tools.end()) -// { -// tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z)); -// tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size()); -// tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size()); -// -// polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume); -// } -// } -// -// return true; -//} -// -//void _3DScene::_load_gcode_retractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs) -//{ -// s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)volumes.volumes.size()); -// -// // nothing to render, return -// if (preview_data.retraction.positions.empty()) -// return; -// -// GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba); -// if (volume != nullptr) -// { -// volumes.volumes.emplace_back(volume); -// -// GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions); -// std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); -// -// for (const GCodePreviewData::Retraction::Position& position : copy) -// { -// volume->print_zs.push_back(unscale(position.position.z)); -// volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); -// volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); -// -// point3_to_verts(position.position, position.width, position.height, *volume); -// } -// -// // finalize volumes and sends geometry to gpu -// volume->bounding_box = volume->indexed_vertex_array.bounding_box(); -// volume->indexed_vertex_array.finalize_geometry(use_VBOs); -// } -//} -// -//void _3DScene::_load_gcode_unretractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs) -//{ -// s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)volumes.volumes.size()); -// -// // nothing to render, return -// if (preview_data.unretraction.positions.empty()) -// return; -// -// GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba); -// if (volume != nullptr) -// { -// volumes.volumes.emplace_back(volume); -// -// GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions); -// std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; }); -// -// for (const GCodePreviewData::Retraction::Position& position : copy) -// { -// volume->print_zs.push_back(unscale(position.position.z)); -// volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); -// volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); -// -// point3_to_verts(position.position, position.width, position.height, *volume); -// } -// -// // finalize volumes and sends geometry to gpu -// volume->bounding_box = volume->indexed_vertex_array.bounding_box(); -// volume->indexed_vertex_array.finalize_geometry(use_VBOs); -// } -//} -// -//void _3DScene::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) -//{ -// unsigned int size = (unsigned int)s_gcode_preview_volume_index.first_volumes.size(); -// for (unsigned int i = 0; i < size; ++i) -// { -// std::vector::iterator begin = volumes.volumes.begin() + s_gcode_preview_volume_index.first_volumes[i].id; -// std::vector::iterator end = (i + 1 < size) ? volumes.volumes.begin() + s_gcode_preview_volume_index.first_volumes[i + 1].id : volumes.volumes.end(); -// -// for (std::vector::iterator it = begin; it != end; ++it) -// { -// GLVolume* volume = *it; -// volume->outside_printer_detection_enabled = false; -// -// switch (s_gcode_preview_volume_index.first_volumes[i].type) -// { -// case GCodePreviewVolumeIndex::Extrusion: -// { -// if ((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag == erCustom) -// volume->zoom_to_volumes = false; -// -// volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag); -// break; -// } -// case GCodePreviewVolumeIndex::Travel: -// { -// volume->is_active = preview_data.travel.is_visible; -// volume->zoom_to_volumes = false; -// break; -// } -// case GCodePreviewVolumeIndex::Retraction: -// { -// volume->is_active = preview_data.retraction.is_visible; -// volume->zoom_to_volumes = false; -// break; -// } -// case GCodePreviewVolumeIndex::Unretraction: -// { -// volume->is_active = preview_data.unretraction.is_visible; -// volume->zoom_to_volumes = false; -// break; -// } -// case GCodePreviewVolumeIndex::Shell: -// { -// volume->is_active = preview_data.shell.is_visible; -// volume->color[3] = 0.25f; -// volume->zoom_to_volumes = false; -// break; -// } -// default: -// { -// volume->is_active = false; -// volume->zoom_to_volumes = false; -// break; -// } -// } -// } -// } -//} -// -//void _3DScene::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors) -//{ -// s_legend_texture.generate(preview_data, tool_colors); -//} -// -//void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs) -//{ -// size_t initial_volumes_count = volumes.volumes.size(); -// s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count); -// -// if (print.objects.empty()) -// // nothing to render, return -// return; -// -// // adds objects' volumes -// unsigned int object_id = 0; -// for (PrintObject* obj : print.objects) -// { -// ModelObject* model_obj = obj->model_object(); -// -// std::vector instance_ids(model_obj->instances.size()); -// for (int i = 0; i < model_obj->instances.size(); ++i) -// { -// instance_ids[i] = i; -// } -// -// for (ModelInstance* instance : model_obj->instances) -// { -// volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", use_VBOs); -// } -// -// ++object_id; -// } -// -// // adds wipe tower's volume -// coordf_t max_z = print.objects[0]->model_object()->get_model()->bounding_box().max.z; -// const PrintConfig& config = print.config; -// unsigned int extruders_count = config.nozzle_diameter.size(); -// if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) { -// const float width_per_extruder = 15.f; // a simple workaround after wipe_tower_per_color_wipe got obsolete -// volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, use_VBOs); -// } -//} -//################################################################################################################## - } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 2d043953df..138b220fc9 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -6,14 +6,10 @@ #include "../../libslic3r/Line.hpp" #include "../../libslic3r/TriangleMesh.hpp" #include "../../libslic3r/Utils.hpp" -//################################################################################################################## #include "../../slic3r/GUI/GLCanvas3DManager.hpp" -//################################################################################################################## class wxBitmap; -//################################################################################################################## class wxWindow; -//################################################################################################################## namespace Slic3r { @@ -23,13 +19,11 @@ class Model; class ModelObject; class GCodePreviewData; class DynamicPrintConfig; -//################################################################################################################## class ExtrusionPath; class ExtrusionMultiPath; class ExtrusionLoop; class ExtrusionEntity; class ExtrusionEntityCollection; -//################################################################################################################## // A container for interleaved arrays of 3D vertices and normals, // possibly indexed by triangles and / or quads. @@ -450,37 +444,6 @@ private: class _3DScene { -//################################################################################################################## -// struct GCodePreviewVolumeIndex -// { -// enum EType -// { -// Extrusion, -// Travel, -// Retraction, -// Unretraction, -// Shell, -// Num_Geometry_Types -// }; -// -// struct FirstVolume -// { -// EType type; -// unsigned int flag; -// // Index of the first volume in a GLVolumeCollection. -// unsigned int id; -// -// FirstVolume(EType type, unsigned int flag, unsigned int id) : type(type), flag(flag), id(id) {} -// }; -// -// std::vector first_volumes; -// -// void reset() { first_volumes.clear(); } -// }; -// -// static GCodePreviewVolumeIndex s_gcode_preview_volume_index; -//################################################################################################################## - class TextureBase { protected: @@ -540,12 +503,9 @@ class _3DScene static LegendTexture s_legend_texture; static WarningTexture s_warning_texture; -//################################################################################################################## static GUI::GLCanvas3DManager s_canvas_mgr; -//################################################################################################################## public: -//################################################################################################################## static void init_gl(); static std::string get_gl_info(bool format_as_html, bool extensions); static bool use_VBOs(); @@ -629,10 +589,6 @@ public: static void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); -// static void _glew_init(); -//################################################################################################################## - -//################################################################################################################## static std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); static std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); @@ -642,13 +598,9 @@ public: static void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector& str_tool_colors); static void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector& str_tool_colors); static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector& str_tool_colors); -// static void load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector& str_tool_colors, bool use_VBOs); -//################################################################################################################## -//################################################################################################################## // generates the legend texture in dependence of the current shown view type static void generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors); -//################################################################################################################## static unsigned int get_legend_texture_width(); static unsigned int get_legend_texture_height(); @@ -663,27 +615,6 @@ public: static void reset_warning_texture(); static unsigned int finalize_warning_texture(); -//################################################################################################################## -// static void _load_print_toolpaths( -// const Print *print, -// GLVolumeCollection *volumes, -// const std::vector &tool_colors, -// bool use_VBOs); -// -// static void _load_print_object_toolpaths( -// const PrintObject *print_object, -// GLVolumeCollection *volumes, -// const std::vector &tool_colors, -// bool use_VBOs); -// -// static void _load_wipe_tower_toolpaths( -// const Print *print, -// GLVolumeCollection *volumes, -// const std::vector &tool_colors_str, -// bool use_VBOs); -//################################################################################################################## - -//################################################################################################################## static void thick_lines_to_verts(const Lines& lines, const std::vector& widths, const std::vector& heights, bool closed, double top_z, GLVolume& volume); static void thick_lines_to_verts(const Lines3& lines, const std::vector& widths, const std::vector& heights, bool closed, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); @@ -694,28 +625,6 @@ public: static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume); static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume); static void point3_to_verts(const Point3& point, double width, double height, GLVolume& volume); -//################################################################################################################## - -private: -//################################################################################################################## -// // generates gcode extrusion paths geometry -// static void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs); -// // generates gcode travel paths geometry -// static void _load_gcode_travel_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors, bool use_VBOs); -// static bool _travel_paths_by_type(const GCodePreviewData& preview_data, GLVolumeCollection& volumes); -// static bool _travel_paths_by_feedrate(const GCodePreviewData& preview_data, GLVolumeCollection& volumes); -// static bool _travel_paths_by_tool(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector& tool_colors); -// // generates gcode retractions geometry -// static void _load_gcode_retractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs); -// // generates gcode unretractions geometry -// static void _load_gcode_unretractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs); -// // sets gcode geometry visibility according to user selection -// static void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data, GLVolumeCollection& volumes); -// // generates the legend texture in dependence of the current shown view type -// static void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector& tool_colors); -// // generates objects and wipe tower geometry -// static void _load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs); -//################################################################################################################## }; } diff --git a/xs/src/slic3r/GUI/GLShader.cpp b/xs/src/slic3r/GUI/GLShader.cpp index a888110d61..903f6c347b 100644 --- a/xs/src/slic3r/GUI/GLShader.cpp +++ b/xs/src/slic3r/GUI/GLShader.cpp @@ -2,10 +2,8 @@ #include "GLShader.hpp" -//############################################################################################################################################ #include "../../libslic3r/Utils.hpp" #include -//############################################################################################################################################ #include #include @@ -27,10 +25,7 @@ inline std::string gl_get_string_safe(GLenum param) return std::string(value ? value : "N/A"); } -//############################################################################################################################################ bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_shader) -//bool GLShader::load(const char *fragment_shader, const char *vertex_shader) -//############################################################################################################################################ { std::string gl_version = gl_get_string_safe(GL_VERSION); int major = atoi(gl_version.c_str()); @@ -131,7 +126,6 @@ bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_sh return true; } -//############################################################################################################################################ bool GLShader::load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename) { const std::string& path = resources_dir() + "/shaders/"; @@ -166,7 +160,6 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char* return load_from_text(fragment_shader.c_str(), vertex_shader.c_str()); } -//############################################################################################################################################ void GLShader::release() { diff --git a/xs/src/slic3r/GUI/GLShader.hpp b/xs/src/slic3r/GUI/GLShader.hpp index 7d52d879df..032640d8d1 100644 --- a/xs/src/slic3r/GUI/GLShader.hpp +++ b/xs/src/slic3r/GUI/GLShader.hpp @@ -16,11 +16,9 @@ public: {} ~GLShader(); -//############################################################################################################################################ bool load_from_text(const char *fragment_shader, const char *vertex_shader); bool load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename); -// bool load(const char *fragment_shader, const char *vertex_shader); -//############################################################################################################################################ + void release(); int get_attrib_location(const char *name) const; From b2cf576bf396aed8743d267a356d06c7f966462c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 13 Jun 2018 09:12:16 +0200 Subject: [PATCH 085/117] 1st installment of gizmos --- lib/Slic3r/GUI/Plater.pm | 1 + resources/icons/overlay/rotate_hover.png | Bin 0 -> 3808 bytes resources/icons/overlay/rotate_off.png | Bin 0 -> 4514 bytes resources/icons/overlay/rotate_on.png | Bin 0 -> 3441 bytes resources/icons/overlay/scale_hover.png | Bin 0 -> 6474 bytes resources/icons/overlay/scale_off.png | Bin 0 -> 7232 bytes resources/icons/overlay/scale_on.png | Bin 0 -> 5293 bytes xs/CMakeLists.txt | 8 +- xs/src/slic3r/GUI/3DScene.cpp | 11 +- xs/src/slic3r/GUI/3DScene.hpp | 3 + xs/src/slic3r/GUI/GLCanvas3D.cpp | 448 ++++++++++++++++------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 126 +++++-- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 21 +- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 3 + xs/src/slic3r/GUI/GLGizmo.cpp | 109 ++++++ xs/src/slic3r/GUI/GLGizmo.hpp | 78 ++++ xs/src/slic3r/GUI/GLTexture.cpp | 138 +++++++ xs/src/slic3r/GUI/GLTexture.hpp | 36 ++ xs/xsp/GUI_3DScene.xsp | 7 + 19 files changed, 812 insertions(+), 177 deletions(-) create mode 100644 resources/icons/overlay/rotate_hover.png create mode 100644 resources/icons/overlay/rotate_off.png create mode 100644 resources/icons/overlay/rotate_on.png create mode 100644 resources/icons/overlay/scale_hover.png create mode 100644 resources/icons/overlay/scale_off.png create mode 100644 resources/icons/overlay/scale_on.png create mode 100644 xs/src/slic3r/GUI/GLGizmo.cpp create mode 100644 xs/src/slic3r/GUI/GLGizmo.hpp create mode 100644 xs/src/slic3r/GUI/GLTexture.cpp create mode 100644 xs/src/slic3r/GUI/GLTexture.hpp diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 18545db3e8..66d2f2f7bf 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -132,6 +132,7 @@ sub new { Slic3r::GUI::_3DScene::register_on_remove_object_callback($self->{canvas3D}, sub { $self->remove() }); Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved); Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); + Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); diff --git a/resources/icons/overlay/rotate_hover.png b/resources/icons/overlay/rotate_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..56d4fd27754854df64a57aabf1268686b755ab8c GIT binary patch literal 3808 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 wZ<&*^uXsgO1L;I{TmS$7 literal 0 HcmV?d00001 diff --git a/resources/icons/overlay/rotate_on.png b/resources/icons/overlay/rotate_on.png new file mode 100644 index 0000000000000000000000000000000000000000..e2db5120c1c63416f73edbe3461243cd20ac8a46 GIT binary patch literal 3441 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 literal 0 HcmV?d00001 diff --git a/resources/icons/overlay/scale_off.png b/resources/icons/overlay/scale_off.png new file mode 100644 index 0000000000000000000000000000000000000000..1ae999bbe8c609cab1edc0033f8a25f49bb1562e GIT binary patch literal 7232 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 H4}<>)4z|#; literal 0 HcmV?d00001 diff --git a/resources/icons/overlay/scale_on.png b/resources/icons/overlay/scale_on.png new file mode 100644 index 0000000000000000000000000000000000000000..62e805f12df414b6e45ee066d068fd7b1550743a GIT binary patch literal 5293 zcmV;e6jJMnP)3`{VGTPZBq0#U4w)=7bIyDJcxK432S_-d&wP?OGjq;) zp7&XP&+mC&MGGC;3(9xS^Sy!VHLAacYZSVNf(}3u5Fh@UA2nOs=o#ykM~*Fd07^xkirT(IjvIJO@~ z-UD5(gNBn(unE#LVZvjmBfH__XX2?Yizi4Y6P>AI7^l6gbx=Ojr*qxe4goK?xO9uK zE=6!o-M}F*!^kuZeJ5H^!Da}WSZp9E7N-3#bk2f3t6=f3Aru6c8)B2>|Ej~X%2o^< z+#o_9SSRoEH-a66zPCW1o1yanSoK%={m6S!6^ByT@#!FHt9l!T9_-BIU$){smyd$8 zpRWhRfxil_zYC`P5*q#R+EmC}6N_e)GU#ub z4AA$fchr2nEFySwcM%eR1N;kB3cw$`)I`Y94q;z-Ygx@uWPCj@o?3>O#{ta=yL zzbisnfV&xYw$3@M&m#Zg!Fyy1}Qc=?0jpw@Oi&jRKIEmn0kakA=XesKQ$Hx#2_1XGEe}COF|2tDju(kymLC`b*S5bd!j=|AAkknq%YDmlXOddyZLVI~V5*RNS1I{p$Nx+jhVINHH5canW zP$mR4=s*jA>8QgAzjV~g0ODdHE*WBzA*mfCro-VpIGzudEvfgF$&k=SGRAj)35o6C zvsd7F{*yM80KWJI_F1lQP74Tm)dcP6$CRn!Nlp<3EPMn8-VS?LLuFAIcD11JHjv&4 zGWtWC&XAA}iESm=rF8*M0=PV2#(+Cc(Bje;VRsJD2&D(WRS4INgz>Y%lL+O7u;#5e z^JMjG$ICIp$0z5tfQV+amrU*Gdz8yHfd03_w|)VYC9vpG_~LZ|H@X#s?{x!=dlF>?0Pmmxn_Zl+qn$2<0t7!#Lr`e*(ss40cC&SC|To4ez|op5?M z*BXxRhrj(qJ}=!XimfVv%Hl~lIuCd$x^S|hS3l%cLqJc#A(h%%IG7_Qf1*@Y-c)jv zmOc!icsu00D@9Pl38+6I_pCY$o+P;a@zXW$@<3dwxMH+LHr7MVVu>YpJq=m6!J%AO z`9`WP+%q}gRV!kAIyWM-6}|cfdb)-oDx-&(S6wyCe*m0d>!vyb$ln6hzmh=Pa1!ck zpgtf0@Q!E1r$L^{odBiXO#ZvQu`V9yW>JsrqBC3B>D z(q4KghcS2(Jpm+yZ#3{`f?Nz-2YNlHlQNI5J9J;HdcBnR8WhFZFhOUFI z4!VmjmQ>g|``j!=5J0d2{{0fHdJ}5H3h*S!OP>ehE_)ST9uHssRpd#}07GSh{sBPp z+aVDxG*el?I4n{rIS6aBVf9<^<^Kt=xLOK`hPu7J0e3tr_#+g6?Mp?F6UUn3P;3|* z=nGUjmK&zN2rul0sV_icdMY;G-1+?k>r@a_FcD3ATyE&n2g(Xy-u;q()KwNf2z2cO zV}A^p!=(H@vJ?LHFgUhkgt{vEZc0aq1Ns8MDzy`|zgAo*y&I%-g37Y?{bFk;081!ge(w1e5xSEiye8(VAl$W@jz-iRF%hLF$%y6^Y-m?l zfBHh0z|PMF-Pu7ncHrW~i=m+VFz7y9FvjMO!C4iPXxYOpL+OEpuoylChTkcK<(m1h zYfbZ_zY|_P6G%#hHklH5TpnS0?GOa(1=3a)OCeRS<)O_e>7gGG;#FnpT)pm-bXSaV^<&<+cq zkRTLzm+PQI9}yygBP_S9QlL%(SH&S=eGhIBZm+t$$vTc7fZUIS2>ogS?E2#TE@;qv z_f%m8Z(Smab$OudFqoJ?PYdXbiNWp_QtJomB)RiH9x&pxaPM^IJYPaqRBO@lv9o0}cj>sp2Dz zq4Tf4Mws9m?h#t7^MLSqM;^T4q!cm@wSwL(m*9AJg8Yq7m7EM>6)XM>>)w{OSx7fo zL`&k20MKUy-1!ppz6p-(LjUGQTh-Sbv4CV86#d6SkC9Na1NN>0|H-pgKm*C`VbYV5 zt38P#_{nf1WA{opxDHBoi!wruBCIC?QoBg=WZ>-*jN(#+*}3C6=sFnw`JA|(qgx() z8R4?}*nY8q!+9t>sDK2ey=V**p85)UTrWQQ{u8iq>Dd7&HW?mVC?%$8iAMIUf=}m& zNe4qsN>k^_5-w8^-pvofsOi#204UlD**}JTIhWC~Y-X^IPZp)u9D{~hjUp(IYun5n=`;4<|?}m zg75tXdJdzh0p7w#Vq2lyQaheX)5`Hus~`?ff+8+e(qLVUn07>St{9l~1WbHPe7VsN ze}7DTx50mTjdqP7vz{Yi)_h5y0Be1)_<1NfaAl2^GlTZELq&$6-<3w}A0|1vuDZqm zYS3s5m$bAyWeJ@W0W+o{(R19xO%&7zPf2&T&VTs=Z*5iFW-Qbe}(Gf zS5A_sFi1ImKr&0GETO*vO7}q7k(ME145WfA-X=)*>-VAjkPW)P01g8VrdvQvatF!9 z5&WIrP0S`jP_B9lDi60@j#fa>5%hUY)`?3NqICCqa2#7hfd%Zx zWGuNKx~Z#@l(uU{xY|mBo}*-S0JbfW3plhaMP>m<3t{&c(te9i6YQSW22NC5nX)wy zmEq!8BU z$S!%Go+;_lxx(ve1dMgck{H)(G_>sj4k2KdWoxy;QaXSFhJ9Ds-oXa==oz7;yA75L zRUd(ZTnGkQhb1x*3{qYwO^?L3(5XMpj}b zactUh%}BXvpkA6H0l%z-6Ot*6y6V;?ME&(m*&sFUr_FVnxueKadI!z;U4F=+z-bs*ea0l+Z@{g60*Pzb76h%!dBA3c>Q( zAHi|_8rGd6MT1%`vn(d6;%)KF|6x=9?>VyRN}*uuD?lM#p@x&9fP{2#yX4-N<90UU z`+aYb%u-(i+m}J{?rLqRWuPUTRfjxFL62g@!K0tR_(!CJR9lONqJTCQgxi4_@e4wP8>Bn6R%gNsJQOKgy!!$1QOB!>VZ}6 zY-f8|X1RQ%LF#}Hi(HQGmKy(~r{U`l!<14Y44N#lp*0i`MA(`Ry}}KVLaEv=m6^7I z_(U1wS@yCFajl&PWrx5ZS3x#F{Y;al)>ybfYGvzO?d6}?c0bCFq*8uF5a2Bj$rrA_ z3)a5_4ToButVoFyt|6_9tX|PJC@pM$NvshU&FCp)OQ1o7->^sup`cx@Ew$K~uX6!n zv+YyxgJ>%N-b1nE)C^GRUSV+wo9N&>z!N8$Zb|bW@qOqqLf)6`l&a0SL?s#~^cgDw ze9{xp?ph&SLpIt}OF~+|cZRG@3W%DhJfvZvhCEb+xO`2bG6<;Xb2C`#%5nOsrbu6D zhNg9qbNkjvAFsKquN1i5;-)9dg!`*3mhoN71VtX`g)u$~^ux`V5;J@((_Y$P@H&n4 zapZmgL#DvsZ%d){onH%F-nH`b2c~rR!P|6#8Lva;P&vPIIpi#as+#6CW*wkShOE3{ z2!cIXE?J~#E7*1&ir+;h#7e6?K*PBeaK>Ei5}*qPzadbMUi28MeJ^Q(WDbFEKO^aC z_X^?pE*bDx!W7T=i&SXJkihrF>##ScdBR7Of89jzz9|T@!7oaP^c57p4a*Xit>LT^ zYb2MYE-9 zGn*l}<(6J1G%rnO1Gl1uS`vDj*5vIM^R4Ca&AsN>C zV9Uo)Q6wo%oguUfsqT7x18$rS{l|#~DMLuug^xh)`!eukTtILTW@p+9k~O}36V@+; zZUdzq7h$O3O%2Z)mpUwc0VT{v^C*@JC%rrB_Qz2-PL~EK2#@HmlnzftF;o@`b#BE# zT32Y-15!H+G>uDX>JH_7CKI0p8-;DYP^M_p5sn{)o`Ym|x$9uqwiFirR7PZ7ZfS)p z<^V58CdF+pn7&J}fb%G!gGSBLT0f~YaVoW=nD2-?q2D)~3K)=#(dd_zPzqde;UUMU zqi{29dLME>l&EbxO$@URm&TEETlam~B`jt@J)O!=`_rSItEQgP^EG@jNh3 z`6#{6$~Q{^XI>!WHPRH$WCI=}NEWeSBDLEPG40M-(s+n)h|x!*U)bM*O@b@(H%dD# z;55w*M5uU=2d=q6GQ+_fQPzL2gzh6`U(#z+rGJs!5vog|rW_3N;Pfps&Dl59tORCz z73S%irX~RQXotxJNl`AKZCAnZ>6wDNT^_jZcH#A`7$H=b|4Ec!SQ5GGPY6j76DNda zT#ER!V+$2O;!fD{3A{QLZhHj!--_P76193RRFp-AE~>SumMHW)%Ez*EE!@TU*o&9d zK7HDDG$lA;k5(#AxtW62LpW9x%h96KH$IgglICYxXX(%9EQd5VjQtVZ{fb2XU0=xO z<7Z1YIaw}PKs(|-mRmT16|YH6e|Q%

YJ89@wnmUpBgRx6MUgFzN<}K)YU|w$3_Um8D@OFwQ`?1yXQ`Keav1it_W+4ubt_N@c5T z$bq4gB{LjY2Nk7F9HHzc^Mh=i1(w^|c-zW$c3i5tx2OWncXRE #include -#include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//#include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include #include @@ -23,6 +28,9 @@ #include #include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static const float TRACKBALLSIZE = 0.8f; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; @@ -237,105 +245,107 @@ void Rect::set_bottom(float bottom) m_bottom = bottom; } -GLCanvas3D::GLTextureData::GLTextureData() - : m_id(0) - , m_width(0) - , m_height(0) - , m_source("") -{ -} - -GLCanvas3D::GLTextureData::~GLTextureData() -{ - reset(); -} - -bool GLCanvas3D::GLTextureData::load_from_file(const std::string& filename) -{ - reset(); - - // Load a PNG with an alpha channel. - wxImage image; - if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) - { - reset(); - return false; - } - - m_width = image.GetWidth(); - m_height = image.GetHeight(); - int n_pixels = m_width * m_height; - - if (n_pixels <= 0) - { - reset(); - return false; - } - - // Get RGB & alpha raw data from wxImage, pack them into an array. - unsigned char* img_rgb = image.GetData(); - if (img_rgb == nullptr) - { - reset(); - return false; - } - - unsigned char* img_alpha = image.GetAlpha(); - - std::vector data(n_pixels * 4, 0); - for (int i = 0; i < n_pixels; ++i) - { - int data_id = i * 4; - int img_id = i * 3; - data[data_id + 0] = img_rgb[img_id + 0]; - data[data_id + 1] = img_rgb[img_id + 1]; - data[data_id + 2] = img_rgb[img_id + 2]; - data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; - } - - // sends data to gpu - ::glGenTextures(1, &m_id); - ::glBindTexture(GL_TEXTURE_2D, m_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(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); - ::glBindTexture(GL_TEXTURE_2D, 0); - - m_source = filename; - return true; -} - -void GLCanvas3D::GLTextureData::reset() -{ - if (m_id != 0) - ::glDeleteTextures(1, &m_id); - - m_id = 0; - m_width = 0; - m_height = 0; - m_source = ""; -} - -unsigned int GLCanvas3D::GLTextureData::get_id() const -{ - return m_id; -} - -int GLCanvas3D::GLTextureData::get_width() const -{ - return m_width; -} - -int GLCanvas3D::GLTextureData::get_height() const -{ - return m_height; -} - -const std::string& GLCanvas3D::GLTextureData::get_source() const -{ - return m_source; -} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//GLCanvas3D::GLTextureData::GLTextureData() +// : m_id(0) +// , m_width(0) +// , m_height(0) +// , m_source("") +//{ +//} +// +//GLCanvas3D::GLTextureData::~GLTextureData() +//{ +// reset(); +//} +// +//bool GLCanvas3D::GLTextureData::load_from_file(const std::string& filename) +//{ +// reset(); +// +// // Load a PNG with an alpha channel. +// wxImage image; +// if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) +// { +// reset(); +// return false; +// } +// +// m_width = image.GetWidth(); +// m_height = image.GetHeight(); +// int n_pixels = m_width * m_height; +// +// if (n_pixels <= 0) +// { +// reset(); +// return false; +// } +// +// // Get RGB & alpha raw data from wxImage, pack them into an array. +// unsigned char* img_rgb = image.GetData(); +// if (img_rgb == nullptr) +// { +// reset(); +// return false; +// } +// +// unsigned char* img_alpha = image.GetAlpha(); +// +// std::vector data(n_pixels * 4, 0); +// for (int i = 0; i < n_pixels; ++i) +// { +// int data_id = i * 4; +// int img_id = i * 3; +// data[data_id + 0] = img_rgb[img_id + 0]; +// data[data_id + 1] = img_rgb[img_id + 1]; +// data[data_id + 2] = img_rgb[img_id + 2]; +// data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; +// } +// +// // sends data to gpu +// ::glGenTextures(1, &m_id); +// ::glBindTexture(GL_TEXTURE_2D, m_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(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); +// ::glBindTexture(GL_TEXTURE_2D, 0); +// +// m_source = filename; +// return true; +//} +// +//void GLCanvas3D::GLTextureData::reset() +//{ +// if (m_id != 0) +// ::glDeleteTextures(1, &m_id); +// +// m_id = 0; +// m_width = 0; +// m_height = 0; +// m_source = ""; +//} +// +//unsigned int GLCanvas3D::GLTextureData::get_id() const +//{ +// return m_id; +//} +// +//int GLCanvas3D::GLTextureData::get_width() const +//{ +// return m_width; +//} +// +//int GLCanvas3D::GLTextureData::get_height() const +//{ +// return m_height; +//} +// +//const std::string& GLCanvas3D::GLTextureData::get_source() const +//{ +// return m_source; +//} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLCanvas3D::Camera::Camera() : type(Ortho) @@ -908,7 +918,10 @@ void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObje ::glLoadIdentity(); _render_tooltip_texture(canvas, bar_rect, reset_rect); - _render_reset_texture(canvas, reset_rect); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + _render_reset_texture(reset_rect); +// _render_reset_texture(canvas, reset_rect); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ _render_active_object_annotations(canvas, volume, print_object, bar_rect); _render_profile(print_object, bar_rect); @@ -1036,10 +1049,16 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas float t = reset_bottom + (float)m_tooltip_texture.get_height() * inv_zoom + gap; float b = reset_bottom + gap; - canvas.render_texture(m_tooltip_texture.get_id(), l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLTexture::render_texture(m_tooltip_texture.get_id(), l, r, b, t); +// canvas.render_texture(m_tooltip_texture.get_id(), l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } -void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) const +//void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { if (m_reset_texture.get_id() == 0) { @@ -1048,7 +1067,10 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, return; } - canvas.render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); +// canvas.render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const @@ -1181,6 +1203,140 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const return (drag.start_position_3D != Drag::Invalid_3D_Point); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f; +const float GLCanvas3D::Gizmos::OverlayGapY = 10.0f; + +GLCanvas3D::Gizmos::Gizmos() + : m_enabled(false) + , m_current(None) +{ +} + +GLCanvas3D::Gizmos::~Gizmos() +{ + _reset(); +} + +bool GLCanvas3D::Gizmos::init() +{ + GLGizmoBase* gizmo = new GLGizmoScale; + if (gizmo == nullptr) + return false; + + if (!gizmo->init()) + return false; + + m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); + + gizmo = new GLGizmoRotate; + if (gizmo == nullptr) + { + _reset(); + return false; + } + + if (!gizmo->init()) + { + _reset(); + return false; + } + + m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); + + return true; +} + +bool GLCanvas3D::Gizmos::is_enabled() const +{ + return m_enabled; +} + +void GLCanvas3D::Gizmos::set_enabled(bool enable) +{ + m_enabled = enable; +} + +void GLCanvas3D::Gizmos::select(EType type) +{ + if (m_gizmos.find(type) != m_gizmos.end()) + m_current = type; +} + +void GLCanvas3D::Gizmos::reset_selection() +{ + m_current = None; +} + +void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas) const +{ + if (!m_enabled) + return; + + ::glDisable(GL_DEPTH_TEST); + + ::glPushMatrix(); + ::glLoadIdentity(); + + _render_overlay(canvas); + _render_current_gizmo(); + + ::glPopMatrix(); +} + +void GLCanvas3D::Gizmos::_reset() +{ + for (GizmosMap::value_type& gizmo : m_gizmos) + { + delete gizmo.second; + gizmo.second = nullptr; + } + + m_gizmos.clear(); +} + +void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const +{ + if (m_gizmos.empty()) + return; + + const Size& cnv_size = canvas.get_canvas_size(); + + float cnv_w = (float)cnv_size.get_width(); + + float zoom = canvas.get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + float total_h = 0.0f; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + total_h += (float)it->second->get_textures_height(); + if (std::distance(it, m_gizmos.end()) > 1) + total_h += OverlayGapY; + } + + float top_x = (OverlayOffsetX - 0.5f * cnv_w) * inv_zoom; + float top_y = 0.5f * total_h * inv_zoom; + float scaled_gap_y = OverlayGapY * inv_zoom; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + float tex_w = (float)it->second->get_textures_width() * inv_zoom; + float tex_h = (float)it->second->get_textures_height() * inv_zoom; + GLTexture::render_texture(it->second->get_textures_id(), top_x, top_x + tex_w, top_y - tex_h, top_y); + top_y -= (tex_h + scaled_gap_y); + } +} + +void GLCanvas3D::Gizmos::_render_current_gizmo() const +{ + GizmosMap::const_iterator it = m_gizmos.find(m_current); + if (it == m_gizmos.end()) + return; + + it->second->render(); +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -1228,8 +1384,6 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (m_initialized) return true; - std::cout << "init: " << (void*)m_canvas << " (" << (void*)this << ")" << std::endl; - ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f); ::glClearDepth(1.0f); @@ -1287,6 +1441,11 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (!m_volumes.empty()) m_volumes.finalize_geometry(m_use_VBOs); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (m_gizmos.is_enabled() && !m_gizmos.init()) + return false; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + m_initialized = true; return true; @@ -1528,6 +1687,13 @@ void GLCanvas3D::enable_moving(bool enable) m_moving_enabled = enable; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void GLCanvas3D::enable_gizmos(bool enable) +{ + m_gizmos.set_enabled(enable); +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void GLCanvas3D::enable_shader(bool enable) { m_shader_enabled = enable; @@ -1645,34 +1811,39 @@ void GLCanvas3D::render() _render_warning_texture(); _render_legend_texture(); _render_layer_editing_overlay(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + _render_gizmo(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_canvas->SwapBuffers(); } -void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const -{ - ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - ::glDisable(GL_LIGHTING); - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - ::glEnable(GL_TEXTURE_2D); - - ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); - - ::glBegin(GL_QUADS); - ::glTexCoord2d(0.0f, 1.0f); glVertex3f(left, bottom, 0.0f); - ::glTexCoord2d(1.0f, 1.0f); glVertex3f(right, bottom, 0.0f); - ::glTexCoord2d(1.0f, 0.0f); glVertex3f(right, top, 0.0f); - ::glTexCoord2d(0.0f, 0.0f); glVertex3f(left, top, 0.0f); - ::glEnd(); - - ::glBindTexture(GL_TEXTURE_2D, 0); - - ::glDisable(GL_TEXTURE_2D); - ::glDisable(GL_BLEND); - ::glEnable(GL_LIGHTING); -} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const +//{ +// ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); +// +// ::glDisable(GL_LIGHTING); +// ::glEnable(GL_BLEND); +// ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +// ::glEnable(GL_TEXTURE_2D); +// +// ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); +// +// ::glBegin(GL_QUADS); +// ::glTexCoord2d(0.0f, 1.0f); ::glVertex3f(left, bottom, 0.0f); +// ::glTexCoord2d(1.0f, 1.0f); ::glVertex3f(right, bottom, 0.0f); +// ::glTexCoord2d(1.0f, 0.0f); ::glVertex3f(right, top, 0.0f); +// ::glTexCoord2d(0.0f, 0.0f); ::glVertex3f(left, top, 0.0f); +// ::glEnd(); +// +// ::glBindTexture(GL_TEXTURE_2D, 0); +// +// ::glDisable(GL_TEXTURE_2D); +// ::glDisable(GL_BLEND); +// ::glEnable(GL_LIGHTING); +//} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector GLCanvas3D::get_current_print_zs(bool active_only) const { @@ -3130,7 +3301,10 @@ void GLCanvas3D::_render_warning_texture() const float r = l + (float)w * inv_zoom; float b = t - (float)h * inv_zoom; - render_texture(tex_id, l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLTexture::render_texture(tex_id, l, r, b, t); +// render_texture(tex_id, l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ::glPopMatrix(); ::glEnable(GL_DEPTH_TEST); @@ -3162,7 +3336,10 @@ void GLCanvas3D::_render_legend_texture() const float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom; float r = l + (float)w * inv_zoom; float b = t - (float)h * inv_zoom; - render_texture(tex_id, l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + GLTexture::render_texture(tex_id, l, r, b, t); +// render_texture(tex_id, l, r, b, t); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ::glPopMatrix(); ::glEnable(GL_DEPTH_TEST); @@ -3248,6 +3425,13 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const ::glEnable(GL_CULL_FACE); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void GLCanvas3D::_render_gizmo() const +{ + m_gizmos.render(*this); +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + float GLCanvas3D::_get_layers_editing_cursor_z_relative() const { return m_layers_editing.get_cursor_z_relative(*this); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index d694db4e29..6421e44ec2 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -2,6 +2,9 @@ #define slic3r_GLCanvas3D_hpp_ #include "../../slic3r/GUI/3DScene.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#include "../../slic3r/GUI/GLTexture.hpp" +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class wxTimer; class wxSizeEvent; @@ -18,6 +21,10 @@ class ExPolygon; namespace GUI { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +class GLGizmoBase; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + class GeometryBuffer { std::vector m_vertices; @@ -102,26 +109,28 @@ class GLCanvas3D void reset() { first_volumes.clear(); } }; - struct GLTextureData - { - private: - unsigned int m_id; - int m_width; - int m_height; - std::string m_source; - - public: - GLTextureData(); - ~GLTextureData(); - - bool load_from_file(const std::string& filename); - void reset(); - - unsigned int get_id() const; - int get_width() const; - int get_height() const; - const std::string& get_source() const; - }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// struct GLTextureData +// { +// private: +// unsigned int m_id; +// int m_width; +// int m_height; +// std::string m_source; +// +// public: +// GLTextureData(); +// ~GLTextureData(); +// +// bool load_from_file(const std::string& filename); +// void reset(); +// +// unsigned int get_id() const; +// int get_width() const; +// int get_height() const; +// const std::string& get_source() const; +// }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: struct Camera @@ -170,8 +179,12 @@ public: Polygon m_polygon; GeometryBuffer m_triangles; GeometryBuffer m_gridlines; - mutable GLTextureData m_top_texture; - mutable GLTextureData m_bottom_texture; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + mutable GLTexture m_top_texture; + mutable GLTexture m_bottom_texture; +// mutable GLTextureData m_top_texture; +// mutable GLTextureData m_bottom_texture; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: Bed(); @@ -266,8 +279,12 @@ public: bool m_enabled; Shader m_shader; unsigned int m_z_texture_id; - mutable GLTextureData m_tooltip_texture; - mutable GLTextureData m_reset_texture; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + mutable GLTexture m_tooltip_texture; + mutable GLTexture m_reset_texture; +// mutable GLTextureData m_tooltip_texture; +// mutable GLTextureData m_reset_texture; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: EState state; @@ -306,7 +323,10 @@ public: private: bool _is_initialized() const; void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; - void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void _render_reset_texture(const Rect& reset_rect) const; +// void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const; void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; }; @@ -340,6 +360,49 @@ public: bool is_start_position_3D_defined() const; }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + class Gizmos + { + static const float OverlayOffsetX; + static const float OverlayGapY; + + public: + enum EType : unsigned char + { + None, + Scale, + Rotate, + Num_Types + }; + + private: + bool m_enabled; + typedef std::map GizmosMap; + GizmosMap m_gizmos; + EType m_current; + + public: + Gizmos(); + ~Gizmos(); + + bool init(); + + bool is_enabled() const; + void set_enabled(bool enable); + + void select(EType type); + void reset_selection(); + + void render(const GLCanvas3D& canvas) const; + + private: + void _reset(); + + void _render_overlay(const GLCanvas3D& canvas) const; + void _render_current_gizmo() const; + }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + private: wxGLCanvas* m_canvas; wxGLContext* m_context; @@ -351,6 +414,9 @@ private: LayersEditing m_layers_editing; Shader m_shader; Mouse m_mouse; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + Gizmos m_gizmos; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ mutable GLVolumeCollection m_volumes; DynamicPrintConfig* m_config; @@ -455,6 +521,9 @@ public: void enable_legend_texture(bool enable); void enable_picking(bool enable); void enable_moving(bool enable); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void enable_gizmos(bool enable); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void enable_shader(bool enable); void enable_force_zoom_to_bed(bool enable); void allow_multisample(bool allow); @@ -467,7 +536,9 @@ public: void update_volumes_colors_by_extruder(); void render(); - void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector get_current_print_zs(bool active_only) const; void set_toolpaths_range(double low, double high); @@ -546,6 +617,9 @@ private: void _render_legend_texture() const; void _render_layer_editing_overlay() const; void _render_volumes(bool fake_colors) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void _render_gizmo() const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ float _get_layers_editing_cursor_z_relative() const; int _get_layers_editing_first_selected_object_id(unsigned int objects_count) const; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index b021e65a8e..8a84d4cb53 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -159,8 +159,6 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas) canvas3D->bind_event_handlers(); m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); - std::cout << "canvas added: " << (void*)canvas << " (" << (void*)canvas3D << ")" << std::endl; - return true; } @@ -174,8 +172,6 @@ bool GLCanvas3DManager::remove(wxGLCanvas* canvas) delete it->second; m_canvases.erase(it); - std::cout << "canvas removed: " << (void*)canvas << std::endl; - return true; } @@ -183,8 +179,6 @@ void GLCanvas3DManager::remove_all() { for (CanvasesMap::value_type& item : m_canvases) { - std::cout << "canvas removed: " << (void*)item.second << std::endl; - item.second->unbind_event_handlers(); delete item.second; } @@ -200,8 +194,6 @@ void GLCanvas3DManager::init_gl() { if (!m_gl_initialized) { - std::cout << "GLCanvas3DManager::init_gl()" << std::endl; - glewInit(); if (m_gl_info.detect()) { @@ -209,10 +201,6 @@ void GLCanvas3DManager::init_gl() m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1"); m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0); m_gl_initialized = true; - - std::cout << "DETECTED OPENGL: " << m_gl_info.version << std::endl; - std::cout << "USE VBOS = " << (m_use_VBOs ? "YES" : "NO") << std::endl; - std::cout << "LAYER EDITING ALLOWED = " << (!m_use_legacy_opengl ? "YES" : "NO") << std::endl; } else throw std::runtime_error(std::string("Unable to initialize OpenGL driver\n")); @@ -439,6 +427,15 @@ void GLCanvas3DManager::enable_moving(wxGLCanvas* canvas, bool enable) it->second->enable_moving(enable); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void GLCanvas3DManager::enable_gizmos(wxGLCanvas* canvas, bool enable) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->enable_gizmos(enable); +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 741c8e29b6..9ec645c1a4 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -110,6 +110,9 @@ public: void enable_legend_texture(wxGLCanvas* canvas, bool enable); void enable_picking(wxGLCanvas* canvas, bool enable); void enable_moving(wxGLCanvas* canvas, bool enable); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void enable_gizmos(wxGLCanvas* canvas, bool enable); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void enable_shader(wxGLCanvas* canvas, bool enable); void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); void allow_multisample(wxGLCanvas* canvas, bool allow); diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp new file mode 100644 index 0000000000..06ceee8814 --- /dev/null +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -0,0 +1,109 @@ +#include "GLGizmo.hpp" + +#include "../../libslic3r/utils.hpp" + +#include + +namespace Slic3r { +namespace GUI { + +GLGizmoBase::GLGizmoBase() + : m_state(Off) +{ +} + +GLGizmoBase::~GLGizmoBase() +{ +} + +GLGizmoBase::EState GLGizmoBase::get_state() const +{ + return m_state; +} + +unsigned int GLGizmoBase::get_textures_id() const +{ + return m_textures[m_state].get_id(); +} + +int GLGizmoBase::get_textures_height() const +{ + return m_textures[Off].get_height(); +} + +int GLGizmoBase::get_textures_width() const +{ + return m_textures[Off].get_width(); +} + +bool GLGizmoBase::init() +{ + return on_init(); +} + +GLGizmoRotate::GLGizmoRotate() + : GLGizmoBase() + , m_angle_x(0.0f) + , m_angle_y(0.0f) + , m_angle_z(0.0f) +{ +} + +void GLGizmoRotate::render() const +{ + std::cout << "GLGizmoRotate::render()" << std::endl; +} + +bool GLGizmoRotate::on_init() +{ + std::string path = resources_dir() + "/icons/overlay/"; + + std::string filename = path + "rotate_off.png"; + if (!m_textures[Off].load_from_file(filename)) + return false; + + filename = path + "rotate_hover.png"; + if (!m_textures[Hover].load_from_file(filename)) + return false; + + filename = path + "rotate_on.png"; + if (!m_textures[On].load_from_file(filename)) + return false; + + return true; +} + +GLGizmoScale::GLGizmoScale() + : GLGizmoBase() + , m_scale_x(1.0f) + , m_scale_y(1.0f) + , m_scale_z(1.0f) +{ +} + +void GLGizmoScale::render() const +{ + std::cout << "GLGizmoScale::render()" << std::endl; +} + +bool GLGizmoScale::on_init() +{ + std::string path = resources_dir() + "/icons/overlay/"; + + std::string filename = path + "scale_off.png"; + if (!m_textures[Off].load_from_file(filename)) + return false; + + filename = path + "scale_hover.png"; + if (!m_textures[Hover].load_from_file(filename)) + return false; + + filename = path + "scale_on.png"; + if (!m_textures[On].load_from_file(filename)) + return false; + + return true; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp new file mode 100644 index 0000000000..1da216ba96 --- /dev/null +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -0,0 +1,78 @@ +#ifndef slic3r_GLGizmo_hpp_ +#define slic3r_GLGizmo_hpp_ + +#include "../../slic3r/GUI/GLTexture.hpp" + +namespace Slic3r { +namespace GUI { + +class GLGizmoBase +{ +public: + enum EState + { + Off, + Hover, + On, + Num_States + }; + +protected: + EState m_state; + // textures are assumed to be all the same size in pixels + // no internal check is done + GLTexture m_textures[Num_States]; + +public: + GLGizmoBase(); + virtual ~GLGizmoBase(); + + bool init(); + + EState get_state() const; + + unsigned int get_textures_id() const; + int get_textures_height() const; + int get_textures_width() const; + + virtual void render() const = 0; + +protected: + virtual bool on_init() = 0; +}; + +class GLGizmoRotate : public GLGizmoBase +{ + float m_angle_x; + float m_angle_y; + float m_angle_z; + +public: + GLGizmoRotate(); + + void render() const; + +protected: + virtual bool on_init(); +}; + +class GLGizmoScale : public GLGizmoBase +{ + float m_scale_x; + float m_scale_y; + float m_scale_z; + +public: + GLGizmoScale(); + + void render() const; + +protected: + virtual bool on_init(); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmo_hpp_ + diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/xs/src/slic3r/GUI/GLTexture.cpp new file mode 100644 index 0000000000..b9eb118a97 --- /dev/null +++ b/xs/src/slic3r/GUI/GLTexture.cpp @@ -0,0 +1,138 @@ +#include "GLTexture.hpp" + +#include + +#include + +#include + +namespace Slic3r { +namespace GUI { + +GLTexture::GLTexture() + : m_id(0) + , m_width(0) + , m_height(0) + , m_source("") +{ +} + +GLTexture::~GLTexture() +{ + reset(); +} + +bool GLTexture::load_from_file(const std::string& filename) +{ + reset(); + + // Load a PNG with an alpha channel. + wxImage image; + if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) + { + reset(); + return false; + } + + m_width = image.GetWidth(); + m_height = image.GetHeight(); + int n_pixels = m_width * m_height; + + if (n_pixels <= 0) + { + reset(); + return false; + } + + // Get RGB & alpha raw data from wxImage, pack them into an array. + unsigned char* img_rgb = image.GetData(); + if (img_rgb == nullptr) + { + reset(); + return false; + } + + unsigned char* img_alpha = image.GetAlpha(); + + std::vector data(n_pixels * 4, 0); + for (int i = 0; i < n_pixels; ++i) + { + int data_id = i * 4; + int img_id = i * 3; + data[data_id + 0] = img_rgb[img_id + 0]; + data[data_id + 1] = img_rgb[img_id + 1]; + data[data_id + 2] = img_rgb[img_id + 2]; + data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; + } + + // sends data to gpu + ::glGenTextures(1, &m_id); + ::glBindTexture(GL_TEXTURE_2D, m_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(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + ::glBindTexture(GL_TEXTURE_2D, 0); + + m_source = filename; + return true; +} + +void GLTexture::reset() +{ + if (m_id != 0) + ::glDeleteTextures(1, &m_id); + + m_id = 0; + m_width = 0; + m_height = 0; + m_source = ""; +} + +unsigned int GLTexture::get_id() const +{ + return m_id; +} + +int GLTexture::get_width() const +{ + return m_width; +} + +int GLTexture::get_height() const +{ + return m_height; +} + +const std::string& GLTexture::get_source() const +{ + return m_source; +} + +void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) +{ + ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + ::glDisable(GL_LIGHTING); + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + ::glEnable(GL_TEXTURE_2D); + + ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); + + ::glBegin(GL_QUADS); + ::glTexCoord2d(0.0f, 1.0f); ::glVertex3f(left, bottom, 0.0f); + ::glTexCoord2d(1.0f, 1.0f); ::glVertex3f(right, bottom, 0.0f); + ::glTexCoord2d(1.0f, 0.0f); ::glVertex3f(right, top, 0.0f); + ::glTexCoord2d(0.0f, 0.0f); ::glVertex3f(left, top, 0.0f); + ::glEnd(); + + ::glBindTexture(GL_TEXTURE_2D, 0); + + ::glDisable(GL_TEXTURE_2D); + ::glDisable(GL_BLEND); + ::glEnable(GL_LIGHTING); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLTexture.hpp b/xs/src/slic3r/GUI/GLTexture.hpp new file mode 100644 index 0000000000..3e3bf83f7c --- /dev/null +++ b/xs/src/slic3r/GUI/GLTexture.hpp @@ -0,0 +1,36 @@ +#ifndef slic3r_GLTexture_hpp_ +#define slic3r_GLTexture_hpp_ + +#include + +namespace Slic3r { +namespace GUI { + + struct GLTexture + { + private: + unsigned int m_id; + int m_width; + int m_height; + std::string m_source; + + public: + GLTexture(); + ~GLTexture(); + + bool load_from_file(const std::string& filename); + void reset(); + + unsigned int get_id() const; + int get_width() const; + int get_height() const; + const std::string& get_source() const; + + static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top); + }; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLTexture_hpp_ + diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index c7f3670fcb..426395ef23 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -410,6 +410,13 @@ enable_moving(canvas, enable) CODE: _3DScene::enable_moving((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); +void +enable_gizmos(canvas, enable) + SV *canvas; + bool enable; + CODE: + _3DScene::enable_gizmos((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable); + void enable_shader(canvas, enable) SV *canvas; From 6079fed9511d8019016c26c8455a8f8b9eef53e2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 13 Jun 2018 09:26:58 +0200 Subject: [PATCH 086/117] Fixed compile on Linux --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 19 ++++++++++--------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 2 +- xs/src/slic3r/GUI/GLGizmo.cpp | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 348588ee21..e4420cf0c1 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1209,7 +1209,7 @@ const float GLCanvas3D::Gizmos::OverlayGapY = 10.0f; GLCanvas3D::Gizmos::Gizmos() : m_enabled(false) - , m_current(None) + , m_current(Undefined) { } @@ -1265,7 +1265,7 @@ void GLCanvas3D::Gizmos::select(EType type) void GLCanvas3D::Gizmos::reset_selection() { - m_current = None; + m_current = Undefined; } void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas) const @@ -2829,7 +2829,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) volume_idxs.push_back(vol_id); else { - for (int i = 0; i < m_volumes.volumes.size(); ++i) + for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) { if (m_volumes.volumes[i]->drag_group_id == group_id) volume_idxs.push_back(i); @@ -3165,7 +3165,7 @@ void GLCanvas3D::_picking_pass() const vol->hover = false; } - if (volume_id < m_volumes.volumes.size()) + if (volume_id < (int)m_volumes.volumes.size()) { m_hover_volume_id = volume_id; m_volumes.volumes[volume_id]->hover = true; @@ -3565,6 +3565,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat return path.feedrate * (float)path.mm3_per_mm; case GCodePreviewData::Extrusion::Tool: return (float)path.extruder_id; + default: + return 0.0f; } return 0.0f; @@ -3590,6 +3592,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float)); return color; } + default: + return GCodePreviewData::Color::Dummy; } return GCodePreviewData::Color::Dummy; @@ -4032,15 +4036,12 @@ void GLCanvas3D::_load_shells() ModelObject* model_obj = obj->model_object(); std::vector instance_ids(model_obj->instances.size()); - for (int i = 0; i < model_obj->instances.size(); ++i) + for (int i = 0; i < (int)model_obj->instances.size(); ++i) { instance_ids[i] = i; } - for (ModelInstance* instance : model_obj->instances) - { - m_volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs && m_initialized); - } + m_volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs && m_initialized); ++object_id; } diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 6421e44ec2..74c95ca749 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -369,7 +369,7 @@ public: public: enum EType : unsigned char { - None, + Undefined, Scale, Rotate, Num_Types diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 06ceee8814..1727203907 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -1,6 +1,6 @@ #include "GLGizmo.hpp" -#include "../../libslic3r/utils.hpp" +#include "../../libslic3r/Utils.hpp" #include From c657654c02a0490734c43dbb682c098652b2e3ac Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 13 Jun 2018 10:49:59 +0200 Subject: [PATCH 087/117] Hovering on gizmo overlay --- xs/src/slic3r/GUI/3DScene.cpp | 2 - xs/src/slic3r/GUI/3DScene.hpp | 2 - xs/src/slic3r/GUI/GLCanvas3D.cpp | 227 +++++------------------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 55 +----- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 4 +- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 - xs/src/slic3r/GUI/GLGizmo.cpp | 12 +- xs/src/slic3r/GUI/GLGizmo.hpp | 6 +- 8 files changed, 61 insertions(+), 249 deletions(-) diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 3770ad0e6b..34d066730a 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1903,12 +1903,10 @@ void _3DScene::enable_moving(wxGLCanvas* canvas, bool enable) s_canvas_mgr.enable_moving(canvas, enable); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _3DScene::enable_gizmos(wxGLCanvas* canvas, bool enable) { s_canvas_mgr.enable_gizmos(canvas, enable); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable) { diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index ad2b8d5c55..26b9911e00 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -557,9 +557,7 @@ public: static void enable_legend_texture(wxGLCanvas* canvas, bool enable); static void enable_picking(wxGLCanvas* canvas, bool enable); static void enable_moving(wxGLCanvas* canvas, bool enable); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static void enable_gizmos(wxGLCanvas* canvas, bool enable); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static void enable_shader(wxGLCanvas* canvas, bool enable); static void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); static void allow_multisample(wxGLCanvas* canvas, bool allow); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index e4420cf0c1..9ad7afeb83 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -4,9 +4,7 @@ #include "../../slic3r/GUI/GLShader.hpp" #include "../../slic3r/GUI/GUI.hpp" #include "../../slic3r/GUI/PresetBundle.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "../../slic3r/GUI/GLGizmo.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "../../libslic3r/ClipperUtils.hpp" #include "../../libslic3r/PrintConfig.hpp" #include "../../libslic3r/Print.hpp" @@ -15,9 +13,6 @@ #include #include -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//#include -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include #include @@ -28,9 +23,7 @@ #include #include -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ static const float TRACKBALLSIZE = 0.8f; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; @@ -245,108 +238,6 @@ void Rect::set_bottom(float bottom) m_bottom = bottom; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//GLCanvas3D::GLTextureData::GLTextureData() -// : m_id(0) -// , m_width(0) -// , m_height(0) -// , m_source("") -//{ -//} -// -//GLCanvas3D::GLTextureData::~GLTextureData() -//{ -// reset(); -//} -// -//bool GLCanvas3D::GLTextureData::load_from_file(const std::string& filename) -//{ -// reset(); -// -// // Load a PNG with an alpha channel. -// wxImage image; -// if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) -// { -// reset(); -// return false; -// } -// -// m_width = image.GetWidth(); -// m_height = image.GetHeight(); -// int n_pixels = m_width * m_height; -// -// if (n_pixels <= 0) -// { -// reset(); -// return false; -// } -// -// // Get RGB & alpha raw data from wxImage, pack them into an array. -// unsigned char* img_rgb = image.GetData(); -// if (img_rgb == nullptr) -// { -// reset(); -// return false; -// } -// -// unsigned char* img_alpha = image.GetAlpha(); -// -// std::vector data(n_pixels * 4, 0); -// for (int i = 0; i < n_pixels; ++i) -// { -// int data_id = i * 4; -// int img_id = i * 3; -// data[data_id + 0] = img_rgb[img_id + 0]; -// data[data_id + 1] = img_rgb[img_id + 1]; -// data[data_id + 2] = img_rgb[img_id + 2]; -// data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; -// } -// -// // sends data to gpu -// ::glGenTextures(1, &m_id); -// ::glBindTexture(GL_TEXTURE_2D, m_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(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); -// ::glBindTexture(GL_TEXTURE_2D, 0); -// -// m_source = filename; -// return true; -//} -// -//void GLCanvas3D::GLTextureData::reset() -//{ -// if (m_id != 0) -// ::glDeleteTextures(1, &m_id); -// -// m_id = 0; -// m_width = 0; -// m_height = 0; -// m_source = ""; -//} -// -//unsigned int GLCanvas3D::GLTextureData::get_id() const -//{ -// return m_id; -//} -// -//int GLCanvas3D::GLTextureData::get_width() const -//{ -// return m_width; -//} -// -//int GLCanvas3D::GLTextureData::get_height() const -//{ -// return m_height; -//} -// -//const std::string& GLCanvas3D::GLTextureData::get_source() const -//{ -// return m_source; -//} -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - GLCanvas3D::Camera::Camera() : type(Ortho) , zoom(1.0f) @@ -918,10 +809,7 @@ void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObje ::glLoadIdentity(); _render_tooltip_texture(canvas, bar_rect, reset_rect); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ _render_reset_texture(reset_rect); -// _render_reset_texture(canvas, reset_rect); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ _render_active_object_annotations(canvas, volume, print_object, bar_rect); _render_profile(print_object, bar_rect); @@ -1049,16 +937,10 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas float t = reset_bottom + (float)m_tooltip_texture.get_height() * inv_zoom + gap; float b = reset_bottom + gap; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLTexture::render_texture(m_tooltip_texture.get_id(), l, r, b, t); -// canvas.render_texture(m_tooltip_texture.get_id(), l, r, b, t); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) const -//void GLCanvas3D::LayersEditing::_render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { if (m_reset_texture.get_id() == 0) { @@ -1067,10 +949,7 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co return; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); -// canvas.render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const @@ -1203,7 +1082,6 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const return (drag.start_position_3D != Drag::Invalid_3D_Point); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f; const float GLCanvas3D::Gizmos::OverlayGapY = 10.0f; @@ -1268,6 +1146,29 @@ void GLCanvas3D::Gizmos::reset_selection() m_current = Undefined; } +void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos) +{ + if (!m_enabled) + return; + + float cnv_h = (float)canvas.get_canvas_size().get_height(); + float height = _get_total_overlay_height(); + float top_y = 0.5f * (cnv_h - height); + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + if ((it->second == nullptr) || (it->second->get_state() == GLGizmoBase::On)) + continue; + + float tex_size = (float)it->second->get_textures_size(); + float half_tex_size = 0.5f * tex_size; + + // we currently use circular icons for gizmo, so we check the radius + bool inside = length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size; + it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); + top_y += (tex_size + OverlayGapY); + } +} + void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas) const { if (!m_enabled) @@ -1300,30 +1201,19 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const if (m_gizmos.empty()) return; - const Size& cnv_size = canvas.get_canvas_size(); - - float cnv_w = (float)cnv_size.get_width(); - + float cnv_w = (float)canvas.get_canvas_size().get_width(); float zoom = canvas.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - float total_h = 0.0f; - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - total_h += (float)it->second->get_textures_height(); - if (std::distance(it, m_gizmos.end()) > 1) - total_h += OverlayGapY; - } - + float height = _get_total_overlay_height(); float top_x = (OverlayOffsetX - 0.5f * cnv_w) * inv_zoom; - float top_y = 0.5f * total_h * inv_zoom; + float top_y = 0.5f * height * inv_zoom; float scaled_gap_y = OverlayGapY * inv_zoom; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - float tex_w = (float)it->second->get_textures_width() * inv_zoom; - float tex_h = (float)it->second->get_textures_height() * inv_zoom; - GLTexture::render_texture(it->second->get_textures_id(), top_x, top_x + tex_w, top_y - tex_h, top_y); - top_y -= (tex_h + scaled_gap_y); + float tex_size = (float)it->second->get_textures_size() * inv_zoom; + GLTexture::render_texture(it->second->get_textures_id(), top_x, top_x + tex_size, top_y - tex_size, top_y); + top_y -= (tex_size + scaled_gap_y); } } @@ -1335,7 +1225,19 @@ void GLCanvas3D::Gizmos::_render_current_gizmo() const it->second->render(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +float GLCanvas3D::Gizmos::_get_total_overlay_height() const +{ + float height = 0.0f; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + height += (float)it->second->get_textures_size(); + if (std::distance(it, m_gizmos.end()) > 1) + height += OverlayGapY; + } + + return height; +} GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) @@ -1441,10 +1343,8 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (!m_volumes.empty()) m_volumes.finalize_geometry(m_use_VBOs); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_gizmos.is_enabled() && !m_gizmos.init()) return false; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_initialized = true; @@ -1687,12 +1587,10 @@ void GLCanvas3D::enable_moving(bool enable) m_moving_enabled = enable; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GLCanvas3D::enable_gizmos(bool enable) { m_gizmos.set_enabled(enable); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GLCanvas3D::enable_shader(bool enable) { @@ -1811,40 +1709,11 @@ void GLCanvas3D::render() _render_warning_texture(); _render_legend_texture(); _render_layer_editing_overlay(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ _render_gizmo(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_canvas->SwapBuffers(); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -//void GLCanvas3D::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const -//{ -// ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f); -// -// ::glDisable(GL_LIGHTING); -// ::glEnable(GL_BLEND); -// ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -// ::glEnable(GL_TEXTURE_2D); -// -// ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); -// -// ::glBegin(GL_QUADS); -// ::glTexCoord2d(0.0f, 1.0f); ::glVertex3f(left, bottom, 0.0f); -// ::glTexCoord2d(1.0f, 1.0f); ::glVertex3f(right, bottom, 0.0f); -// ::glTexCoord2d(1.0f, 0.0f); ::glVertex3f(right, top, 0.0f); -// ::glTexCoord2d(0.0f, 0.0f); ::glVertex3f(left, top, 0.0f); -// ::glEnd(); -// -// ::glBindTexture(GL_TEXTURE_2D, 0); -// -// ::glDisable(GL_TEXTURE_2D); -// ::glDisable(GL_BLEND); -// ::glEnable(GL_LIGHTING); -//} -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - std::vector GLCanvas3D::get_current_print_zs(bool active_only) const { return m_volumes.get_current_print_zs(active_only); @@ -3179,6 +3048,10 @@ void GLCanvas3D::_picking_pass() const } } } + + // updates gizmos overlay + if (!m_volumes.empty()) + m_gizmos.update_hover_state(*this, pos); } } @@ -3301,10 +3174,7 @@ void GLCanvas3D::_render_warning_texture() const float r = l + (float)w * inv_zoom; float b = t - (float)h * inv_zoom; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLTexture::render_texture(tex_id, l, r, b, t); -// render_texture(tex_id, l, r, b, t); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ::glPopMatrix(); ::glEnable(GL_DEPTH_TEST); @@ -3336,10 +3206,7 @@ void GLCanvas3D::_render_legend_texture() const float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom; float r = l + (float)w * inv_zoom; float b = t - (float)h * inv_zoom; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GLTexture::render_texture(tex_id, l, r, b, t); -// render_texture(tex_id, l, r, b, t); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ::glPopMatrix(); ::glEnable(GL_DEPTH_TEST); @@ -3425,12 +3292,10 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const ::glEnable(GL_CULL_FACE); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GLCanvas3D::_render_gizmo() const { m_gizmos.render(*this); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ float GLCanvas3D::_get_layers_editing_cursor_z_relative() const { diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 74c95ca749..2852d82d1a 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -2,9 +2,7 @@ #define slic3r_GLCanvas3D_hpp_ #include "../../slic3r/GUI/3DScene.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #include "../../slic3r/GUI/GLTexture.hpp" -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class wxTimer; class wxSizeEvent; @@ -21,9 +19,7 @@ class ExPolygon; namespace GUI { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class GLGizmoBase; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class GeometryBuffer { @@ -109,29 +105,6 @@ class GLCanvas3D void reset() { first_volumes.clear(); } }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// struct GLTextureData -// { -// private: -// unsigned int m_id; -// int m_width; -// int m_height; -// std::string m_source; -// -// public: -// GLTextureData(); -// ~GLTextureData(); -// -// bool load_from_file(const std::string& filename); -// void reset(); -// -// unsigned int get_id() const; -// int get_width() const; -// int get_height() const; -// const std::string& get_source() const; -// }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - public: struct Camera { @@ -179,12 +152,8 @@ public: Polygon m_polygon; GeometryBuffer m_triangles; GeometryBuffer m_gridlines; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ mutable GLTexture m_top_texture; mutable GLTexture m_bottom_texture; -// mutable GLTextureData m_top_texture; -// mutable GLTextureData m_bottom_texture; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: Bed(); @@ -279,12 +248,8 @@ public: bool m_enabled; Shader m_shader; unsigned int m_z_texture_id; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ mutable GLTexture m_tooltip_texture; mutable GLTexture m_reset_texture; -// mutable GLTextureData m_tooltip_texture; -// mutable GLTextureData m_reset_texture; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ public: EState state; @@ -323,10 +288,7 @@ public: private: bool _is_initialized() const; void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _render_reset_texture(const Rect& reset_rect) const; -// void _render_reset_texture(const GLCanvas3D& canvas, const Rect& reset_rect) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const; void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; }; @@ -360,7 +322,6 @@ public: bool is_start_position_3D_defined() const; }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ class Gizmos { static const float OverlayOffsetX; @@ -393,6 +354,8 @@ public: void select(EType type); void reset_selection(); + void update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos); + void render(const GLCanvas3D& canvas) const; private: @@ -400,8 +363,9 @@ public: void _render_overlay(const GLCanvas3D& canvas) const; void _render_current_gizmo() const; + + float _get_total_overlay_height() const; }; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ private: wxGLCanvas* m_canvas; @@ -414,9 +378,7 @@ private: LayersEditing m_layers_editing; Shader m_shader; Mouse m_mouse; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - Gizmos m_gizmos; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + mutable Gizmos m_gizmos; mutable GLVolumeCollection m_volumes; DynamicPrintConfig* m_config; @@ -521,9 +483,7 @@ public: void enable_legend_texture(bool enable); void enable_picking(bool enable); void enable_moving(bool enable); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void enable_gizmos(bool enable); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void enable_shader(bool enable); void enable_force_zoom_to_bed(bool enable); void allow_multisample(bool allow); @@ -536,9 +496,6 @@ public: void update_volumes_colors_by_extruder(); void render(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -// void render_texture(unsigned int tex_id, float left, float right, float bottom, float top) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ std::vector get_current_print_zs(bool active_only) const; void set_toolpaths_range(double low, double high); @@ -617,9 +574,7 @@ private: void _render_legend_texture() const; void _render_layer_editing_overlay() const; void _render_volumes(bool fake_colors) const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _render_gizmo() const; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ float _get_layers_editing_cursor_z_relative() const; int _get_layers_editing_first_selected_object_id(unsigned int objects_count) const; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 8a84d4cb53..97af89fb7d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -107,7 +107,7 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten GLint num_extensions; ::glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); - for (unsigned int i = 0; i < num_extensions; ++i) + for (GLint i = 0; i < num_extensions; ++i) { const char* e = (const char*)::glGetStringi(GL_EXTENSIONS, i); extensions_list.push_back(e); @@ -427,14 +427,12 @@ void GLCanvas3DManager::enable_moving(wxGLCanvas* canvas, bool enable) it->second->enable_moving(enable); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GLCanvas3DManager::enable_gizmos(wxGLCanvas* canvas, bool enable) { CanvasesMap::iterator it = _get_canvas(canvas); if (it != m_canvases.end()) it->second->enable_gizmos(enable); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) { diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 9ec645c1a4..95bd2af809 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -110,9 +110,7 @@ public: void enable_legend_texture(wxGLCanvas* canvas, bool enable); void enable_picking(wxGLCanvas* canvas, bool enable); void enable_moving(wxGLCanvas* canvas, bool enable); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void enable_gizmos(wxGLCanvas* canvas, bool enable); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void enable_shader(wxGLCanvas* canvas, bool enable); void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); void allow_multisample(wxGLCanvas* canvas, bool allow); diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 1727203907..b4237e162e 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -21,17 +21,17 @@ GLGizmoBase::EState GLGizmoBase::get_state() const return m_state; } +void GLGizmoBase::set_state(GLGizmoBase::EState state) +{ + m_state = state; +} + unsigned int GLGizmoBase::get_textures_id() const { return m_textures[m_state].get_id(); } -int GLGizmoBase::get_textures_height() const -{ - return m_textures[Off].get_height(); -} - -int GLGizmoBase::get_textures_width() const +int GLGizmoBase::get_textures_size() const { return m_textures[Off].get_width(); } diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 1da216ba96..3ca8e32137 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -19,7 +19,7 @@ public: protected: EState m_state; - // textures are assumed to be all the same size in pixels + // textures are assumed to be square and all with the same size in pixels // no internal check is done GLTexture m_textures[Num_States]; @@ -30,10 +30,10 @@ public: bool init(); EState get_state() const; + void set_state(EState state); unsigned int get_textures_id() const; - int get_textures_height() const; - int get_textures_width() const; + int get_textures_size() const; virtual void render() const = 0; From 099d59ad2755bb3b008bc8172c8c39107c4a1ae7 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 13 Jun 2018 13:14:17 +0200 Subject: [PATCH 088/117] Selection on gizmo overlay --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 162 ++++++++++++++++++++----------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 15 +-- 2 files changed, 116 insertions(+), 61 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 9ad7afeb83..e3db3af106 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -564,11 +564,14 @@ GLCanvas3D::Axes::Axes() { } -void GLCanvas3D::Axes::render() const +void GLCanvas3D::Axes::render(bool depth_test) const { ::glDisable(GL_LIGHTING); - // disable depth testing so that axes are not covered by ground - ::glDisable(GL_DEPTH_TEST); + if (depth_test) + ::glEnable(GL_DEPTH_TEST); + else + ::glDisable(GL_DEPTH_TEST); + ::glLineWidth(2.0f); ::glBegin(GL_LINES); // draw line for x axis @@ -582,7 +585,9 @@ void GLCanvas3D::Axes::render() const ::glEnd(); // draw line for Z axis // (re-enable depth test so that axis is correctly shown when objects are behind it) - ::glEnable(GL_DEPTH_TEST); + if (!depth_test) + ::glEnable(GL_DEPTH_TEST); + ::glBegin(GL_LINES); ::glColor3f(0.0f, 0.0f, 1.0f); ::glVertex3f((GLfloat)origin.x, (GLfloat)origin.y, (GLfloat)origin.z); @@ -841,21 +846,6 @@ float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) -1000.0f; } -int GLCanvas3D::LayersEditing::get_first_selected_object_id(const GLVolumeCollection& volumes, unsigned int objects_count) -{ - for (const GLVolume* vol : volumes.volumes) - { - if ((vol != nullptr) && vol->selected) - { - int object_id = vol->select_group_id / 1000000; - // Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy. - if (object_id < 10000) - return (object_id >= (int)objects_count) ? -1 : object_id; - } - } - return -1; -} - bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, float x, float y) { const Rect& rect = get_bar_rect_screen(canvas); @@ -1156,19 +1146,68 @@ void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Poin float top_y = 0.5f * (cnv_h - height); for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - if ((it->second == nullptr) || (it->second->get_state() == GLGizmoBase::On)) + if (it->second == nullptr) continue; float tex_size = (float)it->second->get_textures_size(); float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius - bool inside = length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size; - it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); + if (it->second->get_state() != GLGizmoBase::On) + { + bool inside = length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size; + it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); + } top_y += (tex_size + OverlayGapY); } } +void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Pointf& mouse_pos) +{ + if (!m_enabled) + return; + + float cnv_h = (float)canvas.get_canvas_size().get_height(); + float height = _get_total_overlay_height(); + float top_y = 0.5f * (cnv_h - height); + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + if (it->second == nullptr) + continue; + + float tex_size = (float)it->second->get_textures_size(); + float half_tex_size = 0.5f * tex_size; + + // we currently use circular icons for gizmo, so we check the radius + if (length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size) + it->second->set_state((it->second->get_state() == GLGizmoBase::On) ? GLGizmoBase::Off : GLGizmoBase::On); + else + it->second->set_state(GLGizmoBase::Off); + + top_y += (tex_size + OverlayGapY); + } +} + +void GLCanvas3D::Gizmos::reset_all_states() +{ + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + if (it->second != nullptr) + it->second->set_state(GLGizmoBase::Off); + } +} + +bool GLCanvas3D::Gizmos::contains_mouse() const +{ + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::Hover)) + return true; + } + + return false; +} + void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas) const { if (!m_enabled) @@ -1696,14 +1735,15 @@ void GLCanvas3D::render() if (is_custom_bed) { _render_bed(theta); - _render_axes(); + // disable depth testing so that axes are not covered by ground + _render_axes(false); } _render_objects(); // textured bed needs to be rendered after objects if (!is_custom_bed) { + _render_axes(true); _render_bed(theta); - _render_axes(); } _render_cutting_plane(); _render_warning_texture(); @@ -2439,13 +2479,13 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) return; // Performs layers editing updates, if enabled - if (is_layers_editing_enabled() && (m_print != nullptr)) + if (is_layers_editing_enabled()) { - int object_idx_selected = _get_layers_editing_first_selected_object_id((unsigned int)m_print->objects.size()); + int object_idx_selected = _get_first_selected_object_id(); if (object_idx_selected != -1) { // A volume is selected. Test, whether hovering over a layer thickness bar. - if (_bar_rect_contains((float)evt.GetX(), (float)evt.GetY())) + if (m_layers_editing.bar_rect_contains(*this, (float)evt.GetX(), (float)evt.GetY())) { // Adjust the width of the selection. m_layers_editing.band_width = std::max(std::min(m_layers_editing.band_width * (1.0f + 0.1f * (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta()), 10.0f), 1.5f); @@ -2485,8 +2525,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { Point pos(evt.GetX(), evt.GetY()); - int selected_object_idx = (is_layers_editing_enabled() && (m_print != nullptr)) ? _get_layers_editing_first_selected_object_id(m_print->objects.size()) : -1; - m_layers_editing.last_object_id = selected_object_idx; + int selected_object_idx = _get_first_selected_object_id(); + int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; + m_layers_editing.last_object_id = layer_editing_object_idx; + bool gizmos_contains_mouse = m_gizmos.contains_mouse(); if (evt.Entering()) { @@ -2506,25 +2548,30 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // on a volume or not. int volume_idx = m_hover_volume_id; m_layers_editing.state = LayersEditing::Unknown; - if ((selected_object_idx != -1) && _bar_rect_contains(pos.x, pos.y)) + if ((layer_editing_object_idx != -1) && m_layers_editing.bar_rect_contains(*this, pos.x, pos.y)) { // A volume is selected and the mouse is inside the layer thickness bar. // Start editing the layer height. m_layers_editing.state = LayersEditing::Editing; _perform_layer_editing_action(&evt); } - else if ((selected_object_idx != -1) && _reset_rect_contains(pos.x, pos.y)) + else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos.x, pos.y)) { if (evt.LeftDown()) { // A volume is selected and the mouse is inside the reset button. - m_print->get_object(selected_object_idx)->reset_layer_height_profile(); + m_print->get_object(layer_editing_object_idx)->reset_layer_height_profile(); // Index 2 means no editing, just wait for mouse up event. m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } } + else if ((selected_object_idx != -1) && gizmos_contains_mouse) + { + m_gizmos.update_on_off_state(*this, m_mouse.position); + m_dirty = true; + } else { // Select volume in this 3D canvas. @@ -2586,7 +2633,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } } - else if (evt.Dragging() && evt.LeftIsDown() && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1)) + else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1)) { m_mouse.dragging = true; @@ -2633,11 +2680,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } - else if (evt.Dragging()) + else if (evt.Dragging() && !gizmos_contains_mouse) { m_mouse.dragging = true; - if ((m_layers_editing.state != LayersEditing::Unknown) && (selected_object_idx != -1)) + if ((m_layers_editing.state != LayersEditing::Unknown) && (layer_editing_object_idx != -1)) { if (m_layers_editing.state == LayersEditing::Editing) _perform_layer_editing_action(&evt); @@ -2685,7 +2732,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_layers_editing.state = LayersEditing::Unknown; _stop_timer(); - if (selected_object_idx != -1) + if (layer_editing_object_idx != -1) m_on_model_update_callback.call(); } else if ((m_mouse.drag.volume_idx != -1) && m_mouse.dragging) @@ -3050,8 +3097,10 @@ void GLCanvas3D::_picking_pass() const } // updates gizmos overlay - if (!m_volumes.empty()) + if (_get_first_selected_object_id() != -1) m_gizmos.update_hover_state(*this, pos); + else + m_gizmos.reset_all_states(); } } @@ -3093,9 +3142,9 @@ void GLCanvas3D::_render_bed(float theta) const m_bed.render(theta); } -void GLCanvas3D::_render_axes() const +void GLCanvas3D::_render_axes(bool depth_test) const { - m_axes.render(); + m_axes.render(depth_test); } void GLCanvas3D::_render_objects() const @@ -3302,11 +3351,6 @@ float GLCanvas3D::_get_layers_editing_cursor_z_relative() const return m_layers_editing.get_cursor_z_relative(*this); } -int GLCanvas3D::_get_layers_editing_first_selected_object_id(unsigned int objects_count) const -{ - return m_layers_editing.get_first_selected_object_id(m_volumes, objects_count); -} - void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) { int object_idx_selected = m_layers_editing.last_object_id; @@ -3355,16 +3399,6 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) _start_timer(); } -bool GLCanvas3D::_bar_rect_contains(float x, float y) const -{ - return m_layers_editing.bar_rect_contains(*this, x, y); -} - -bool GLCanvas3D::_reset_rect_contains(float x, float y) const -{ - return m_layers_editing.reset_rect_contains(*this, x, y); -} - Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) { if (!set_current()) @@ -3401,6 +3435,26 @@ void GLCanvas3D::_stop_timer() m_timer->Stop(); } +int GLCanvas3D::_get_first_selected_object_id() const +{ + if (m_print != nullptr) + { + int objects_count = (int)m_print->objects.size(); + + for (const GLVolume* vol : m_volumes.volumes) + { + if ((vol != nullptr) && vol->selected) + { + int object_id = vol->select_group_id / 1000000; + // Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy. + if (object_id < 10000) + return (object_id >= objects_count) ? -1 : object_id; + } + } + } + return -1; +} + static inline int hex_digit_to_int(const char c) { return diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 2852d82d1a..738df637c4 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -189,7 +189,7 @@ public: Axes(); - void render() const; + void render(bool depth_test) const; }; class CuttingPlane @@ -277,7 +277,6 @@ public: int get_shader_program_id() const; static float get_cursor_z_relative(const GLCanvas3D& canvas); - static int get_first_selected_object_id(const GLVolumeCollection& volumes, unsigned int objects_count); static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y); static Rect get_bar_rect_screen(const GLCanvas3D& canvas); @@ -355,6 +354,10 @@ public: void reset_selection(); void update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos); + void update_on_off_state(const GLCanvas3D& canvas, const Pointf& mouse_pos); + void reset_all_states(); + + bool contains_mouse() const; void render(const GLCanvas3D& canvas) const; @@ -567,7 +570,7 @@ private: void _picking_pass() const; void _render_background() const; void _render_bed(float theta) const; - void _render_axes() const; + void _render_axes(bool depth_test) const; void _render_objects() const; void _render_cutting_plane() const; void _render_warning_texture() const; @@ -577,12 +580,8 @@ private: void _render_gizmo() const; float _get_layers_editing_cursor_z_relative() const; - int _get_layers_editing_first_selected_object_id(unsigned int objects_count) const; void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); - bool _bar_rect_contains(float x, float y) const; - bool _reset_rect_contains(float x, float y) const; - // 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. Pointf3 _mouse_to_3d(const Point& mouse_pos, float* z = nullptr); @@ -590,6 +589,8 @@ private: void _start_timer(); void _stop_timer(); + int _get_first_selected_object_id() const; + // generates gcode extrusion paths geometry void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector& tool_colors); // generates gcode travel paths geometry From 3a19b81cefd9438eb4ac128a976914e03cf0c684 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 13 Jun 2018 15:44:04 +0200 Subject: [PATCH 089/117] Scale gizmo rendering --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 76 ++++++++++++++++++------------ xs/src/slic3r/GUI/GLCanvas3D.hpp | 8 ++-- xs/src/slic3r/GUI/GLGizmo.cpp | 81 +++++++++++++++++++++++++++----- xs/src/slic3r/GUI/GLGizmo.hpp | 19 ++++++-- 4 files changed, 132 insertions(+), 52 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index e3db3af106..9dbfeaff3a 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1125,17 +1125,6 @@ void GLCanvas3D::Gizmos::set_enabled(bool enable) m_enabled = enable; } -void GLCanvas3D::Gizmos::select(EType type) -{ - if (m_gizmos.find(type) != m_gizmos.end()) - m_current = type; -} - -void GLCanvas3D::Gizmos::reset_selection() -{ - m_current = Undefined; -} - void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos) { if (!m_enabled) @@ -1180,7 +1169,18 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Poi // we currently use circular icons for gizmo, so we check the radius if (length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size) - it->second->set_state((it->second->get_state() == GLGizmoBase::On) ? GLGizmoBase::Off : GLGizmoBase::On); + { + if ((it->second->get_state() == GLGizmoBase::On)) + { + it->second->set_state(GLGizmoBase::Off); + m_current = Undefined; + } + else + { + it->second->set_state(GLGizmoBase::On); + m_current = it->first; + } + } else it->second->set_state(GLGizmoBase::Off); @@ -1190,15 +1190,23 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Poi void GLCanvas3D::Gizmos::reset_all_states() { + if (!m_enabled) + return; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if (it->second != nullptr) it->second->set_state(GLGizmoBase::Off); } + + m_current = Undefined; } bool GLCanvas3D::Gizmos::contains_mouse() const { + if (!m_enabled) + return false; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::Hover)) @@ -1208,7 +1216,7 @@ bool GLCanvas3D::Gizmos::contains_mouse() const return false; } -void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas) const +void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const { if (!m_enabled) return; @@ -1219,9 +1227,10 @@ void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas) const ::glLoadIdentity(); _render_overlay(canvas); - _render_current_gizmo(); ::glPopMatrix(); + + _render_current_gizmo(box); } void GLCanvas3D::Gizmos::_reset() @@ -1256,13 +1265,13 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const } } -void GLCanvas3D::Gizmos::_render_current_gizmo() const +void GLCanvas3D::Gizmos::_render_current_gizmo(const BoundingBoxf3& box) const { GizmosMap::const_iterator it = m_gizmos.find(m_current); if (it == m_gizmos.end()) return; - it->second->render(); + it->second->render(box); } float GLCanvas3D::Gizmos::_get_total_overlay_height() const @@ -2603,14 +2612,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_picking_enabled) _on_select(volume_idx); - // 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. - Pointf3 pos3d = (volume_idx == -1) ? Pointf3(DBL_MAX, DBL_MAX) : _mouse_to_3d(pos); - if (volume_idx != -1) { if (evt.LeftDown() && m_moving_enabled) { + // 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. + Pointf3 pos3d = (volume_idx == -1) ? Pointf3(DBL_MAX, DBL_MAX) : _mouse_to_3d(pos); + // Only accept the initial position, if it is inside the volume bounding box. BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box(); volume_bbox.offset(1.0); @@ -2895,6 +2904,17 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box() const return bb; } +BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const +{ + BoundingBoxf3 bb; + for (const GLVolume* volume : m_volumes.volumes) + { + if ((volume != nullptr) && volume->selected) + bb.merge(volume->transformed_bounding_box()); + } + return bb; +} + void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { // Calculate the zoom factor needed to adjust viewport to bounding box. @@ -3150,7 +3170,6 @@ void GLCanvas3D::_render_axes(bool depth_test) const void GLCanvas3D::_render_objects() const { if (m_volumes.empty()) - return; ::glEnable(GL_LIGHTING); @@ -3343,7 +3362,7 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const void GLCanvas3D::_render_gizmo() const { - m_gizmos.render(*this); + m_gizmos.render(*this, _selected_volumes_bounding_box()); } float GLCanvas3D::_get_layers_editing_cursor_z_relative() const @@ -4079,15 +4098,12 @@ void GLCanvas3D::_on_move(const std::vector& volume_idxs) void GLCanvas3D::_on_select(int volume_idx) { int id = -1; - if (volume_idx < (int)m_volumes.volumes.size()) + if ((volume_idx != -1) && (volume_idx < (int)m_volumes.volumes.size())) { - if (volume_idx != -1) - { - if (m_select_by == "volume") - id = m_volumes.volumes[volume_idx]->volume_idx(); - else if (m_select_by == "object") - id = m_volumes.volumes[volume_idx]->object_idx(); - } + if (m_select_by == "volume") + id = m_volumes.volumes[volume_idx]->volume_idx(); + else if (m_select_by == "object") + id = m_volumes.volumes[volume_idx]->object_idx(); } m_on_select_object_callback.call(id); } diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 738df637c4..291b07553f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -350,22 +350,19 @@ public: bool is_enabled() const; void set_enabled(bool enable); - void select(EType type); - void reset_selection(); - void update_hover_state(const GLCanvas3D& canvas, const Pointf& mouse_pos); void update_on_off_state(const GLCanvas3D& canvas, const Pointf& mouse_pos); void reset_all_states(); bool contains_mouse() const; - void render(const GLCanvas3D& canvas) const; + void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const; private: void _reset(); void _render_overlay(const GLCanvas3D& canvas) const; - void _render_current_gizmo() const; + void _render_current_gizmo(const BoundingBoxf3& box) const; float _get_total_overlay_height() const; }; @@ -557,6 +554,7 @@ private: void _resize(unsigned int w, unsigned int h); BoundingBoxf3 _max_bounding_box() const; + BoundingBoxf3 _selected_volumes_bounding_box() const; void _zoom_to_bounding_box(const BoundingBoxf3& bbox); float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index b4237e162e..75f39aad98 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -1,6 +1,9 @@ #include "GLGizmo.hpp" #include "../../libslic3r/Utils.hpp" +#include "../../libslic3r/BoundingBox.hpp" + +#include #include @@ -16,6 +19,11 @@ GLGizmoBase::~GLGizmoBase() { } +bool GLGizmoBase::init() +{ + return on_init(); +} + GLGizmoBase::EState GLGizmoBase::get_state() const { return m_state; @@ -36,9 +44,9 @@ int GLGizmoBase::get_textures_size() const return m_textures[Off].get_width(); } -bool GLGizmoBase::init() +void GLGizmoBase::render(const BoundingBoxf3& box) const { - return on_init(); + on_render(box); } GLGizmoRotate::GLGizmoRotate() @@ -49,11 +57,6 @@ GLGizmoRotate::GLGizmoRotate() { } -void GLGizmoRotate::render() const -{ - std::cout << "GLGizmoRotate::render()" << std::endl; -} - bool GLGizmoRotate::on_init() { std::string path = resources_dir() + "/icons/overlay/"; @@ -73,6 +76,14 @@ bool GLGizmoRotate::on_init() return true; } +void GLGizmoRotate::on_render(const BoundingBoxf3& box) const +{ + std::cout << "GLGizmoRotate::render()" << std::endl; +} + +const float GLGizmoScale::Offset = 5.0f; +const float GLGizmoScale::SquareHalfSize = 2.0f; + GLGizmoScale::GLGizmoScale() : GLGizmoBase() , m_scale_x(1.0f) @@ -81,11 +92,6 @@ GLGizmoScale::GLGizmoScale() { } -void GLGizmoScale::render() const -{ - std::cout << "GLGizmoScale::render()" << std::endl; -} - bool GLGizmoScale::on_init() { std::string path = resources_dir() + "/icons/overlay/"; @@ -105,5 +111,56 @@ bool GLGizmoScale::on_init() return true; } +void GLGizmoScale::on_render(const BoundingBoxf3& box) const +{ + ::glDisable(GL_LIGHTING); + ::glDisable(GL_DEPTH_TEST); + + const Pointf3& size = box.size(); + const Pointf3& center = box.center(); + + Pointf3 half_scaled_size = 0.5 * Pointf3((coordf_t)m_scale_x * size.x, (coordf_t)m_scale_y * size.y, (coordf_t)m_scale_z * size.z); + coordf_t min_x = center.x - half_scaled_size.x - (coordf_t)Offset; + coordf_t max_x = center.x + half_scaled_size.x + (coordf_t)Offset; + coordf_t min_y = center.y - half_scaled_size.y - (coordf_t)Offset; + coordf_t max_y = center.y + half_scaled_size.y + (coordf_t)Offset; + + ::glLineWidth(2.0f); + ::glBegin(GL_LINE_LOOP); + // draw outline + ::glColor3f(1.0f, 1.0f, 1.0f); + ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); + ::glEnd(); + + // draw grabbers + ::glColor3f(1.0f, 0.38f, 0.0f); + ::glDisable(GL_CULL_FACE); + _render_square(Pointf3(min_x, min_y, 0.0)); + _render_square(Pointf3(max_x, min_y, 0.0)); + _render_square(Pointf3(max_x, max_y, 0.0)); + _render_square(Pointf3(min_x, max_y, 0.0)); + ::glEnable(GL_CULL_FACE); +} + +void GLGizmoScale::_render_square(const Pointf3& center) const +{ + float min_x = (float)center.x - SquareHalfSize; + float max_x = (float)center.x + SquareHalfSize; + float min_y = (float)center.y - SquareHalfSize; + float max_y = (float)center.y + SquareHalfSize; + + ::glBegin(GL_TRIANGLES); + ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); + ::glEnd(); +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 3ca8e32137..a626305c10 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -4,6 +4,10 @@ #include "../../slic3r/GUI/GLTexture.hpp" namespace Slic3r { + +class BoundingBoxf3; +class Pointf3; + namespace GUI { class GLGizmoBase @@ -35,10 +39,11 @@ public: unsigned int get_textures_id() const; int get_textures_size() const; - virtual void render() const = 0; + void render(const BoundingBoxf3& box) const; protected: virtual bool on_init() = 0; + virtual void on_render(const BoundingBoxf3& box) const = 0; }; class GLGizmoRotate : public GLGizmoBase @@ -50,14 +55,16 @@ class GLGizmoRotate : public GLGizmoBase public: GLGizmoRotate(); - void render() const; - protected: virtual bool on_init(); + virtual void on_render(const BoundingBoxf3& box) const; }; class GLGizmoScale : public GLGizmoBase { + static const float Offset; + static const float SquareHalfSize; + float m_scale_x; float m_scale_y; float m_scale_z; @@ -65,10 +72,12 @@ class GLGizmoScale : public GLGizmoBase public: GLGizmoScale(); - void render() const; - protected: virtual bool on_init(); + virtual void on_render(const BoundingBoxf3& box) const; + +private: + void _render_square(const Pointf3& center) const; }; } // namespace GUI From bc5640eef4e9f3dcbe83cc52dfa35fed6fe6a480 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 14 Jun 2018 10:00:59 +0200 Subject: [PATCH 090/117] Rotate gizmo rendering --- xs/src/slic3r/GUI/GLGizmo.cpp | 158 ++++++++++++++++++++++++++++------ xs/src/slic3r/GUI/GLGizmo.hpp | 28 +++++- 2 files changed, 157 insertions(+), 29 deletions(-) diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 75f39aad98..d988649311 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -10,6 +10,10 @@ namespace Slic3r { namespace GUI { +const float GLGizmoBase::BaseColor[3] = { 1.0f, 1.0f, 1.0f }; +const float GLGizmoBase::HighlightColor[3] = { 1.0f, 0.38f, 0.0f }; +const float GLGizmoBase::GrabberHalfSize = 2.0f; + GLGizmoBase::GLGizmoBase() : m_state(Off) { @@ -49,6 +53,35 @@ void GLGizmoBase::render(const BoundingBoxf3& box) const on_render(box); } +void GLGizmoBase::_render_square(const Pointf3& center) const +{ + float min_x = (float)center.x - GrabberHalfSize; + float max_x = (float)center.x + GrabberHalfSize; + float min_y = (float)center.y - GrabberHalfSize; + float max_y = (float)center.y + GrabberHalfSize; + + ::glDisable(GL_CULL_FACE); + ::glBegin(GL_TRIANGLES); + ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); + ::glEnd(); + ::glEnable(GL_CULL_FACE); +} + +const float GLGizmoRotate::Offset = 5.0f; +const unsigned int GLGizmoRotate::CircleResolution = 64; +const unsigned int GLGizmoRotate::ScaleStepsCount = 60; +const float GLGizmoRotate::ScaleStepRad = 2.0f * (float)PI / GLGizmoRotate::ScaleStepsCount; +const unsigned int GLGizmoRotate::ScaleLongEvery = 5; +const float GLGizmoRotate::ScaleLongTooth = 2.0f; +const float GLGizmoRotate::ScaleShortTooth = 1.0f; +const unsigned int GLGizmoRotate::SnapRegionsCount = 8; +const float GLGizmoRotate::GrabberOffset = 5.0f; + GLGizmoRotate::GLGizmoRotate() : GLGizmoBase() , m_angle_x(0.0f) @@ -78,11 +111,105 @@ bool GLGizmoRotate::on_init() void GLGizmoRotate::on_render(const BoundingBoxf3& box) const { - std::cout << "GLGizmoRotate::render()" << std::endl; + ::glDisable(GL_LIGHTING); + ::glDisable(GL_DEPTH_TEST); + + const Pointf3& size = box.size(); + const Pointf3& center = box.center(); + + float radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y)); + + ::glLineWidth(2.0f); + ::glColor3fv(BaseColor); + + _render_circle(center, radius); + _render_scale(center, radius); + _render_snap_radii(center, radius); + _render_reference_radius(center, radius); + _render_grabber(center, radius); +} + +void GLGizmoRotate::_render_circle(const Pointf3& center, float radius) const +{ + ::glBegin(GL_LINE_LOOP); + for (unsigned int i = 0; i < ScaleStepsCount; ++i) + { + float angle = (float)i * ScaleStepRad; + float x = center.x + ::cos(angle) * radius; + float y = center.y + ::sin(angle) * radius; + ::glVertex3f((GLfloat)x, (GLfloat)y, 0.0f); + } + ::glEnd(); +} + +void GLGizmoRotate::_render_scale(const Pointf3& center, float radius) const +{ + float out_radius_long = radius + ScaleLongTooth; + float out_radius_short = radius + ScaleShortTooth; + + ::glBegin(GL_LINES); + for (unsigned int i = 0; i < ScaleStepsCount; ++i) + { + float angle = (float)i * ScaleStepRad; + float cosa = ::cos(angle); + float sina = ::sin(angle); + float in_x = center.x + cosa * radius; + float in_y = center.y + sina * radius; + float out_x = (i % ScaleLongEvery == 0) ? center.x + cosa * out_radius_long : center.x + cosa * out_radius_short; + float out_y = (i % ScaleLongEvery == 0) ? center.y + sina * out_radius_long : center.y + sina * out_radius_short; + ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, 0.0f); + ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, 0.0f); + } + ::glEnd(); +} + +void GLGizmoRotate::_render_snap_radii(const Pointf3& center, float radius) const +{ + float step_deg = 2.0f * (float)PI / (float)SnapRegionsCount; + + float in_radius = radius / 3.0f; + float out_radius = 2.0f * in_radius; + + ::glBegin(GL_LINES); + for (unsigned int i = 0; i < SnapRegionsCount; ++i) + { + float angle = (float)i * step_deg; + float cosa = ::cos(angle); + float sina = ::sin(angle); + float in_x = center.x + cosa * in_radius; + float in_y = center.y + sina * in_radius; + float out_x = center.x + cosa * out_radius; + float out_y = center.y + sina * out_radius; + ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, 0.0f); + ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, 0.0f); + } + ::glEnd(); +} + +void GLGizmoRotate::_render_reference_radius(const Pointf3& center, float radius) const +{ + ::glBegin(GL_LINES); + ::glVertex3f((GLfloat)center.x, (GLfloat)center.y, 0.0f); + ::glVertex3f((GLfloat)center.x + radius, (GLfloat)center.y, 0.0f); + ::glEnd(); +} + +void GLGizmoRotate::_render_grabber(const Pointf3& center, float radius) const +{ + float grabber_radius = radius + GrabberOffset; + float x = center.x + ::cos(m_angle_z) * grabber_radius; + float y = center.y + ::sin(m_angle_z) * grabber_radius; + + ::glBegin(GL_LINES); + ::glVertex3f((GLfloat)center.x, (GLfloat)center.y, 0.0f); + ::glVertex3f((GLfloat)x, (GLfloat)y, 0.0f); + ::glEnd(); + + ::glColor3fv(HighlightColor); + _render_square(Pointf3((coordf_t)x, (coordf_t)y, 0.0)); } const float GLGizmoScale::Offset = 5.0f; -const float GLGizmoScale::SquareHalfSize = 2.0f; GLGizmoScale::GLGizmoScale() : GLGizmoBase() @@ -119,16 +246,16 @@ void GLGizmoScale::on_render(const BoundingBoxf3& box) const const Pointf3& size = box.size(); const Pointf3& center = box.center(); - Pointf3 half_scaled_size = 0.5 * Pointf3((coordf_t)m_scale_x * size.x, (coordf_t)m_scale_y * size.y, (coordf_t)m_scale_z * size.z); + Pointf half_scaled_size = 0.5 * Pointf((coordf_t)m_scale_x * size.x, (coordf_t)m_scale_y * size.y); coordf_t min_x = center.x - half_scaled_size.x - (coordf_t)Offset; coordf_t max_x = center.x + half_scaled_size.x + (coordf_t)Offset; coordf_t min_y = center.y - half_scaled_size.y - (coordf_t)Offset; coordf_t max_y = center.y + half_scaled_size.y + (coordf_t)Offset; ::glLineWidth(2.0f); - ::glBegin(GL_LINE_LOOP); + ::glColor3fv(BaseColor); // draw outline - ::glColor3f(1.0f, 1.0f, 1.0f); + ::glBegin(GL_LINE_LOOP); ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); @@ -136,30 +263,11 @@ void GLGizmoScale::on_render(const BoundingBoxf3& box) const ::glEnd(); // draw grabbers - ::glColor3f(1.0f, 0.38f, 0.0f); - ::glDisable(GL_CULL_FACE); + ::glColor3fv(HighlightColor); _render_square(Pointf3(min_x, min_y, 0.0)); _render_square(Pointf3(max_x, min_y, 0.0)); _render_square(Pointf3(max_x, max_y, 0.0)); _render_square(Pointf3(min_x, max_y, 0.0)); - ::glEnable(GL_CULL_FACE); -} - -void GLGizmoScale::_render_square(const Pointf3& center) const -{ - float min_x = (float)center.x - SquareHalfSize; - float max_x = (float)center.x + SquareHalfSize; - float min_y = (float)center.y - SquareHalfSize; - float max_y = (float)center.y + SquareHalfSize; - - ::glBegin(GL_TRIANGLES); - ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); - ::glEnd(); } } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index a626305c10..b3e6fd8fd2 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -12,6 +12,11 @@ namespace GUI { class GLGizmoBase { +protected: + static const float BaseColor[3]; + static const float HighlightColor[3]; + static const float GrabberHalfSize; + public: enum EState { @@ -44,10 +49,22 @@ public: protected: virtual bool on_init() = 0; virtual void on_render(const BoundingBoxf3& box) const = 0; + + void _render_square(const Pointf3& center) const; }; class GLGizmoRotate : public GLGizmoBase { + static const float Offset; + static const unsigned int CircleResolution; + static const unsigned int ScaleStepsCount; + static const float ScaleStepRad; + static const unsigned int ScaleLongEvery; + static const float ScaleLongTooth; + static const float ScaleShortTooth; + static const unsigned int SnapRegionsCount; + static const float GrabberOffset; + float m_angle_x; float m_angle_y; float m_angle_z; @@ -58,12 +75,18 @@ public: protected: virtual bool on_init(); virtual void on_render(const BoundingBoxf3& box) const; + +private: + void _render_circle(const Pointf3& center, float radius) const; + void _render_scale(const Pointf3& center, float radius) const; + void _render_snap_radii(const Pointf3& center, float radius) const; + void _render_reference_radius(const Pointf3& center, float radius) const; + void _render_grabber(const Pointf3& center, float radius) const; }; class GLGizmoScale : public GLGizmoBase { static const float Offset; - static const float SquareHalfSize; float m_scale_x; float m_scale_y; @@ -75,9 +98,6 @@ public: protected: virtual bool on_init(); virtual void on_render(const BoundingBoxf3& box) const; - -private: - void _render_square(const Pointf3& center) const; }; } // namespace GUI From 7fb6e2aa033f0e3f50ea846dc911556b8c1e66fb Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 14 Jun 2018 10:37:28 +0200 Subject: [PATCH 091/117] Use mipmaps for bed textures --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 12 +++---- xs/src/slic3r/GUI/GLGizmo.cpp | 12 +++---- xs/src/slic3r/GUI/GLTexture.cpp | 55 +++++++++++++++++++++++++++++--- xs/src/slic3r/GUI/GLTexture.hpp | 9 ++++-- 4 files changed, 70 insertions(+), 18 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 9dbfeaff3a..e768e96894 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -426,7 +426,7 @@ void GLCanvas3D::Bed::_render_mk2(float theta) const std::string filename = resources_dir() + "/icons/bed/mk2_top.png"; if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) { - if (!m_top_texture.load_from_file(filename)) + if (!m_top_texture.load_from_file(filename, true)) { _render_custom(); return; @@ -436,7 +436,7 @@ void GLCanvas3D::Bed::_render_mk2(float theta) const filename = resources_dir() + "/icons/bed/mk2_bottom.png"; if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename)) { - if (!m_bottom_texture.load_from_file(filename)) + if (!m_bottom_texture.load_from_file(filename, true)) { _render_custom(); return; @@ -451,7 +451,7 @@ void GLCanvas3D::Bed::_render_mk3(float theta) const std::string filename = resources_dir() + "/icons/bed/mk3_top.png"; if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) { - if (!m_top_texture.load_from_file(filename)) + if (!m_top_texture.load_from_file(filename, true)) { _render_custom(); return; @@ -461,7 +461,7 @@ void GLCanvas3D::Bed::_render_mk3(float theta) const filename = resources_dir() + "/icons/bed/mk3_bottom.png"; if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename)) { - if (!m_bottom_texture.load_from_file(filename)) + if (!m_bottom_texture.load_from_file(filename, true)) { _render_custom(); return; @@ -911,7 +911,7 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas if (m_tooltip_texture.get_id() == 0) { std::string filename = resources_dir() + "/icons/variable_layer_height_tooltip.png"; - if (!m_tooltip_texture.load_from_file(filename)) + if (!m_tooltip_texture.load_from_file(filename, false)) return; } @@ -935,7 +935,7 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co if (m_reset_texture.get_id() == 0) { std::string filename = resources_dir() + "/icons/variable_layer_height_reset.png"; - if (!m_reset_texture.load_from_file(filename)) + if (!m_reset_texture.load_from_file(filename, false)) return; } diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index d988649311..64df649d4c 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -95,15 +95,15 @@ bool GLGizmoRotate::on_init() std::string path = resources_dir() + "/icons/overlay/"; std::string filename = path + "rotate_off.png"; - if (!m_textures[Off].load_from_file(filename)) + if (!m_textures[Off].load_from_file(filename, false)) return false; filename = path + "rotate_hover.png"; - if (!m_textures[Hover].load_from_file(filename)) + if (!m_textures[Hover].load_from_file(filename, false)) return false; filename = path + "rotate_on.png"; - if (!m_textures[On].load_from_file(filename)) + if (!m_textures[On].load_from_file(filename, false)) return false; return true; @@ -224,15 +224,15 @@ bool GLGizmoScale::on_init() std::string path = resources_dir() + "/icons/overlay/"; std::string filename = path + "scale_off.png"; - if (!m_textures[Off].load_from_file(filename)) + if (!m_textures[Off].load_from_file(filename, false)) return false; filename = path + "scale_hover.png"; - if (!m_textures[Hover].load_from_file(filename)) + if (!m_textures[Hover].load_from_file(filename, false)) return false; filename = path + "scale_on.png"; - if (!m_textures[On].load_from_file(filename)) + if (!m_textures[On].load_from_file(filename, false)) return false; return true; diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/xs/src/slic3r/GUI/GLTexture.cpp index b9eb118a97..593362e546 100644 --- a/xs/src/slic3r/GUI/GLTexture.cpp +++ b/xs/src/slic3r/GUI/GLTexture.cpp @@ -5,6 +5,7 @@ #include #include +#include namespace Slic3r { namespace GUI { @@ -22,7 +23,7 @@ GLTexture::~GLTexture() reset(); } -bool GLTexture::load_from_file(const std::string& filename) +bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmaps) { reset(); @@ -68,10 +69,20 @@ bool GLTexture::load_from_file(const std::string& filename) // sends data to gpu ::glGenTextures(1, &m_id); ::glBindTexture(GL_TEXTURE_2D, m_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(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + if (generate_mipmaps) + { + // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards + _generate_mipmaps(image); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } + else + { + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + } + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ::glBindTexture(GL_TEXTURE_2D, 0); m_source = filename; @@ -134,5 +145,41 @@ void GLTexture::render_texture(unsigned int tex_id, float left, float right, flo ::glEnable(GL_LIGHTING); } +void GLTexture::_generate_mipmaps(wxImage& image) +{ + int w = image.GetWidth(); + int h = image.GetHeight(); + GLint level = 0; + std::vector data(w * h * 4, 0); + + while ((w > 1) && (h > 1)) + { + ++level; + + w = std::max(w / 2, 1); + h = std::max(h / 2, 1); + + int n_pixels = w * h; + + image = image.ResampleBicubic(w, h); + + unsigned char* img_rgb = image.GetData(); + unsigned char* img_alpha = image.GetAlpha(); + + data.resize(n_pixels * 4); + for (int i = 0; i < n_pixels; ++i) + { + int data_id = i * 4; + int img_id = i * 3; + data[data_id + 0] = img_rgb[img_id + 0]; + data[data_id + 1] = img_rgb[img_id + 1]; + data[data_id + 2] = img_rgb[img_id + 2]; + data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; + } + + ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + } +} + } // namespace GUI } // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLTexture.hpp b/xs/src/slic3r/GUI/GLTexture.hpp index 3e3bf83f7c..70480c605e 100644 --- a/xs/src/slic3r/GUI/GLTexture.hpp +++ b/xs/src/slic3r/GUI/GLTexture.hpp @@ -3,10 +3,12 @@ #include +class wxImage; + namespace Slic3r { namespace GUI { - struct GLTexture + class GLTexture { private: unsigned int m_id; @@ -18,7 +20,7 @@ namespace GUI { GLTexture(); ~GLTexture(); - bool load_from_file(const std::string& filename); + bool load_from_file(const std::string& filename, bool generate_mipmaps); void reset(); unsigned int get_id() const; @@ -27,6 +29,9 @@ namespace GUI { const std::string& get_source() const; static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top); + + private: + void _generate_mipmaps(wxImage& image); }; } // namespace GUI From 4d405977dda6805e32b98ecb0836ad060c7da16a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 14 Jun 2018 12:34:19 +0200 Subject: [PATCH 092/117] Keep selection when panning/rotating 3D view --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 53 ++++++++++++++++++++++---------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 2 +- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index e768e96894..0112f7a3c8 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1202,15 +1202,27 @@ void GLCanvas3D::Gizmos::reset_all_states() m_current = Undefined; } -bool GLCanvas3D::Gizmos::contains_mouse() const +bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const { if (!m_enabled) return false; + float cnv_h = (float)canvas.get_canvas_size().get_height(); + float height = _get_total_overlay_height(); + float top_y = 0.5f * (cnv_h - height); for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::Hover)) + if (it->second == nullptr) + continue; + + float tex_size = (float)it->second->get_textures_size(); + float half_tex_size = 0.5f * tex_size; + + // we currently use circular icons for gizmo, so we check the radius + if (length(Pointf(OverlayOffsetX + half_tex_size, top_y + half_tex_size).vector_to(mouse_pos)) < half_tex_size) return true; + + top_y += (tex_size + OverlayGapY); } return false; @@ -1223,14 +1235,14 @@ void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& b ::glDisable(GL_DEPTH_TEST); + _render_current_gizmo(box); + ::glPushMatrix(); ::glLoadIdentity(); _render_overlay(canvas); ::glPopMatrix(); - - _render_current_gizmo(box); } void GLCanvas3D::Gizmos::_reset() @@ -1757,8 +1769,8 @@ void GLCanvas3D::render() _render_cutting_plane(); _render_warning_texture(); _render_legend_texture(); - _render_layer_editing_overlay(); _render_gizmo(); + _render_layer_editing_overlay(); m_canvas->SwapBuffers(); } @@ -2537,7 +2549,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) int selected_object_idx = _get_first_selected_object_id(); int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; m_layers_editing.last_object_id = layer_editing_object_idx; - bool gizmos_contains_mouse = m_gizmos.contains_mouse(); + bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); if (evt.Entering()) { @@ -2549,7 +2561,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.set_start_position_2D_as_invalid(); #endif } - else if (evt.LeftDClick()) + else if (evt.LeftDClick() && (m_hover_volume_id != -1)) m_on_double_click_callback.call(); else if (evt.LeftDown() || evt.RightDown()) { @@ -2576,7 +2588,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } } - else if ((selected_object_idx != -1) && gizmos_contains_mouse) + else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse) { m_gizmos.update_on_off_state(*this, m_mouse.position); m_dirty = true; @@ -2589,11 +2601,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_picking_enabled && ((volume_idx != -1) || !is_layers_editing_enabled())) { - deselect_volumes(); - select_volume(volume_idx); - if (volume_idx != -1) { + deselect_volumes(); + select_volume(volume_idx); int group_id = m_volumes.volumes[volume_idx]->select_group_id; if (group_id != -1) { @@ -2603,13 +2614,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) vol->selected = true; } } + m_dirty = true; } - - m_dirty = true; } // propagate event through callback - if (m_picking_enabled) + if (m_picking_enabled && (volume_idx != -1)) _on_select(volume_idx); if (volume_idx != -1) @@ -2642,7 +2652,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } } - else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1)) + else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1)) { m_mouse.dragging = true; @@ -2689,7 +2699,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } - else if (evt.Dragging() && !gizmos_contains_mouse) + else if (evt.Dragging() && !gizmos_overlay_contains_mouse) { m_mouse.dragging = true; @@ -2763,7 +2773,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) _on_move(volume_idxs); } - + else if (!m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !is_layers_editing_enabled()) + { + // deselect and propagate event through callback + if (m_picking_enabled) + { + deselect_volumes(); + _on_select(-1); + } + } + m_mouse.drag.volume_idx = -1; m_mouse.set_start_position_3D_as_invalid(); m_mouse.set_start_position_2D_as_invalid(); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 291b07553f..cdfc758765 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -354,7 +354,7 @@ public: void update_on_off_state(const GLCanvas3D& canvas, const Pointf& mouse_pos); void reset_all_states(); - bool contains_mouse() const; + bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const; void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const; From c624d6bb0a2ea122c6ba63cea30acb31452ecba0 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 14 Jun 2018 15:32:26 +0200 Subject: [PATCH 093/117] Hover on gizmo grabbers rendering --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 40 +++++++++++++--- xs/src/slic3r/GUI/GLCanvas3D.hpp | 3 ++ xs/src/slic3r/GUI/GLGizmo.cpp | 82 +++++++++++++++++++++++++++++--- xs/src/slic3r/GUI/GLGizmo.hpp | 10 +++- 4 files changed, 121 insertions(+), 14 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 0112f7a3c8..64538d940c 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1202,6 +1202,18 @@ void GLCanvas3D::Gizmos::reset_all_states() m_current = Undefined; } +void GLCanvas3D::Gizmos::set_hover_id(int id) +{ + if (!m_enabled) + return; + + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::On)) + it->second->set_hover_id(id); + } +} + bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const { if (!m_enabled) @@ -1245,6 +1257,20 @@ void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& b ::glPopMatrix(); } +void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const +{ + if (!m_enabled) + return; + + ::glDisable(GL_DEPTH_TEST); + + GizmosMap::const_iterator it = m_gizmos.find(m_current); + if (it == m_gizmos.end()) + return; + + it->second->render_for_picking(box); +} + void GLCanvas3D::Gizmos::_reset() { for (GizmosMap::value_type& gizmo : m_gizmos) @@ -3098,11 +3124,8 @@ void GLCanvas3D::_picking_pass() const ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - ::glPushAttrib(GL_ENABLE_BIT); - _render_volumes(true); - - ::glPopAttrib(); + m_gizmos.render_current_gizmo_for_picking_pass(_selected_volumes_bounding_box()); if (m_multisample_allowed) ::glEnable(GL_MULTISAMPLE); @@ -3110,7 +3133,7 @@ void GLCanvas3D::_picking_pass() const const Size& cnv_size = get_canvas_size(); GLubyte color[4]; - ::glReadPixels(pos.x, cnv_size.get_height() - pos.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); + ::glReadPixels(pos.x, cnv_size.get_height() - pos.y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); int volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256; m_hover_volume_id = -1; @@ -3133,7 +3156,10 @@ void GLCanvas3D::_picking_pass() const vol->hover = true; } } + m_gizmos.set_hover_id(-1); } + else + m_gizmos.set_hover_id(254 - (int)color[2]); // updates gizmos overlay if (_get_first_selected_object_id() != -1) @@ -3335,7 +3361,7 @@ void GLCanvas3D::_render_layer_editing_overlay() const void GLCanvas3D::_render_volumes(bool fake_colors) const { - static const float INV_255 = 1.0f / 255.0f; + static const GLfloat INV_255 = 1.0f / 255.0f; if (fake_colors) ::glDisable(GL_LIGHTING); @@ -3360,7 +3386,7 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const unsigned int r = (volume_id & 0x000000FF) >> 0; unsigned int g = (volume_id & 0x0000FF00) >> 8; unsigned int b = (volume_id & 0x00FF0000) >> 16; - ::glColor4f((float)r * INV_255, (float)g * INV_255, (float)b * INV_255, 1.0f); + ::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255); } else { diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index cdfc758765..5b835a806d 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -354,9 +354,12 @@ public: void update_on_off_state(const GLCanvas3D& canvas, const Pointf& mouse_pos); void reset_all_states(); + void set_hover_id(int id); + bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const; void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const; + void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const; private: void _reset(); diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 64df649d4c..dd4e097b55 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -13,9 +13,11 @@ namespace GUI { const float GLGizmoBase::BaseColor[3] = { 1.0f, 1.0f, 1.0f }; const float GLGizmoBase::HighlightColor[3] = { 1.0f, 0.38f, 0.0f }; const float GLGizmoBase::GrabberHalfSize = 2.0f; +const float GLGizmoBase::HoverOffset = 0.5f; GLGizmoBase::GLGizmoBase() : m_state(Off) + , m_hover_id(-1) { } @@ -48,12 +50,22 @@ int GLGizmoBase::get_textures_size() const return m_textures[Off].get_width(); } +void GLGizmoBase::set_hover_id(int id) +{ + m_hover_id = id; +} + void GLGizmoBase::render(const BoundingBoxf3& box) const { on_render(box); } -void GLGizmoBase::_render_square(const Pointf3& center) const +void GLGizmoBase::render_for_picking(const BoundingBoxf3& box) const +{ + on_render_for_picking(box); +} + +void GLGizmoBase::render_grabber(const Pointf3& center, bool hover) const { float min_x = (float)center.x - GrabberHalfSize; float max_x = (float)center.x + GrabberHalfSize; @@ -70,6 +82,21 @@ void GLGizmoBase::_render_square(const Pointf3& center) const ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); ::glEnd(); ::glEnable(GL_CULL_FACE); + + if (hover) + { + min_x -= HoverOffset; + max_x += HoverOffset; + min_y -= HoverOffset; + max_y += HoverOffset; + + ::glBegin(GL_LINE_LOOP); + ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); + ::glEnd(); + } } const float GLGizmoRotate::Offset = 5.0f; @@ -129,6 +156,22 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const _render_grabber(center, radius); } +void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const +{ + ::glDisable(GL_LIGHTING); + ::glDisable(GL_DEPTH_TEST); + + const Pointf3& size = box.size(); + const Pointf3& center = box.center(); + + float radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y)) + GrabberOffset; + float x = center.x + ::cos(m_angle_z) * radius; + float y = center.y + ::sin(m_angle_z) * radius; + + ::glColor3f(1.0f, 1.0f, 254.0f / 255.0f); + render_grabber(Pointf3((coordf_t)x, (coordf_t)y, 0.0), false); +} + void GLGizmoRotate::_render_circle(const Pointf3& center, float radius) const { ::glBegin(GL_LINE_LOOP); @@ -206,7 +249,7 @@ void GLGizmoRotate::_render_grabber(const Pointf3& center, float radius) const ::glEnd(); ::glColor3fv(HighlightColor); - _render_square(Pointf3((coordf_t)x, (coordf_t)y, 0.0)); + render_grabber(Pointf3((coordf_t)x, (coordf_t)y, 0.0), (m_hover_id != -1)); } const float GLGizmoScale::Offset = 5.0f; @@ -264,10 +307,37 @@ void GLGizmoScale::on_render(const BoundingBoxf3& box) const // draw grabbers ::glColor3fv(HighlightColor); - _render_square(Pointf3(min_x, min_y, 0.0)); - _render_square(Pointf3(max_x, min_y, 0.0)); - _render_square(Pointf3(max_x, max_y, 0.0)); - _render_square(Pointf3(min_x, max_y, 0.0)); + render_grabber(Pointf3(min_x, min_y, 0.0), (m_hover_id == 0)); + render_grabber(Pointf3(max_x, min_y, 0.0), (m_hover_id == 1)); + render_grabber(Pointf3(max_x, max_y, 0.0), (m_hover_id == 2)); + render_grabber(Pointf3(min_x, max_y, 0.0), (m_hover_id == 3)); +} + +void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const +{ + static const GLfloat INV_255 = 1.0f / 255.0f; + + ::glDisable(GL_LIGHTING); + ::glDisable(GL_DEPTH_TEST); + + const Pointf3& size = box.size(); + const Pointf3& center = box.center(); + + Pointf half_scaled_size = 0.5 * Pointf((coordf_t)m_scale_x * size.x, (coordf_t)m_scale_y * size.y); + coordf_t min_x = center.x - half_scaled_size.x - (coordf_t)Offset; + coordf_t max_x = center.x + half_scaled_size.x + (coordf_t)Offset; + coordf_t min_y = center.y - half_scaled_size.y - (coordf_t)Offset; + coordf_t max_y = center.y + half_scaled_size.y + (coordf_t)Offset; + + // draw grabbers + ::glColor3f(1.0f, 1.0f, 254.0f * INV_255); + render_grabber(Pointf3(min_x, min_y, 0.0), false); + ::glColor3f(1.0f, 1.0f, 253.0f * INV_255); + render_grabber(Pointf3(max_x, min_y, 0.0), false); + ::glColor3f(1.0f, 1.0f, 252.0f * INV_255); + render_grabber(Pointf3(max_x, max_y, 0.0), false); + ::glColor3f(1.0f, 1.0f, 251.0f * INV_255); + render_grabber(Pointf3(min_x, max_y, 0.0), false); } } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index b3e6fd8fd2..5ee5f3bee5 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -16,6 +16,7 @@ protected: static const float BaseColor[3]; static const float HighlightColor[3]; static const float GrabberHalfSize; + static const float HoverOffset; public: enum EState @@ -31,6 +32,7 @@ protected: // textures are assumed to be square and all with the same size in pixels // no internal check is done GLTexture m_textures[Num_States]; + int m_hover_id; public: GLGizmoBase(); @@ -44,13 +46,17 @@ public: unsigned int get_textures_id() const; int get_textures_size() const; + void set_hover_id(int id); + void render(const BoundingBoxf3& box) const; + void render_for_picking(const BoundingBoxf3& box) const; protected: virtual bool on_init() = 0; virtual void on_render(const BoundingBoxf3& box) const = 0; + virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; - void _render_square(const Pointf3& center) const; + void render_grabber(const Pointf3& center, bool hover) const; }; class GLGizmoRotate : public GLGizmoBase @@ -75,6 +81,7 @@ public: protected: virtual bool on_init(); virtual void on_render(const BoundingBoxf3& box) const; + virtual void on_render_for_picking(const BoundingBoxf3& box) const; private: void _render_circle(const Pointf3& center, float radius) const; @@ -98,6 +105,7 @@ public: protected: virtual bool on_init(); virtual void on_render(const BoundingBoxf3& box) const; + virtual void on_render_for_picking(const BoundingBoxf3& box) const; }; } // namespace GUI From 52a7d7ed09efa37378b601630d5b81dc1c532604 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 14 Jun 2018 16:09:36 +0200 Subject: [PATCH 094/117] Partial perl code cleanup --- lib/Slic3r/GUI/MainFrame.pm | 2 - lib/Slic3r/GUI/Plater.pm | 131 +----------------- lib/Slic3r/GUI/Plater/2D.pm | 3 - lib/Slic3r/GUI/Plater/3DPreview.pm | 42 ------ lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 23 --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 58 -------- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 2 - xs/xsp/Print.xsp | 11 -- 8 files changed, 2 insertions(+), 270 deletions(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 6a2246fa16..910b86dd86 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -90,10 +90,8 @@ sub new { # Save the slic3r.ini. Usually the ini file is saved from "on idle" callback, # but in rare cases it may not have been called yet. wxTheApp->{app_config}->save; -#============================================================================================================================== $self->{plater}->{print} = undef if($self->{plater}); Slic3r::GUI::_3DScene::remove_all_canvases(); -#============================================================================================================================== # propagate event $event->Skip; }); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 66d2f2f7bf..ed1ebcc6dc 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -78,28 +78,19 @@ sub new { my $on_select_object = sub { my ($obj_idx) = @_; # Ignore the special objects (the wipe tower proxy and such). -#============================================================================================================================== $self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef); -# $self->select_object((defined($obj_idx) && $obj_idx < 1000) ? $obj_idx : undef); -#============================================================================================================================== }; my $on_double_click = sub { $self->object_settings_dialog if $self->selected_object; }; my $on_right_click = sub { -#============================================================================================================================== my ($canvas, $click_pos_x, $click_pos_y) = @_; -# my ($canvas, $click_pos) = @_; -#============================================================================================================================== my ($obj_idx, $object) = $self->selected_object; return if !defined $obj_idx; my $menu = $self->object_menu; -#============================================================================================================================== $canvas->PopupMenu($menu, $click_pos_x, $click_pos_y); -# $canvas->PopupMenu($menu, $click_pos); -#============================================================================================================================== $menu->Destroy; }; my $on_instances_moved = sub { @@ -119,7 +110,6 @@ sub new { if ($Slic3r::GUI::have_OpenGL) { $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config}); $self->{preview_notebook}->AddPage($self->{canvas3D}, L('3D')); -#============================================================================================================================== Slic3r::GUI::_3DScene::register_on_select_object_callback($self->{canvas3D}, $on_select_object); Slic3r::GUI::_3DScene::register_on_double_click_callback($self->{canvas3D}, $on_double_click); Slic3r::GUI::_3DScene::register_on_right_click_callback($self->{canvas3D}, sub { $on_right_click->($self->{canvas3D}, @_); }); @@ -154,41 +144,6 @@ sub new { }); Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); }); - -# $self->{canvas3D}->set_on_select_object($on_select_object); -# $self->{canvas3D}->set_on_double_click($on_double_click); -# $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); -# $self->{canvas3D}->set_on_arrange(sub { $self->arrange }); -# $self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') }); -# $self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') }); -# $self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) }); -# $self->{canvas3D}->set_on_increase_objects(sub { $self->increase() }); -# $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() }); -# $self->{canvas3D}->set_on_remove_object(sub { $self->remove() }); -# $self->{canvas3D}->set_on_instances_moved($on_instances_moved); -# $self->{canvas3D}->set_on_enable_action_buttons($enable_action_buttons); -# $self->{canvas3D}->use_plain_shader(1); -# -# $self->{canvas3D}->set_on_wipe_tower_moved(sub { -# my ($new_pos_3f) = @_; -# my $cfg = Slic3r::Config->new; -# $cfg->set('wipe_tower_x', $new_pos_3f->x); -# $cfg->set('wipe_tower_y', $new_pos_3f->y); -# $self->GetFrame->{options_tabs}{print}->load_config($cfg); -# }); -# -# $self->{canvas3D}->set_on_model_update(sub { -# if (wxTheApp->{app_config}->get("background_processing")) { -# $self->schedule_background_process; -# } else { -# # Hide the print info box, it is no more valid. -# $self->{"print_info_box_show"}->(0); -# } -# }); -# $self->{canvas3D}->on_viewport_changed(sub { -# $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); -# }); -#============================================================================================================================== } # Initialize 2D preview canvas @@ -202,13 +157,8 @@ sub new { # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); -#============================================================================================================================== Slic3r::GUI::_3DScene::set_active($self->{preview3D}->canvas, 0); Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); }); -# $self->{preview3D}->canvas->on_viewport_changed(sub { -# $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); -# }); -#============================================================================================================================== $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview')); $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; } @@ -223,21 +173,14 @@ sub new { my $preview = $self->{preview_notebook}->GetCurrentPage; if ($preview == $self->{preview3D}) { -#============================================================================================================================== Slic3r::GUI::_3DScene::set_active($self->{preview3D}->canvas, 1); Slic3r::GUI::_3DScene::set_active($self->{canvas3D}, 0); Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1); -# $self->{preview3D}->canvas->set_legend_enabled(1); -#============================================================================================================================== $self->{preview3D}->load_print(1); } else { -#============================================================================================================================== Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 0); -# $self->{preview3D}->canvas->set_legend_enabled(0); -#============================================================================================================================== } -#============================================================================================================================== if ($preview == $self->{canvas3D}) { Slic3r::GUI::_3DScene::set_active($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::set_active($self->{preview3D}->canvas, 0); @@ -246,13 +189,9 @@ sub new { Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1); } + } else { + $preview->OnActivate if $preview->can('OnActivate'); } - else { -#============================================================================================================================== - $preview->OnActivate if $preview->can('OnActivate'); -#============================================================================================================================== - } -#============================================================================================================================== }); # toolbar for object manipulation @@ -389,10 +328,7 @@ sub new { EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog }); EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog }); EVT_TOOL($self, TB_LAYER_EDITING, sub { -#============================================================================================================================== my $state = Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D}); -# my $state = $self->{canvas3D}->layer_editing_enabled; -#============================================================================================================================== $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state); $self->on_layer_editing_toggled(! $state); }); @@ -447,18 +383,11 @@ sub new { $self->{canvas}->update_bed_size; if ($self->{canvas3D}) { -#============================================================================================================================== Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape); Slic3r::GUI::_3DScene::zoom_to_bed($self->{canvas3D}); -# $self->{canvas3D}->update_bed_size; -# $self->{canvas3D}->zoom_to_bed; -#============================================================================================================================== } if ($self->{preview3D}) { -#============================================================================================================================== Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape); -# $self->{preview3D}->set_bed_shape($self->{config}->bed_shape); -#============================================================================================================================== } $self->update; @@ -675,12 +604,8 @@ sub _on_select_preset { sub on_layer_editing_toggled { my ($self, $new_state) = @_; -#============================================================================================================================== Slic3r::GUI::_3DScene::enable_layers_editing($self->{canvas3D}, $new_state); if ($new_state && ! Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D})) { -# $self->{canvas3D}->layer_editing_enabled($new_state); -# if ($new_state && ! $self->{canvas3D}->layer_editing_enabled) { -#============================================================================================================================== # Initialization of the OpenGL shaders failed. Disable the tool. if ($self->{htoolbar}) { $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0); @@ -914,10 +839,7 @@ sub load_model_objects { $self->update; # zoom to objects -#============================================================================================================================== Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; -# $self->{canvas3D}->zoom_to_volumes if $self->{canvas3D}; -#============================================================================================================================== $self->{list}->Update; $self->{list}->Select($obj_idx[-1], 1); @@ -1308,12 +1230,7 @@ sub async_apply_config { my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config); # Just redraw the 3D canvas without reloading the scene. -#============================================================================================================================== -# $self->{canvas3D}->Refresh if ($invalidated && Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D})); $self->{canvas3D}->Refresh if Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D}); -## $self->{canvas3D}->Refresh if ($invalidated && $self->{canvas3D}->layer_editing_enabled); -# $self->{canvas3D}->Refresh if ($self->{canvas3D}->layer_editing_enabled); -#============================================================================================================================== # Hide the slicing results if the current slicing status is no more valid. $self->{"print_info_box_show"}->(0) if $invalidated; @@ -1818,12 +1735,9 @@ sub update { } $self->{canvas}->reload_scene if $self->{canvas}; -#============================================================================================================================== my $selections = $self->collect_selections; Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0); -# $self->{canvas3D}->reload_scene if $self->{canvas3D}; -#============================================================================================================================== $self->{preview3D}->reset_gcode_preview_data if $self->{preview3D}; $self->{preview3D}->reload_print if $self->{preview3D}; } @@ -1878,13 +1792,8 @@ sub on_config_change { $self->{config}->set($opt_key, $config->get($opt_key)); if ($opt_key eq 'bed_shape') { $self->{canvas}->update_bed_size; -#============================================================================================================================== Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D}; Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D}; -# $self->{canvas3D}->update_bed_size if $self->{canvas3D}; -# $self->{preview3D}->set_bed_shape($self->{config}->bed_shape) -# if $self->{preview3D}; -#============================================================================================================================== $update_scheduled = 1; } elsif ($opt_key =~ '^wipe_tower' || $opt_key eq 'single_extruder_multi_material') { $update_scheduled = 1; @@ -1903,16 +1812,10 @@ sub on_config_change { $self->{"btn_layer_editing"}->Disable; $self->{"btn_layer_editing"}->SetValue(0); } -#============================================================================================================================== Slic3r::GUI::_3DScene::enable_layers_editing($self->{canvas3D}, 0); -# $self->{canvas3D}->layer_editing_enabled(0); -#============================================================================================================================== $self->{canvas3D}->Refresh; $self->{canvas3D}->Update; -#============================================================================================================================== } elsif (Slic3r::GUI::_3DScene::is_layers_editing_allowed($self->{canvas3D})) { -# } elsif ($self->{canvas3D}->layer_editing_allowed) { -#============================================================================================================================== # Want to allow the layer editing, but do it only if the OpenGL supports it. if ($self->{htoolbar}) { $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 1); @@ -1944,12 +1847,8 @@ sub list_item_deselected { if ($self->{list}->GetFirstSelected == -1) { $self->select_object(undef); $self->{canvas}->Refresh; -#============================================================================================================================== Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}) if $self->{canvas3D}; Slic3r::GUI::_3DScene::render($self->{canvas3D}) if $self->{canvas3D}; -# $self->{canvas3D}->deselect_volumes if $self->{canvas3D}; -# $self->{canvas3D}->Render if $self->{canvas3D}; -#============================================================================================================================== } undef $self->{_lecursor}; } @@ -1961,19 +1860,14 @@ sub list_item_selected { my $obj_idx = $event->GetIndex; $self->select_object($obj_idx); $self->{canvas}->Refresh; -#============================================================================================================================== if ($self->{canvas3D}) { my $selections = $self->collect_selections; Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections); Slic3r::GUI::_3DScene::render($self->{canvas3D}); } -# $self->{canvas3D}->update_volumes_selection if $self->{canvas3D}; -# $self->{canvas3D}->Render if $self->{canvas3D}; -#============================================================================================================================== undef $self->{_lecursor}; } -#============================================================================================================================== sub collect_selections { my ($self) = @_; my $selections = []; @@ -1982,7 +1876,6 @@ sub collect_selections { } return $selections; } -#============================================================================================================================== sub list_item_activated { my ($self, $event, $obj_idx) = @_; @@ -2040,10 +1933,7 @@ sub object_cut_dialog { $self->remove($obj_idx); $self->load_model_objects(grep defined($_), @new_objects); $self->arrange; -#============================================================================================================================== Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; -# $self->{canvas3D}->zoom_to_volumes if $self->{canvas3D}; -#============================================================================================================================== } } @@ -2079,12 +1969,9 @@ sub object_settings_dialog { $self->{print}->reload_object($obj_idx); $self->schedule_background_process; $self->{canvas}->reload_scene if $self->{canvas}; -#============================================================================================================================== my $selections = $self->collect_selections; Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections); Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0); -# $self->{canvas3D}->reload_scene if $self->{canvas3D}; -#============================================================================================================================== } else { $self->resume_background_process; } @@ -2097,10 +1984,7 @@ sub object_list_changed { # Enable/disable buttons depending on whether there are any objects on the platter. my $have_objects = @{$self->{objects}} ? 1 : 0; -#============================================================================================================================== my $variable_layer_height_allowed = $self->{config}->variable_layer_height && Slic3r::GUI::_3DScene::is_layers_editing_allowed($self->{canvas3D}); -# my $variable_layer_height_allowed = $self->{config}->variable_layer_height && $self->{canvas3D}->layer_editing_allowed; -#============================================================================================================================== if ($self->{htoolbar}) { # On OSX or Linux $self->{htoolbar}->EnableTool($_, $have_objects) @@ -2115,10 +1999,7 @@ sub object_list_changed { } my $export_in_progress = $self->{export_gcode_output_file} || $self->{send_gcode_file}; -#============================================================================================================================== my $model_fits = $self->{canvas3D} ? Slic3r::GUI::_3DScene::check_volumes_outside_state($self->{canvas3D}, $self->{config}) : 1; -# my $model_fits = $self->{canvas3D} ? $self->{canvas3D}->volumes->check_outside_state($self->{config}) : 1; -#============================================================================================================================== my $method = ($have_objects && ! $export_in_progress && $model_fits) ? 'Enable' : 'Disable'; $self->{"btn_$_"}->$method for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode); @@ -2333,19 +2214,11 @@ sub select_view { my $idx_page = $self->{preview_notebook}->GetSelection; my $page = ($idx_page == &Wx::wxNOT_FOUND) ? L('3D') : $self->{preview_notebook}->GetPageText($idx_page); if ($page eq L('Preview')) { -#============================================================================================================================== Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction); Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); -# $self->{preview3D}->canvas->select_view($direction); -# $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); -#============================================================================================================================== } else { -#============================================================================================================================== Slic3r::GUI::_3DScene::select_view($self->{canvas3D}, $direction); Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); -# $self->{canvas3D}->select_view($direction); -# $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); -#============================================================================================================================== } } diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm index 120fec8301..ad8f54ddbb 100644 --- a/lib/Slic3r/GUI/Plater/2D.pm +++ b/lib/Slic3r/GUI/Plater/2D.pm @@ -222,10 +222,7 @@ sub mouse_event { ]; $self->{drag_object} = [ $obj_idx, $instance_idx ]; } elsif ($event->RightDown) { -#======================================================================================================================================= $self->{on_right_click}->($pos->x, $pos->y); -# $self->{on_right_click}->($pos); -#======================================================================================================================================= } last OBJECTS; diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 86a862c758..9ed2374ec4 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -24,10 +24,7 @@ sub new { # init GUI elements my $canvas = Slic3r::GUI::3DScene->new($self); -#=================================================================================================================================== Slic3r::GUI::_3DScene::enable_shader($canvas, 1); -# $canvas->use_plain_shader(1); -#=================================================================================================================================== $self->canvas($canvas); my $slider_low = Wx::Slider->new( $self, -1, @@ -281,10 +278,7 @@ sub new { sub reload_print { my ($self, $force) = @_; -#============================================================================================================================== Slic3r::GUI::_3DScene::reset_volumes($self->canvas); -# $self->canvas->reset_objects; -#============================================================================================================================== $self->_loaded(0); if (! $self->IsShown && ! $force) { @@ -310,10 +304,7 @@ sub refresh_print { sub reset_gcode_preview_data { my ($self) = @_; $self->gcode_preview_data->reset; -#============================================================================================================================== Slic3r::GUI::_3DScene::reset_legend_texture(); -# $self->canvas->reset_legend_texture(); -#============================================================================================================================== } sub load_print { @@ -338,10 +329,7 @@ sub load_print { if ($n_layers == 0) { $self->reset_sliders; -#============================================================================================================================== Slic3r::GUI::_3DScene::reset_legend_texture(); -# $self->canvas->reset_legend_texture(); -#============================================================================================================================== $self->canvas->Refresh; # clears canvas return; } @@ -376,42 +364,25 @@ sub load_print { if ($self->gcode_preview_data->empty) { # load skirt and brim -#============================================================================================================================== Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print); Slic3r::GUI::_3DScene::load_print_toolpaths($self->canvas); Slic3r::GUI::_3DScene::load_wipe_tower_toolpaths($self->canvas, \@colors); -# $self->canvas->load_print_toolpaths($self->print, \@colors); -# $self->canvas->load_wipe_tower_toolpaths($self->print, \@colors); -#============================================================================================================================== foreach my $object (@{$self->print->objects}) { -#============================================================================================================================== Slic3r::GUI::_3DScene::load_print_object_toolpaths($self->canvas, $object, \@colors); -# $self->canvas->load_print_object_toolpaths($object, \@colors); -#============================================================================================================================== # Show the objects in very transparent color. #my @volume_ids = $self->canvas->load_object($object->model_object); #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; } $self->show_hide_ui_elements('simple'); -#============================================================================================================================== Slic3r::GUI::_3DScene::reset_legend_texture(); -# $self->canvas->reset_legend_texture(); -#============================================================================================================================== } else { -#============================================================================================================================== $self->{force_sliders_full_range} = (Slic3r::GUI::_3DScene::get_volumes_count($self->canvas) == 0); Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print); Slic3r::GUI::_3DScene::load_gcode_preview($self->canvas, $self->gcode_preview_data, \@colors); -# $self->{force_sliders_full_range} = (scalar(@{$self->canvas->volumes}) == 0); -# $self->canvas->load_gcode_preview($self->print, $self->gcode_preview_data, \@colors); -#============================================================================================================================== $self->show_hide_ui_elements('full'); # recalculates zs and update sliders accordingly -#============================================================================================================================== $self->{layers_z} = Slic3r::GUI::_3DScene::get_current_print_zs($self->canvas, 1); -# $self->{layers_z} = $self->canvas->get_current_print_zs(1); -#============================================================================================================================== $n_layers = scalar(@{$self->{layers_z}}); if ($n_layers == 0) { # all layers filtered out @@ -497,10 +468,7 @@ sub set_z_range $self->{z_label_low}->SetLabel(sprintf '%.2f', $z_low); $self->{z_label_high}->SetLabel(sprintf '%.2f', $z_high); -#============================================================================================================================== my $layers_z = Slic3r::GUI::_3DScene::get_current_print_zs($self->canvas, 0); -# my $layers_z = $self->canvas->get_current_print_zs(0); -#============================================================================================================================== for (my $i = 0; $i < scalar(@{$layers_z}); $i += 1) { if (($z_low - 1e-6 < @{$layers_z}[$i]) && (@{$layers_z}[$i] < $z_low + 1e-6)) { $self->{z_label_low_idx}->SetLabel(sprintf '%d', $i + 1); @@ -514,10 +482,7 @@ sub set_z_range } } -#============================================================================================================================== Slic3r::GUI::_3DScene::set_toolpaths_range($self->canvas, $z_low - 1e-6, $z_high + 1e-6); -# $self->canvas->set_toolpaths_range($z_low - 1e-6, $z_high + 1e-6); -#============================================================================================================================== $self->canvas->Refresh if $self->IsShown; } @@ -547,13 +512,6 @@ sub set_z_idx_high } } -#============================================================================================================================== -#sub set_bed_shape { -# my ($self, $bed_shape) = @_; -# $self->canvas->set_bed_shape($bed_shape); -#} -#============================================================================================================================== - sub set_number_extruders { my ($self, $number_extruders) = @_; if ($self->{number_extruders} != $number_extruders) { diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 56ca9d7383..35aa28818b 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -9,9 +9,7 @@ use utf8; use Slic3r::Geometry qw(PI X); use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); use Wx::Event qw(EVT_CLOSE EVT_BUTTON); -#============================================================================================================================== use List::Util qw(max); -#============================================================================================================================== use base 'Wx::Dialog'; sub new { @@ -115,20 +113,13 @@ sub new { my $canvas; if ($Slic3r::GUI::have_OpenGL) { $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); -#============================================================================================================================== Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); - -# $canvas->load_object($self->{model_object}, undef, undef, [0]); -# $canvas->set_auto_bed_shape; -#============================================================================================================================== $canvas->SetSize([500,500]); $canvas->SetMinSize($canvas->GetSize); -#============================================================================================================================== Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->{config}); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1); -#============================================================================================================================== } $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); @@ -157,9 +148,7 @@ sub new { # Note that the window was already closed, so a pending update will not be executed. $self->{already_closed} = 1; $self->EndModal(wxID_OK); -#============================================================================================================================= $self->{canvas}->Destroy; -#============================================================================================================================= $self->Destroy(); }); @@ -167,9 +156,7 @@ sub new { # Note that the window was already closed, so a pending update will not be executed. $self->{already_closed} = 1; $self->EndModal(wxID_CANCEL); -#============================================================================================================================= $self->{canvas}->Destroy; -#============================================================================================================================= $self->Destroy(); }); @@ -261,21 +248,11 @@ sub _update { $expolygon->translate(map Slic3r::Geometry::scale($_), @{ $self->{model_object}->instances->[0]->offset }); } -#============================================================================================================================== Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects; Slic3r::GUI::_3DScene::set_cutting_plane($self->{canvas}, $self->{cut_options}{z}, [@expolygons]); Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); Slic3r::GUI::_3DScene::render($self->{canvas}); -# $self->{canvas}->reset_objects; -# $self->{canvas}->load_object($_, undef, undef, [0]) for @objects; -# $self->{canvas}->SetCuttingPlane( -# $self->{cut_options}{z}, -# [@expolygons], -# ); -# $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->{config}); -# $self->{canvas}->Render; -#============================================================================================================================== } } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 546ec5445f..1ec0ce1cb5 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -10,9 +10,7 @@ use File::Basename qw(basename); use Wx qw(:misc :sizer :treectrl :button :keycode wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxMOD_CONTROL wxTheApp); use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN EVT_KEY_DOWN); -#============================================================================================================================== use List::Util qw(max); -#============================================================================================================================== use base 'Wx::Panel'; use constant ICON_OBJECT => 0; @@ -153,7 +151,6 @@ sub new { my $canvas; if ($Slic3r::GUI::have_OpenGL) { $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); -#============================================================================================================================== Slic3r::GUI::_3DScene::enable_picking($canvas, 1); Slic3r::GUI::_3DScene::set_select_by($canvas, 'volume'); Slic3r::GUI::_3DScene::register_on_select_object_callback($canvas, sub { @@ -163,24 +160,10 @@ sub new { Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]); Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); - -# $canvas->enable_picking(1); -# $canvas->select_by('volume'); -# $canvas->on_select(sub { -# my ($volume_idx) = @_; -# # convert scene volume to model object volume -# $self->reload_tree(($volume_idx == -1) ? undef : $canvas->volumes->[$volume_idx]->volume_idx); -# }); -# $canvas->load_object($self->{model_object}, undef, undef, [0]); -# $canvas->set_auto_bed_shape; -#============================================================================================================================== $canvas->SetSize([500,700]); -#============================================================================================================================== Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->GetParent->GetParent->{config}); Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($canvas); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1); -# $canvas->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); -#============================================================================================================================== } $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); @@ -280,10 +263,7 @@ sub selection_changed { # deselect all meshes if ($self->{canvas}) { -#============================================================================================================================== Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas}); -# $_->set_selected(0) for @{$self->{canvas}->volumes}; -#============================================================================================================================== } # disable things as if nothing is selected @@ -311,10 +291,7 @@ sub selection_changed { if ($itemData->{type} eq 'volume') { # select volume in 3D preview if ($self->{canvas}) { -#============================================================================================================================== Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id}); -# $self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1); -#============================================================================================================================== } $self->{btn_delete}->Enable; $self->{btn_split}->Enable; @@ -357,10 +334,7 @@ sub selection_changed { $self->{settings_panel}->enable; } -#============================================================================================================================== Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas}; -# $self->{canvas}->Render if $self->{canvas}; -#============================================================================================================================== } sub on_btn_load { @@ -456,10 +430,7 @@ sub on_btn_move_up { if ($itemData && $itemData->{type} eq 'volume') { my $volume_id = $itemData->{volume_id}; if ($self->{model_object}->move_volume_up($volume_id)) { -#============================================================================================================================== Slic3r::GUI::_3DScene::move_volume_up($self->{canvas}, $volume_id); -# $self->{canvas}->volumes->move_volume_up($volume_id); -#============================================================================================================================== $self->{parts_changed} = 1; $self->reload_tree($volume_id - 1); } @@ -472,10 +443,7 @@ sub on_btn_move_down { if ($itemData && $itemData->{type} eq 'volume') { my $volume_id = $itemData->{volume_id}; if ($self->{model_object}->move_volume_down($volume_id)) { -#============================================================================================================================== Slic3r::GUI::_3DScene::move_volume_down($self->{canvas}, $volume_id); -# $self->{canvas}->volumes->move_volume_down($volume_id); -#============================================================================================================================== $self->{parts_changed} = 1; $self->reload_tree($volume_id + 1); } @@ -520,18 +488,11 @@ sub _parts_changed { $self->reload_tree; if ($self->{canvas}) { -#============================================================================================================================== Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas}); Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); Slic3r::GUI::_3DScene::render($self->{canvas}); -# $self->{canvas}->reset_objects; -# $self->{canvas}->load_object($self->{model_object}); -# $self->{canvas}->zoom_to_volumes; -# $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); -# $self->{canvas}->Render; -#============================================================================================================================== } } @@ -551,12 +512,10 @@ sub CanClose { return ! Slic3r::GUI::catch_error($self); } -#============================================================================================================================= sub Destroy { my ($self) = @_; $self->{canvas}->Destroy if ($self->{canvas}); } -#============================================================================================================================= sub PartsChanged { my ($self) = @_; @@ -572,29 +531,18 @@ sub _update_canvas { my ($self) = @_; if ($self->{canvas}) { -#============================================================================================================================== Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); -# $self->{canvas}->reset_objects; -# $self->{canvas}->load_object($self->{model_object}); -#============================================================================================================================== # restore selection, if any if (my $itemData = $self->get_selection) { if ($itemData->{type} eq 'volume') { -#============================================================================================================================== Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id}); -# $self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1); -#============================================================================================================================== } } -#============================================================================================================================== Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); Slic3r::GUI::_3DScene::render($self->{canvas}); -# $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); -# $self->{canvas}->Render; -#============================================================================================================================== } } @@ -616,16 +564,10 @@ sub _update { $self->{parts_changed} = 1; my @objects = (); push @objects, $self->{model_object}; -#============================================================================================================================== Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects; Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); Slic3r::GUI::_3DScene::render($self->{canvas}); -# $self->{canvas}->reset_objects; -# $self->{canvas}->load_object($_, undef, [0]) for @objects; -# $self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config}); -# $self->{canvas}->Render; -#============================================================================================================================== } 1; diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index 2cf41f169a..3befba708d 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -36,9 +36,7 @@ sub new { wxTheApp->save_window_pos($self, "object_settings"); $self->EndModal(wxID_OK); -#============================================================================================================================= $self->{parts}->Destroy; -#============================================================================================================================= $self->Destroy; }); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 60b589336d..e05112932b 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -122,17 +122,6 @@ _constant() RETVAL.push_back(slicing_params.layer_height); %}; -//################################################################################################################################################################### -// void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) -// %code%{ -// THIS->update_layer_height_profile(THIS->model_object()->layer_height_profile); -// adjust_layer_height_profile( -// THIS->slicing_parameters(), THIS->model_object()->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); -// THIS->model_object()->layer_height_profile_valid = true; -// THIS->layer_height_profile_valid = false; -// %}; -//################################################################################################################################################################### - void reset_layer_height_profile(); int ptr() From 859e13ece591ef3beabe678ce57ac4ac97c67342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Here=C3=B1=C3=BA?= Date: Thu, 14 Jun 2018 21:03:20 -0300 Subject: [PATCH 095/117] Typo on string #20 --- doc/updating/Updatig.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/updating/Updatig.md b/doc/updating/Updatig.md index 3ce1f109c4..13a2de144e 100644 --- a/doc/updating/Updatig.md +++ b/doc/updating/Updatig.md @@ -17,7 +17,7 @@ A derived User preset keeps track of wich settings are inherited from the parent This system ensures that we don't overwrite user's settings when there is an update to the built in presets. Slic3r GUI now displays accurately which settings are inherited and which are modified. -A setting derived from a System preset is represeted by green label and a locked lock icon: +A setting derived from a System preset is represented by green label and a locked lock icon: ![a system setting](setting_sys.png) From 6874949556f62c56f26285fea57275ce0900a2f9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 15 Jun 2018 14:10:28 +0200 Subject: [PATCH 096/117] Scale gizmo interaction with mouse --- lib/Slic3r/GUI.pm | 4 - xs/src/slic3r/GUI/GLCanvas3D.cpp | 110 +++++++++++++++-- xs/src/slic3r/GUI/GLCanvas3D.hpp | 11 ++ xs/src/slic3r/GUI/GLGizmo.cpp | 201 ++++++++++++++++++++----------- xs/src/slic3r/GUI/GLGizmo.hpp | 31 ++++- 5 files changed, 263 insertions(+), 94 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index c06f0ccdbb..52c4828133 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -223,12 +223,8 @@ sub system_info { my $opengl_info_txt = ''; if (defined($self->{mainframe}) && defined($self->{mainframe}->{plater}) && defined($self->{mainframe}->{plater}->{canvas3D})) { -#============================================================================================================================== $opengl_info = Slic3r::GUI::_3DScene::get_gl_info(1, 1); $opengl_info_txt = Slic3r::GUI::_3DScene::get_gl_info(0, 1); -# $opengl_info = $self->{mainframe}->{plater}->{canvas3D}->opengl_info(format => 'html'); -# $opengl_info_txt = $self->{mainframe}->{plater}->{canvas3D}->opengl_info; -#============================================================================================================================== } my $about = Slic3r::GUI::SystemInfo->new( parent => undef, diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index 64538d940c..ae5125d6cb 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1078,6 +1078,7 @@ const float GLCanvas3D::Gizmos::OverlayGapY = 10.0f; GLCanvas3D::Gizmos::Gizmos() : m_enabled(false) , m_current(Undefined) + , m_dragging(false) { } @@ -1196,7 +1197,10 @@ void GLCanvas3D::Gizmos::reset_all_states() for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if (it->second != nullptr) + { it->second->set_state(GLGizmoBase::Off); + it->second->set_hover_id(-1); + } } m_current = Undefined; @@ -1240,6 +1244,43 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const return false; } +bool GLCanvas3D::Gizmos::grabber_contains_mouse() const +{ + if (!m_enabled) + return false; + + GLGizmoBase* curr = _get_current(); + return (curr != nullptr) ? (curr->get_hover_id() != -1) : false; +} + +void GLCanvas3D::Gizmos::update(const Pointf& mouse_pos) +{ + if (!m_enabled) + return; + + GLGizmoBase* curr = _get_current(); + if (curr != nullptr) + curr->update(mouse_pos); +} + +bool GLCanvas3D::Gizmos::is_dragging() const +{ + return m_dragging; +} + +void GLCanvas3D::Gizmos::start_dragging() +{ + m_dragging = true; + GLGizmoBase* curr = _get_current(); + if (curr != nullptr) + curr->start_dragging(); +} + +void GLCanvas3D::Gizmos::stop_dragging() +{ + m_dragging = false; +} + void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const { if (!m_enabled) @@ -1264,11 +1305,9 @@ void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const BoundingBox ::glDisable(GL_DEPTH_TEST); - GizmosMap::const_iterator it = m_gizmos.find(m_current); - if (it == m_gizmos.end()) - return; - - it->second->render_for_picking(box); + GLGizmoBase* curr = _get_current(); + if (curr != nullptr) + curr->render_for_picking(box); } void GLCanvas3D::Gizmos::_reset() @@ -1305,16 +1344,15 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const void GLCanvas3D::Gizmos::_render_current_gizmo(const BoundingBoxf3& box) const { - GizmosMap::const_iterator it = m_gizmos.find(m_current); - if (it == m_gizmos.end()) - return; - - it->second->render(box); + GLGizmoBase* curr = _get_current(); + if (curr != nullptr) + curr->render(box); } float GLCanvas3D::Gizmos::_get_total_overlay_height() const { float height = 0.0f; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { height += (float)it->second->get_textures_size(); @@ -1325,6 +1363,12 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const return height; } +GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const +{ + GizmosMap::const_iterator it = m_gizmos.find(m_current); + return (it != m_gizmos.end()) ? it->second : nullptr; +} + GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context) : m_canvas(canvas) , m_context(context) @@ -2619,6 +2663,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_gizmos.update_on_off_state(*this, m_mouse.position); m_dirty = true; } + else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse()) + { + m_gizmos.start_dragging(); + } else { // Select volume in this 3D canvas. @@ -2725,6 +2773,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } + else if (evt.Dragging() && m_gizmos.is_dragging()) + { + m_mouse.dragging = true; + + const Pointf3& cur_pos = _mouse_to_bed_3d(pos); + m_gizmos.update(Pointf(cur_pos.x, cur_pos.y)); + m_dirty = true; + } else if (evt.Dragging() && !gizmos_overlay_contains_mouse) { m_mouse.dragging = true; @@ -2799,7 +2855,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) _on_move(volume_idxs); } - else if (!m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !is_layers_editing_enabled()) + else if (!m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) { // deselect and propagate event through callback if (m_picking_enabled) @@ -2808,6 +2864,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) _on_select(-1); } } + else if (evt.LeftUp() && m_gizmos.is_dragging()) + { + m_gizmos.stop_dragging(); + } m_mouse.drag.volume_idx = -1; m_mouse.set_start_position_3D_as_invalid(); @@ -3483,10 +3543,36 @@ Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) mouse_z = *z; GLdouble out_x, out_y, out_z; - ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)y, mouse_z, modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); + ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)y, (GLdouble)mouse_z, modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); return Pointf3((coordf_t)out_x, (coordf_t)out_y, (coordf_t)out_z); } +Pointf3 GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) +{ + if (!set_current()) + return Pointf3(DBL_MAX, DBL_MAX, DBL_MAX); + + GLint viewport[4]; + ::glGetIntegerv(GL_VIEWPORT, viewport); + + _camera_tranform(); + + GLdouble modelview_matrix[16]; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix); + GLdouble projection_matrix[16]; + ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); + + GLint y = viewport[3] - (GLint)mouse_pos.y; + + GLdouble x0, y0, z0; + ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)y, 0.1, modelview_matrix, projection_matrix, viewport, &x0, &y0, &z0); + + GLdouble x1, y1, z1; + ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)y, 0.9, modelview_matrix, projection_matrix, viewport, &x1, &y1, &z1); + + return Linef3(Pointf3(x0, y0, z0), Pointf3(x1, y1, z1)).intersect_plane(0.0); +} + void GLCanvas3D::_start_timer() { if (m_timer != nullptr) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 5b835a806d..32dc1d934b 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -340,6 +340,7 @@ public: typedef std::map GizmosMap; GizmosMap m_gizmos; EType m_current; + bool m_dragging; public: Gizmos(); @@ -357,6 +358,12 @@ public: void set_hover_id(int id); bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const; + bool grabber_contains_mouse() const; + void update(const Pointf& mouse_pos); + + bool is_dragging() const; + void start_dragging(); + void stop_dragging(); void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const; void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const; @@ -368,6 +375,7 @@ public: void _render_current_gizmo(const BoundingBoxf3& box) const; float _get_total_overlay_height() const; + GLGizmoBase* _get_current() const; }; private: @@ -587,6 +595,9 @@ private: // If the Z screen space coordinate is not provided, a depth buffer value is substituted. Pointf3 _mouse_to_3d(const Point& mouse_pos, float* z = nullptr); + // Convert the screen space coordinate to world coordinate on the bed. + Pointf3 _mouse_to_bed_3d(const Point& mouse_pos); + void _start_timer(); void _stop_timer(); diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index dd4e097b55..343df751cf 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -10,10 +10,54 @@ namespace Slic3r { namespace GUI { +const float GLGizmoBase::Grabber::HalfSize = 2.0f; +const float GLGizmoBase::Grabber::HoverOffset = 0.5f; const float GLGizmoBase::BaseColor[3] = { 1.0f, 1.0f, 1.0f }; const float GLGizmoBase::HighlightColor[3] = { 1.0f, 0.38f, 0.0f }; -const float GLGizmoBase::GrabberHalfSize = 2.0f; -const float GLGizmoBase::HoverOffset = 0.5f; + +GLGizmoBase::Grabber::Grabber() + : center(Pointf(0.0, 0.0)) +{ + color[0] = 1.0f; + color[1] = 1.0f; + color[2] = 1.0f; +} + +void GLGizmoBase::Grabber::render(bool hover) const +{ + float min_x = (float)center.x - HalfSize; + float max_x = (float)center.x + HalfSize; + float min_y = (float)center.y - HalfSize; + float max_y = (float)center.y + HalfSize; + + ::glColor3f((GLfloat)color[0], (GLfloat)color[1], (GLfloat)color[2]); + + ::glDisable(GL_CULL_FACE); + ::glBegin(GL_TRIANGLES); + ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); + ::glEnd(); + ::glEnable(GL_CULL_FACE); + + if (hover) + { + min_x -= HoverOffset; + max_x += HoverOffset; + min_y -= HoverOffset; + max_y += HoverOffset; + + ::glBegin(GL_LINE_LOOP); + ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); + ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); + ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); + ::glEnd(); + } +} GLGizmoBase::GLGizmoBase() : m_state(Off) @@ -50,9 +94,27 @@ int GLGizmoBase::get_textures_size() const return m_textures[Off].get_width(); } +int GLGizmoBase::get_hover_id() const +{ + return m_hover_id; +} + void GLGizmoBase::set_hover_id(int id) { - m_hover_id = id; + if (id < (int)m_grabbers.size()) + m_hover_id = id; +} + +void GLGizmoBase::start_dragging() +{ + if (m_hover_id != -1) + m_start_drag_position = m_grabbers[m_hover_id].center; +} + +void GLGizmoBase::update(const Pointf& mouse_pos) +{ + if (m_hover_id != -1) + on_update(mouse_pos); } void GLGizmoBase::render(const BoundingBoxf3& box) const @@ -65,37 +127,11 @@ void GLGizmoBase::render_for_picking(const BoundingBoxf3& box) const on_render_for_picking(box); } -void GLGizmoBase::render_grabber(const Pointf3& center, bool hover) const +void GLGizmoBase::render_grabbers() const { - float min_x = (float)center.x - GrabberHalfSize; - float max_x = (float)center.x + GrabberHalfSize; - float min_y = (float)center.y - GrabberHalfSize; - float max_y = (float)center.y + GrabberHalfSize; - - ::glDisable(GL_CULL_FACE); - ::glBegin(GL_TRIANGLES); - ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); - ::glEnd(); - ::glEnable(GL_CULL_FACE); - - if (hover) + for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { - min_x -= HoverOffset; - max_x += HoverOffset; - min_y -= HoverOffset; - max_y += HoverOffset; - - ::glBegin(GL_LINE_LOOP); - ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); - ::glEnd(); + m_grabbers[i].render(m_hover_id == i); } } @@ -133,9 +169,16 @@ bool GLGizmoRotate::on_init() if (!m_textures[On].load_from_file(filename, false)) return false; + m_grabbers.push_back(Grabber()); + return true; } +void GLGizmoRotate::on_update(const Pointf& mouse_pos) +{ +// std::cout << "GLGizmoRotate::on_update() - delta (" << delta.x << ", " << delta.y << ")" << std::endl; +} + void GLGizmoRotate::on_render(const BoundingBoxf3& box) const { ::glDisable(GL_LIGHTING); @@ -161,15 +204,10 @@ void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const ::glDisable(GL_LIGHTING); ::glDisable(GL_DEPTH_TEST); - const Pointf3& size = box.size(); - const Pointf3& center = box.center(); - - float radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y)) + GrabberOffset; - float x = center.x + ::cos(m_angle_z) * radius; - float y = center.y + ::sin(m_angle_z) * radius; - - ::glColor3f(1.0f, 1.0f, 254.0f / 255.0f); - render_grabber(Pointf3((coordf_t)x, (coordf_t)y, 0.0), false); + m_grabbers[0].color[0] = 1.0f; + m_grabbers[0].color[1] = 1.0f; + m_grabbers[0].color[2] = 254.0f / 255.0f; + render_grabbers(); } void GLGizmoRotate::_render_circle(const Pointf3& center, float radius) const @@ -240,16 +278,16 @@ void GLGizmoRotate::_render_reference_radius(const Pointf3& center, float radius void GLGizmoRotate::_render_grabber(const Pointf3& center, float radius) const { float grabber_radius = radius + GrabberOffset; - float x = center.x + ::cos(m_angle_z) * grabber_radius; - float y = center.y + ::sin(m_angle_z) * grabber_radius; + m_grabbers[0].center.x = center.x + ::cos(m_angle_z) * grabber_radius; + m_grabbers[0].center.y = center.y + ::sin(m_angle_z) * grabber_radius; ::glBegin(GL_LINES); ::glVertex3f((GLfloat)center.x, (GLfloat)center.y, 0.0f); - ::glVertex3f((GLfloat)x, (GLfloat)y, 0.0f); + ::glVertex3f((GLfloat)m_grabbers[0].center.x, (GLfloat)m_grabbers[0].center.y, 0.0f); ::glEnd(); - ::glColor3fv(HighlightColor); - render_grabber(Pointf3((coordf_t)x, (coordf_t)y, 0.0), (m_hover_id != -1)); + ::memcpy((void*)m_grabbers[0].color, (const void*)HighlightColor, 4 * sizeof(float)); + render_grabbers(); } const float GLGizmoScale::Offset = 5.0f; @@ -278,9 +316,28 @@ bool GLGizmoScale::on_init() if (!m_textures[On].load_from_file(filename, false)) return false; + for (unsigned int i = 0; i < 4; ++i) + { + m_grabbers.push_back(Grabber()); + } + return true; } +void GLGizmoScale::on_update(const Pointf& mouse_pos) +{ + Pointf center(0.5 * (m_grabbers[1].center.x + m_grabbers[0].center.x), 0.5 * (m_grabbers[3].center.y + m_grabbers[0].center.y)); + + coordf_t orig_len = length(m_start_drag_position - center); + coordf_t new_len = length(mouse_pos - center); + + coordf_t ratio = (orig_len != 0.0) ? new_len / orig_len : 1.0; + + m_scale_x = (float)ratio; + m_scale_y = (float)ratio; + m_scale_z = (float)ratio; +} + void GLGizmoScale::on_render(const BoundingBoxf3& box) const { ::glDisable(GL_LIGHTING); @@ -295,22 +352,31 @@ void GLGizmoScale::on_render(const BoundingBoxf3& box) const coordf_t min_y = center.y - half_scaled_size.y - (coordf_t)Offset; coordf_t max_y = center.y + half_scaled_size.y + (coordf_t)Offset; + m_grabbers[0].center.x = min_x; + m_grabbers[0].center.y = min_y; + m_grabbers[1].center.x = max_x; + m_grabbers[1].center.y = min_y; + m_grabbers[2].center.x = max_x; + m_grabbers[2].center.y = max_y; + m_grabbers[3].center.x = min_x; + m_grabbers[3].center.y = max_y; + ::glLineWidth(2.0f); ::glColor3fv(BaseColor); // draw outline ::glBegin(GL_LINE_LOOP); - ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)min_y, 0.0f); - ::glVertex3f((GLfloat)max_x, (GLfloat)max_y, 0.0f); - ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); + for (unsigned int i = 0; i < 4; ++i) + { + ::glVertex3f((GLfloat)m_grabbers[i].center.x, (GLfloat)m_grabbers[i].center.y, 0.0f); + } ::glEnd(); // draw grabbers - ::glColor3fv(HighlightColor); - render_grabber(Pointf3(min_x, min_y, 0.0), (m_hover_id == 0)); - render_grabber(Pointf3(max_x, min_y, 0.0), (m_hover_id == 1)); - render_grabber(Pointf3(max_x, max_y, 0.0), (m_hover_id == 2)); - render_grabber(Pointf3(min_x, max_y, 0.0), (m_hover_id == 3)); + for (unsigned int i = 0; i < 4; ++i) + { + ::memcpy((void*)m_grabbers[i].color, (const void*)HighlightColor, 4 * sizeof(float)); + } + render_grabbers(); } void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const @@ -320,24 +386,13 @@ void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const ::glDisable(GL_LIGHTING); ::glDisable(GL_DEPTH_TEST); - const Pointf3& size = box.size(); - const Pointf3& center = box.center(); - - Pointf half_scaled_size = 0.5 * Pointf((coordf_t)m_scale_x * size.x, (coordf_t)m_scale_y * size.y); - coordf_t min_x = center.x - half_scaled_size.x - (coordf_t)Offset; - coordf_t max_x = center.x + half_scaled_size.x + (coordf_t)Offset; - coordf_t min_y = center.y - half_scaled_size.y - (coordf_t)Offset; - coordf_t max_y = center.y + half_scaled_size.y + (coordf_t)Offset; - - // draw grabbers - ::glColor3f(1.0f, 1.0f, 254.0f * INV_255); - render_grabber(Pointf3(min_x, min_y, 0.0), false); - ::glColor3f(1.0f, 1.0f, 253.0f * INV_255); - render_grabber(Pointf3(max_x, min_y, 0.0), false); - ::glColor3f(1.0f, 1.0f, 252.0f * INV_255); - render_grabber(Pointf3(max_x, max_y, 0.0), false); - ::glColor3f(1.0f, 1.0f, 251.0f * INV_255); - render_grabber(Pointf3(min_x, max_y, 0.0), false); + for (unsigned int i = 0; i < 4; ++i) + { + m_grabbers[i].color[0] = 1.0f; + m_grabbers[i].color[1] = 1.0f; + m_grabbers[i].color[2] = (254.0f - (float)i) * INV_255; + } + render_grabbers(); } } // namespace GUI diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 5ee5f3bee5..bc334d26b9 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -2,6 +2,9 @@ #define slic3r_GLGizmo_hpp_ #include "../../slic3r/GUI/GLTexture.hpp" +#include "../../libslic3r/Point.hpp" + +#include namespace Slic3r { @@ -15,8 +18,18 @@ class GLGizmoBase protected: static const float BaseColor[3]; static const float HighlightColor[3]; - static const float GrabberHalfSize; - static const float HoverOffset; + + struct Grabber + { + static const float HalfSize; + static const float HoverOffset; + + Pointf center; + float color[3]; + + Grabber(); + void render(bool hover) const; + }; public: enum EState @@ -29,10 +42,11 @@ public: protected: EState m_state; - // textures are assumed to be square and all with the same size in pixels - // no internal check is done + // textures are assumed to be square and all with the same size in pixels, no internal check is done GLTexture m_textures[Num_States]; int m_hover_id; + mutable std::vector m_grabbers; + Pointf m_start_drag_position; public: GLGizmoBase(); @@ -46,17 +60,22 @@ public: unsigned int get_textures_id() const; int get_textures_size() const; + int get_hover_id() const; void set_hover_id(int id); + void start_dragging(); + void update(const Pointf& mouse_pos); + void render(const BoundingBoxf3& box) const; void render_for_picking(const BoundingBoxf3& box) const; protected: virtual bool on_init() = 0; + virtual void on_update(const Pointf& mouse_pos) = 0; virtual void on_render(const BoundingBoxf3& box) const = 0; virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; - void render_grabber(const Pointf3& center, bool hover) const; + void render_grabbers() const; }; class GLGizmoRotate : public GLGizmoBase @@ -80,6 +99,7 @@ public: protected: virtual bool on_init(); + virtual void on_update(const Pointf& mouse_pos); virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; @@ -104,6 +124,7 @@ public: protected: virtual bool on_init(); + virtual void on_update(const Pointf& mouse_pos); virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; }; From 53f8706805c430bc2a3dc739b336976980aea0ca Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 15 Jun 2018 16:16:55 +0200 Subject: [PATCH 097/117] Rotate gizmo interaction with mouse --- xs/src/libslic3r/Point.hpp | 5 ++ xs/src/slic3r/GUI/GLGizmo.cpp | 129 +++++++++++++++++++++++----------- xs/src/slic3r/GUI/GLGizmo.hpp | 20 ++++-- 3 files changed, 105 insertions(+), 49 deletions(-) diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index 6c9096a3d1..a52cdceb68 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -238,6 +238,11 @@ inline coordf_t dot(const Pointf &v1, const Pointf &v2) { return v1.x * v2.x + v inline coordf_t dot(const Pointf &v) { return v.x * v.x + v.y * v.y; } inline double length(const Vectorf &v) { return sqrt(dot(v)); } inline double l2(const Vectorf &v) { return dot(v); } +inline Vectorf normalize(const Vectorf& v) +{ + coordf_t len = ::sqrt(sqr(v.x) + sqr(v.y)); + return (len != 0.0) ? 1.0 / len * v : Vectorf(0.0, 0.0); +} class Pointf3 : public Pointf { diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 343df751cf..706c416752 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -17,6 +17,7 @@ const float GLGizmoBase::HighlightColor[3] = { 1.0f, 0.38f, 0.0f }; GLGizmoBase::Grabber::Grabber() : center(Pointf(0.0, 0.0)) + , angle_z(0.0f) { color[0] = 1.0f; color[1] = 1.0f; @@ -25,13 +26,18 @@ GLGizmoBase::Grabber::Grabber() void GLGizmoBase::Grabber::render(bool hover) const { - float min_x = (float)center.x - HalfSize; - float max_x = (float)center.x + HalfSize; - float min_y = (float)center.y - HalfSize; - float max_y = (float)center.y + HalfSize; + float min_x = -HalfSize; + float max_x = +HalfSize; + float min_y = -HalfSize; + float max_y = +HalfSize; ::glColor3f((GLfloat)color[0], (GLfloat)color[1], (GLfloat)color[2]); + float angle_z_in_deg = angle_z * 180.0f / (float)PI; + ::glPushMatrix(); + ::glTranslatef((GLfloat)center.x, (GLfloat)center.y, 0.0f); + ::glRotatef((GLfloat)angle_z_in_deg, 0.0f, 0.0f, 1.0f); + ::glDisable(GL_CULL_FACE); ::glBegin(GL_TRIANGLES); ::glVertex3f((GLfloat)min_x, (GLfloat)min_y, 0.0f); @@ -57,6 +63,8 @@ void GLGizmoBase::Grabber::render(bool hover) const ::glVertex3f((GLfloat)min_x, (GLfloat)max_y, 0.0f); ::glEnd(); } + + ::glPopMatrix(); } GLGizmoBase::GLGizmoBase() @@ -137,6 +145,7 @@ void GLGizmoBase::render_grabbers() const const float GLGizmoRotate::Offset = 5.0f; const unsigned int GLGizmoRotate::CircleResolution = 64; +const unsigned int GLGizmoRotate::AngleResolution = 64; const unsigned int GLGizmoRotate::ScaleStepsCount = 60; const float GLGizmoRotate::ScaleStepRad = 2.0f * (float)PI / GLGizmoRotate::ScaleStepsCount; const unsigned int GLGizmoRotate::ScaleLongEvery = 5; @@ -147,9 +156,11 @@ const float GLGizmoRotate::GrabberOffset = 5.0f; GLGizmoRotate::GLGizmoRotate() : GLGizmoBase() - , m_angle_x(0.0f) - , m_angle_y(0.0f) +// , m_angle_x(0.0f) +// , m_angle_y(0.0f) , m_angle_z(0.0f) + , m_center(Pointf(0.0, 0.0)) + , m_radius(0.0f) { } @@ -176,7 +187,22 @@ bool GLGizmoRotate::on_init() void GLGizmoRotate::on_update(const Pointf& mouse_pos) { -// std::cout << "GLGizmoRotate::on_update() - delta (" << delta.x << ", " << delta.y << ")" << std::endl; + Vectorf orig_dir(1.0, 0.0); + Vectorf new_dir = normalize(mouse_pos - m_center); + coordf_t theta = ::acos(clamp(-1.0, 1.0, dot(new_dir, orig_dir))); + if (cross(orig_dir, new_dir) < 0.0) + theta = 2.0 * (coordf_t)PI - theta; + + if (length(m_center.vector_to(mouse_pos)) < 2.0 * (double)m_radius / 3.0) + { + coordf_t step = 2.0 * (coordf_t)PI / (coordf_t)SnapRegionsCount; + theta = step * (coordf_t)std::round(theta / step); + } + + if (theta == 2.0 * (coordf_t)PI) + theta = 0.0; + + m_angle_z = (float)theta; } void GLGizmoRotate::on_render(const BoundingBoxf3& box) const @@ -185,18 +211,20 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const ::glDisable(GL_DEPTH_TEST); const Pointf3& size = box.size(); - const Pointf3& center = box.center(); - - float radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y)); + m_center = box.center(); + m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y)); ::glLineWidth(2.0f); ::glColor3fv(BaseColor); - _render_circle(center, radius); - _render_scale(center, radius); - _render_snap_radii(center, radius); - _render_reference_radius(center, radius); - _render_grabber(center, radius); + _render_circle(); + _render_scale(); + _render_snap_radii(); + _render_reference_radius(); + + ::glColor3fv(HighlightColor); + _render_angle_z(); + _render_grabber(); } void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const @@ -210,23 +238,23 @@ void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const render_grabbers(); } -void GLGizmoRotate::_render_circle(const Pointf3& center, float radius) const +void GLGizmoRotate::_render_circle() const { ::glBegin(GL_LINE_LOOP); for (unsigned int i = 0; i < ScaleStepsCount; ++i) { float angle = (float)i * ScaleStepRad; - float x = center.x + ::cos(angle) * radius; - float y = center.y + ::sin(angle) * radius; + float x = m_center.x + ::cos(angle) * m_radius; + float y = m_center.y + ::sin(angle) * m_radius; ::glVertex3f((GLfloat)x, (GLfloat)y, 0.0f); } ::glEnd(); } -void GLGizmoRotate::_render_scale(const Pointf3& center, float radius) const +void GLGizmoRotate::_render_scale() const { - float out_radius_long = radius + ScaleLongTooth; - float out_radius_short = radius + ScaleShortTooth; + float out_radius_long = m_radius + ScaleLongTooth; + float out_radius_short = m_radius + ScaleShortTooth; ::glBegin(GL_LINES); for (unsigned int i = 0; i < ScaleStepsCount; ++i) @@ -234,55 +262,73 @@ void GLGizmoRotate::_render_scale(const Pointf3& center, float radius) const float angle = (float)i * ScaleStepRad; float cosa = ::cos(angle); float sina = ::sin(angle); - float in_x = center.x + cosa * radius; - float in_y = center.y + sina * radius; - float out_x = (i % ScaleLongEvery == 0) ? center.x + cosa * out_radius_long : center.x + cosa * out_radius_short; - float out_y = (i % ScaleLongEvery == 0) ? center.y + sina * out_radius_long : center.y + sina * out_radius_short; + float in_x = m_center.x + cosa * m_radius; + float in_y = m_center.y + sina * m_radius; + float out_x = (i % ScaleLongEvery == 0) ? m_center.x + cosa * out_radius_long : m_center.x + cosa * out_radius_short; + float out_y = (i % ScaleLongEvery == 0) ? m_center.y + sina * out_radius_long : m_center.y + sina * out_radius_short; ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, 0.0f); ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, 0.0f); } ::glEnd(); } -void GLGizmoRotate::_render_snap_radii(const Pointf3& center, float radius) const +void GLGizmoRotate::_render_snap_radii() const { - float step_deg = 2.0f * (float)PI / (float)SnapRegionsCount; + float step = 2.0f * (float)PI / (float)SnapRegionsCount; - float in_radius = radius / 3.0f; + float in_radius = m_radius / 3.0f; float out_radius = 2.0f * in_radius; ::glBegin(GL_LINES); for (unsigned int i = 0; i < SnapRegionsCount; ++i) { - float angle = (float)i * step_deg; + float angle = (float)i * step; float cosa = ::cos(angle); float sina = ::sin(angle); - float in_x = center.x + cosa * in_radius; - float in_y = center.y + sina * in_radius; - float out_x = center.x + cosa * out_radius; - float out_y = center.y + sina * out_radius; + float in_x = m_center.x + cosa * in_radius; + float in_y = m_center.y + sina * in_radius; + float out_x = m_center.x + cosa * out_radius; + float out_y = m_center.y + sina * out_radius; ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, 0.0f); ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, 0.0f); } ::glEnd(); } -void GLGizmoRotate::_render_reference_radius(const Pointf3& center, float radius) const +void GLGizmoRotate::_render_reference_radius() const { ::glBegin(GL_LINES); - ::glVertex3f((GLfloat)center.x, (GLfloat)center.y, 0.0f); - ::glVertex3f((GLfloat)center.x + radius, (GLfloat)center.y, 0.0f); + ::glVertex3f((GLfloat)m_center.x, (GLfloat)m_center.y, 0.0f); + ::glVertex3f((GLfloat)m_center.x + m_radius + GrabberOffset, (GLfloat)m_center.y, 0.0f); ::glEnd(); } -void GLGizmoRotate::_render_grabber(const Pointf3& center, float radius) const +void GLGizmoRotate::_render_angle_z() const { - float grabber_radius = radius + GrabberOffset; - m_grabbers[0].center.x = center.x + ::cos(m_angle_z) * grabber_radius; - m_grabbers[0].center.y = center.y + ::sin(m_angle_z) * grabber_radius; + float step_angle = m_angle_z / AngleResolution; + float ex_radius = m_radius + GrabberOffset; + ::glBegin(GL_LINE_STRIP); + for (unsigned int i = 0; i <= AngleResolution; ++i) + { + float angle = (float)i * step_angle; + float x = m_center.x + ::cos(angle) * ex_radius; + float y = m_center.y + ::sin(angle) * ex_radius; + ::glVertex3f((GLfloat)x, (GLfloat)y, 0.0f); + } + ::glEnd(); +} + +void GLGizmoRotate::_render_grabber() const +{ + float grabber_radius = m_radius + GrabberOffset; + m_grabbers[0].center.x = m_center.x + ::cos(m_angle_z) * grabber_radius; + m_grabbers[0].center.y = m_center.y + ::sin(m_angle_z) * grabber_radius; + m_grabbers[0].angle_z = m_angle_z; + + ::glColor3fv(BaseColor); ::glBegin(GL_LINES); - ::glVertex3f((GLfloat)center.x, (GLfloat)center.y, 0.0f); + ::glVertex3f((GLfloat)m_center.x, (GLfloat)m_center.y, 0.0f); ::glVertex3f((GLfloat)m_grabbers[0].center.x, (GLfloat)m_grabbers[0].center.y, 0.0f); ::glEnd(); @@ -330,7 +376,6 @@ void GLGizmoScale::on_update(const Pointf& mouse_pos) coordf_t orig_len = length(m_start_drag_position - center); coordf_t new_len = length(mouse_pos - center); - coordf_t ratio = (orig_len != 0.0) ? new_len / orig_len : 1.0; m_scale_x = (float)ratio; diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index bc334d26b9..432a20958a 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -25,6 +25,7 @@ protected: static const float HoverOffset; Pointf center; + float angle_z; float color[3]; Grabber(); @@ -82,6 +83,7 @@ class GLGizmoRotate : public GLGizmoBase { static const float Offset; static const unsigned int CircleResolution; + static const unsigned int AngleResolution; static const unsigned int ScaleStepsCount; static const float ScaleStepRad; static const unsigned int ScaleLongEvery; @@ -90,10 +92,13 @@ class GLGizmoRotate : public GLGizmoBase static const unsigned int SnapRegionsCount; static const float GrabberOffset; - float m_angle_x; - float m_angle_y; +// float m_angle_x; +// float m_angle_y; float m_angle_z; + mutable Pointf m_center; + mutable float m_radius; + public: GLGizmoRotate(); @@ -104,11 +109,12 @@ protected: virtual void on_render_for_picking(const BoundingBoxf3& box) const; private: - void _render_circle(const Pointf3& center, float radius) const; - void _render_scale(const Pointf3& center, float radius) const; - void _render_snap_radii(const Pointf3& center, float radius) const; - void _render_reference_radius(const Pointf3& center, float radius) const; - void _render_grabber(const Pointf3& center, float radius) const; + void _render_circle() const; + void _render_scale() const; + void _render_snap_radii() const; + void _render_reference_radius() const; + void _render_angle_z() const; + void _render_grabber() const; }; class GLGizmoScale : public GLGizmoBase From a3949b9f01f60ad3f0543de270f4dc662f6c9249 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 18 Jun 2018 15:07:17 +0200 Subject: [PATCH 098/117] Object updated by scale gizmo --- lib/Slic3r/GUI/Plater.pm | 29 +++++++++ xs/src/libslic3r/Utils.hpp | 1 + xs/src/libslic3r/utils.cpp | 15 +++++ xs/src/slic3r/GUI/3DScene.cpp | 5 ++ xs/src/slic3r/GUI/3DScene.hpp | 5 +- xs/src/slic3r/GUI/GLCanvas3D.cpp | 86 ++++++++++++++++++------- xs/src/slic3r/GUI/GLCanvas3D.hpp | 8 +++ xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 7 ++ xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 1 + xs/src/slic3r/GUI/GLGizmo.cpp | 52 +++++++++------ xs/src/slic3r/GUI/GLGizmo.hpp | 15 +++-- xs/xsp/GUI_3DScene.xsp | 7 ++ 12 files changed, 179 insertions(+), 52 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ed1ebcc6dc..d1ccf07d53 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -105,6 +105,34 @@ sub new { $self->{btn_print}->Enable($enable); $self->{btn_send_gcode}->Enable($enable); }; + + # callback to react to gizmo scale + my $on_gizmo_scale_uniformly = sub { + my ($scale) = @_; + + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_instance = $model_object->instances->[0]; + + my $variation = $scale / $model_instance->scaling_factor; + #FIXME Scale the layer height profile? + foreach my $range (@{ $model_object->layer_height_ranges }) { + $range->[0] *= $variation; + $range->[1] *= $variation; + } + $_->set_scaling_factor($scale) for @{ $model_object->instances }; + $object->transform_thumbnail($self->{model}, $obj_idx); + + #update print and start background processing + $self->stop_background_process; + $self->{print}->add_model_object($model_object, $obj_idx); + + $self->selection_changed(1); # refresh info (size, volume etc.) + $self->update; + $self->schedule_background_process; + }; # Initialize 3D plater if ($Slic3r::GUI::have_OpenGL) { @@ -122,6 +150,7 @@ sub new { Slic3r::GUI::_3DScene::register_on_remove_object_callback($self->{canvas3D}, sub { $self->remove() }); Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved); Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); + Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly); Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 05eaf282f1..921841a279 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -95,6 +95,7 @@ public: void call(int i) const; void call(int i, int j) const; void call(const std::vector& ints) const; + void call(double d) const; void call(double x, double y) const; void call(bool b) const; private: diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 83c45b1906..745d07fcdb 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -247,6 +247,21 @@ void PerlCallback::call(const std::vector& ints) const LEAVE; } +void PerlCallback::call(double d) const +{ + if (!m_callback) + return; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVnv(d))); + PUTBACK; + perl_call_sv(SvRV((SV*)m_callback), G_DISCARD); + FREETMPS; + LEAVE; +} + void PerlCallback::call(double x, double y) const { if (!m_callback) diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 34d066730a..1879b30826 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2038,6 +2038,11 @@ void _3DScene::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, vo s_canvas_mgr.register_on_enable_action_buttons_callback(canvas, callback); } +void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_gizmo_scale_uniformly_callback(canvas, callback); +} + static inline int hex_digit_to_int(const char c) { return diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 26b9911e00..c6a166397f 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -312,9 +312,9 @@ public: // Boolean: Is mouse over this object? bool hover; // Wheter or not this volume has been generated from a modifier - bool is_modifier; + bool is_modifier; // Wheter or not this volume has been generated from the wipe tower - bool is_wipe_tower; + bool is_wipe_tower; // Interleaved triangles & normals with indexed triangles & quads. GLIndexedVertexArray indexed_vertex_array; @@ -589,6 +589,7 @@ public: static void register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback); static void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); + static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); static std::vector load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector instance_idxs); static std::vector load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index ae5125d6cb..f9c10017eb 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -1263,6 +1263,25 @@ void GLCanvas3D::Gizmos::update(const Pointf& mouse_pos) curr->update(mouse_pos); } +void GLCanvas3D::Gizmos::update_data(float scale) +{ + if (!m_enabled) + return; + + GizmosMap::const_iterator it = m_gizmos.find(Scale); + if (it != m_gizmos.end()) + reinterpret_cast(it->second)->set_scale(scale); +} + +bool GLCanvas3D::Gizmos::is_running() const +{ + if (!m_enabled) + return false; + + GLGizmoBase* curr = _get_current(); + return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false; +} + bool GLCanvas3D::Gizmos::is_dragging() const { return m_dragging; @@ -1281,6 +1300,15 @@ void GLCanvas3D::Gizmos::stop_dragging() m_dragging = false; } +float GLCanvas3D::Gizmos::get_scale() const +{ + if (!m_enabled) + return 1.0f; + + GizmosMap::const_iterator it = m_gizmos.find(Scale); + return (it != m_gizmos.end()) ? reinterpret_cast(it->second)->get_scale() : 1.0f; +} + void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const { if (!m_enabled) @@ -2443,6 +2471,12 @@ void GLCanvas3D::register_on_enable_action_buttons_callback(void* callback) m_on_enable_action_buttons_callback.register_callback(callback); } +void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback) +{ + if (callback != nullptr) + m_on_gizmo_scale_uniformly_callback.register_callback(callback); +} + void GLCanvas3D::bind_event_handlers() { if (m_canvas != nullptr) @@ -2661,11 +2695,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse) { m_gizmos.update_on_off_state(*this, m_mouse.position); + _update_gizmos_data(); m_dirty = true; } else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse()) - { + { + _update_gizmos_data(); m_gizmos.start_dragging(); + m_dirty = true; } else { @@ -2688,6 +2725,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) vol->selected = true; } } + + if (m_gizmos.is_running()) + _update_gizmos_data(); + m_dirty = true; } } @@ -2779,6 +2820,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) const Pointf3& cur_pos = _mouse_to_bed_3d(pos); m_gizmos.update(Pointf(cur_pos.x, cur_pos.y)); + + m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); m_dirty = true; } else if (evt.Dragging() && !gizmos_overlay_contains_mouse) @@ -3120,6 +3163,7 @@ void GLCanvas3D::_deregister_callbacks() m_on_instance_moved_callback.deregister_callback(); m_on_wipe_tower_moved_callback.deregister_callback(); m_on_enable_action_buttons_callback.deregister_callback(); + m_on_gizmo_scale_uniformly_callback.deregister_callback(); } void GLCanvas3D::_mark_volumes_for_layer_height() const @@ -3549,28 +3593,9 @@ Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) Pointf3 GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) { - if (!set_current()) - return Pointf3(DBL_MAX, DBL_MAX, DBL_MAX); - - GLint viewport[4]; - ::glGetIntegerv(GL_VIEWPORT, viewport); - - _camera_tranform(); - - GLdouble modelview_matrix[16]; - ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix); - GLdouble projection_matrix[16]; - ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); - - GLint y = viewport[3] - (GLint)mouse_pos.y; - - GLdouble x0, y0, z0; - ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)y, 0.1, modelview_matrix, projection_matrix, viewport, &x0, &y0, &z0); - - GLdouble x1, y1, z1; - ::gluUnProject((GLdouble)mouse_pos.x, (GLdouble)y, 0.9, modelview_matrix, projection_matrix, viewport, &x1, &y1, &z1); - - return Linef3(Pointf3(x0, y0, z0), Pointf3(x1, y1, z1)).intersect_plane(0.0); + float z0 = 0.0f; + float z1 = 1.0f; + return Linef3(_mouse_to_3d(mouse_pos, &z0), _mouse_to_3d(mouse_pos, &z1)).intersect_plane(0.0); } void GLCanvas3D::_start_timer() @@ -4239,6 +4264,21 @@ void GLCanvas3D::_on_select(int volume_idx) m_on_select_object_callback.call(id); } +void GLCanvas3D::_update_gizmos_data() +{ + int id = _get_first_selected_object_id(); + if ((id != -1) && (m_model != nullptr)) + { + ModelObject* model_object = m_model->objects[id]; + if (model_object != nullptr) + { + ModelInstance* model_instance = model_object->instances[0]; + if (model_instance != nullptr) + m_gizmos.update_data(model_instance->scaling_factor); + } + } +} + std::vector GLCanvas3D::_parse_colors(const std::vector& colors) { static const float INV_255 = 1.0f / 255.0f; diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp index 32dc1d934b..c503d18456 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp @@ -360,11 +360,15 @@ public: bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const; bool grabber_contains_mouse() const; void update(const Pointf& mouse_pos); + void update_data(float scale); + bool is_running() const; bool is_dragging() const; void start_dragging(); void stop_dragging(); + float get_scale() const; + void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const; void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const; @@ -437,6 +441,7 @@ private: PerlCallback m_on_instance_moved_callback; PerlCallback m_on_wipe_tower_moved_callback; PerlCallback m_on_enable_action_buttons_callback; + PerlCallback m_on_gizmo_scale_uniformly_callback; public: GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context); @@ -542,6 +547,7 @@ public: void register_on_instance_moved_callback(void* callback); void register_on_wipe_tower_moved_callback(void* callback); void register_on_enable_action_buttons_callback(void* callback); + void register_on_gizmo_scale_uniformly_callback(void* callback); void bind_event_handlers(); void unbind_event_handlers(); @@ -622,6 +628,8 @@ private: void _on_move(const std::vector& volume_idxs); void _on_select(int volume_idx); + void _update_gizmos_data(); + static std::vector _parse_colors(const std::vector& colors); }; diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index 97af89fb7d..f288ee456a 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -678,6 +678,13 @@ void GLCanvas3DManager::register_on_enable_action_buttons_callback(wxGLCanvas* c it->second->register_on_enable_action_buttons_callback(callback); } +void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback) +{ + CanvasesMap::iterator it = _get_canvas(canvas); + if (it != m_canvases.end()) + it->second->register_on_gizmo_scale_uniformly_callback(callback); +} + GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) { return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 95bd2af809..6989da791f 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -152,6 +152,7 @@ public: void register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback); void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); + void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); private: CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp index 706c416752..d3aae33e85 100644 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ b/xs/src/slic3r/GUI/GLGizmo.cpp @@ -115,8 +115,7 @@ void GLGizmoBase::set_hover_id(int id) void GLGizmoBase::start_dragging() { - if (m_hover_id != -1) - m_start_drag_position = m_grabbers[m_hover_id].center; + on_start_dragging(); } void GLGizmoBase::update(const Pointf& mouse_pos) @@ -135,6 +134,10 @@ void GLGizmoBase::render_for_picking(const BoundingBoxf3& box) const on_render_for_picking(box); } +void GLGizmoBase::on_start_dragging() +{ +} + void GLGizmoBase::render_grabbers() const { for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) @@ -156,8 +159,6 @@ const float GLGizmoRotate::GrabberOffset = 5.0f; GLGizmoRotate::GLGizmoRotate() : GLGizmoBase() -// , m_angle_x(0.0f) -// , m_angle_y(0.0f) , m_angle_z(0.0f) , m_center(Pointf(0.0, 0.0)) , m_radius(0.0f) @@ -332,7 +333,7 @@ void GLGizmoRotate::_render_grabber() const ::glVertex3f((GLfloat)m_grabbers[0].center.x, (GLfloat)m_grabbers[0].center.y, 0.0f); ::glEnd(); - ::memcpy((void*)m_grabbers[0].color, (const void*)HighlightColor, 4 * sizeof(float)); + ::memcpy((void*)m_grabbers[0].color, (const void*)HighlightColor, 3 * sizeof(float)); render_grabbers(); } @@ -340,12 +341,21 @@ const float GLGizmoScale::Offset = 5.0f; GLGizmoScale::GLGizmoScale() : GLGizmoBase() - , m_scale_x(1.0f) - , m_scale_y(1.0f) - , m_scale_z(1.0f) + , m_scale(1.0f) + , m_starting_scale(1.0f) { } +float GLGizmoScale::get_scale() const +{ + return m_scale; +} + +void GLGizmoScale::set_scale(float scale) +{ + m_starting_scale = scale; +} + bool GLGizmoScale::on_init() { std::string path = resources_dir() + "/icons/overlay/"; @@ -370,17 +380,21 @@ bool GLGizmoScale::on_init() return true; } +void GLGizmoScale::on_start_dragging() +{ + if (m_hover_id != -1) + m_starting_drag_position = m_grabbers[m_hover_id].center; +} + void GLGizmoScale::on_update(const Pointf& mouse_pos) { Pointf center(0.5 * (m_grabbers[1].center.x + m_grabbers[0].center.x), 0.5 * (m_grabbers[3].center.y + m_grabbers[0].center.y)); - coordf_t orig_len = length(m_start_drag_position - center); + coordf_t orig_len = length(m_starting_drag_position - center); coordf_t new_len = length(mouse_pos - center); coordf_t ratio = (orig_len != 0.0) ? new_len / orig_len : 1.0; - m_scale_x = (float)ratio; - m_scale_y = (float)ratio; - m_scale_z = (float)ratio; + m_scale = m_starting_scale * (float)ratio; } void GLGizmoScale::on_render(const BoundingBoxf3& box) const @@ -388,14 +402,10 @@ void GLGizmoScale::on_render(const BoundingBoxf3& box) const ::glDisable(GL_LIGHTING); ::glDisable(GL_DEPTH_TEST); - const Pointf3& size = box.size(); - const Pointf3& center = box.center(); - - Pointf half_scaled_size = 0.5 * Pointf((coordf_t)m_scale_x * size.x, (coordf_t)m_scale_y * size.y); - coordf_t min_x = center.x - half_scaled_size.x - (coordf_t)Offset; - coordf_t max_x = center.x + half_scaled_size.x + (coordf_t)Offset; - coordf_t min_y = center.y - half_scaled_size.y - (coordf_t)Offset; - coordf_t max_y = center.y + half_scaled_size.y + (coordf_t)Offset; + coordf_t min_x = box.min.x - (coordf_t)Offset; + coordf_t max_x = box.max.x + (coordf_t)Offset; + coordf_t min_y = box.min.y - (coordf_t)Offset; + coordf_t max_y = box.max.y + (coordf_t)Offset; m_grabbers[0].center.x = min_x; m_grabbers[0].center.y = min_y; @@ -419,7 +429,7 @@ void GLGizmoScale::on_render(const BoundingBoxf3& box) const // draw grabbers for (unsigned int i = 0; i < 4; ++i) { - ::memcpy((void*)m_grabbers[i].color, (const void*)HighlightColor, 4 * sizeof(float)); + ::memcpy((void*)m_grabbers[i].color, (const void*)HighlightColor, 3 * sizeof(float)); } render_grabbers(); } diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp index 432a20958a..2baec8f9b1 100644 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ b/xs/src/slic3r/GUI/GLGizmo.hpp @@ -47,7 +47,6 @@ protected: GLTexture m_textures[Num_States]; int m_hover_id; mutable std::vector m_grabbers; - Pointf m_start_drag_position; public: GLGizmoBase(); @@ -72,6 +71,7 @@ public: protected: virtual bool on_init() = 0; + virtual void on_start_dragging(); virtual void on_update(const Pointf& mouse_pos) = 0; virtual void on_render(const BoundingBoxf3& box) const = 0; virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; @@ -92,8 +92,6 @@ class GLGizmoRotate : public GLGizmoBase static const unsigned int SnapRegionsCount; static const float GrabberOffset; -// float m_angle_x; -// float m_angle_y; float m_angle_z; mutable Pointf m_center; @@ -121,15 +119,20 @@ class GLGizmoScale : public GLGizmoBase { static const float Offset; - float m_scale_x; - float m_scale_y; - float m_scale_z; + float m_scale; + + Pointf m_starting_drag_position; + float m_starting_scale; public: GLGizmoScale(); + float get_scale() const; + void set_scale(float scale); + protected: virtual bool on_init(); + virtual void on_start_dragging(); virtual void on_update(const Pointf& mouse_pos); virtual void on_render(const BoundingBoxf3& box) const; virtual void on_render_for_picking(const BoundingBoxf3& box) const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 426395ef23..29f35293bb 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -598,6 +598,13 @@ register_on_enable_action_buttons_callback(canvas, callback) CODE: _3DScene::register_on_enable_action_buttons_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); +void +register_on_gizmo_scale_uniformly_callback(canvas, callback) + SV *canvas; + SV *callback; + CODE: + _3DScene::register_on_gizmo_scale_uniformly_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback); + unsigned int finalize_legend_texture() CODE: From 7499a4dea438c0619cfaaa53dc713c0b2e52f809 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 19 Jun 2018 16:14:10 +0200 Subject: [PATCH 099/117] Disabled the UI gizmos, they are not yet ready for the prime time --- lib/Slic3r/GUI/Plater.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 0ac24664c4..e15b8c34c3 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -151,7 +151,7 @@ sub new { Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved); Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly); - Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); +# Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); From 478488972c7832c669f7ae7c47360555c95a7e7d Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 19 Jun 2018 18:26:38 +0200 Subject: [PATCH 100/117] Updating bugfixes (#973) * ConfigWizard: Fix MM legacy profile detect * Remove Perl BedShapeDialog * PresetUpdater: Look for updates in resources as well * ConfigWizard: Startup condition based on printer profiles only rather than all profiles Previously wizard would not run if there was a leftover filament profile but no printer profiles * ConfigWizard: Fix button labels * ConfigWizard: Pick the very first printer variant by default --- lib/Slic3r/GUI.pm | 1 - lib/Slic3r/GUI/BedShapeDialog.pm | 316 -------------------------- xs/src/slic3r/GUI/ConfigWizard.cpp | 17 +- xs/src/slic3r/GUI/GUI.cpp | 2 +- xs/src/slic3r/Utils/PresetUpdater.cpp | 27 ++- 5 files changed, 28 insertions(+), 335 deletions(-) delete mode 100644 lib/Slic3r/GUI/BedShapeDialog.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 52c4828133..80130fefe0 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -7,7 +7,6 @@ use File::Basename qw(basename); use FindBin; use List::Util qw(first); use Slic3r::GUI::2DBed; -use Slic3r::GUI::BedShapeDialog; use Slic3r::GUI::Controller; use Slic3r::GUI::Controller::ManualControlDialog; use Slic3r::GUI::Controller::PrinterPanel; diff --git a/lib/Slic3r/GUI/BedShapeDialog.pm b/lib/Slic3r/GUI/BedShapeDialog.pm deleted file mode 100644 index 70c8e02562..0000000000 --- a/lib/Slic3r/GUI/BedShapeDialog.pm +++ /dev/null @@ -1,316 +0,0 @@ -# The bed shape dialog. -# The dialog opens from Print Settins tab -> Bed Shape: Set... - -package Slic3r::GUI::BedShapeDialog; -use strict; -use warnings; -use utf8; - -use List::Util qw(min max); -use Slic3r::Geometry qw(X Y unscale); -use Wx qw(:dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE); -use base 'Wx::Dialog'; - -sub new { - my $class = shift; - my ($parent, $default) = @_; - my $self = $class->SUPER::new($parent, -1, "Bed Shape", wxDefaultPosition, [350,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - - $self->{panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $default); - - my $main_sizer = Wx::BoxSizer->new(wxVERTICAL); - $main_sizer->Add($panel, 1, wxEXPAND); - $main_sizer->Add($self->CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND); - - $self->SetSizer($main_sizer); - $self->SetMinSize($self->GetSize); - $main_sizer->SetSizeHints($self); - - # needed to actually free memory - EVT_CLOSE($self, sub { - $self->EndModal(wxID_OK); - $self->Destroy; - }); - - return $self; -} - -sub GetValue { - my ($self) = @_; - return $self->{panel}->GetValue; -} - -package Slic3r::GUI::BedShapePanel; - -use List::Util qw(min max sum first); -use Scalar::Util qw(looks_like_number); -use Slic3r::Geometry qw(PI X Y unscale scaled_epsilon); -use Wx qw(:font :id :misc :sizer :choicebook :filedialog :pen :brush wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE EVT_CHOICEBOOK_PAGE_CHANGED EVT_BUTTON); -use base 'Wx::Panel'; - -use constant SHAPE_RECTANGULAR => 0; -use constant SHAPE_CIRCULAR => 1; -use constant SHAPE_CUSTOM => 2; - -sub new { - my $class = shift; - my ($parent, $default) = @_; - my $self = $class->SUPER::new($parent, -1); - - $self->on_change(undef); - - my $box = Wx::StaticBox->new($self, -1, "Shape"); - my $sbsizer = Wx::StaticBoxSizer->new($box, wxVERTICAL); - - # shape options - $self->{shape_options_book} = Wx::Choicebook->new($self, -1, wxDefaultPosition, [300,-1], wxCHB_TOP); - $sbsizer->Add($self->{shape_options_book}); - - $self->{optgroups} = []; - { - my $optgroup = $self->_init_shape_options_page('Rectangular'); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'rect_size', - type => 'point', - label => 'Size', - tooltip => 'Size in X and Y of the rectangular plate.', - default => [200,200], - )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'rect_origin', - type => 'point', - label => 'Origin', - tooltip => 'Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.', - default => [0,0], - )); - } - { - my $optgroup = $self->_init_shape_options_page('Circular'); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'diameter', - type => 'f', - label => 'Diameter', - tooltip => 'Diameter of the print bed. It is assumed that origin (0,0) is located in the center.', - sidetext => 'mm', - default => 200, - )); - } - { - my $optgroup = $self->_init_shape_options_page('Custom'); - $optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new( - full_width => 1, - widget => sub { - my ($parent) = @_; - - my $btn = Wx::Button->new($parent, -1, "Load shape from STL...", wxDefaultPosition, wxDefaultSize); - EVT_BUTTON($self, $btn, sub { $self->_load_stl }); - return $btn; - } - )); - } - - EVT_CHOICEBOOK_PAGE_CHANGED($self, -1, sub { - $self->_update_shape; - }); - - # right pane with preview canvas - my $canvas = $self->{canvas} = Slic3r::GUI::2DBed->new($self); - - # main sizer - my $top_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $top_sizer->Add($sbsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); - $top_sizer->Add($canvas, 1, wxEXPAND | wxALL, 10) if $canvas; - - $self->SetSizerAndFit($top_sizer); - - $self->_set_shape($default); - $self->_update_preview; - - return $self; -} - -sub on_change { - my ($self, $cb) = @_; - $self->{on_change} = $cb // sub {}; -} - -# Called from the constructor. -# Set the initial bed shape from a list of points. -# Deduce the bed shape type (rect, circle, custom) -# This routine shall be smart enough if the user messes up -# with the list of points in the ini file directly. -sub _set_shape { - my ($self, $points) = @_; - - # is this a rectangle? - if (@$points == 4) { - my $polygon = Slic3r::Polygon->new_scale(@$points); - my $lines = $polygon->lines; - if ($lines->[0]->parallel_to_line($lines->[2]) && $lines->[1]->parallel_to_line($lines->[3])) { - # okay, it's a rectangle - - # find origin - # the || 0 hack prevents "-0" which might confuse the user - my $x_min = min(map $_->[X], @$points) || 0; - my $x_max = max(map $_->[X], @$points) || 0; - my $y_min = min(map $_->[Y], @$points) || 0; - my $y_max = max(map $_->[Y], @$points) || 0; - my $origin = [-$x_min, -$y_min]; - - $self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR); - my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR]; - $optgroup->set_value('rect_size', [ $x_max-$x_min, $y_max-$y_min ]); - $optgroup->set_value('rect_origin', $origin); - $self->_update_shape; - return; - } - } - - # is this a circle? - { - # Analyze the array of points. Do they reside on a circle? - my $polygon = Slic3r::Polygon->new_scale(@$points); - my $center = $polygon->bounding_box->center; - my @vertex_distances = map $center->distance_to($_), @$polygon; - my $avg_dist = sum(@vertex_distances)/@vertex_distances; - if (!defined first { abs($_ - $avg_dist) > 10*scaled_epsilon } @vertex_distances) { - # all vertices are equidistant to center - $self->{shape_options_book}->SetSelection(SHAPE_CIRCULAR); - my $optgroup = $self->{optgroups}[SHAPE_CIRCULAR]; - $optgroup->set_value('diameter', sprintf("%.0f", unscale($avg_dist*2))); - $self->_update_shape; - return; - } - } - - if (@$points < 3) { - # Invalid polygon. Revert to default bed dimensions. - $self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR); - my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR]; - $optgroup->set_value('rect_size', [200, 200]); - $optgroup->set_value('rect_origin', [0, 0]); - $self->_update_shape; - return; - } - - # This is a custom bed shape, use the polygon provided. - $self->{shape_options_book}->SetSelection(SHAPE_CUSTOM); - # Copy the polygon to the canvas, make a copy of the array. - $self->{canvas}->bed_shape([@$points]); - $self->_update_shape; -} - -# Update the bed shape from the dialog fields. -sub _update_shape { - my ($self) = @_; - - my $page_idx = $self->{shape_options_book}->GetSelection; - if ($page_idx == SHAPE_RECTANGULAR) { - my $rect_size = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_size'); - my $rect_origin = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_origin'); - my ($x, $y) = @$rect_size; - return if !looks_like_number($x) || !looks_like_number($y); # empty strings or '-' or other things - return if !$x || !$y or $x == 0 or $y == 0; - my ($x0, $y0) = (0,0); - my ($x1, $y1) = ($x ,$y); - { - my ($dx, $dy) = @$rect_origin; - return if !looks_like_number($dx) || !looks_like_number($dy); # empty strings or '-' or other things - $x0 -= $dx; - $x1 -= $dx; - $y0 -= $dy; - $y1 -= $dy; - } - $self->{canvas}->bed_shape([ - [$x0,$y0], - [$x1,$y0], - [$x1,$y1], - [$x0,$y1], - ]); - } elsif ($page_idx == SHAPE_CIRCULAR) { - my $diameter = $self->{optgroups}[SHAPE_CIRCULAR]->get_value('diameter'); - return if !$diameter or $diameter == 0; - my $r = $diameter/2; - my $twopi = 2*PI; - my $edges = 60; - my $polygon = Slic3r::Polygon->new_scale( - map [ $r * cos $_, $r * sin $_ ], - map { $twopi/$edges*$_ } 1..$edges - ); - $self->{canvas}->bed_shape([ - map [ unscale($_->x), unscale($_->y) ], @$polygon #)) - ]); - } - - $self->{on_change}->(); - $self->_update_preview; -} - -sub _update_preview { - my ($self) = @_; - $self->{canvas}->Refresh if $self->{canvas}; - $self->Refresh; -} - -# Called from the constructor. -# Create a panel for a rectangular / circular / custom bed shape. -sub _init_shape_options_page { - my ($self, $title) = @_; - - my $panel = Wx::Panel->new($self->{shape_options_book}); - my $optgroup; - push @{$self->{optgroups}}, $optgroup = Slic3r::GUI::OptionsGroup->new( - parent => $panel, - title => 'Settings', - label_width => 100, - on_change => sub { - my ($opt_id) = @_; - #$self->{"_$opt_id"} = $optgroup->get_value($opt_id); - $self->_update_shape; - }, - ); - $panel->SetSizerAndFit($optgroup->sizer); - $self->{shape_options_book}->AddPage($panel, $title); - - return $optgroup; -} - -# Loads an stl file, projects it to the XY plane and calculates a polygon. -sub _load_stl { - my ($self) = @_; - - my $dialog = Wx::FileDialog->new($self, 'Choose a file to import bed shape from (STL/OBJ/AMF/PRUSA):', "", "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if ($dialog->ShowModal != wxID_OK) { - $dialog->Destroy; - return; - } - my $input_file = $dialog->GetPaths; - $dialog->Destroy; - - my $model = Slic3r::Model->read_from_file($input_file); - my $mesh = $model->mesh; - my $expolygons = $mesh->horizontal_projection; - - if (@$expolygons == 0) { - Slic3r::GUI::show_error($self, "The selected file contains no geometry."); - return; - } - if (@$expolygons > 1) { - Slic3r::GUI::show_error($self, "The selected file contains several disjoint areas. This is not supported."); - return; - } - - my $polygon = $expolygons->[0]->contour; - $self->{canvas}->bed_shape([ map [ unscale($_->x), unscale($_->y) ], @$polygon ]); - $self->_update_preview(); -} - -# Returns the resulting bed shape polygon. This value will be stored to the ini file. -sub GetValue { - my ($self) = @_; - return $self->{canvas}->bed_shape; -} - -1; diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp index aed0c35343..ce06da8536 100644 --- a/xs/src/slic3r/GUI/ConfigWizard.cpp +++ b/xs/src/slic3r/GUI/ConfigWizard.cpp @@ -113,6 +113,11 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons sizer->Add(all_none_sizer, 0, wxEXPAND); SetSizer(sizer); + + if (cboxes.size() > 0) { + cboxes[0]->SetValue(true); + on_checkbox(cboxes[0], true); + } } void PrinterPicker::select_all(bool select) @@ -598,10 +603,10 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) static const std::unordered_map> legacy_preset_map {{ { "Original Prusa i3 MK2.ini", std::make_pair("MK2S", "0.4") }, - { "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2S", "0.4") }, - { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, - { "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2S", "0.4") }, - { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, + { "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2SMM", "0.4") }, + { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, + { "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2SMM", "0.4") }, + { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, { "Original Prusa i3 MK2 0.25 nozzle.ini", std::make_pair("MK2S", "0.25") }, { "Original Prusa i3 MK2 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, { "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") }, @@ -809,8 +814,8 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) : topsizer->AddSpacer(INDEX_MARGIN); topsizer->Add(p->hscroll, 1, wxEXPAND); - p->btn_prev = new wxButton(this, wxID_BACKWARD); - p->btn_next = new wxButton(this, wxID_FORWARD); + p->btn_prev = new wxButton(this, wxID_NONE, _(L("< &Back"))); + p->btn_next = new wxButton(this, wxID_NONE, _(L("&Next >"))); p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish"))); p->btn_cancel = new wxButton(this, wxID_CANCEL); p->btnsizer->AddStretchSpacer(); diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 974c554b68..e2f3925fcb 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -423,7 +423,7 @@ bool check_unsaved_changes() bool config_wizard_startup(bool app_config_exists) { - if (! app_config_exists || g_PresetBundle->has_defauls_only()) { + if (! app_config_exists || g_PresetBundle->printers.size() <= 1) { config_wizard(ConfigWizard::RR_DATA_EMPTY); return true; } else if (g_AppConfig->legacy_datadir()) { diff --git a/xs/src/slic3r/Utils/PresetUpdater.cpp b/xs/src/slic3r/Utils/PresetUpdater.cpp index f34fc4c19d..8159a75e2f 100644 --- a/xs/src/slic3r/Utils/PresetUpdater.cpp +++ b/xs/src/slic3r/Utils/PresetUpdater.cpp @@ -259,7 +259,7 @@ void PresetUpdater::priv::sync_config(const std::set vendors) con } const auto recommended = recommended_it->config_version; - BOOST_LOG_TRIVIAL(debug) << boost::format("New index for vendor: %1%: current version: %2%, recommended version: %3%") + BOOST_LOG_TRIVIAL(debug) << boost::format("Got index for vendor: %1%: current version: %2%, recommended version: %3%") % vendor.name % vendor.config_version.to_string() % recommended.to_string(); @@ -352,20 +352,25 @@ Updates PresetUpdater::priv::get_config_updates() const continue; } - auto path_in_cache = cache_path / (idx.vendor() + ".ini"); - if (! fs::exists(path_in_cache)) { - BOOST_LOG_TRIVIAL(warning) << "Index indicates update, but new bundle not found in cache: " << path_in_cache.string(); - continue; + auto path_src = cache_path / (idx.vendor() + ".ini"); + if (! fs::exists(path_src)) { + auto path_in_rsrc = rsrc_path / (idx.vendor() + ".ini"); + if (! fs::exists(path_in_rsrc)) { + BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update, but bundle found in neither cache nor resources") + % idx.vendor();; + continue; + } else { + path_src = std::move(path_in_rsrc); + } } - const auto cached_vp = VendorProfile::from_ini(path_in_cache, false); - if (cached_vp.config_version == recommended->config_version) { - updates.updates.emplace_back(std::move(path_in_cache), std::move(bundle_path), *recommended); + const auto new_vp = VendorProfile::from_ini(path_src, false); + if (new_vp.config_version == recommended->config_version) { + updates.updates.emplace_back(std::move(path_src), std::move(bundle_path), *recommended); } else { - BOOST_LOG_TRIVIAL(warning) << boost::format("Vendor: %1%: Index indicates update (%2%) but cached bundle has a different version: %3%") + BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources") % idx.vendor() - % recommended->config_version.to_string() - % cached_vp.config_version.to_string(); + % recommended->config_version.to_string(); } } } From 1602ddd56cca7a77d3d893c49df7efca926d108b Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 22 May 2018 12:54:46 +0200 Subject: [PATCH 101/117] avrdude: Reduce retries to make timeout time more reasonable --- xs/src/avrdude/libavrdude.h | 2 +- xs/src/avrdude/stk500v2.c | 17 ++++++----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/xs/src/avrdude/libavrdude.h b/xs/src/avrdude/libavrdude.h index e8197f9c2a..238f596157 100644 --- a/xs/src/avrdude/libavrdude.h +++ b/xs/src/avrdude/libavrdude.h @@ -737,7 +737,7 @@ extern bool cancel_flag; #define RETURN_IF_CANCEL() \ do { \ if (cancel_flag) { \ - avrdude_message(MSG_INFO, "%s(): Cancelled, exiting...\n", __func__); \ + avrdude_message(MSG_INFO, "avrdude: %s(): Cancelled, exiting...\n", __func__); \ return -99; \ } \ } while (0) diff --git a/xs/src/avrdude/stk500v2.c b/xs/src/avrdude/stk500v2.c index d3acb639c3..4d62640c0d 100644 --- a/xs/src/avrdude/stk500v2.c +++ b/xs/src/avrdude/stk500v2.c @@ -79,7 +79,7 @@ #define SERIAL_TIMEOUT 2 // Retry count -#define RETRIES 5 +#define RETRIES 0 #if 0 #define DEBUG(...) avrdude_message(MSG_INFO, __VA_ARGS__) @@ -745,7 +745,7 @@ static int stk500v2_recv(PROGRAMMER * pgm, unsigned char *msg, size_t maxsize) { -static int stk500v2_getsync_internal(PROGRAMMER * pgm, int retries) { +int stk500v2_getsync(PROGRAMMER * pgm) { int tries = 0; unsigned char buf[1], resp[32]; int status; @@ -804,7 +804,7 @@ retry: progname, pgmname[PDATA(pgm)->pgmtype]); return 0; } else { - if (tries > retries) { + if (tries > RETRIES) { avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): can't communicate with device: resp=0x%02x\n", progname, resp[0]); return -6; @@ -814,7 +814,7 @@ retry: // or if we got a timeout } else if (status == -1) { - if (tries > retries) { + if (tries > RETRIES) { avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): timeout communicating with programmer\n", progname); return -1; @@ -823,7 +823,7 @@ retry: // or any other error } else { - if (tries > retries) { + if (tries > RETRIES) { avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): error communicating with programmer: (%d)\n", progname,status); } else @@ -833,11 +833,6 @@ retry: return 0; } -int stk500v2_getsync(PROGRAMMER * pgm) { - // This is to avoid applying RETRIES exponentially - return stk500v2_getsync_internal(pgm, RETRIES); -} - static int stk500v2_command(PROGRAMMER * pgm, unsigned char * buf, size_t len, size_t maxlen) { int i; @@ -947,7 +942,7 @@ retry: } // otherwise try to sync up again - status = stk500v2_getsync_internal(pgm, 1); + status = stk500v2_getsync(pgm); if (status != 0) { if (tries > RETRIES) { avrdude_message(MSG_INFO, "%s: stk500v2_command(): failed miserably to execute command 0x%02x\n", From 2a07f3a0d58fc5785652bc3deee5e742dac16f05 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 23 May 2018 17:21:01 +0200 Subject: [PATCH 102/117] Firmware updater: Fix filename encoding on Windows --- xs/src/avrdude/fileio.c | 24 ++++++++++++++++++++++-- xs/src/slic3r/GUI/FirmwareDialog.cpp | 6 ++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/xs/src/avrdude/fileio.c b/xs/src/avrdude/fileio.c index f2d6178236..2ed19bd157 100644 --- a/xs/src/avrdude/fileio.c +++ b/xs/src/avrdude/fileio.c @@ -45,6 +45,8 @@ #define MAX_LINE_LEN 256 /* max line length for ASCII format input files */ +#define MAX_MODE_LEN 32 // For fopen_utf8() + struct ihexrec { unsigned char reclen; @@ -100,6 +102,23 @@ static int fmt_autodetect(char * fname); +static FILE *fopen_utf8(const char *filename, const char *mode) +{ + // On Windows we need to convert the filename to UTF-16 +#if defined(WIN32NATIVE) + static wchar_t fname_buffer[PATH_MAX]; + static wchar_t mode_buffer[MAX_MODE_LEN]; + + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname_buffer, PATH_MAX) == 0) { return NULL; } + if (MultiByteToWideChar(CP_ACP, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; } + + return _wfopen(fname_buffer, mode_buffer); +#else + return fopen(filename, mode); +#endif +} + + char * fmtstr(FILEFMT format) { switch (format) { @@ -1368,10 +1387,11 @@ static int fmt_autodetect(char * fname) int first = 1; #if defined(WIN32NATIVE) - f = fopen(fname, "r"); + f = fopen_utf8(fname, "r"); #else f = fopen(fname, "rb"); #endif + if (f == NULL) { avrdude_message(MSG_INFO, "%s: error opening %s: %s\n", progname, fname, strerror(errno)); @@ -1533,7 +1553,7 @@ int fileio(int op, char * filename, FILEFMT format, if (format != FMT_IMM) { if (!using_stdio) { - f = fopen(fname, fio.mode); + f = fopen_utf8(fname, fio.mode); if (f == NULL) { avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n", progname, fio.iodesc, fname, strerror(errno)); diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index 8ea9d2d6ec..e57ec6326d 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -163,6 +163,7 @@ void FirmwareDialog::priv::perform_upload() flashing_status(true); + const auto filename_utf8 = filename.utf8_str(); std::vector args {{ "-v", "-p", "atmega2560", @@ -170,7 +171,7 @@ void FirmwareDialog::priv::perform_upload() "-P", port, "-b", "115200", // XXX: is this ok to hardcode? "-D", - "-U", (boost::format("flash:w:%1%:i") % filename.ToStdString()).str() + "-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str() }}; BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " @@ -187,8 +188,9 @@ void FirmwareDialog::priv::perform_upload() .args(args) .on_message(std::move([q](const char *msg, unsigned /* size */) { auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); + auto wxmsg = wxString::FromUTF8(msg); evt->SetExtraLong(AE_MESSAGE); - evt->SetString(msg); + evt->SetString(std::move(wxmsg)); wxQueueEvent(q, evt); })) .on_progress(std::move([q](const char * /* task */, unsigned progress) { From 5414f7379d9ba0592f60a37a0c610a569787456c Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 5 Jun 2018 11:55:23 +0200 Subject: [PATCH 103/117] FirmwareDialog: Fix progress display --- xs/src/avrdude/avrdude-slic3r.cpp | 23 ++++++++++++++++++----- xs/src/avrdude/avrdude-slic3r.hpp | 6 ++++++ xs/src/avrdude/ser_posix.c | 4 ++++ xs/src/slic3r/GUI/FirmwareDialog.cpp | 23 +++++++++++++++-------- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp index a859200fb4..cf4380fdba 100644 --- a/xs/src/avrdude/avrdude-slic3r.cpp +++ b/xs/src/avrdude/avrdude-slic3r.cpp @@ -34,6 +34,7 @@ struct AvrDude::priv { std::string sys_config; std::vector args; + RunFn run_fn; MessageFn message_fn; ProgressFn progress_fn; CompleteFn complete_fn; @@ -94,6 +95,12 @@ AvrDude& AvrDude::args(std::vector args) return *this; } +AvrDude& AvrDude::on_run(RunFn fn) +{ + if (p) { p->run_fn = std::move(fn); } + return *this; +} + AvrDude& AvrDude::on_message(MessageFn fn) { if (p) { p->message_fn = std::move(fn); } @@ -123,11 +130,17 @@ AvrDude::Ptr AvrDude::run() if (self->p) { auto avrdude_thread = std::thread([self]() { - auto res = self->p->run(); - if (self->p->complete_fn) { - self->p->complete_fn(res); - } - }); + if (self->p->run_fn) { + self->p->run_fn(); + } + + auto res = self->p->run(); + + if (self->p->complete_fn) { + self->p->complete_fn(res); + } + }); + self->p->avrdude_thread = std::move(avrdude_thread); } diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp index 8d881b0947..29d96f72d2 100644 --- a/xs/src/avrdude/avrdude-slic3r.hpp +++ b/xs/src/avrdude/avrdude-slic3r.hpp @@ -12,6 +12,7 @@ class AvrDude { public: typedef std::shared_ptr Ptr; + typedef std::function RunFn; typedef std::function MessageFn; typedef std::function ProgressFn; typedef std::function CompleteFn; @@ -29,6 +30,11 @@ public: // Set avrdude cli arguments AvrDude& args(std::vector args); + // Set a callback to be called just after run() before avrdude is ran + // This can be used to perform any needed setup tasks from the background thread. + // This has no effect when using run_sync(). + AvrDude& on_run(RunFn fn); + // Set message output callback AvrDude& on_message(MessageFn fn); diff --git a/xs/src/avrdude/ser_posix.c b/xs/src/avrdude/ser_posix.c index 91b18e9455..cb0fc03859 100644 --- a/xs/src/avrdude/ser_posix.c +++ b/xs/src/avrdude/ser_posix.c @@ -376,6 +376,10 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen FD_SET(fd->ifd, &rfds); nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &to2); + // FIXME: The timeout has different behaviour on Linux vs other Unices + // On Linux, the timeout is modified by subtracting the time spent, + // on OS X (for example), it is not modified. + // POSIX recommends re-initializing it before selecting. if (nfds == 0) { avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n", progname); diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index e57ec6326d..bbb00e4450 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,7 @@ namespace Slic3r { enum AvrdudeEvent { AE_MESSAGE, - AE_PRORGESS, + AE_PROGRESS, AE_EXIT, }; @@ -62,7 +63,6 @@ struct FirmwareDialog::priv std::vector ports; wxFilePickerCtrl *hex_picker; wxStaticText *txt_status; - wxStaticText *txt_progress; wxGauge *progressbar; wxCollapsiblePane *spoiler; wxTextCtrl *txt_stdout; @@ -72,6 +72,8 @@ struct FirmwareDialog::priv wxString btn_flash_label_ready; wxString btn_flash_label_flashing; + wxTimer timer_pulse; + // This is a shared pointer holding the background AvrDude task // also serves as a status indication (it is set _iff_ the background task is running, otherwise it is reset). AvrDude::Ptr avrdude; @@ -83,6 +85,7 @@ struct FirmwareDialog::priv q(q), btn_flash_label_ready(_(L("Flash!"))), btn_flash_label_flashing(_(L("Cancel"))), + timer_pulse(q), avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()), progress_tasks_done(0), cancelled(false) @@ -131,6 +134,7 @@ void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete) progressbar->SetValue(0); progress_tasks_done = 0; cancelled = false; + timer_pulse.Start(50); } else { auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); port_picker->Enable(); @@ -186,6 +190,7 @@ void FirmwareDialog::priv::perform_upload() avrdude = AvrDude() .sys_config(avrdude_config) .args(args) + .on_run([]() { /* TODO: needed? */ }) .on_message(std::move([q](const char *msg, unsigned /* size */) { auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); auto wxmsg = wxString::FromUTF8(msg); @@ -195,7 +200,7 @@ void FirmwareDialog::priv::perform_upload() })) .on_progress(std::move([q](const char * /* task */, unsigned progress) { auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); - evt->SetExtraLong(AE_PRORGESS); + evt->SetExtraLong(AE_PROGRESS); evt->SetInt(progress); wxQueueEvent(q, evt); })) @@ -226,19 +231,19 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt) txt_stdout->AppendText(evt.GetString()); break; - case AE_PRORGESS: + case AE_PROGRESS: // We try to track overall progress here. // When uploading the firmware, avrdude first reads a littlebit of status data, // then performs write, then reading (verification). - // We Pulse() during the first read and combine progress of the latter two tasks. + // We ignore the first task (which just let's the timer_pulse work) + // and then display overall progress during the latter two tasks. - if (progress_tasks_done == 0) { - progressbar->Pulse(); - } else { + if (progress_tasks_done > 0) { progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt()); } if (evt.GetInt() == 100) { + timer_pulse.Stop(); progress_tasks_done += 100; } @@ -376,6 +381,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) : } }); + Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->p->progressbar->Pulse(); }); + Bind(EVT_AVRDUDE, [this](wxCommandEvent &evt) { this->p->on_avrdude(evt); }); Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) { From 7863412687376a5882aceb46085bfdaf0cadf2b0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 14 Jun 2018 15:03:16 +0200 Subject: [PATCH 104/117] Firwmare updater for the Einsy external flash memory, to be used as a storage for localization strings. Hacked into the avrdude Arduino STK500 (not STK500v2) protocol. --- xs/CMakeLists.txt | 16 ++-- xs/src/avrdude/arduino.c | 51 +++++++++++ xs/src/avrdude/stk500.c | 125 +++++++++++++++++---------- xs/src/slic3r/GUI/FirmwareDialog.cpp | 12 ++- 4 files changed, 147 insertions(+), 57 deletions(-) diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 117af19592..66c1cdd6ae 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -353,8 +353,6 @@ add_library(semver STATIC ) -add_subdirectory(src/avrdude) - # Generate the Slic3r Perl module (XS) typemap file. set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap) add_custom_command( @@ -517,12 +515,12 @@ if (WIN32 AND ";${PerlEmbed_CCFLAGS};" MATCHES ";[-/]Od;") message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}") - set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG") - set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG") - set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG") - set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG") + set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") + set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32") + set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") + set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32") endif() # The following line will add -fPIC on Linux to make the XS.so rellocable. add_definitions(${PerlEmbed_CCCDLFLAGS}) @@ -530,6 +528,8 @@ if (WIN32) target_link_libraries(XS ${PERL_LIBRARY}) endif() +add_subdirectory(src/avrdude) + ## REQUIRED packages # Find and configure boost diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c index 566f56abde..886a43f0bc 100644 --- a/xs/src/avrdude/arduino.c +++ b/xs/src/avrdude/arduino.c @@ -102,6 +102,57 @@ static int arduino_open(PROGRAMMER * pgm, char * port) */ stk500_drain(pgm, 0); +{ + //FIXME initialization sequence for programming the external FLASH. + const char entry_magic_send [] = "start\n"; + const char entry_magic_receive[] = "w25x20cl_enter\n"; + const char entry_magic_cfm [] = "w25x20cl_cfm\n"; + const char *entry_magic_ptr = entry_magic_send; + struct timeval tv; + double tstart, tnow; + char c; + gettimeofday(&tv, NULL); + tstart = tv.tv_sec; + while (*entry_magic_ptr != 0) { + if (serial_recv(&pgm->fd, &c, 1) < 0) + goto timedout; + printf("Received: %c (%d)\n", c, (int)c); + if (c != *entry_magic_ptr ++) { + avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname); + return -1; + } + gettimeofday(&tv, NULL); + tnow = tv.tv_sec; + if (tnow-tstart > 2.) { // wuff - signed/unsigned/overflow + timedout: + avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname); + return -1; + } + } + if (serial_send(&pgm->fd, entry_magic_receive, strlen(entry_magic_receive)) < 0) { + avrdude_message(MSG_INFO, "%s: stk500v2_send(): failed to send command to serial port\n",progname); + return -1; + } + + entry_magic_ptr = entry_magic_cfm; + while (*entry_magic_ptr != 0) { + if (serial_recv(&pgm->fd, &c, 1) < 0) + goto timedout2; + printf("Received: %c (%d)\n", c, (int)c); + if (c != *entry_magic_ptr++) { + avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname); + return -1; + } + gettimeofday(&tv, NULL); + tnow = tv.tv_sec; + if (tnow - tstart > 2.) { // wuff - signed/unsigned/overflow + timedout2: + avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname); + return -1; + } + } +} + if (stk500_getsync(pgm) < 0) return -1; diff --git a/xs/src/avrdude/stk500.c b/xs/src/avrdude/stk500.c index 5d2d3c1dff..63deb228fb 100644 --- a/xs/src/avrdude/stk500.c +++ b/xs/src/avrdude/stk500.c @@ -716,11 +716,14 @@ static int stk500_loadaddr(PROGRAMMER * pgm, AVRMEM * mem, unsigned int addr) } buf[0] = Cmnd_STK_LOAD_ADDRESS; - buf[1] = addr & 0xff; - buf[2] = (addr >> 8) & 0xff; - buf[3] = Sync_CRC_EOP; - - stk500_send(pgm, buf, 4); + // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter. + // Send the binary data by nibbles to avoid transmitting the ';' character. + buf[1] = addr & 0x0f; + buf[2] = addr & 0xf0; + buf[3] = (addr >> 8) & 0x0f; + buf[4] = (addr >> 8) & 0xf0; + buf[5] = Sync_CRC_EOP; + stk500_send(pgm, buf, 6); if (stk500_recv(pgm, buf, 1) < 0) return -1; @@ -765,7 +768,9 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int block_size; int tries; unsigned int n; - unsigned int i; + unsigned int i, j; + unsigned int prusa3d_semicolon_workaround_round = 0; + bool has_semicolon = false; if (strcmp(m->desc, "flash") == 0) { memtype = 'F'; @@ -806,44 +811,64 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, tries++; stk500_loadaddr(pgm, m, addr/a_div); - /* build command block and avoid multiple send commands as it leads to a crash - of the silabs usb serial driver on mac os x */ - i = 0; - buf[i++] = Cmnd_STK_PROG_PAGE; - buf[i++] = (block_size >> 8) & 0xff; - buf[i++] = block_size & 0xff; - buf[i++] = memtype; - memcpy(&buf[i], &m->buf[addr], block_size); - i += block_size; - buf[i++] = Sync_CRC_EOP; - stk500_send( pgm, buf, i); - - if (stk500_recv(pgm, buf, 1) < 0) - return -1; - if (buf[0] == Resp_STK_NOSYNC) { - if (tries > 33) { - avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): can't get into sync\n", - progname); - return -3; + for (i = 0; i < n_bytes; ++ i) + if (m->buf[addr + i] == ';') { + has_semicolon = true; + break; + } + + for (prusa3d_semicolon_workaround_round = 0; prusa3d_semicolon_workaround_round < (has_semicolon ? 2 : 1); ++ prusa3d_semicolon_workaround_round) { + /* build command block and avoid multiple send commands as it leads to a crash + of the silabs usb serial driver on mac os x */ + i = 0; + buf[i++] = Cmnd_STK_PROG_PAGE; + // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter. + // Send the binary data by nibbles to avoid transmitting the ';' character. + buf[i++] = (block_size >> 8) & 0xf0; + buf[i++] = (block_size >> 8) & 0x0f; + buf[i++] = block_size & 0xf0; + buf[i++] = block_size & 0x0f; + buf[i++] = memtype; + if (has_semicolon) { + for (j = 0; j < block_size; ++i, ++ j) { + buf[i] = m->buf[addr + j]; + if (buf[i] == ';') + buf[i] |= (prusa3d_semicolon_workaround_round ? 0xf0 : 0x0f); + } + } else { + memcpy(&buf[i], &m->buf[addr], block_size); + i += block_size; + } + buf[i++] = Sync_CRC_EOP; + stk500_send( pgm, buf, i); + + if (stk500_recv(pgm, buf, 1) < 0) + return -1; + if (buf[0] == Resp_STK_NOSYNC) { + if (tries > 33) { + avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): can't get into sync\n", + progname); + return -3; + } + if (stk500_getsync(pgm) < 0) + return -1; + goto retry; + } + else if (buf[0] != Resp_STK_INSYNC) { + avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, " + "expect=0x%02x, resp=0x%02x\n", + progname, Resp_STK_INSYNC, buf[0]); + return -4; + } + + if (stk500_recv(pgm, buf, 1) < 0) + return -1; + if (buf[0] != Resp_STK_OK) { + avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, " + "expect=0x%02x, resp=0x%02x\n", + progname, Resp_STK_INSYNC, buf[0]); + return -5; } - if (stk500_getsync(pgm) < 0) - return -1; - goto retry; - } - else if (buf[0] != Resp_STK_INSYNC) { - avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, " - "expect=0x%02x, resp=0x%02x\n", - progname, Resp_STK_INSYNC, buf[0]); - return -4; - } - - if (stk500_recv(pgm, buf, 1) < 0) - return -1; - if (buf[0] != Resp_STK_OK) { - avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, " - "expect=0x%02x, resp=0x%02x\n", - progname, Resp_STK_INSYNC, buf[0]); - return -5; } } @@ -893,11 +918,15 @@ static int stk500_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, tries++; stk500_loadaddr(pgm, m, addr/a_div); buf[0] = Cmnd_STK_READ_PAGE; - buf[1] = (block_size >> 8) & 0xff; - buf[2] = block_size & 0xff; - buf[3] = memtype; - buf[4] = Sync_CRC_EOP; - stk500_send(pgm, buf, 5); + // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter. + // Send the binary data by nibbles to avoid transmitting the ';' character. + buf[1] = (block_size >> 8) & 0xf0; + buf[2] = (block_size >> 8) & 0x0f; + buf[3] = block_size & 0xf0; + buf[4] = block_size & 0x0f; + buf[5] = memtype; + buf[6] = Sync_CRC_EOP; + stk500_send(pgm, buf, 7); if (stk500_recv(pgm, buf, 1) < 0) return -1; diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index bbb00e4450..136b17af67 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -171,11 +171,19 @@ void FirmwareDialog::priv::perform_upload() std::vector args {{ "-v", "-p", "atmega2560", - "-c", "wiring", + // Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500). + // The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip + // is flashed with a buggy firmware. +// "-c", "wiring", + // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2). + // The Prusa's avrdude is patched again to never send semicolons inside the data packets. + "-c", "arduino", "-P", port, "-b", "115200", // XXX: is this ok to hardcode? "-D", + "-u", // disable safe mode "-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str() +// "-v", "-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange }}; BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " @@ -192,6 +200,8 @@ void FirmwareDialog::priv::perform_upload() .args(args) .on_run([]() { /* TODO: needed? */ }) .on_message(std::move([q](const char *msg, unsigned /* size */) { + // Debugging output to console, useful when avrdude is executed in a super verbose mode (with -v -v -v). + // printf("%s", msg); auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); auto wxmsg = wxString::FromUTF8(msg); evt->SetExtraLong(AE_MESSAGE); From 15f943938b622a3f32bbb8847809acb53782df2a Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Mon, 18 Jun 2018 18:10:50 +0200 Subject: [PATCH 105/117] avrdude: add file offset to update operation spec, refactoring --- xs/src/avrdude/arduino.c | 95 ++++++++++++++-------------- xs/src/avrdude/fileio.c | 57 +++++++++++------ xs/src/avrdude/libavrdude.h | 5 +- xs/src/avrdude/main.c | 2 +- xs/src/avrdude/update.c | 38 ++++++++--- xs/src/slic3r/GUI/FirmwareDialog.cpp | 4 +- 6 files changed, 118 insertions(+), 83 deletions(-) diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c index 886a43f0bc..5e0693e947 100644 --- a/xs/src/avrdude/arduino.c +++ b/xs/src/avrdude/arduino.c @@ -80,6 +80,49 @@ static int arduino_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m) return 3; } +static int prusa_init_external_flash(PROGRAMMER * pgm) +{ + // Note: send/receive as in _the firmare_ send & receives + const char entry_magic_send [] = "start\n"; + const char entry_magic_receive[] = "w25x20cl_enter\n"; + const char entry_magic_cfm [] = "w25x20cl_cfm\n"; + const size_t buffer_len = 32; // Should be large enough for the above messages + + int res; + size_t recv_size; + char *buffer = alloca(buffer_len); + + // 1. receive the "start" command + recv_size = sizeof(entry_magic_send) - 1; + res = serial_recv(&pgm->fd, buffer, recv_size); + if (res < 0) { + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname); + return -1; + } else if (strncmp(buffer, entry_magic_send, recv_size) != 0) { + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname); + return -1; + } + + // 2. Send the external flash programmer enter command + if (serial_send(&pgm->fd, entry_magic_receive, sizeof(entry_magic_receive) - 1) < 0) { + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): Failed to send command to the printer\n",progname); + return -1; + } + + // 3. Receive the entry confirmation command + recv_size = sizeof(entry_magic_cfm) - 1; + res = serial_recv(&pgm->fd, buffer, recv_size); + if (res < 0) { + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname); + return -1; + } else if (strncmp(buffer, entry_magic_cfm, recv_size) != 0) { + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname); + return -1; + } + + return 0; +} + static int arduino_open(PROGRAMMER * pgm, char * port) { union pinfo pinfo; @@ -102,56 +145,10 @@ static int arduino_open(PROGRAMMER * pgm, char * port) */ stk500_drain(pgm, 0); -{ - //FIXME initialization sequence for programming the external FLASH. - const char entry_magic_send [] = "start\n"; - const char entry_magic_receive[] = "w25x20cl_enter\n"; - const char entry_magic_cfm [] = "w25x20cl_cfm\n"; - const char *entry_magic_ptr = entry_magic_send; - struct timeval tv; - double tstart, tnow; - char c; - gettimeofday(&tv, NULL); - tstart = tv.tv_sec; - while (*entry_magic_ptr != 0) { - if (serial_recv(&pgm->fd, &c, 1) < 0) - goto timedout; - printf("Received: %c (%d)\n", c, (int)c); - if (c != *entry_magic_ptr ++) { - avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname); - return -1; - } - gettimeofday(&tv, NULL); - tnow = tv.tv_sec; - if (tnow-tstart > 2.) { // wuff - signed/unsigned/overflow - timedout: - avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname); - return -1; - } + // Initialization sequence for programming the external FLASH on the Prusa MK3 + if (prusa_init_external_flash(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: arduino_open(): Failed to initialize MK3 external flash programming mode\n", progname); } - if (serial_send(&pgm->fd, entry_magic_receive, strlen(entry_magic_receive)) < 0) { - avrdude_message(MSG_INFO, "%s: stk500v2_send(): failed to send command to serial port\n",progname); - return -1; - } - - entry_magic_ptr = entry_magic_cfm; - while (*entry_magic_ptr != 0) { - if (serial_recv(&pgm->fd, &c, 1) < 0) - goto timedout2; - printf("Received: %c (%d)\n", c, (int)c); - if (c != *entry_magic_ptr++) { - avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname); - return -1; - } - gettimeofday(&tv, NULL); - tnow = tv.tv_sec; - if (tnow - tstart > 2.) { // wuff - signed/unsigned/overflow - timedout2: - avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname); - return -1; - } - } -} if (stk500_getsync(pgm) < 0) return -1; diff --git a/xs/src/avrdude/fileio.c b/xs/src/avrdude/fileio.c index 2ed19bd157..aa57f55870 100644 --- a/xs/src/avrdude/fileio.c +++ b/xs/src/avrdude/fileio.c @@ -45,7 +45,7 @@ #define MAX_LINE_LEN 256 /* max line length for ASCII format input files */ -#define MAX_MODE_LEN 32 // For fopen_utf8() +#define MAX_MODE_LEN 32 // For fopen_and_seek() struct ihexrec { @@ -98,12 +98,13 @@ static int fileio_num(struct fioparms * fio, char * filename, FILE * f, AVRMEM * mem, int size, FILEFMT fmt); -static int fmt_autodetect(char * fname); +static int fmt_autodetect(char * fname, size_t offset); -static FILE *fopen_utf8(const char *filename, const char *mode) +static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offset) { + FILE *file; // On Windows we need to convert the filename to UTF-16 #if defined(WIN32NATIVE) static wchar_t fname_buffer[PATH_MAX]; @@ -112,10 +113,24 @@ static FILE *fopen_utf8(const char *filename, const char *mode) if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname_buffer, PATH_MAX) == 0) { return NULL; } if (MultiByteToWideChar(CP_ACP, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; } - return _wfopen(fname_buffer, mode_buffer); + file = _wfopen(fname_buffer, mode_buffer); #else - return fopen(filename, mode); + file = fopen(filename, mode); #endif + + if (file != NULL) { + // Some systems allow seeking past the end of file, so we need check for that first and disallow + if (fseek(file, 0, SEEK_END) != 0 + || offset >= ftell(file) + || fseek(file, offset, SEEK_SET) != 0 + ) { + fclose(file); + file = NULL; + errno = EINVAL; + } + } + + return file; } @@ -1377,7 +1392,7 @@ int fileio_setparms(int op, struct fioparms * fp, -static int fmt_autodetect(char * fname) +static int fmt_autodetect(char * fname, size_t offset) { FILE * f; unsigned char buf[MAX_LINE_LEN]; @@ -1387,9 +1402,9 @@ static int fmt_autodetect(char * fname) int first = 1; #if defined(WIN32NATIVE) - f = fopen_utf8(fname, "r"); + f = fopen_and_seek(fname, "r", offset); #else - f = fopen(fname, "rb"); + f = fopen_and_seek(fname, "rb", offset); #endif if (f == NULL) { @@ -1465,7 +1480,7 @@ static int fmt_autodetect(char * fname) int fileio(int op, char * filename, FILEFMT format, - struct avrpart * p, char * memtype, int size) + struct avrpart * p, char * memtype, int size, size_t offset) { int rc; FILE * f; @@ -1497,15 +1512,17 @@ int fileio(int op, char * filename, FILEFMT format, using_stdio = 0; if (strcmp(filename, "-")==0) { - if (fio.op == FIO_READ) { - fname = ""; - f = stdin; - } - else { - fname = ""; - f = stdout; - } - using_stdio = 1; + return -1; + // Note: we don't want to read stdin or write to stdout as part of Slic3r + // if (fio.op == FIO_READ) { + // fname = ""; + // f = stdin; + // } + // else { + // fname = ""; + // f = stdout; + // } + // using_stdio = 1; } else { fname = filename; @@ -1522,7 +1539,7 @@ int fileio(int op, char * filename, FILEFMT format, return -1; } - format_detect = fmt_autodetect(fname); + format_detect = fmt_autodetect(fname, offset); if (format_detect < 0) { avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n", progname, fname); @@ -1553,7 +1570,7 @@ int fileio(int op, char * filename, FILEFMT format, if (format != FMT_IMM) { if (!using_stdio) { - f = fopen_utf8(fname, fio.mode); + f = fopen_and_seek(fname, fio.mode, offset); if (f == NULL) { avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n", progname, fio.iodesc, fname, strerror(errno)); diff --git a/xs/src/avrdude/libavrdude.h b/xs/src/avrdude/libavrdude.h index 238f596157..536f1a2f79 100644 --- a/xs/src/avrdude/libavrdude.h +++ b/xs/src/avrdude/libavrdude.h @@ -821,7 +821,7 @@ extern "C" { char * fmtstr(FILEFMT format); int fileio(int op, char * filename, FILEFMT format, - struct avrpart * p, char * memtype, int size); + struct avrpart * p, char * memtype, int size, size_t offset); #ifdef __cplusplus } @@ -870,6 +870,7 @@ enum updateflags { typedef struct update_t { char * memtype; int op; + size_t offset; char * filename; int format; } UPDATE; @@ -881,7 +882,7 @@ extern "C" { extern UPDATE * parse_op(char * s); extern UPDATE * dup_update(UPDATE * upd); extern UPDATE * new_update(int op, char * memtype, int filefmt, - char * filename); + char * filename, size_t offset); extern void free_update(UPDATE * upd); extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags flags); diff --git a/xs/src/avrdude/main.c b/xs/src/avrdude/main.c index 0550ceff11..91f2fc8275 100644 --- a/xs/src/avrdude/main.c +++ b/xs/src/avrdude/main.c @@ -194,7 +194,7 @@ static void usage(void) " -F Override invalid signature check.\n" " -e Perform a chip erase.\n" " -O Perform RC oscillator calibration (see AVR053). \n" - " -U :r|w|v:[:format]\n" + " -U :r|w|v::[:format]\n" " Memory operation specification.\n" " Multiple -U options are allowed, each request\n" " is performed in the order specified.\n" diff --git a/xs/src/avrdude/update.c b/xs/src/avrdude/update.c index a73461dfad..fa3372476c 100644 --- a/xs/src/avrdude/update.c +++ b/xs/src/avrdude/update.c @@ -101,6 +101,25 @@ UPDATE * parse_op(char * s) p++; + // Extension: Parse file contents offset + size_t offset = 0; + + for (; *p != ':'; p++) { + if (*p >= '0' && *p <= '9') { + offset *= 10; + offset += *p - 0x30; + } else { + avrdude_message(MSG_INFO, "%s: invalid update specification: offset is not a number\n", progname); + free(upd->memtype); + free(upd); + return NULL; + } + } + + upd->offset = offset; + printf("parse_op: offset: %lu\n", offset); + p++; + /* * Now, parse the filename component. Instead of looking for the * leftmost possible colon delimiter, we look for the rightmost one. @@ -176,7 +195,7 @@ UPDATE * dup_update(UPDATE * upd) return u; } -UPDATE * new_update(int op, char * memtype, int filefmt, char * filename) +UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t offset) { UPDATE * u; @@ -190,6 +209,7 @@ UPDATE * new_update(int op, char * memtype, int filefmt, char * filename) u->filename = strdup(filename); u->op = op; u->format = filefmt; + u->offset = offset; return u; } @@ -250,7 +270,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f progname, strcmp(upd->filename, "-")==0 ? "" : upd->filename); } - rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size); + rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size, 0); if (rc < 0) { avrdude_message(MSG_INFO, "%s: write to file '%s' failed\n", progname, upd->filename); @@ -267,7 +287,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f progname, strcmp(upd->filename, "-")==0 ? "" : upd->filename); } - rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1); + rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset); if (rc < 0) { avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n", progname, upd->filename); @@ -296,11 +316,11 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f report_progress(1,1,NULL); } else { - /* - * test mode, don't actually write to the chip, output the buffer - * to stdout in intel hex instead - */ - rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size); + // /* + // * test mode, don't actually write to the chip, output the buffer + // * to stdout in intel hex instead + // */ + // rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size, 0); } if (rc < 0) { @@ -332,7 +352,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f progname, mem->desc, upd->filename); } - rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1); + rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset); if (rc < 0) { avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n", progname, upd->filename); diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index 136b17af67..f9aabacc02 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -182,8 +182,8 @@ void FirmwareDialog::priv::perform_upload() "-b", "115200", // XXX: is this ok to hardcode? "-D", "-u", // disable safe mode - "-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str() -// "-v", "-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange + "-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(), // FIXME + // "-vvvvv", //"-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange }}; BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " From 635bb1e484e319b1d403faac39170cc4d0dfa6f7 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 19 Jun 2018 11:16:56 +0200 Subject: [PATCH 106/117] Firmware updater: Add support for l10n firmware images --- xs/src/avrdude/avrdude-slic3r.cpp | 35 ++++-- xs/src/avrdude/avrdude-slic3r.hpp | 19 +-- xs/src/avrdude/main.c | 2 +- xs/src/slic3r/GUI/FirmwareDialog.cpp | 178 ++++++++++++++++++--------- 4 files changed, 156 insertions(+), 78 deletions(-) diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp index cf4380fdba..030353413c 100644 --- a/xs/src/avrdude/avrdude-slic3r.cpp +++ b/xs/src/avrdude/avrdude-slic3r.cpp @@ -1,5 +1,6 @@ #include "avrdude-slic3r.hpp" +#include #include extern "C" { @@ -33,7 +34,8 @@ static void avrdude_progress_handler_closure(const char *task, unsigned progress struct AvrDude::priv { std::string sys_config; - std::vector args; + std::deque> args; + size_t current_args_set = 0; RunFn run_fn; MessageFn message_fn; ProgressFn progress_fn; @@ -41,10 +43,13 @@ struct AvrDude::priv std::thread avrdude_thread; + priv(std::string &&sys_config) : sys_config(sys_config) {} + + int run_one(const std::vector &args); int run(); }; -int AvrDude::priv::run() { +int AvrDude::priv::run_one(const std::vector &args) { std::vector c_args {{ const_cast(PACKAGE_NAME) }}; for (const auto &arg : args) { c_args.push_back(const_cast(arg.data())); @@ -69,10 +74,22 @@ int AvrDude::priv::run() { return res; } +int AvrDude::priv::run() { + for (; args.size() > 0; current_args_set++) { + int res = run_one(args.front()); + args.pop_front(); + if (res != 0) { + return res; + } + } + + return 0; +} + // Public -AvrDude::AvrDude() : p(new priv()) {} +AvrDude::AvrDude(std::string sys_config) : p(new priv(std::move(sys_config))) {} AvrDude::AvrDude(AvrDude &&other) : p(std::move(other.p)) {} @@ -83,15 +100,9 @@ AvrDude::~AvrDude() } } -AvrDude& AvrDude::sys_config(std::string sys_config) +AvrDude& AvrDude::push_args(std::vector args) { - if (p) { p->sys_config = std::move(sys_config); } - return *this; -} - -AvrDude& AvrDude::args(std::vector args) -{ - if (p) { p->args = std::move(args); } + if (p) { p->args.push_back(std::move(args)); } return *this; } @@ -137,7 +148,7 @@ AvrDude::Ptr AvrDude::run() auto res = self->p->run(); if (self->p->complete_fn) { - self->p->complete_fn(res); + self->p->complete_fn(res, self->p->current_args_set); } }); diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp index 29d96f72d2..273aa23782 100644 --- a/xs/src/avrdude/avrdude-slic3r.hpp +++ b/xs/src/avrdude/avrdude-slic3r.hpp @@ -15,20 +15,20 @@ public: typedef std::function RunFn; typedef std::function MessageFn; typedef std::function ProgressFn; - typedef std::function CompleteFn; + typedef std::function CompleteFn; - AvrDude(); + // Main c-tor, sys_config is the location of avrdude's main configuration file + AvrDude(std::string sys_config); AvrDude(AvrDude &&); AvrDude(const AvrDude &) = delete; AvrDude &operator=(AvrDude &&) = delete; AvrDude &operator=(const AvrDude &) = delete; ~AvrDude(); - // Set location of avrdude's main configuration file - AvrDude& sys_config(std::string sys_config); - - // Set avrdude cli arguments - AvrDude& args(std::vector args); + // Push a set of avrdude cli arguments + // Each set makes one avrdude invocation - use this method multiple times to push + // more than one avrdude invocations. + AvrDude& push_args(std::vector args); // Set a callback to be called just after run() before avrdude is ran // This can be used to perform any needed setup tasks from the background thread. @@ -42,7 +42,10 @@ public: // Progress is reported per each task (reading / writing) in percents. AvrDude& on_progress(ProgressFn fn); - // Called when avrdude's main function finishes + // Called when the last avrdude invocation finishes with the exit status of zero, + // or earlier, if one of the invocations return a non-zero status. + // The second argument contains the sequential id of the last avrdude invocation argument set. + // This has no effect when using run_sync(). AvrDude& on_complete(CompleteFn fn); int run_sync(); diff --git a/xs/src/avrdude/main.c b/xs/src/avrdude/main.c index 91f2fc8275..d4c34fe44c 100644 --- a/xs/src/avrdude/main.c +++ b/xs/src/avrdude/main.c @@ -374,7 +374,7 @@ static void list_parts(FILE * f, const char *prefix, LISTID avrparts) static int cleanup_main(int status) { - if (pgm_setup && pgm->teardown) { + if (pgm_setup && pgm != NULL && pgm->teardown) { pgm->teardown(pgm); } diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index f9aabacc02..d747430554 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -92,7 +93,9 @@ struct FirmwareDialog::priv {} void find_serial_ports(); - void flashing_status(bool flashing, AvrDudeComplete complete = AC_NONE); + void flashing_start(bool flashing_l10n); + void flashing_done(AvrDudeComplete complete); + size_t hex_lang_offset(const wxString &path); void perform_upload(); void cancel(); void on_avrdude(const wxCommandEvent &evt); @@ -119,43 +122,76 @@ void FirmwareDialog::priv::find_serial_ports() } } -void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete) +void FirmwareDialog::priv::flashing_start(bool flashing_l10n) { - if (value) { - txt_stdout->Clear(); - txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!"))); - txt_status->SetForegroundColour(GUI::get_label_clr_modified()); - port_picker->Disable(); - btn_rescan->Disable(); - hex_picker->Disable(); - btn_close->Disable(); - btn_flash->SetLabel(btn_flash_label_flashing); - progressbar->SetRange(200); // See progress callback below - progressbar->SetValue(0); - progress_tasks_done = 0; - cancelled = false; - timer_pulse.Start(50); - } else { - auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - port_picker->Enable(); - btn_rescan->Enable(); - hex_picker->Enable(); - btn_close->Enable(); - btn_flash->SetLabel(btn_flash_label_ready); - txt_status->SetForegroundColour(text_color); - progressbar->SetValue(200); + txt_stdout->Clear(); + txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!"))); + txt_status->SetForegroundColour(GUI::get_label_clr_modified()); + port_picker->Disable(); + btn_rescan->Disable(); + hex_picker->Disable(); + btn_close->Disable(); + btn_flash->SetLabel(btn_flash_label_flashing); + progressbar->SetRange(flashing_l10n ? 500 : 200); // See progress callback below + progressbar->SetValue(0); + progress_tasks_done = 0; + cancelled = false; + timer_pulse.Start(50); +} - switch (complete) { - case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break; - case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break; - case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break; +void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete) +{ + auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + port_picker->Enable(); + btn_rescan->Enable(); + hex_picker->Enable(); + btn_close->Enable(); + btn_flash->SetLabel(btn_flash_label_ready); + txt_status->SetForegroundColour(text_color); + timer_pulse.Stop(); + progressbar->SetValue(progressbar->GetRange()); + + switch (complete) { + case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break; + case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break; + case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break; + } +} + +size_t FirmwareDialog::priv::hex_lang_offset(const wxString &path) +{ + fs::ifstream file(fs::path(path.wx_str())); + if (! file.good()) { + return 0; + } + + static const char *hex_terminator = ":00000001FF\r"; + size_t res = 0; + std::string line; + while (getline(file, line, '\n').good()) { + // Account for LF vs CRLF + if (!line.empty() && line.back() != '\r') { + line.push_back('\r'); + } + + if (line == hex_terminator) { + if (res == 0) { + // This is the first terminator seen, save the position + res = file.tellg(); + } else { + // We've found another terminator, return the offset just after the first one + // which is the start of the second 'section'. + return res; + } } } + + return 0; } void FirmwareDialog::priv::perform_upload() { - auto filename = hex_picker->GetPath(); + auto filename = hex_picker->GetPath(); std::string port = port_picker->GetValue().ToStdString(); int selection = port_picker->GetSelection(); if (selection != -1) { @@ -165,25 +201,32 @@ void FirmwareDialog::priv::perform_upload() } if (filename.IsEmpty() || port.empty()) { return; } - flashing_status(true); - + const bool extra_verbose = false; // For debugging + const auto lang_offset = hex_lang_offset(filename); const auto filename_utf8 = filename.utf8_str(); + + flashing_start(lang_offset > 0); + + // It is ok here to use the q-pointer to the FirmwareDialog + // because the dialog ensures it doesn't exit before the background thread is done. + auto q = this->q; + + // Init the avrdude object + AvrDude avrdude(avrdude_config); + + // Build argument list(s) std::vector args {{ - "-v", + extra_verbose ? "-vvvvv" : "-v", "-p", "atmega2560", // Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500). // The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip // is flashed with a buggy firmware. -// "-c", "wiring", - // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2). - // The Prusa's avrdude is patched again to never send semicolons inside the data packets. - "-c", "arduino", + "-c", "wiring", "-P", port, - "-b", "115200", // XXX: is this ok to hardcode? + "-b", "115200", // TODO: Allow other rates? Ditto below. "-D", - "-u", // disable safe mode - "-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(), // FIXME - // "-vvvvv", //"-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange + // XXX: Safe mode? + "-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(), }}; BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " @@ -191,17 +234,38 @@ void FirmwareDialog::priv::perform_upload() return a + ' ' + b; }); - // It is ok here to use the q-pointer to the FirmwareDialog - // because the dialog ensures it doesn't exit before the background thread is done. - auto q = this->q; + avrdude.push_args(std::move(args)); + + if (lang_offset > 0) { + // The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy) + // This is done via another avrdude invocation, here we build arg list for that: + std::vector args_l10n {{ + extra_verbose ? "-vvvvv" : "-v", + "-p", "atmega2560", + // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2). + // The Prusa's avrdude is patched again to never send semicolons inside the data packets. + "-c", "arduino", + "-P", port, + "-b", "115200", + "-D", + "-u", // disable safe mode + "-U", (boost::format("flash:w:%1%:%2%:i") % lang_offset % filename_utf8.data()).str(), + }}; + + BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: " + << std::accumulate(std::next(args_l10n.begin()), args_l10n.end(), args_l10n[0], [](std::string a, const std::string &b) { + return a + ' ' + b; + }); + + avrdude.push_args(std::move(args_l10n)); + } + + this->avrdude = avrdude + .on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) { + if (extra_verbose) { + BOOST_LOG_TRIVIAL(debug) << "avrdude: " << msg; + } - avrdude = AvrDude() - .sys_config(avrdude_config) - .args(args) - .on_run([]() { /* TODO: needed? */ }) - .on_message(std::move([q](const char *msg, unsigned /* size */) { - // Debugging output to console, useful when avrdude is executed in a super verbose mode (with -v -v -v). - // printf("%s", msg); auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); auto wxmsg = wxString::FromUTF8(msg); evt->SetExtraLong(AE_MESSAGE); @@ -214,7 +278,7 @@ void FirmwareDialog::priv::perform_upload() evt->SetInt(progress); wxQueueEvent(q, evt); })) - .on_complete(std::move([q](int status) { + .on_complete(std::move([q](int status, size_t /* args_id */) { auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); evt->SetExtraLong(AE_EXIT); evt->SetInt(status); @@ -243,10 +307,10 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt) case AE_PROGRESS: // We try to track overall progress here. - // When uploading the firmware, avrdude first reads a littlebit of status data, - // then performs write, then reading (verification). - // We ignore the first task (which just let's the timer_pulse work) - // and then display overall progress during the latter two tasks. + // Avrdude performs 3 tasks per one memory operation ("-U" arg), + // first of which is reading of status data (very short). + // We use the timer_pulse during the very first task to indicate intialization + // and then display overall progress during the latter tasks. if (progress_tasks_done > 0) { progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt()); @@ -263,7 +327,7 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt) BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << evt.GetInt(); complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE); - flashing_status(false, complete_kind); + flashing_done(complete_kind); // Make sure the background thread is collected and the AvrDude object reset if (avrdude) { avrdude->join(); } From 725b8524f2c922dcaa32b280d6553eaba81ae30f Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 19 Jun 2018 15:45:30 +0200 Subject: [PATCH 107/117] avrdude: Fix error handling in arduino, fix various outputs --- xs/src/avrdude/arduino.c | 1 + xs/src/avrdude/avrpart.c | 10 +++++----- xs/src/avrdude/update.c | 1 - 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c index 5e0693e947..fc9f4571f3 100644 --- a/xs/src/avrdude/arduino.c +++ b/xs/src/avrdude/arduino.c @@ -148,6 +148,7 @@ static int arduino_open(PROGRAMMER * pgm, char * port) // Initialization sequence for programming the external FLASH on the Prusa MK3 if (prusa_init_external_flash(pgm) < 0) { avrdude_message(MSG_INFO, "%s: arduino_open(): Failed to initialize MK3 external flash programming mode\n", progname); + return -1; } if (stk500_getsync(pgm) < 0) diff --git a/xs/src/avrdude/avrpart.c b/xs/src/avrdude/avrpart.c index 621a85b98c..b04851ac14 100644 --- a/xs/src/avrdude/avrpart.c +++ b/xs/src/avrdude/avrpart.c @@ -378,7 +378,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type, char * optr; if (m == NULL) { - fprintf(f, + avrdude_message(MSG_INFO, "%s Block Poll Page Polled\n" "%sMemory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack\n" "%s----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------\n", @@ -386,13 +386,13 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type, } else { if (verbose > 2) { - fprintf(f, + avrdude_message(MSG_INFO, "%s Block Poll Page Polled\n" "%sMemory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack\n" "%s----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------\n", prefix, prefix, prefix); } - fprintf(f, + avrdude_message(MSG_INFO, "%s%-11s %4d %5d %5d %4d %-6s %6d %4d %6d %5d %5d 0x%02x 0x%02x\n", prefix, m->desc, m->mode, m->delay, m->blocksize, m->pollindex, m->paged ? "yes" : "no", @@ -415,7 +415,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type, optr = avr_op_str(i); else optr = " "; - fprintf(f, + avrdude_message(MSG_INFO, "%s %-11s %8d %8s %5d %5d\n", prefix, optr, j, bittype(m->op[i]->bit[j].type), @@ -620,7 +620,7 @@ void avr_display(FILE * f, AVRPART * p, const char * prefix, int verbose) LNODEID ln; AVRMEM * m; - fprintf(f, + avrdude_message(MSG_INFO, "%sAVR Part : %s\n" "%sChip Erase delay : %d us\n" "%sPAGEL : P%02X\n" diff --git a/xs/src/avrdude/update.c b/xs/src/avrdude/update.c index fa3372476c..e9dd6e3251 100644 --- a/xs/src/avrdude/update.c +++ b/xs/src/avrdude/update.c @@ -117,7 +117,6 @@ UPDATE * parse_op(char * s) } upd->offset = offset; - printf("parse_op: offset: %lu\n", offset); p++; /* From 86b02224aeaea2003dfb743ddadfff81cae330e3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 20 Jun 2018 09:42:14 +0200 Subject: [PATCH 108/117] Updated PL language --- resources/localization/pl/Slic3rPE.mo | Bin 133651 -> 133591 bytes resources/localization/pl/Slic3rPE_pl.po | 50 +++++++++++------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/resources/localization/pl/Slic3rPE.mo b/resources/localization/pl/Slic3rPE.mo index 103d4a5eec52bd02d7c5f566d008d5b56a0df9e5..93a42160bec8b90bcbf680be213671e6af70a9ba 100644 GIT binary patch delta 8282 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 JZlJG*@4vS`4sZYf delta 8354 zcmZ|Ud3;UR-pBE^VhDoUqOIQ29`h7KjiJVxXA;#aK}c{S)0GejdWfq+tEz@*X;H&P zQEE75F_%&`&sy5vx-La=^=k2azFGVE>v=tYz4uyYpS{-l{eElh6VA>oG;>y=wP~*e zDjH+5%UKhKtFa1h!C`n6pT$n)tto;1@UNJRWpOH2#^q@77#79j_zYgbMR*(a{TUUk zse?i*+c z8!vjFhgEXVD}gW2U%FDj-B^bXt+WYh0$orKNX8J%@EnU8IDm@WVz2!*?xB4XJK&d< zt%=9qQTGq7;+{7d3(=l|MevgV7Yg}O?}nqEXK@<+S8*RESG8s~)~{yGb9fm0;SZK}~eL_xV!PLf3c(a=nWasEPc91F>igH&7y~e!_Lk2OFZT zYfVRtK!rF9i(@t_5;>S3SD`|^4mH3z)cyZPh5oMB|9m}HBh^sfzlkLn-`M&tgr!i$ zR|yrW4ycKBM};sNYv5#5j@P3imy56C1yog+ZD36$Y=8=NUu=bAurBVvCU^$}x~S36 zRp$`Y04b;y%*W^PbJT;jpo;H2YGt=k6E4-r^*2I=ysOuaL47|RHPH{SDlSJY?06$@ z|9?-1a(3UdbcD-UQ`9!>f;ymjqpEih>HtbZ9Y`OcCcN5nJ*sH8U`xD;;aI-0OJR2m zrQN?V`(HU7LWgoU()%FWyYWL*O)Nl7U?qNnn@}m~*~Cp?3@)HO2UWBUUvn#u#Cf#+ zs0kNn>LywNHEy*47g|9CYM>}=hy77GUW)qSSLnmF0oue%V}L?7)2*bIB4YHSv6)BexpLb(~++U0x&YQ|r(&tWJ01%p_(gNx)1&p%Q374PU?MrBcv?uwNd-wfbFE1BfE0=4p^ zUi&_38B-5&!Yy)*Vz^KbEwo*MMby`29(P|TxdmqM-|-% zsIz?@D#xo(6F7n@x*t*d{0Z*C5?!px#?z=G?%&lNM1ItHF#)yTbFefn_4;>qW&dly zQ*>y1T}2Ia3w4s^@8&{R26eXAK<)F_P$_AJN=YZr0jL$GdF>BTD_x2jcrR)qm%Pt! zbYuUQq2nnX3RRizZs1y|^Pn9z#6GwHKSWKmP7gPMrl<+DMJ?oQROE)DCOq0}XQRgX z7&W0y=)+S1F0|4Is3I)Z(?y^ZHm6+!8(_TGzYvwGpx3_}kJ5gK?QnmjHS4ixl>6b5 zi+b)})C3QE?X#%2ZQu$Q3dv*Cio<%jefu&Vq+J;m(qFNjHKxcL)=Z+^ytg%}xDRJw z#G5Ynhfym(jT7-_oQ(drtQm}VQ4xBhPu>rhfEmJtLb?|fiDTFYf5Dd+u+`hv+@T%a zmxd!vtofAwgrV*&S7exb>(xZ<=SWmYV^9%FLhX*JsJG%$)bE3@ zQMGjxwZQMOCO*Wx_kSh73w;Du<%7f*RlgY67<~6bmG{92WC@8MV^dsQq8x zvjujg-3hgA=c10}&rl0Hl)(PiiFJ(*RrQ~!{a-NAg}5xLSi(JtFw$wr0zSc;qAJydQV zqaNt{o3pd$DAd8U2$iChsE7q|7w$q$FfhVJc7ZOmoxVbCn}}2wst(wJb~jY0a&jsI1KgR>DUSvqEd1VwY{IBPRg)!rw?oE{ojBKJtz)Uy<igU98B%u^!)ag7@FJAyB2ymq+$va9`@ezL z@j5CbJy1Csh+5Hj)Cy*Je(Jdb71E8UqTGgB;Q>^NE_$EeKo#{J)OHLV#~Vv`eFkvN396(WsUCQN=k4RfNk?2huv!d2$4`pexu9 zZ(~3mUBW--MWWYP@f4$|>>CntKp;mGpRg@1<#Z{7RssWmyCfE^`!$_}xASwmPsPiNfzYO6= zCMuO}vfKh=Q7M{$n#i0iZcxW6I+UB;sO|GLYJj8I9xq}nR+#9DDI2vdXM3)}O0*B6 z&W~HD+9>*WSG*-q5p9G$u`?=ip9Z+l1eT&gw-puYOQ@CK^x7pSxm;F2ecl{Z{oPOl zjziVTeAM%nVO89QTIo;N10P{Lc7E60AK1i&W}1sCrUR&%oj?`g1=K)=Cc6Wr5jLfr ziW+z=*2QCu=tReMRJDdqam7_0RV4LM19d?? za5!p(qp&u zz*1DM_n>Be0=2Dvz-ssqhjP-Do5qQVQ$FDBhP$V81Y_6?b^&(%$ePdb@l1ZE?;QK=cX$VFlqYQh^( zD?5TM@rKu5gE(uVtx*#nhMK_psMLLon($iB{Q)jiY!^@=zlkcAho~=BTI{TgnsE!% zgQ8Hy6pvMK8fqaMQ4`#Uo$(Szkm9OKtVy69v(%b5@LLSWK*eP)Bt5Yk9nq+>dKGTO zU8t3g{D(Df;Y{@5P1Mm^Y`HbZuqJ+qk5R?BdWAK;@GOpH0^uv&Pp=hUxZel&aB|-J zf0a9lc6c5{<@Pw1!}F+J@CT}R!dAN+S3%{nCaO3aqayh_DpI{rsThX$a2iJ7gf;G5 z*ou|3|9|FU4j+WES2Xj_F$<4iDt2G%{^@luYQ=rlx#G$|<#IZzXqTW;vkNt`GpH0^ z!(DhA71=Mpa-Z+P{ETnD<>C^a#IA&D?Rqz%CmXEUPXFwUZeo#}+(|hWb>tqz1NaDM z;qIU{UtsUe?jIsTwpi1h_Ev0*MYg(}_dwOaI1G>jbCL_amHKY8W(uyx7T9RJE5>xx z%9mn$+>b}F*beW+#9K`KHynUJ?4m}na`Dwe*2H0_!|o4>wcijj+D|YX^Btx5u+F#cZ$0y!HShDh4{#8^ zblm+F{SNM+y#uu?#+-0Jau1$h|7)L5Jn8<5wWr))t26Pg9&pn#T-s%Rv4%0tWz~Ke zPR05-^55=gUWqzq4xx%OaEc36{Z(vHD@D`+MypSdaF3)C7zD=8C#1R#Wx$<|2y^#$y!byJw9L-^7wQ z+Vg$X{$7aM-$B&=-;0&;9H!z^)I?M7yGXu^ipVrnL}sI2Qp+$*)xV01hPWAZhTlfL zJf2`3EdRjGJPMV|B+S6YI1tPJ$L*eXP!W9bp&OtwDstiI!{(^s9e~I1ufMbZBPpUA zTr|UGkF5EI561jq%?51$r_0r!m_pk=aocMIDgsYYD++t+&WAFnl{fY5j!MlS@ALUs zgZ6e*Dy~0e|M%e{AOFdwj%d7xqp>9>*pR#suSTtW4}O6Uu^lcB3Ca6Wc>{aV{uLF; z*7-v6{?#lKKcu}DPqV;Q`9t!4w!cs?B(Emw6byvqRdXyIDw>I??Qs_sx<64bk>a7Q zzaDC$%~7?`2etiDP&rM<@;DB4VlG5QBo_^1VX9`56>|GxloKn}?(9m8<{`hO-m*vc9 zY|j;o8=jgrVq|P`dQ4JWd@!+z?OD7+TFmf$lYH?p!+o&{F}}p))U>_p(|vy5;9>i- zl4CRDlKgSOs~v1sft<|wwn#9sn;jV9>pm!c-^Xdm`{xbz^DtkEKW;>NYGz!#FFj^N zYFc_m@LCT$F|0r$Uyjf5_p=+H<+(Ame9427{QI{jC&dISM%!rX-7=}a&2^W-jsxt3 z;yJyNY?gs*&N#j7v`-|#LQG;@Qd))?T6=Kfp7ka@BPDKs7C}=A KHiU*$2>Bl+aW&Kc diff --git a/resources/localization/pl/Slic3rPE_pl.po b/resources/localization/pl/Slic3rPE_pl.po index 282cb84598..9ab5f61b7a 100644 --- a/resources/localization/pl/Slic3rPE_pl.po +++ b/resources/localization/pl/Slic3rPE_pl.po @@ -97,7 +97,7 @@ msgstr " jako:" #: c:\src\Slic3r\xs\src\slic3r\GUI\PresetHints.cpp:226 #, c-format msgid " at filament speed %3.2f mm/s." -msgstr " z prędkocią filamentu %3.2f mm/s." +msgstr " z prędkością filamentu %3.2f mm/s." #: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:1035 msgid " Browse " @@ -574,7 +574,7 @@ msgstr "G-code wykonywany przy przejściach pomiędzy modelami (druk sekwencyjny #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:68 #: c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm:370 msgid "Bottom" -msgstr "Spód" +msgstr "Dolne" #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:72 msgid "Bottom solid layers" @@ -834,7 +834,7 @@ msgstr "Własne ustawienia" #: c:\src\Slic3r\lib\Slic3r\GUI\Plater.pm:205 #: c:\src\Slic3r\lib\Slic3r\GUI\Plater.pm:2102 msgid "Cut…" -msgstr "Tnij…" +msgstr "Obcinanie..." #: c:\src\Slic3r\lib\Slic3r\GUI\Plater.pm:2027 msgid "Decrease copies" @@ -859,7 +859,7 @@ msgstr "Domyślny kąt linii wypełnienia. Mosty będą wypełniane z użyciem n #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:376 msgid "Default extrusion width" -msgstr "Domyślna szerokość ekstrudowanej linii" +msgstr "Domyślna szerokość linii" #: xs/src/slic3r/GUI/Tab.cpp:767 msgid "default filament profile" @@ -1166,7 +1166,7 @@ msgstr "Funkcja eksperymentalna mająca zapobiegać tworzeniu podpór pod mostam #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:942 msgid "Experimental option to adjust flow for overhangs (bridge flow will be used), to apply bridge speed to them and enable fan." -msgstr "Opcja eksperymentalna dostosowująca przepływ przy zwisach (zostanie zastosowany przepływ taki jak dla mostów), zastosuje również prędkość taką jak dla mostów i chłodzenie." +msgstr "Opcja eksperymentalna dostosowująca przepływ przy zwisach (zostanie zastosowany przepływ taki jak dla mostów), zastosuje również prędkość i chłodzenie takie jak dla mostów." #: c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm:263 msgid "Export all presets to file" @@ -1248,7 +1248,7 @@ msgstr "Obrysy zewnętrzne" #: c:\src\Slic3r\xs\src\slic3r\GUI\PresetHints.cpp:151 msgid "external perimeters" -msgstr "obrysy zewnętrzne" +msgstr "obrysów zewnętrznych" #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:289 msgid "External perimeters first" @@ -1399,7 +1399,7 @@ msgstr "Właściwości filamentu" #: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.hpp:202 msgid "Filament Settings" -msgstr "Ustawienia filamentu" +msgstr "Ustawienia Filamentu" #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:450 msgid "Filament type" @@ -1478,7 +1478,7 @@ msgstr "Prędkość pierwszej warstwy" #: c:\src\Slic3r\xs\src\slic3r\GUI\PresetHints.cpp:214 msgid "First layer volumetric" -msgstr "Objętościowa pierwszej warstwy" +msgstr "Na pierwszej warstwie" #: xs/src/slic3r/GUI/GUI.cpp:326 msgid "Flash printer firmware" @@ -1651,7 +1651,7 @@ msgstr "Włączenie powoduje pobieranie wbudowanych systemowych zestawów ustawi #: c:\src\Slic3r\xs\src\slic3r\GUI\PresetHints.cpp:26 #, c-format msgid "If estimated layer time is below ~%ds, fan will run at %d%% and print speed will be reduced so that no less than %ds are spent on that layer (however, speed will never be reduced below %dmm/s)." -msgstr "Jeśli szacowany czas druku warstwy jest mniejszy niż ~%d s, wentylator będzie pracował na %d %% a prędkość druku zostanie obniżona tak, aby warstwa była drukowana przez nie mniej niż %d s (jednakże prędkość nie zostanie obniżona poniżej %d mm/s)." +msgstr "Jeśli szacowany czas druku warstwy jest niższy niż ~%d s, wentylator będzie pracował na %d %% a prędkość druku zostanie obniżona tak, aby warstwa była drukowana przez nie mniej niż %d s (jednakże prędkość nie zejdzie poniżej %d mm/s)." #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:616 msgid "If expressed as absolute value in mm/s, this speed will be applied to all the print moves of the first layer, regardless of their type. If expressed as a percentage (for example: 40%) it will scale the default speeds." @@ -1659,11 +1659,11 @@ msgstr "Jeśli ustawisz wartość bezwzględną wyrażoną w mm/s, taka prędko #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:395 msgid "If layer print time is estimated below this number of seconds, fan will be enabled and its speed will be calculated by interpolating the minimum and maximum speeds." -msgstr "Jeśli szacowany czas druku warstwy będzie mniejszy niż ta wartość to wentylator będzie włączony a jego prędkość będzie interpolowana na podstawie górnego i dolnego limitu prędkości." +msgstr "Jeśli szacowany czas druku warstwy będzie niższy niż ta wartość to wentylator będzie włączony a jego prędkość będzie interpolowana na podstawie górnego i dolnego limitu prędkości." #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:1240 msgid "If layer print time is estimated below this number of seconds, print moves speed will be scaled down to extend duration to this value." -msgstr "Jeśli szacowany czas druku warstwy będzie mniejszy niż ta wartość to prędkość ruchów drukujących będzie zmniejszona aby wydłużyć czas druku." +msgstr "Jeśli szacowany czas druku warstwy będzie niższy niż ta wartość to prędkość ruchów drukujących będzie zmniejszona aby wydłużyć czas druku." #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:388 msgid "If this is enabled, fan will never be disabled and will be kept running at least at its minimum speed. Useful for PLA, harmful for ABS." @@ -1742,7 +1742,7 @@ msgstr "Wypełnienie" #: c:\src\Slic3r\xs\src\slic3r\GUI\PresetHints.cpp:169 msgid "infill" -msgstr "wypełnienie" +msgstr "wypełnienia" #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:720 msgid "Infill before perimeters" @@ -1987,7 +1987,7 @@ msgstr "Maksymalny objętościowo kąt pozytywny" #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:421 #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:817 msgid "Max volumetric speed" -msgstr "Maksymalna prędkość objętościowa" +msgstr "Maksymalny przepływ" #: xs/src/libslic3r/PrintConfig.cpp:1854 msgid "Maximal bridging distance" @@ -2220,7 +2220,7 @@ msgstr "Nowa wersja:" #: c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm:469 msgid "No previously sliced file." -msgstr "Brak poprzednio pociętych pliku." +msgstr "Brak poprzednio pociętych plików." #: xs/src/slic3r/GUI/RammingChart.cpp:28 msgid "NO RAMMING AT ALL" @@ -2325,7 +2325,7 @@ msgstr "Retrakcja tylko przy przechodzeniu nad obrysami" #: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:438 msgid "Ooze prevention" -msgstr "Zapobieganie wyciekom" +msgstr "Zapobieganie wyciekom (ooze)" #: c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm:251 msgid "Open a model" @@ -2341,7 +2341,7 @@ msgstr "Otwórz plik STL/OBJ/AMF/3MF... \tCrtl+O" #: c:\src\Slic3r\lib\Slic3r\GUI\Plater.pm:2102 msgid "Open the 3D cutting tool" -msgstr "Otwórz narzędzie do przecinania 3D" +msgstr "Otwórz narzędzie do wycinania 3D" #: c:\src\Slic3r\lib\Slic3r\GUI\Plater.pm:2106 msgid "Open the object editor dialog" @@ -2463,7 +2463,7 @@ msgstr "Umieść jedną kopię zaznaczonego modelu" #: c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm:118 msgid "Plater" -msgstr "Zawartość stołu" +msgstr "Zawartość Stołu" #: lib/Slic3r/GUI/Plater.pm:1897 msgid "Please install the OpenGL modules to use this feature (see build instructions)." @@ -2691,7 +2691,7 @@ msgstr "Widok z tyłu" #: c:\src\Slic3r\xs\src\slic3r\GUI\PresetHints.cpp:262 #, c-format msgid "Recommended object thin wall thickness for layer height %.2f and " -msgstr "Zalecana grubość ściany obiektu dla danej wysokości warstwy %.2f i " +msgstr "Zalecana grubość ściany modelu dla wysokości warstwy %.2f i " #: c:\src\Slic3r\xs\src\slic3r\GUI\PresetHints.cpp:245 msgid "Recommended object thin wall thickness: Not available due to invalid layer height." @@ -3265,23 +3265,23 @@ msgstr "Slic3r nie będzie skalował prędkości poniżej tej wartości." #: c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm:268 msgid "Slice a file into a G-code" -msgstr "Potnij plik jako G-code" +msgstr "Cięcie jako G-code" #: c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm:274 msgid "Slice a file into a G-code, save as" -msgstr "Potnij plik jako G-code, zapisz jako" +msgstr "Cięcie jako G-code, zapisz jako" #: c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm:287 msgid "Slice file to a multi-layer SVG" -msgstr "Potnij plik jako wielowarstwowy SVG" +msgstr "Cięcie jako wielowarstwowy SVG" #: c:\src\Slic3r\lib\Slic3r\GUI\Plater.pm:237 msgid "Slice now" -msgstr "Potnij teraz" +msgstr "Cięcie" #: c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm:287 msgid "Slice to SV&G…\tCtrl+G" -msgstr "Tnij do SV&G...\tCtrl+G" +msgstr "Cięcie do SV&G...\tCtrl+G" #: c:\src\Slic3r\lib\Slic3r\GUI\Plater.pm:438 msgid "Sliced Info" @@ -3485,7 +3485,7 @@ msgstr "podpora" #: xs/src/slic3r/GUI/GUI.cpp:879 msgid "Support" -msgstr "Wsparcie" +msgstr "Podpory" #: c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp:620 msgid "Support Generator" @@ -4011,7 +4011,7 @@ msgstr "Parametry zmiany narzędzia dla drukarek MM z jednym ekstruderem" #: C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp:1638 #: c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm:369 msgid "Top" -msgstr "Szczyt" +msgstr "Górne" #: c:\src\Slic3r\xs\src\slic3r\GUI\PresetHints.cpp:187 msgid "top solid infill" From 4803e7fc84fd0bdb7edab93a246480bba8725f1e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 20 Jun 2018 14:34:20 +0200 Subject: [PATCH 109/117] Fixed crash at startup on OpenGL 1.1 cards --- xs/src/slic3r/GUI/GLCanvas3DManager.cpp | 50 +++++++++---------------- xs/src/slic3r/GUI/GLCanvas3DManager.hpp | 2 +- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp index f288ee456a..bdca738541 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -26,33 +26,23 @@ GLCanvas3DManager::GLInfo::GLInfo() { } -bool GLCanvas3DManager::GLInfo::detect() +void GLCanvas3DManager::GLInfo::detect() { const char* data = (const char*)::glGetString(GL_VERSION); - if (data == nullptr) - return false; - - version = data; + if (data != nullptr) + version = data; data = (const char*)::glGetString(GL_SHADING_LANGUAGE_VERSION); - if (data == nullptr) - return false; - - glsl_version = data; + if (data != nullptr) + glsl_version = data; data = (const char*)::glGetString(GL_VENDOR); - if (data == nullptr) - return false; - - vendor = data; + if (data != nullptr) + vendor = data; data = (const char*)::glGetString(GL_RENDERER); - if (data == nullptr) - return false; - - renderer = data; - - return true; + if (data != nullptr) + renderer = data; } bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const @@ -94,10 +84,10 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten std::string line_end = format_as_html ? "
" : "\n"; out << h2_start << "OpenGL installation" << h2_end << line_end; - out << b_start << "GL version: " << b_end << version << line_end; - out << b_start << "Vendor: " << b_end << vendor << line_end; - out << b_start << "Renderer: " << b_end << renderer << line_end; - out << b_start << "GLSL version: " << b_end << glsl_version << line_end; + out << b_start << "GL version: " << b_end << (version.empty() ? "N/A" : version) << line_end; + out << b_start << "Vendor: " << b_end << (vendor.empty() ? "N/A" : vendor) << line_end; + out << b_start << "Renderer: " << b_end << (renderer.empty() ? "N/A" : renderer) << line_end; + out << b_start << "GLSL version: " << b_end << (glsl_version.empty() ? "N/A" : glsl_version) << line_end; if (extensions) { @@ -195,15 +185,11 @@ void GLCanvas3DManager::init_gl() if (!m_gl_initialized) { glewInit(); - if (m_gl_info.detect()) - { - const AppConfig* config = GUI::get_app_config(); - m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1"); - m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0); - m_gl_initialized = true; - } - else - throw std::runtime_error(std::string("Unable to initialize OpenGL driver\n")); + m_gl_info.detect(); + const AppConfig* config = GUI::get_app_config(); + m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1"); + m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0); + m_gl_initialized = true; } } diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp index 6989da791f..3092925d38 100644 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -35,7 +35,7 @@ class GLCanvas3DManager GLInfo(); - bool detect(); + void detect(); bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const; std::string to_string(bool format_as_html, bool extensions) const; From f8bbfad1523e2630a7f34de969c02f45c0bd5e06 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 20 Jun 2018 14:08:41 +0200 Subject: [PATCH 110/117] avrdude: Line noise prevention on MK3 xflash boot --- xs/src/avrdude/arduino.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c index fc9f4571f3..5a9cb465e8 100644 --- a/xs/src/avrdude/arduino.c +++ b/xs/src/avrdude/arduino.c @@ -99,7 +99,7 @@ static int prusa_init_external_flash(PROGRAMMER * pgm) avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname); return -1; } else if (strncmp(buffer, entry_magic_send, recv_size) != 0) { - avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname); + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code: `%*s`\n", progname, recv_size, buffer); return -1; } @@ -116,7 +116,7 @@ static int prusa_init_external_flash(PROGRAMMER * pgm) avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname); return -1; } else if (strncmp(buffer, entry_magic_cfm, recv_size) != 0) { - avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname); + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect cfm code: `%*s`\n", progname, recv_size, buffer); return -1; } @@ -140,6 +140,13 @@ static int arduino_open(PROGRAMMER * pgm, char * port) serial_set_dtr_rts(&pgm->fd, 1); usleep(50*1000); + // Sometimes there may be line noise generating input on the printer's USB-to-serial IC + // Here we try to clean its input buffer with a sequence of newlines (a minimum of 9 is needed): + const char cleanup_newlines[] = "\n\n\n\n\n\n\n\n\n\n"; + if (serial_send(&pgm->fd, cleanup_newlines, sizeof(cleanup_newlines) - 1) < 0) { + return -1; + } + /* * drain any extraneous input */ From b386f52accc85907c9c69a35e7948382f674333c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 21 Jun 2018 09:54:43 +0200 Subject: [PATCH 111/117] Removed error dialog when texture file does not exist --- xs/src/slic3r/GUI/GLTexture.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/xs/src/slic3r/GUI/GLTexture.cpp index 593362e546..924920bd81 100644 --- a/xs/src/slic3r/GUI/GLTexture.cpp +++ b/xs/src/slic3r/GUI/GLTexture.cpp @@ -4,6 +4,8 @@ #include +#include + #include #include @@ -27,6 +29,9 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap { reset(); + if (!boost::filesystem::exists(filename)) + return false; + // Load a PNG with an alpha channel. wxImage image; if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) From 80e4155cbc46cf04e69a880880ed5c189cf4771e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 21 Jun 2018 10:24:57 +0200 Subject: [PATCH 112/117] Bed textures selection using config inheritance --- xs/src/slic3r/GUI/GLCanvas3D.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp index f9c10017eb..d3153bdb42 100644 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp @@ -405,16 +405,27 @@ GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const const PresetBundle* bundle = get_preset_bundle(); if (bundle != nullptr) { - const Preset& curr = bundle->printers.get_selected_preset(); - if (curr.config.has("bed_shape") && _are_equal(m_shape, dynamic_cast(curr.config.option("bed_shape"))->values)) + const Preset* curr = &bundle->printers.get_selected_preset(); + while (curr != nullptr) { - if ((curr.vendor != nullptr) && (curr.vendor->name == "Prusa Research")) + if (curr->config.has("bed_shape") && _are_equal(m_shape, dynamic_cast(curr->config.option("bed_shape"))->values)) { - if (boost::contains(curr.name, "MK2")) - type = MK2; - else if (boost::contains(curr.name, "MK3")) - type = MK3; + if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research")) + { + if (boost::contains(curr->name, "MK2")) + { + type = MK2; + break; + } + else if (boost::contains(curr->name, "MK3")) + { + type = MK3; + break; + } + } } + + curr = bundle->printers.get_preset_parent(*curr); } } From a3a8333d203606d2c8f404e9e5e7957c6105d1db Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 21 Jun 2018 10:43:01 +0200 Subject: [PATCH 113/117] Force update when changing selected printer using the GUI --- lib/Slic3r/GUI/Plater.pm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index e15b8c34c3..f5ef10e9c0 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -53,7 +53,7 @@ sub new { bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width - wipe_tower_rotation_angle extruder_colour filament_colour max_print_height + wipe_tower_rotation_angle extruder_colour filament_colour max_print_height printer_model )]); # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm $self->{model} = Slic3r::Model->new; @@ -1886,6 +1886,9 @@ sub on_config_change { $self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors})); } elsif ($opt_key eq 'max_print_height') { $update_scheduled = 1; + } elsif ($opt_key eq 'printer_model') { + # update to force bed selection (for texturing) + $update_scheduled = 1; } } From 1ba81655e2685c40b5c345684868e8d6d96cee2b Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 23 May 2018 12:47:39 +0200 Subject: [PATCH 114/117] Octoprint: Add a dialog for setting the filename/path and a "print now" option cf. #880, #245, #55, #87 --- xs/src/slic3r/GUI/MsgDialog.hpp | 2 + xs/src/slic3r/Utils/Http.cpp | 39 +++++++++++---- xs/src/slic3r/Utils/Http.hpp | 35 ++++++++++--- xs/src/slic3r/Utils/OctoPrint.cpp | 81 +++++++++++++++++++++++++++++-- xs/src/slic3r/Utils/OctoPrint.hpp | 2 +- xs/xsp/Utils_OctoPrint.xsp | 2 +- 6 files changed, 140 insertions(+), 21 deletions(-) diff --git a/xs/src/slic3r/GUI/MsgDialog.hpp b/xs/src/slic3r/GUI/MsgDialog.hpp index 2d570a0bf7..ca349eb5c8 100644 --- a/xs/src/slic3r/GUI/MsgDialog.hpp +++ b/xs/src/slic3r/GUI/MsgDialog.hpp @@ -28,6 +28,8 @@ struct MsgDialog : wxDialog MsgDialog &operator=(const MsgDialog &) = delete; virtual ~MsgDialog(); + // TODO: refactor with CreateStdDialogButtonSizer usage + protected: enum { CONTENT_WIDTH = 500, diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp index 0826284d8a..adc8ac8165 100644 --- a/xs/src/slic3r/Utils/Http.cpp +++ b/xs/src/slic3r/Utils/Http.cpp @@ -50,6 +50,9 @@ struct Http::priv static size_t writecb(void *data, size_t size, size_t nmemb, void *userp); static int xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow); + + void form_add_file(const char *name, const char *path, const char* filename); + std::string curl_error(CURLcode curlcode); std::string body_size_error(); void http_perform(); @@ -135,6 +138,26 @@ int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double return xfercb(userp, dltotal, dlnow, ultotal, ulnow); } +void Http::priv::form_add_file(const char *name, const char *path, const char* filename) +{ + if (filename != nullptr) { + ::curl_formadd(&form, &form_end, + CURLFORM_COPYNAME, name, + CURLFORM_FILE, path, + CURLFORM_FILENAME, filename, + CURLFORM_CONTENTTYPE, "application/octet-stream", + CURLFORM_END + ); + } else { + ::curl_formadd(&form, &form_end, + CURLFORM_COPYNAME, name, + CURLFORM_FILE, path, + CURLFORM_CONTENTTYPE, "application/octet-stream", + CURLFORM_END + ); + } +} + std::string Http::priv::curl_error(CURLcode curlcode) { return (boost::format("%1% (%2%)") @@ -265,17 +288,15 @@ Http& Http::form_add(const std::string &name, const std::string &contents) return *this; } -Http& Http::form_add_file(const std::string &name, const std::string &filename) +Http& Http::form_add_file(const std::string &name, const std::string &path) { - if (p) { - ::curl_formadd(&p->form, &p->form_end, - CURLFORM_COPYNAME, name.c_str(), - CURLFORM_FILE, filename.c_str(), - CURLFORM_CONTENTTYPE, "application/octet-stream", - CURLFORM_END - ); - } + if (p) { p->form_add_file(name.c_str(), path.c_str(), nullptr); } + return *this; +} +Http& Http::form_add_file(const std::string &name, const std::string &path, const std::string &filename) +{ + if (p) { p->form_add_file(name.c_str(), path.c_str(), filename.c_str()); } return *this; } diff --git a/xs/src/slic3r/Utils/Http.hpp b/xs/src/slic3r/Utils/Http.hpp index 7ed8196e63..2b81263348 100644 --- a/xs/src/slic3r/Utils/Http.hpp +++ b/xs/src/slic3r/Utils/Http.hpp @@ -16,11 +16,11 @@ private: public: struct Progress { - size_t dltotal; - size_t dlnow; - size_t ultotal; - size_t ulnow; - + size_t dltotal; // Total bytes to download + size_t dlnow; // Bytes downloaded so far + size_t ultotal; // Total bytes to upload + size_t ulnow; // Bytes uploaded so far + Progress(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) : dltotal(dltotal), dlnow(dlnow), ultotal(ultotal), ulnow(ulnow) {} @@ -33,6 +33,8 @@ public: Http(Http &&other); + // These are the primary constructors that create a HTTP object + // for a GET and a POST request respectively. static Http get(std::string url); static Http post(std::string url); ~Http(); @@ -41,21 +43,42 @@ public: Http& operator=(const Http &) = delete; Http& operator=(Http &&) = delete; + // Sets a maximum size of the data that can be received. The default is 5MB. Http& size_limit(size_t sizeLimit); + // Sets a HTTP header field. Http& header(std::string name, const std::string &value); + // Removes a header field. Http& remove_header(std::string name); + // Sets a CA certificate file for usage with HTTPS. This is only supported on some backends, + // specifically, this is supported with OpenSSL and NOT supported with Windows and OS X native certificate store. + // See also ca_file_supported(). Http& ca_file(const std::string &filename); + // Add a HTTP multipart form field Http& form_add(const std::string &name, const std::string &contents); - Http& form_add_file(const std::string &name, const std::string &filename); + // Add a HTTP multipart form file data contents + Http& form_add_file(const std::string &name, const std::string &path); + // Same as above except also override the file's filename with a custom one + Http& form_add_file(const std::string &name, const std::string &path, const std::string &filename); + // Callback called on HTTP request complete Http& on_complete(CompleteFn fn); + // Callback called on an error occuring at any stage of the requests: Url parsing, DNS lookup, + // TCP connection, HTTP transfer, and finally also when the response indicates an error (status >= 400). + // Therefore, a response body may or may not be present. Http& on_error(ErrorFn fn); + // Callback called on data download/upload prorgess (called fairly frequently). + // See the `Progress` structure for description of the data passed. + // Writing a true-ish value into the cancel reference parameter cancels the request. Http& on_progress(ProgressFn fn); + // Starts performing the request in a background thread Ptr perform(); + // Starts performing the request on the current thread void perform_sync(); + // Cancels a request in progress void cancel(); + // Tells whether current backend supports seting up a CA file using ca_file() static bool ca_file_supported(); private: Http(const std::string &url); diff --git a/xs/src/slic3r/Utils/OctoPrint.cpp b/xs/src/slic3r/Utils/OctoPrint.cpp index e63a16c386..1794c6024b 100644 --- a/xs/src/slic3r/Utils/OctoPrint.cpp +++ b/xs/src/slic3r/Utils/OctoPrint.cpp @@ -1,20 +1,66 @@ #include "OctoPrint.hpp" #include +#include #include +#include #include #include #include +#include +#include +#include +#include #include "libslic3r/PrintConfig.hpp" #include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/MsgDialog.hpp" #include "Http.hpp" +namespace fs = boost::filesystem; + namespace Slic3r { +struct SendDialog : public GUI::MsgDialog +{ + wxTextCtrl *txt_filename; + wxCheckBox *box_print; + + SendDialog(const fs::path &path) : + MsgDialog(nullptr, _(L("Send G-Code to printer")), _(L("Upload to OctoPrint with the following filename:")), wxID_NONE), + txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().string())), + box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload")))) + { + auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed."))); + label_dir_hint->Wrap(CONTENT_WIDTH); + + content_sizer->Add(txt_filename, 0, wxEXPAND); + content_sizer->Add(label_dir_hint); + content_sizer->AddSpacer(VERT_SPACING); + content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING); + + btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL)); + + txt_filename->SetFocus(); + txt_filename->SetSelection(0, path.stem().size()); + + Fit(); + } + + fs::path filename() const { + // The buffer object that utf8_str() returns may just point to data owned by the source string + // so we need to copy the string in any case to be on the safe side. + return fs::path(txt_filename->GetValue().utf8_str().data()); + } + + bool print() const { return box_print->GetValue(); } +}; + + + OctoPrint::OctoPrint(DynamicPrintConfig *config) : host(config->opt_string("octoprint_host")), apikey(config->opt_string("octoprint_apikey")), @@ -27,24 +73,39 @@ bool OctoPrint::test(wxString &msg) const // it is ok to refer to `msg` from within the closure bool res = true; - auto url = std::move(make_url("api/version")); + + BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Get version at: %1%") % url; + auto http = Http::get(std::move(url)); set_auth(http); http.on_error([&](std::string, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error getting version: %1% (HTTP %2%)") % error % status; res = false; msg = format_error(error, status); }) + .on_complete([&](std::string body, unsigned) { + BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body; + }) .perform_sync(); return res; } -bool OctoPrint::send_gcode(const std::string &filename, bool print) const +bool OctoPrint::send_gcode(const std::string &filename) const { enum { PROGRESS_RANGE = 1000 }; const auto errortitle = _(L("Error while uploading to the OctoPrint server")); + fs::path filepath(filename); + + SendDialog send_dialog(filepath.filename().string()); + if (send_dialog.ShowModal() != wxID_OK) { return false; } + + const bool print = send_dialog.print(); + const auto upload_filepath = send_dialog.filename(); + const auto upload_filename = upload_filepath.filename(); + const auto upload_parent_path = upload_filepath.parent_path(); wxProgressDialog progress_dialog( _(L("OctoPrint upload")), @@ -61,14 +122,26 @@ bool OctoPrint::send_gcode(const std::string &filename, bool print) const bool res = true; - auto http = Http::post(std::move(make_url("api/files/local"))); + auto url = make_url("api/files/local"); + + BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%") + % filepath + % url + % upload_filename + % upload_parent_path + % print; + + auto http = Http::post(std::move(url)); set_auth(http); http.form_add("print", print ? "true" : "false") - .form_add_file("file", filename) + .form_add("path", upload_parent_path.string()) + .form_add_file("file", filename, upload_filename.string()) .on_complete([&](std::string body, unsigned status) { + BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body; progress_dialog.Update(PROGRESS_RANGE); }) .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1% (HTTP %2%)") % error % status; auto errormsg = wxString::Format("%s: %s", errortitle, format_error(error, status)); GUI::show_error(&progress_dialog, std::move(errormsg)); res = false; diff --git a/xs/src/slic3r/Utils/OctoPrint.hpp b/xs/src/slic3r/Utils/OctoPrint.hpp index 744b4fcc11..e278c81412 100644 --- a/xs/src/slic3r/Utils/OctoPrint.hpp +++ b/xs/src/slic3r/Utils/OctoPrint.hpp @@ -17,7 +17,7 @@ public: OctoPrint(DynamicPrintConfig *config); bool test(wxString &curl_msg) const; - bool send_gcode(const std::string &filename, bool print = false) const; + bool send_gcode(const std::string &filename) const; private: std::string host; std::string apikey; diff --git a/xs/xsp/Utils_OctoPrint.xsp b/xs/xsp/Utils_OctoPrint.xsp index 282a3055d4..28610cb01e 100644 --- a/xs/xsp/Utils_OctoPrint.xsp +++ b/xs/xsp/Utils_OctoPrint.xsp @@ -9,5 +9,5 @@ OctoPrint(DynamicPrintConfig *config); ~OctoPrint(); - bool send_gcode(std::string filename, bool print = false) const; + bool send_gcode(std::string filename) const; }; From 9ee10a877938e371b9200601c53b69b78886d015 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 12 Jun 2018 12:32:03 +0200 Subject: [PATCH 115/117] Octoprint: Fix unicode support --- xs/src/slic3r/Utils/Http.cpp | 72 +++++++++++++++++++++++-------- xs/src/slic3r/Utils/Http.hpp | 9 ++-- xs/src/slic3r/Utils/OctoPrint.cpp | 31 +++++++------ xs/src/slic3r/Utils/OctoPrint.hpp | 3 +- 4 files changed, 77 insertions(+), 38 deletions(-) diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp index adc8ac8165..7c41266339 100644 --- a/xs/src/slic3r/Utils/Http.cpp +++ b/xs/src/slic3r/Utils/Http.cpp @@ -3,13 +3,16 @@ #include #include #include -#include +#include +#include #include #include #include "../../libslic3r/libslic3r.h" +namespace fs = boost::filesystem; + namespace Slic3r { @@ -34,7 +37,11 @@ struct Http::priv ::curl_httppost *form; ::curl_httppost *form_end; ::curl_slist *headerlist; + // Used for reading the body std::string buffer; + // Used for storing file streams added as multipart form parts + // Using a deque here because unlike vector it doesn't ivalidate pointers on insertion + std::deque form_files; size_t limit; bool cancel; @@ -50,8 +57,9 @@ struct Http::priv static size_t writecb(void *data, size_t size, size_t nmemb, void *userp); static int xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow); + static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp); - void form_add_file(const char *name, const char *path, const char* filename); + void form_add_file(const char *name, const fs::path &path, const char* filename); std::string curl_error(CURLcode curlcode); std::string body_size_error(); @@ -138,21 +146,42 @@ int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double return xfercb(userp, dltotal, dlnow, ultotal, ulnow); } -void Http::priv::form_add_file(const char *name, const char *path, const char* filename) +size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp) { + auto stream = reinterpret_cast(userp); + + try { + stream->read(buffer, size * nitems); + } catch (...) { + return CURL_READFUNC_ABORT; + } + + return stream->gcount(); +} + +void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename) +{ + // We can't use CURLFORM_FILECONTENT, because curl doesn't support Unicode filenames on Windows + // and so we use CURLFORM_STREAM with boost ifstream to read the file. + + if (filename == nullptr) { + filename = path.string().c_str(); + } + + fs::ifstream stream(path, std::ios::in | std::ios::binary); + stream.seekg(0, std::ios::end); + size_t size = stream.tellg(); + stream.seekg(0); + form_files.push_back(std::move(stream)); + auto stream_ptr = &form_files.back(); + if (filename != nullptr) { ::curl_formadd(&form, &form_end, CURLFORM_COPYNAME, name, - CURLFORM_FILE, path, CURLFORM_FILENAME, filename, CURLFORM_CONTENTTYPE, "application/octet-stream", - CURLFORM_END - ); - } else { - ::curl_formadd(&form, &form_end, - CURLFORM_COPYNAME, name, - CURLFORM_FILE, path, - CURLFORM_CONTENTTYPE, "application/octet-stream", + CURLFORM_STREAM, static_cast(stream_ptr), + CURLFORM_CONTENTSLENGTH, static_cast(size), CURLFORM_END ); } @@ -177,6 +206,7 @@ void Http::priv::http_perform() ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb); ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast(this)); + ::curl_easy_setopt(curl, CURLOPT_READFUNCTION, form_file_read_cb); ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); #if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32 @@ -206,14 +236,20 @@ void Http::priv::http_perform() if (res != CURLE_OK) { if (res == CURLE_ABORTED_BY_CALLBACK) { - Progress dummyprogress(0, 0, 0, 0); - bool cancel = true; - if (progressfn) { progressfn(dummyprogress, cancel); } + if (cancel) { + // The abort comes from the request being cancelled programatically + Progress dummyprogress(0, 0, 0, 0); + bool cancel = true; + if (progressfn) { progressfn(dummyprogress, cancel); } + } else { + // The abort comes from the CURLOPT_READFUNCTION callback, which means reading file failed + if (errorfn) { errorfn(std::move(buffer), "Error reading file for file upload", http_status); } + } } else if (res == CURLE_WRITE_ERROR) { - if (errorfn) { errorfn(std::move(buffer), std::move(body_size_error()), http_status); } + if (errorfn) { errorfn(std::move(buffer), body_size_error(), http_status); } } else { - if (errorfn) { errorfn(std::move(buffer), std::move(curl_error(res)), http_status); } + if (errorfn) { errorfn(std::move(buffer), curl_error(res), http_status); } }; } else { if (completefn) { @@ -288,13 +324,13 @@ Http& Http::form_add(const std::string &name, const std::string &contents) return *this; } -Http& Http::form_add_file(const std::string &name, const std::string &path) +Http& Http::form_add_file(const std::string &name, const fs::path &path) { if (p) { p->form_add_file(name.c_str(), path.c_str(), nullptr); } return *this; } -Http& Http::form_add_file(const std::string &name, const std::string &path, const std::string &filename) +Http& Http::form_add_file(const std::string &name, const fs::path &path, const std::string &filename) { if (p) { p->form_add_file(name.c_str(), path.c_str(), filename.c_str()); } return *this; diff --git a/xs/src/slic3r/Utils/Http.hpp b/xs/src/slic3r/Utils/Http.hpp index 2b81263348..73656bf88b 100644 --- a/xs/src/slic3r/Utils/Http.hpp +++ b/xs/src/slic3r/Utils/Http.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace Slic3r { @@ -33,6 +34,8 @@ public: Http(Http &&other); + // Note: strings are expected to be UTF-8-encoded + // These are the primary constructors that create a HTTP object // for a GET and a POST request respectively. static Http get(std::string url); @@ -55,10 +58,10 @@ public: Http& ca_file(const std::string &filename); // Add a HTTP multipart form field Http& form_add(const std::string &name, const std::string &contents); - // Add a HTTP multipart form file data contents - Http& form_add_file(const std::string &name, const std::string &path); + // Add a HTTP multipart form file data contents, `name` is the name of the part + Http& form_add_file(const std::string &name, const boost::filesystem::path &path); // Same as above except also override the file's filename with a custom one - Http& form_add_file(const std::string &name, const std::string &path, const std::string &filename); + Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename); // Callback called on HTTP request complete Http& on_complete(CompleteFn fn); diff --git a/xs/src/slic3r/Utils/OctoPrint.cpp b/xs/src/slic3r/Utils/OctoPrint.cpp index 1794c6024b..86049de16a 100644 --- a/xs/src/slic3r/Utils/OctoPrint.cpp +++ b/xs/src/slic3r/Utils/OctoPrint.cpp @@ -31,7 +31,7 @@ struct SendDialog : public GUI::MsgDialog SendDialog(const fs::path &path) : MsgDialog(nullptr, _(L("Send G-Code to printer")), _(L("Upload to OctoPrint with the following filename:")), wxID_NONE), - txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().string())), + txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())), box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload")))) { auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed."))); @@ -45,15 +45,14 @@ struct SendDialog : public GUI::MsgDialog btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL)); txt_filename->SetFocus(); - txt_filename->SetSelection(0, path.stem().size()); + wxString stem(path.stem().wstring()); + txt_filename->SetSelection(0, stem.Length()); Fit(); } fs::path filename() const { - // The buffer object that utf8_str() returns may just point to data owned by the source string - // so we need to copy the string in any case to be on the safe side. - return fs::path(txt_filename->GetValue().utf8_str().data()); + return fs::path(txt_filename->GetValue().wx_str()); } bool print() const { return box_print->GetValue(); } @@ -73,7 +72,7 @@ bool OctoPrint::test(wxString &msg) const // it is ok to refer to `msg` from within the closure bool res = true; - auto url = std::move(make_url("api/version")); + auto url = make_url("api/version"); BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Get version at: %1%") % url; @@ -99,7 +98,7 @@ bool OctoPrint::send_gcode(const std::string &filename) const const auto errortitle = _(L("Error while uploading to the OctoPrint server")); fs::path filepath(filename); - SendDialog send_dialog(filepath.filename().string()); + SendDialog send_dialog(filepath.filename()); if (send_dialog.ShowModal() != wxID_OK) { return false; } const bool print = send_dialog.print(); @@ -125,10 +124,10 @@ bool OctoPrint::send_gcode(const std::string &filename) const auto url = make_url("api/files/local"); BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%") - % filepath + % filepath.string() % url - % upload_filename - % upload_parent_path + % upload_filename.string() + % upload_parent_path.string() % print; auto http = Http::post(std::move(url)); @@ -175,24 +174,24 @@ std::string OctoPrint::make_url(const std::string &path) const { if (host.find("http://") == 0 || host.find("https://") == 0) { if (host.back() == '/') { - return std::move((boost::format("%1%%2%") % host % path).str()); + return (boost::format("%1%%2%") % host % path).str(); } else { - return std::move((boost::format("%1%/%2%") % host % path).str()); + return (boost::format("%1%/%2%") % host % path).str(); } } else { - return std::move((boost::format("http://%1%/%2%") % host % path).str()); + return (boost::format("http://%1%/%2%") % host % path).str(); } } -wxString OctoPrint::format_error(std::string error, unsigned status) +wxString OctoPrint::format_error(const std::string &error, unsigned status) { - const wxString wxerror = error; + auto wxerror = wxString::FromUTF8(error.data()); if (status != 0) { return wxString::Format("HTTP %u: %s", status, (status == 401 ? _(L("Invalid API key")) : wxerror)); } else { - return std::move(wxerror); + return wxerror; } } diff --git a/xs/src/slic3r/Utils/OctoPrint.hpp b/xs/src/slic3r/Utils/OctoPrint.hpp index e278c81412..a8599faa94 100644 --- a/xs/src/slic3r/Utils/OctoPrint.hpp +++ b/xs/src/slic3r/Utils/OctoPrint.hpp @@ -17,6 +17,7 @@ public: OctoPrint(DynamicPrintConfig *config); bool test(wxString &curl_msg) const; + // Send gcode file to octoprint, filename is expected to be in UTF-8 bool send_gcode(const std::string &filename) const; private: std::string host; @@ -25,7 +26,7 @@ private: void set_auth(Http &http) const; std::string make_url(const std::string &path) const; - static wxString format_error(std::string error, unsigned status); + static wxString format_error(const std::string &error, unsigned status); }; From 7cf39227076fbb04fd9b99d7313b8d265e34c47c Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 21 Jun 2018 11:41:08 +0200 Subject: [PATCH 116/117] Http: Fix nowide fstream usage --- xs/src/slic3r/Utils/Http.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp index 7c41266339..47021d39f2 100644 --- a/xs/src/slic3r/Utils/Http.cpp +++ b/xs/src/slic3r/Utils/Http.cpp @@ -41,7 +41,7 @@ struct Http::priv std::string buffer; // Used for storing file streams added as multipart form parts // Using a deque here because unlike vector it doesn't ivalidate pointers on insertion - std::deque form_files; + std::deque form_files; size_t limit; bool cancel; @@ -168,19 +168,18 @@ void Http::priv::form_add_file(const char *name, const fs::path &path, const cha filename = path.string().c_str(); } - fs::ifstream stream(path, std::ios::in | std::ios::binary); + form_files.emplace_back(path, std::ios::in | std::ios::binary); + auto &stream = form_files.back(); stream.seekg(0, std::ios::end); size_t size = stream.tellg(); stream.seekg(0); - form_files.push_back(std::move(stream)); - auto stream_ptr = &form_files.back(); if (filename != nullptr) { ::curl_formadd(&form, &form_end, CURLFORM_COPYNAME, name, CURLFORM_FILENAME, filename, CURLFORM_CONTENTTYPE, "application/octet-stream", - CURLFORM_STREAM, static_cast(stream_ptr), + CURLFORM_STREAM, static_cast(&stream), CURLFORM_CONTENTSLENGTH, static_cast(size), CURLFORM_END ); From e622401599f715b741ffc2ce4eeaa00fc1f222ec Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 21 Jun 2018 14:05:07 +0200 Subject: [PATCH 117/117] Wipe tower bugfix - the tower was printed incorrectly when the rotation angle was close to 90 degrees --- xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 6486f29171..fbde837544 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -142,24 +142,21 @@ public: } m_gcode += "G1"; - if (rot.x != rotated_current_pos.x) { - m_gcode += set_format_X(rot.x); // Transform current position back to wipe tower coordinates (was updated by set_format_X) - m_current_pos.x = x; - } - if (rot.y != rotated_current_pos.y) { + if (std::abs(dx) > EPSILON) + m_gcode += set_format_X(rot.x); + + if (std::abs(dy) > EPSILON) m_gcode += set_format_Y(rot.y); - m_current_pos.y = y; - } if (e != 0.f) m_gcode += set_format_E(e); if (f != 0.f && f != m_current_feedrate) m_gcode += set_format_F(f); - - - - + + m_current_pos.x = x; + m_current_pos.y = y; + // Update the elapsed time with a rough estimate. m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f; m_gcode += "\n";