From 5024fc4be7b03d6b5b065d42666d1b26a6cc3c1a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 9 May 2018 10:47:04 +0200 Subject: [PATCH] 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: