From 5024fc4be7b03d6b5b065d42666d1b26a6cc3c1a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 May 2018 10:47:04 +0200 Subject: [PATCH 001/103] 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 1b6adf800..6cb5f398a 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 fbcd34a3f..d510c87e4 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 4d55e313a..f0f50a4f3 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 a632edeea..322491f9e 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 908d5eff7..d0ee98ee6 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 4f44fc7bf..7d7d37126 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 06eb041df..bd0b698ee 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 6b2f5c830..761485a12 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 8f03e4774..46fbb02fb 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 000000000..4074cdf78 --- /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 000000000..154870255 --- /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 000000000..e27c6b793 --- /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 000000000..3933fc921 --- /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 86d0aeba2..c1bee85e8 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/103] 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 6cb5f398a..6a2bec1ca 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 a6f26b586..7603c9b84 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 46fbb02fb..618b8ec34 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 4074cdf78..9e9f5e45a 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 154870255..db8aa9d61 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 e27c6b793..5a565be28 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 3933fc921..858c2d9e7 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 c1bee85e8..c7933f012 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/103] 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 6a2bec1ca..a7de71d4d 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 9e9f5e45a..93186a8ab 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 db8aa9d61..14c1031dd 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/103] 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 a7de71d4d..60f119980 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 7603c9b84..6f485d511 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 618b8ec34..0e8cd2bcc 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 93186a8ab..0e539aaef 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 14c1031dd..26d0949f0 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 5a565be28..b9bdde161 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 858c2d9e7..c53d6b5d3 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 c7933f012..7e7fbc417 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/103] 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 60f119980..9cdc51693 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 0e539aaef..c52027974 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 26d0949f0..7a8e763f9 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 b9bdde161..a3036c2ad 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/103] 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 9cdc51693..86e5d9095 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 eb655ae55..fb76914f0 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 91ba88d84..e23accbde 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 92a2bd451..7ce24f3a4 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 6f485d511..79de5e42d 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 0e8cd2bcc..219316c1b 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 c52027974..706e694d7 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 7a8e763f9..9eda704a1 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 a3036c2ad..90c72f950 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 c53d6b5d3..a092fcfa5 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 7e7fbc417..5e13fed65 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/103] 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 67f5866b7..b12f84a94 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 98326e95e..c6effb0a9 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 66f506a7f..5c603028d 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 d358ebeca..487ffbc75 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 41c0e0b4a..d32421542 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 7c0db99d3..9bde6210d 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 d7e8f0b2f..4edf97dd6 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 4d155816d..ee1a94cc8 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/103] 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 b12f84a94..afe79330d 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 c6effb0a9..ed2b7f8b8 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 5c603028d..5010d9c2c 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 487ffbc75..940a5b79c 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 d32421542..7568dec39 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 9bde6210d..f5b76fe60 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 4edf97dd6..c1ce8048c 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 ee1a94cc8..7211cc513 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/103] 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 afe79330d..89e46a55d 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 c9c954276..2706df509 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 b746de98f..88aab7786 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 f0f50a4f3..6d3234ff3 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 322491f9e..ee1bf22d1 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 ed2b7f8b8..f5193668b 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 5010d9c2c..8d6345f97 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 940a5b79c..2e4b15af4 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 7568dec39..f3b459b96 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 f5b76fe60..f4b367c95 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 c1ce8048c..bf0b9c066 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 7211cc513..c62c7e958 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/103] 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 89e46a55d..a654f6554 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 2e4b15af4..4a5a97c95 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 f3b459b96..507964136 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 f4b367c95..61867c61c 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/103] 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 a654f6554..e2fdcaaca 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 d2e778ac6..9babd8bc6 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 8d6345f97..eb03a5313 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 4a5a97c95..9631f7996 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 507964136..a7e9dbfb0 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 61867c61c..911fb2f0e 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 bf0b9c066..4d8066597 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 c62c7e958..2efe8f11b 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/103] 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 e2fdcaaca..23b87d661 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 6d3234ff3..80ba4db50 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 ee1bf22d1..9320e6666 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 9babd8bc6..02dfd18ae 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 eb03a5313..2ae07d858 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 9631f7996..de875841c 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 a7e9dbfb0..201a840be 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 911fb2f0e..48241dafb 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 4d8066597..4877618f0 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 2efe8f11b..55679ba16 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/103] 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 23b87d661..17e2e6377 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 2706df509..6c3e02e09 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 96c28fee5..0aff64310 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 80ba4db50..b379ba8ef 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 9320e6666..a2b779a0a 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 02dfd18ae..bb1527f8d 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 2ae07d858..3f4ecc637 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 de875841c..7a4702880 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 201a840be..558e6c350 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 48241dafb..f4cf4235e 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 4877618f0..6abe1bbf7 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 55679ba16..c55ee8fbd 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/103] 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 17e2e6377..72fdd6d6a 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 000000000..9edc8fa76 --- /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 000000000..22ba91a93 --- /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 000000000..f87e6627e --- /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 000000000..2c918c0d4 --- /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 ce9a80f05..a888110d6 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 d91463f19..7d52d879d 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 c55ee8fbd..240e62dba 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/103] 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 72fdd6d6a..e3dd848db 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 a678be67a..ecf841b27 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 bb1527f8d..be170a282 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 3f4ecc637..a98482396 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 7a4702880..c97a9566c 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 558e6c350..d7614d4e4 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 f4cf4235e..22eff5a01 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 6abe1bbf7..d0d2bb609 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 240e62dba..aa4c98d15 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/103] 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 e3dd848db..31982f6a9 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 9f80b1e25..b136d89a1 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 be170a282..87a3c9aad 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 a98482396..a3d976018 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 c97a9566c..fce045906 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 d7614d4e4..e1ce33fad 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 22eff5a01..7647e3525 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 d0d2bb609..d79152f43 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 aa4c98d15..32500b3eb 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/103] 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 31982f6a9..dcd8e3110 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 87a3c9aad..40e6f1851 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 a3d976018..22b869563 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 fce045906..f55563f7a 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 e1ce33fad..de244511a 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 7647e3525..4ff07b782 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 d79152f43..f7293049e 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 32500b3eb..6fd23a2f0 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/103] 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 dcd8e3110..d720b4060 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 40e6f1851..a7f5bd89e 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 22b869563..24223b6b6 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 f55563f7a..80eea86f0 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 de244511a..29be07490 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 4ff07b782..7e8cf913f 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 f7293049e..f2ff8a112 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 6fd23a2f0..40332e7ec 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/103] 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 d720b4060..fce0231e2 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 ecf841b27..407a0b6a9 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 a2b779a0a..54fdc249e 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 a7f5bd89e..57c926f7e 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 24223b6b6..ad8ca75e4 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 80eea86f0..4dadf8181 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 29be07490..dcf14e725 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 7e8cf913f..4349ae845 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 f2ff8a112..46eb872db 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 40332e7ec..0bb22888b 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/103] 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 5f35fb8e3..3975c12ea 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 481ffd013..41bcead29 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 eb4077579..390e1b85e 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 76353c15d..38cfde713 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 9c56fbb59..747bd3d42 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 4dadf8181..e243db011 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 dcf14e725..6a2af8324 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 4349ae845..f612b5530 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 46eb872db..7c8b82075 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 51317ec5d..5f80741aa 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/103] 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 3975c12ea..4c8e790b4 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 c6d9d1c79..641c800cb 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 38cfde713..b3630bbf2 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 747bd3d42..4f513ab4f 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 e243db011..f844ba647 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 6a2af8324..f108f1495 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 f612b5530..2dc41302e 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 7c8b82075..b2cb238dd 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 5f80741aa..e3b1df2db 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 90c50b281a10d2cc59335eee9a1f3161e39540bd Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 23 May 2018 13:56:54 +0200 Subject: [PATCH 022/103] 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 4c8e790b4..d81db3dc9 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 b3630bbf2..eee111066 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 4f513ab4f..a34e96286 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 f844ba647..65ca54612 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 f108f1495..5198c89c6 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 2dc41302e..c08d08156 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 b2cb238dd..13cedce8a 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 e3b1df2db..83bf8b797 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 023/103] 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 d81db3dc9..7696e2718 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 eee111066..a6e46b11a 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 a34e96286..03b242164 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 65ca54612..da639b144 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 5198c89c6..3c0275c9e 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 c08d08156..c5a15696d 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 13cedce8a..e0b2a1d2e 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 83bf8b797..a2be3b1df 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 024/103] 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 83b50ec9e..263363756 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 025/103] 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 7696e2718..ae91b5247 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 a6e46b11a..5c3492edc 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 03b242164..cc2d0d5c5 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 da639b144..a0065348a 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 3c0275c9e..7c32ea06c 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 c5a15696d..adcae3ddb 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 e0b2a1d2e..7f64e94b4 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 a2be3b1df..306ecf53f 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 026/103] 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 ae91b5247..c2f1b8fa8 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 5c3492edc..0f4b020da 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 cc2d0d5c5..edbb42bc2 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 a0065348a..2e0c35e7c 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 7c32ea06c..a15e632fc 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 adcae3ddb..c28bfb675 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 7f64e94b4..19baaf94f 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 306ecf53f..f87cbda44 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 027/103] 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 2e0c35e7c..942a7e6ef 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 028/103] 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 c2f1b8fa8..d8ee842bd 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 4581de41b..e7de0eb9b 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 b379ba8ef..ab53da4a5 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 54fdc249e..bab628e23 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 029/103] 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 d8ee842bd..e48d2cf5c 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 e7de0eb9b..e5e6a73d4 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 641c800cb..5d905c896 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 0f4b020da..affca1083 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 edbb42bc2..e09c226c7 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 942a7e6ef..3e93b8a1b 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 a15e632fc..2a0a19772 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 c28bfb675..6dd7744c5 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 19baaf94f..98baa3070 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 f87cbda44..c2314e3f0 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 030/103] 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 e48d2cf5c..7debb898a 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 e5e6a73d4..3e78b5c95 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 bd0b698ee..a4847fb45 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 affca1083..2969f3e8a 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 e09c226c7..ab7f1ba7d 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 3e93b8a1b..31680148e 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 2a0a19772..a11780349 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 6dd7744c5..66a66307e 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 98baa3070..3b3e35c36 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 c2314e3f0..0f47454da 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 031/103] 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 7debb898a..b27b7d55f 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 8ce23b1e5..147353abd 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 8b63c3641..b148ec29d 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 2969f3e8a..87071ce20 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 ab7f1ba7d..153744987 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 31680148e..823a4df0c 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 a11780349..e110fee3b 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 66a66307e..730badb87 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 3b3e35c36..cab1be2f5 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 0f47454da..53688e8da 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 032/103] 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 b27b7d55f..b2a572c30 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 3e78b5c95..f1ce8fe0a 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 87071ce20..a9262337b 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 153744987..94ce59a3b 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 823a4df0c..95ec9c342 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 e110fee3b..2a78ded07 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 730badb87..2406f0094 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 cab1be2f5..14b857920 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 53688e8da..bbc8fc2f9 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 951e8528b4352c8f412dfd8b588b4b614ef87ae5 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 28 May 2018 13:43:29 +0200 Subject: [PATCH 033/103] 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 b2a572c30..c5626d4bd 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 a9262337b..46b7a5eca 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 94ce59a3b..73835df2f 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 95ec9c342..ef82e7f25 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 2a78ded07..3a83321f5 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 2406f0094..b2a7f5bad 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 14b857920..4a804e921 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 bbc8fc2f9..cca3583fb 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 ef9c5345f..f21923b41 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 034/103] 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 c5626d4bd..765fde9ab 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 46b7a5eca..41aa1b77d 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 73835df2f..38dbfc804 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 ef82e7f25..3b218b88f 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 3a83321f5..9b01be081 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 b2a7f5bad..91963a9ad 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 4a804e921..5166da2e9 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 cca3583fb..6cc9e0d6a 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 035/103] 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 765fde9ab..8a80eeac7 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 41aa1b77d..89532677b 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 38dbfc804..0f39aa537 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 3b218b88f..3c264f25d 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 9b01be081..9a7c342c4 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 91963a9ad..27390a14e 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 5166da2e9..27807bd84 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 6cc9e0d6a..fb0a549fd 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 036/103] 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 8a80eeac7..435a4892b 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 5d905c896..8e6f2b81f 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 89532677b..23f0f0194 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 0f39aa537..973b5eeb2 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 3c264f25d..cc968755b 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 9a7c342c4..c5aa5a1cd 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 27390a14e..fea8dab8a 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 27807bd84..786c095e8 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 fb0a549fd..8aff20ae3 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 037/103] 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 435a4892b..327d2976c 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 e5157741e..8e4d654de 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 582488c5a..c691073a4 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 23f0f0194..469c03cbc 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 973b5eeb2..434f7a5b0 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 cc968755b..406e8939c 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 c5aa5a1cd..d5065986c 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 fea8dab8a..9d2e3733e 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 786c095e8..dcb10e63b 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 8aff20ae3..fc57ce89b 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 038/103] 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 406e8939c..1c8d87d2c 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 039/103] 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 327d2976c..0fd70cd91 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 469c03cbc..79ddd6acd 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 434f7a5b0..ab62723b7 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 1c8d87d2c..9487c3fcc 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 d5065986c..b0985e71e 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 9d2e3733e..0cce91157 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 dcb10e63b..a3aeab957 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 fc57ce89b..2ca1e614e 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 040/103] 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 0fd70cd91..991371182 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 8e6f2b81f..a8db8ccbf 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 ab53da4a5..bbb3543bb 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 bab628e23..c2e7be5f2 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 79ddd6acd..a7b7d6e34 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 ab62723b7..69f48e092 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 9487c3fcc..28f105d3c 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 b0985e71e..620f68858 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 0cce91157..27aaa3188 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 a3aeab957..b3580ec63 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 2ca1e614e..7e1e80d4c 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 041/103] 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 991371182..a6ec2e692 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 f1ce8fe0a..3d1f75221 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 a7b7d6e34..80102cc50 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 69f48e092..45fdbbb41 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 28f105d3c..4946ae807 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 620f68858..30d219cff 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 27aaa3188..16398b6d6 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 b3580ec63..301650990 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 7e1e80d4c..b155c0cb0 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 042/103] 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 a6ec2e692..319a15535 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 c56e64c6c..8b6b3773f 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 b0341db16..9d0fe03fb 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 80102cc50..cb87a43d2 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 45fdbbb41..22a5e8d61 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 4946ae807..c9b31a94c 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 30d219cff..bbb5880f1 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 16398b6d6..2d692cc27 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 301650990..1cb4c316e 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 b155c0cb0..0ffe3677b 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 f21923b41..60b589336 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 043/103] 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 319a15535..83cf5b45d 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 c9b31a94c..4e2355c89 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 bbb5880f1..22f561cbd 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 044/103] 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 83cf5b45d..4715b6ecf 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 3d1f75221..35468aad5 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 7beba9299..120fec830 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 a8db8ccbf..04aca9aaf 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 cb87a43d2..12c1fc180 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 22a5e8d61..11969df6c 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 4e2355c89..31c7b81b2 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 22f561cbd..7d490984f 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 2d692cc27..c1eb7f37a 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 1cb4c316e..da6e00e7b 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 0ffe3677b..8023efab2 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 045/103] 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 4715b6ecf..019873936 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 35468aad5..2ff798899 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 04aca9aaf..ff4e2ad29 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 8e4d654de..45e83b1b8 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 c691073a4..883f5e753 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 12c1fc180..d2ae0a4af 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 11969df6c..a41188435 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 31c7b81b2..53f301ad5 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 7d490984f..404d65e26 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 c1eb7f37a..e9a4d6ce8 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 da6e00e7b..bf6ac91ee 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 8023efab2..21b872cfd 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 046/103] 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 019873936..3b7987343 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 53f301ad5..3f0c821cb 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 404d65e26..cb0e4a724 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 047/103] 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 3f0c821cb..0165b0940 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 048/103] 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 3b7987343..68d749305 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 ff4e2ad29..f63090835 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 d2ae0a4af..45d1d5b76 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 a41188435..615655a00 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 0165b0940..50913ae3a 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 cb0e4a724..1caf69c0c 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 e9a4d6ce8..7a53e2fc3 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 bf6ac91ee..1b22863d0 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 21b872cfd..0d5715564 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 049/103] 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 04dc80323..d04d470f6 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 68d749305..eca90890e 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 45d1d5b76..328e7dcd5 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 615655a00..1ab958d9e 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 7a53e2fc3..10f825201 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 1b22863d0..1a8689c81 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 0d5715564..862b9645c 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 050/103] 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 eca90890e..c13cd48ad 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 50913ae3a..008324a62 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 1caf69c0c..8e2cc1d0a 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 10f825201..4a35d61fd 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 051/103] 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 008324a62..892fa5bc5 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 052/103] 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 892fa5bc5..dbf7c7654 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 053/103] 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 328e7dcd5..fc609caac 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 1ab958d9e..266c48ce5 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 dbf7c7654..e4db61072 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 8e2cc1d0a..f71827327 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 4a35d61fd..85caa2476 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 1a8689c81..1ebf39320 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 862b9645c..ac1ae1420 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 054/103] 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 e4db61072..72d27a00a 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 055/103] 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 c13cd48ad..e787fb5a1 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 f63090835..774027540 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 056/103] 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 e787fb5a1..b8a45dd46 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 fc609caac..e03ed3cc3 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 266c48ce5..06500cf33 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 72d27a00a..8a4bdd9a2 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 f71827327..cb6ed64e9 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 85caa2476..1dd679c0c 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 1ebf39320..6739b03df 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 ac1ae1420..60c86eaa2 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 057/103] 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 e03ed3cc3..eed1e8a1c 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 06500cf33..df395bccb 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 8a4bdd9a2..240581aec 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 cb6ed64e9..fe27de0b2 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 1dd679c0c..a136f4766 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 6739b03df..dd8f965d6 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 058/103] 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 b8a45dd46..204e19d3d 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 2ff798899..fafd5da7c 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 774027540..d6b7acf2b 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 390e1b85e..2b7f56cc1 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 bbb3543bb..bb9ecd41c 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 c2e7be5f2..6fff40769 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 eed1e8a1c..e55985b0e 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 df395bccb..aff40f4d7 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 240581aec..d48cc0ccc 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 fe27de0b2..d2c9f259c 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 a136f4766..cbd9c15cb 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 dd8f965d6..e28a0aba9 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 60c86eaa2..fac52a02e 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 059/103] 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 77747cd07..64f4b9274 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 000000000..e4b1952a4 --- /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 000000000..268b614ef --- /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 060/103] 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 e8b2a6faa..60d67553b 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 d27be785c..40c2a1351 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 64f4b9274..866374584 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 e4b1952a4..27a79f84e 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 268b614ef..299c9b75b 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 897e63693..1d860d284 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 061/103] 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 204e19d3d..1476e8c22 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 fafd5da7c..309c2e5ed 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 2b7f56cc1..9ea5069b4 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 bb9ecd41c..84177dd0f 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 6fff40769..ad9c4df94 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 e55985b0e..47095e1b1 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 aff40f4d7..69530b84f 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 d48cc0ccc..7251e600b 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 d2c9f259c..b7ba7ef6b 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 cbd9c15cb..850ccc697 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 e28a0aba9..5a874de12 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 fac52a02e..8b454d3c3 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 062/103] 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 7251e600b..37f1d6b50 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 b7ba7ef6b..69934a822 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 063/103] 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 1476e8c22..a55c4ab16 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 9ea5069b4..7b713b46f 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 47095e1b1..89b7d0a11 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 69530b84f..ee6547657 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 37f1d6b50..a06079744 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 69934a822..4b2f82d85 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 850ccc697..3f903b9b3 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 5a874de12..0d741d550 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 8b454d3c3..f6f5134d6 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 c6e44509e09f0fac77ef3a50d302bc2bc192c00e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 6 Jun 2018 10:16:58 +0200 Subject: [PATCH 064/103] 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 a55c4ab16..b2240b714 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 d6b7acf2b..4137d1458 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 84177dd0f..56ca9d738 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 ad9c4df94..f7bfc3796 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 89b7d0a11..ec90011c6 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 ee6547657..832ebafc8 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 a06079744..e1b70710f 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 4b2f82d85..c70cff360 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 3f903b9b3..74949c5fe 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 0d741d550..6ca22737b 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 f6f5134d6..bf8a6a67e 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 065/103] 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 b2240b714..1995805f8 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 4137d1458..2a710b6f9 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 f7bfc3796..b0735c349 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 ec90011c6..5ee02a780 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 832ebafc8..6d94dd43b 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 e1b70710f..783ae52f8 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 c70cff360..c8533f514 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 74949c5fe..bfb11d972 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 6ca22737b..ba9d3d36b 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 bf8a6a67e..8c03e4887 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 066/103] 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 1995805f8..5f33b17ab 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 ebbbcf43e..6a2246fa1 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 2a710b6f9..6641d45e5 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 783ae52f8..41741eef8 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 c8533f514..7bd93a32e 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 bfb11d972..26c19e9fd 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 067/103] 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 41741eef8..bb5db8614 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 068/103] 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 309c2e5ed..734eaaa95 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 6641d45e5..6c6b56478 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 7b713b46f..55198eeb6 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 069/103] 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 27a79f84e..556035a5b 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 299c9b75b..c148a6970 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 070/103] 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 734eaaa95..d44a4d393 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 6c6b56478..da3bfc617 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 5ee02a780..79d4cba25 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 6d94dd43b..38cb82ccb 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 bb5db8614..d0baa6563 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 7bd93a32e..dde51a277 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 26c19e9fd..a957d8b9a 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 ba9d3d36b..50d282cc9 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 8c03e4887..b70d49b76 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 071/103] 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 d44a4d393..82249bfd4 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 da3bfc617..28bc6490c 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 45e83b1b8..913f91ccf 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 883f5e753..017e2f1b3 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 79d4cba25..2d341996e 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 38cb82ccb..7c4d8b3e9 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 d0baa6563..f969e1d46 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 dde51a277..e18b80345 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 a957d8b9a..34d62927a 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 50d282cc9..f012b7703 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 b70d49b76..0635cea31 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 072/103] 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 0467962c3..2c32db1a6 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 073/103] 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 82249bfd4..9dad79807 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 28bc6490c..fdd55cdd8 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 913f91ccf..472b0478f 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 017e2f1b3..7209d86f6 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 2d341996e..e773ec6eb 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 7c4d8b3e9..a66130a3b 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 f969e1d46..745268627 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 e18b80345..49d06c135 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 34d62927a..cd4549bd8 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 f012b7703..d0765932a 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 0635cea31..46c4381fd 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 074/103] 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 9dad79807..f74bc7d82 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 fdd55cdd8..7f83e0f57 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 e773ec6eb..21204b0ff 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 a66130a3b..8e82849e8 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 745268627..34df13a54 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 49d06c135..b8c64228b 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 cd4549bd8..e01f85396 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 d0765932a..76f8ffcb5 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 46c4381fd..f8d22843e 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 075/103] 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 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 z

gSF~x{ue!~pZel4|Tb;dWyLvLW!8Cu=k|u0LaUW%Ecy72%W>y#Wt=%Y z%-PwUH-5i+*ZIM$;HvG9u=a(o+)ezeCTeiKi&UF7)1rRf;eR=59$T)BdVh@#>nhsz zEX7{!*?4RuHKexR16L1iCJf72T&yrXR*PGzMN3J~-aRH&!+w}pn0KBO@Vxlv$-(Z- z!u;?W)ioK$x!cG=dJdho$BXU>xxd;1KMo-IJ8~Dg29Fkw+a8y5fuv%Jb*_dSr(k z3r&U&wXJ;poZk};^$hsP%^|Xgw?cWfn*A6KSY2gTti5by?}Y zGZrJ0t4vXeq)$aRo<}*#t$_??Cf@&Q8}o|OL0%fW+DzD&K8XcV#2}lI=`{xGoCP}g zuT^W>1a;1NzhCMQ*^kf6ctUR7HXYJqi|YEuTPFP))I9I4L!Y2AaOc2hGh6(?r?Iai z_Rg{-%!?mN=PFs-?HD2Xrjj=5^g_o+5|PXN_R_Ghsww!hu20xkhYmJ(bWM1Gld&j-nA`|peDdEtw3ETd9FL?&SG&;ALB7US(n;Vp(Zi3sL|l*!>1!Bgi> zj!ibPbBvmb3X|MFm+C5F#*bLgs6O|C$ak{yprup3=YK2&3MH+OB^jF5%(a)A2OwpI zRK+je*uQK6$RYzV$2y*(NRf}Yb6H$Hvt91j2xjpGcP z9??)vO~UudIqMBJSqb0n6OGNYP<9-J$;tHbNyL3pCt)BxxbonuJHGPRvv}!CkGhSh zBLBFty25yZ^UsT7+25}#SL%yGavPmfjcT2HjLX`zEDd5u3?h<(woOf17y^WF&*_!9 zVh?D;?7ePf8P8uvW`yxWGCoQ0KOQ$lXO3KWQE@&OwTwX)UPE>K(})Z{7-pYUDXY-W zVP)lD?3%B&K9j=}-wW!LSn&`o(()<~5d;fNvI!|tljd>=q_kn}Va8>I;q9j&k`BbXs z62xOSi)g_{f9uAc`S$}4>Ds47GWH%k-LH&zx=VAKdj`H>ZfxFHOr`RdTTXqOgAB1q zaqU&4Ue{B+AGH}nRFG3>>lvuXX{K9@A$EgDnyef#jDHBXYK3+1%G?{$T7z^`HM(*( z^!bvXoJA1kP`8+CKN%eay~Ca{Hpll!ysjvwt4#6x*e!2m8C)eV?$&>fy<>IL=VnX2 z5Iws%n6k0Z{hjed4Qp}}b@QRM^z@ZAU0(|;qg^+F>7VA)?9t1YU43-bTTUC;p-s2Y zr4-GV(5N&lsM;kv98tlG%^R4_cHx4^I-_nh=?2O zV7@D$x?0d8``w4@%2NiMyk6a-?iJ z@S$rtLD)g7WeStI{kXMY0@JJSnB5i%Bu)q;-OFx3`z(gy&o9#1eS%^+Jhzexw{`Op zt}&WerLg=d<&Hx;5iMgMwyrh=g()sizQr>;^}XzXCoegCZ83AgevKyHHT3lCec4#- z!es01jC2eedy+lR1&kTf)U0^#wdq*_9|w_Oi|T(OT{!%j_JA( z>OXNM>;uo<$c_ms=anp%Ke?3G?{?nX-T|MEDI8D|tIv#f){|?2ufYO&j_cTJqNkrW z75s!S)jj$b;siq{$&LUu6ILD7(ZsbsShKP5xyMdep1>F3EMZjJw{WAwMY~j7k2%fk zF4?{RT4UFCP#UEUw_>(peEI{4H)DmEhL`9{Uk&@gaWkX(wQ*=$Eu?cLQu@y+C4Zxw zPBzE_eqP^e zWu1bF_0hpSt`G6it`u6CInZxkEqj=s91G^>4pKbP+T{k<_Ame+zAyULq%)2=`(|s) z8NaF!X(+06*zx|QnSz#2_S06^tYsh{x^a5c z32yPv`+Fyo=fC12VA+$~Bm56Gy1~dk!uh1~i z8b;Y^8PKR*ZvMc5CK^V}=?yx8h9x4NYK8UjPwo8zJoUyXDgplY&707p!F5$o4cKA$ zf-=IAHd%jC-f*j6)RMoAFw0?7aO!pd8wfNCKFPA8o4*TWtahdXDVOJ{|2b;OGrkuL zP|$r;MJ^0>p%TNh9+&1Gqs?9U8e-z_uZC5m34;utN8KXuP6OV){!HZYy%nO`J3*^x z5o&KNvX=!V0A|s;PNBqL6zR?w&0t0YSn)%8e71onG-N696)@_P?lW4ghZQ2>b`_+s zV82DLx7$)-s^@1x07)2fRp%Ka;uTPbYIvvpDp1U31oInzMWf4SUO>9un12(4mSYaNny5+ITcv$dRJT&@Lw2=> zrn#rI{Dx?;D)AS?EE}@RC`iS;npz_&-k(jyDu&A2Wh9a9?UY$o;DpP+`&cGi7BDl^ zZzmLZ4RHrj_8pm4G@wBP9DAW#X{rYH`G^%k5_ZfeTU|U4IM9T$_)oT_4ga&`OJBSUegDh-HK>K~gCH`= zYnp~c@s^&Gc5AImo!7atK|nhRYl;@IF|@l@+i!35>=j&FlUD+A1k`8|Larj5q4t9z z=dB09mOub6zvMm}Kuy`D&^@{DZpEaYm@y+h@!4#_F~)tn64?;%;NK{S zfB{d2-wv=+WpJi+E!=qbz^*9cccQlg2V!z~ar;I~dI+For%nN4wm7%sKfQFHH?m6` z;)6j>aWCNv)gFa#p8QR@`E5<-nM;HRvfMX*HWmMaW9`B;LjJn;0g<(iVvg*GX5suI za2Wd>57l4iJN(G#KD+kR|E3mo!>P?2l6A3N9YC=;yhG~9oohRllL#a>l%iqZs4Xq9 zosHz8dn$wYt%K)2hvy>;DNF9EvoV2c0@R0s!CGax28ZCw0&Ak^>d3B9VCy<6GU(+k zgvY^|H^9429J`|TjzCGazaBETmY;>-v`xGXz+888aKJ@=hCMrHw%6brcM*h*oMiD; zlraUI?x`Yr1+8}V*Ke$zj>Z64`h7DJ??=l{^3*ao=L}?+8C{17Jd;pk{`CWEhLNW9 z=EDsh9KrY`u))vN1AOhMZobDifl_FnUjCBnYY&=ZMy~*4X#Beq;yoF!ZU&&^<YZ#5XV(~%~(m#I4P`LIx~PO>XO zMow_W*9og0L(DgLaBEquCB@4NoqOXok9%)*5p`lIKFy)G+BV?|5-(|TNRMTft1k`O=+Q4hLdpw>ocyOqXe(- z(FVJyw(^( z6McMJC-kn5l2+lH>yTZajiB#H_oAN#A1DGI*lvLz=pdIzO^Z{Hn9J@~EfHTHayK4L z(xUuwDFQ5YW`t^QE>l@aLIiwS6dq=5hQENbp<)cTXfq9T(qp2zZxwlT(f=3!aPQ44 z+eZf@RAPUb8Jbj9t zutmAu*GZ%Op%oRHnJNxh`ANe!2n+hu(B;ZE^u|xM=Z(2Dm(z`P$eTDtw-ARYqILe22$~p`> zI3o6=C;75-Djs&30Qr8LQB)r(q8+O)m)LYz1I`@YIe(GmYzVQ|eY8BF7oP@X_99}TsrlPekw8|R}W66Fre+Em6_p)ZM11Bg6 z`NHI1&`yef4sGN1>+ND)Gs!?U`BjkeIrpuKLAEyJ;aSaez$^#jtpa98C>y(IpeDe} z!F~-z@$g-n#&|;ilLB~R;9SLRb*&fuOYa#GkZdVn^zTN=$W5&d2+vedZmqQi>ZrCokBo-`&~z z>GVrjZ^?eU{3O9KX2;or?Wf&{R1fPG|_k2bp~q+p<;UqRA7a^(Au=;1E` ztslJ}k}H3`#dw>YVbHNGlWeibT+k|2X7!HTD7oo)4=(^OblddQ{Ehc|k?z}R+NAgw zyQ3D644O2B-Qfk^kfxhh>m9TcMjG54Y#7dU8skf(6(HE32s3HsUpz z<6Gyq`*J}+K%H0v7q=&%)FAhooP5jOKD7QJ~HzRRJH zFKG1tzHvXZyiqa@n)4QzBCl5bA~hQc(`EbFKf=wZ2yl_z!aH~$jF+^|^8g=OIUU(< ziI9!N9t!Z{5Nc%8JD~pe)Bj#6aee(JeL4g1v2*8kl3+^koBOdX>+SRZ;GfnB@2-L4 z)vh*oLNV7+TCQWdfpg^vkK!|~75;OST@EkL10^__uq|rS477h^Pu9~zboAu)`%m2pNfDt9UWQ9NbF>9zV4 zLTf*pfP@iZi$S6aBLwQAWD}r{{na7OpV@~UDw{Bgo>zPb7WDOw6OUm# z#r^K)v6ub~*4PEzSciqA@7H&E_gaGht=&NI_yZau5#pnQ8wYs6sLGQ-8ALmP_0Sg{ zPN8x8F;i0Zov~}er)v3(`u011-UCb?V*Q5<6DzhdTE0|I($=7fGiLn<^R>oFhl-PJ#TF-3a}-WXVsH@(t`#r729dq~U%YuMsn8f` zVD#g;Ztq&7Z31vg|3&El)fGe+Es~e}|4V!y+zQTME;=(V1p$UmEq@-0ilnP?I#b!k z8Zx@|X@x#NNcg;M{rdTWNQHknR*3zbRqVSd&%%t_ct7uuBV~)F6t~ltMoHh`YPWFe zlF_tF6YuerKnEwzQfv1J8s>bqar&uH_FDmjNP}KsPg>-MvQhp^wHWv7`F9_^@~NR$ zOK(Xn2H$QGWawjq9tCbBTX>ma$4?3u&4u-stdnH=&UK(Fv*M#=_qz>zXwaje8>wZW ziD*0@;0z^&<1bS$)p+fvO?Y-c8tJ!y2BSB$cYV++90`lp)OiY2%o6V@cbv3MClYR= zT|T8H=S;_M82O-m(lc@}NcFw@>RVd7ny-hs&yA$fit%b%%F_JG!p^hP8F)%G&8kcI z>k!)_c!;4CG_WL=e$TMa=x3BL>I~FEdD?1d$B+s8sG#&7FAs(NAqDG&=p}`$Q$hP9 zt4`O;4__YAHRla}y}x^18!LVg@EJVo0ImNtm5eu_|NizF6|UZp@h19}D>Sv;pKDWs z@z37Zn4(F6==4Bw^q>Kwu<{^sk5au)aqf%Z?qzk-cOJ}LTp$!TxesSTM?Ko**4cfa z-eeIsnmfm-x2wzj#5k56-T25m{oXE%Ize|=0Uctx?QwV4+~dO|Lq_9CPlK7P{#qH_ zDzqMzmhjd~7%km#AstIy%0&!9!;$#nd__&uaIc{5%e~=2p(vh}A9Je0Inp2EH;jFZ z(Tk6~FYMxkUD-8seH`hq^alagaAY`0SUDVzU9=hDc z2`hLLJuv4@hZC(kR*H$18|qR|$In2$?!Wv46Al!?Z5rAY@wD!UsssA%r}Us{9s?g^ z^{@mysJ1Hg%7IS|53gbhp9HivBRx(?xfQXOGln>I3NLF=_79DrnEfoH?OQrx&Vki{ zuB_I`$Ko%Pcb^n_Y(mhM@m$U)bbQN6BGn5+d~CVME;Rw{Wyr1&StXqIym^$ZISF`9 znc|2q3p3T(qS{6b#75HrQ9Af)fUzv72zXk8yme!^#~pA%b{QM=8H#^bsG;`-&Z1Cu z`lqDt{)3$eIqdc74;1z*Ou~AIj@tI7J{n&TX2^!;VmJ(gB z=WNkfrP0Cn16w0}*0tSZSB>suY=Kd7zy>NRx^&hII^kq(M)_jMpIn(}8}KBVcD$H9 zQA2lvj%_|3TI%ik1HzOym!6RrR3vxUW1i6_#gf)*DGJw9yU#vf?(LYjwun1`|{0?^g#ZafEW8(Zxp}? z4TK<)V|-V%08FctoaM8z!vRatnD=P|Pu<#&Y=%2+AMGaQqhyot5h=yONTMjtn54?g1>7rGZn5fRA_#1sDBOL%KdUVCo8=wYKR;uJFx&1k5FGb;K&v zm}*-Iq5gVl=xF~spg{GIYR>m-QB%5BhZ}`j-ufCN7^MsF;7p6P0kVSB63{lV%b!UX zB*Sd`?*-pK2{DNM^UlB`Ep_!&zsE&N;YtR3txZ&s1zo{G!;dVuF0_aZby#kd42H3z z)7(YuUv9wfo@wZ%30im<+lB}jc)rLS)eXY_D29OxE`EKRZd3N1&&^J2H*7PMr7LhZ zmlm3TVqTSJ_dcHMdEu;T4qi-HSvDx6c*R_F^tsu;y6Ab=B;N14lp9RU$0ok z(w(QqmvJfnap6I`X=W`|$ly$%_x#j5=DMyQ!NB9c>f~VmPaq~K%%~0)fENV^`FooF iKhJPcT|twM!w!^nFJ5TTSCa320t|H@=oD)`efvM&R<9NS 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 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 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 34df13a54..f68e58d7d 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 b8c64228b..8917fb9eb 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 076/103] 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 b0735c349..135d75233 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 f68e58d7d..e5f352bf7 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 8917fb9eb..dde96ff41 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 077/103] 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 5f33b17ab..e40beda26 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 f74bc7d82..85d9426fb 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 55198eeb6..86a862c75 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 135d75233..546ec5445 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 21204b0ff..4f95f4ba6 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 8e82849e8..1f179b009 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 e5f352bf7..7d39d97d3 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 dde96ff41..2bdf9e692 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 e01f85396..54bd54ba8 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 76f8ffcb5..435993acd 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 f8d22843e..8deb6419d 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 078/103] 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 7d39d97d3..0d234a764 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 2bdf9e692..30f4cb37f 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 54bd54ba8..fe0b64d0e 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 435993acd..9de44256a 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 079/103] 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 e40beda26..11ca0611d 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 4f95f4ba6..dc40118b5 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 1f179b009..9685be64d 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 0d234a764..51e03a949 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 fe0b64d0e..298c78143 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 9de44256a..f5187d3d3 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 8deb6419d..91200eb2d 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 080/103] 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 85d9426fb..18545db3e 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 dc40118b5..046974ba9 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 9685be64d..2d043953d 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 51e03a949..b032ce353 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 30f4cb37f..d694db4e2 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 298c78143..b021e65a8 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 f5187d3d3..741c8e29b 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 91200eb2d..c7f3670fc 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 081/103] 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 11ca0611d..157e7229c 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 e23accbde..ceb968a50 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 7ce24f3a4..5de94aa9c 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 8b6b3773f..86c15b679 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 9d0fe03fb..ba0876a85 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 472b0478f..05eaf282f 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 7209d86f6..83c45b190 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 046974ba9..9a11699a2 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 2d043953d..138b220fc 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 a888110d6..903f6c347 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 7d52d879d..032640d8d 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 082/103] 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 18545db3e..66d2f2f7b 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 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 #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 d694db4e2..6421e44ec 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 b021e65a8..8a84d4cb5 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 741c8e29b..9ec645c1a 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 000000000..06ceee881 --- /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 000000000..1da216ba9 --- /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 000000000..b9eb118a9 --- /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 000000000..3e3bf83f7 --- /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 c7f3670fc..426395ef2 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 083/103] 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 348588ee2..e4420cf0c 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 6421e44ec..74c95ca74 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 06ceee881..172720390 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 084/103] 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 3770ad0e6..34d066730 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 ad2b8d5c5..26b9911e0 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 e4420cf0c..9ad7afeb8 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 74c95ca74..2852d82d1 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 8a84d4cb5..97af89fb7 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 9ec645c1a..95bd2af80 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 172720390..b4237e162 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 1da216ba9..3ca8e3213 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 085/103] 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 9ad7afeb8..e3db3af10 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 2852d82d1..738df637c 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 086/103] 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 e3db3af10..9dbfeaff3 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 738df637c..291b07553 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 b4237e162..75f39aad9 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 3ca8e3213..a626305c1 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 087/103] 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 75f39aad9..d98864931 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 a626305c1..b3e6fd8fd 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 088/103] 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 9dbfeaff3..e768e9689 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 d98864931..64df649d4 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 b9eb118a9..593362e54 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 3e3bf83f7..70480c605 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 089/103] 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 e768e9689..0112f7a3c 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 291b07553..cdfc75876 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 090/103] 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 0112f7a3c..64538d940 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 cdfc75876..5b835a806 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 64df649d4..dd4e097b5 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 b3e6fd8fd..5ee5f3bee 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 091/103] 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 6a2246fa1..910b86dd8 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 66d2f2f7b..ed1ebcc6d 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 120fec830..ad8f54ddb 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 86a862c75..9ed2374ec 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 56ca9d738..35aa28818 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 546ec5445..1ec0ce1cb 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 2cf41f169..3befba708 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 60b589336..e05112932 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 6874949556f62c56f26285fea57275ce0900a2f9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 15 Jun 2018 14:10:28 +0200 Subject: [PATCH 092/103] 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 c06f0ccdb..52c482813 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 64538d940..ae5125d6c 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 5b835a806..32dc1d934 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 dd4e097b5..343df751c 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 5ee5f3bee..bc334d26b 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 093/103] 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 6c9096a3d..a52cdceb6 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 343df751c..706c41675 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 bc334d26b..432a20958 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 094/103] 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 ed1ebcc6d..d1ccf07d5 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 05eaf282f..921841a27 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 83c45b190..745d07fcd 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 34d066730..1879b3082 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 26b9911e0..c6a166397 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 ae5125d6c..f9c10017e 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 32dc1d934..c503d1845 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 97af89fb7..f288ee456 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 95bd2af80..6989da791 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 706c41675..d3aae33e8 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 432a20958..2baec8f9b 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 426395ef2..29f35293b 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 095/103] 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 0ac24664c..e15b8c34c 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 096/103] 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 52c482813..80130fefe 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 70c8e0256..000000000 --- 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 aed0c3534..ce06da853 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 974c554b6..e2f3925fc 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 f34fc4c19..8159a75e2 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 097/103] 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 e8197f9c2..238f59615 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 d3acb639c..4d62640c0 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 098/103] 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 f2d617823..2ed19bd15 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 8ea9d2d6e..e57ec6326 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 099/103] 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 a859200fb..cf4380fdb 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 8d881b094..29d96f72d 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 91b18e945..cb0fc0385 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 e57ec6326..bbb00e445 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 100/103] 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 117af1959..66c1cdd6a 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 566f56abd..886a43f0b 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 5d2d3c1df..63deb228f 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 bbb00e445..136b17af6 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 101/103] 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 886a43f0b..5e0693e94 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 2ed19bd15..aa57f5587 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 238f59615..536f1a2f7 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 0550ceff1..91f2fc827 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 a73461dfa..fa3372476 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 136b17af6..f9aabacc0 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 102/103] 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 cf4380fdb..030353413 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 29d96f72d..273aa2378 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 91f2fc827..d4c34fe44 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 f9aabacc0..d74743055 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 103/103] 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 5e0693e94..fc9f4571f 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 621a85b98..b04851ac1 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 fa3372476..e9dd6e325 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++; /*