2019-03-19 12:30:21 +00:00
# include "libslic3r/libslic3r.h"
2019-03-15 11:53:15 +00:00
# include "slic3r/GUI/Gizmos/GLGizmos.hpp"
2018-05-09 08:47:04 +00:00
# include "GLCanvas3D.hpp"
2018-11-13 16:45:44 +00:00
# include "admesh/stl.h"
2019-01-24 18:08:58 +00:00
# include "polypartition.h"
2018-11-26 13:41:58 +00:00
# include "libslic3r/ClipperUtils.hpp"
# include "libslic3r/PrintConfig.hpp"
# include "libslic3r/GCode/PreviewData.hpp"
# include "libslic3r/Geometry.hpp"
2018-11-27 15:55:54 +00:00
# include "libslic3r/Utils.hpp"
2019-01-24 10:30:29 +00:00
# include "libslic3r/Technologies.hpp"
2019-02-05 18:45:52 +00:00
# include "libslic3r/Tesselate.hpp"
2018-11-13 16:45:44 +00:00
# include "slic3r/GUI/3DScene.hpp"
2018-11-22 14:29:59 +00:00
# include "slic3r/GUI/BackgroundSlicingProcess.hpp"
2018-11-13 16:45:44 +00:00
# include "slic3r/GUI/GLShader.hpp"
# include "slic3r/GUI/GUI.hpp"
# include "slic3r/GUI/PresetBundle.hpp"
2019-04-29 12:32:02 +00:00
# include "slic3r/GUI/Tab.hpp"
2018-10-01 13:09:31 +00:00
# include "GUI_App.hpp"
2018-10-05 21:29:15 +00:00
# include "GUI_ObjectList.hpp"
2018-10-04 14:43:10 +00:00
# include "GUI_ObjectManipulation.hpp"
2018-11-26 13:41:58 +00:00
# include "I18N.hpp"
2018-05-14 12:14:19 +00:00
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
# include "slic3r/Utils/RetinaHelper.hpp"
# endif
2018-05-23 13:35:11 +00:00
# include <GL/glew.h>
2018-05-24 11:46:17 +00:00
2018-05-09 08:47:04 +00:00
# include <wx/glcanvas.h>
2018-07-19 11:18:19 +00:00
# include <wx/bitmap.h>
# include <wx/dcmemory.h>
# include <wx/image.h>
# include <wx/settings.h>
2018-10-25 08:36:47 +00:00
# include <wx/tooltip.h>
2018-11-27 15:55:54 +00:00
# include <wx/debug.h>
2019-03-21 13:33:55 +00:00
# include <wx/fontutil.h>
2018-05-09 08:47:04 +00:00
2018-09-12 11:17:47 +00:00
// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx.
2018-11-13 16:45:44 +00:00
# include "libslic3r/Print.hpp"
# include "libslic3r/SLAPrint.hpp"
2018-09-12 09:59:02 +00:00
2018-11-14 07:53:56 +00:00
# include "wxExtensions.hpp"
2018-06-05 12:09:36 +00:00
# include <tbb/parallel_for.h>
# include <tbb/spin_mutex.h>
# include <boost/log/trivial.hpp>
2018-06-11 08:46:32 +00:00
# include <boost/algorithm/string/predicate.hpp>
2018-06-05 12:09:36 +00:00
2018-05-09 08:47:04 +00:00
# include <iostream>
2018-05-24 13:22:53 +00:00
# include <float.h>
2018-06-13 07:12:16 +00:00
# include <algorithm>
2019-01-24 10:30:29 +00:00
# include <cmath>
2019-04-25 09:10:01 +00:00
# if ENABLE_RENDER_STATISTICS
# include <chrono>
# endif // ENABLE_RENDER_STATISTICS
2018-05-09 08:47:04 +00:00
2018-06-01 07:00:30 +00:00
static const float TRACKBALLSIZE = 0.8f ;
2018-05-15 13:38:25 +00:00
static const float GROUND_Z = - 0.02f ;
2018-05-14 10:08:23 +00:00
2018-09-19 12:59:57 +00:00
static const float GIZMO_RESET_BUTTON_HEIGHT = 22.0f ;
static const float GIZMO_RESET_BUTTON_WIDTH = 70.f ;
2018-05-24 11:46:17 +00:00
2018-06-21 06:37:04 +00:00
static const float UNIT_MATRIX [ ] = { 1.0f , 0.0f , 0.0f , 0.0f ,
0.0f , 1.0f , 0.0f , 0.0f ,
0.0f , 0.0f , 1.0f , 0.0f ,
0.0f , 0.0f , 0.0f , 1.0f } ;
2018-12-12 09:38:07 +00:00
static const float DEFAULT_BG_DARK_COLOR [ 3 ] = { 0.478f , 0.478f , 0.478f } ;
static const float DEFAULT_BG_LIGHT_COLOR [ 3 ] = { 0.753f , 0.753f , 0.753f } ;
static const float ERROR_BG_DARK_COLOR [ 3 ] = { 0.478f , 0.192f , 0.039f } ;
static const float ERROR_BG_LIGHT_COLOR [ 3 ] = { 0.753f , 0.192f , 0.039f } ;
2019-03-15 11:53:15 +00:00
//static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } };
2018-12-20 10:14:53 +00:00
2018-05-09 08:47:04 +00:00
namespace Slic3r {
namespace GUI {
2018-05-24 11:46:17 +00:00
Size : : Size ( )
: m_width ( 0 )
, m_height ( 0 )
{
}
2019-01-24 10:30:29 +00:00
Size : : Size ( int width , int height , float scale_factor )
2018-05-24 11:46:17 +00:00
: m_width ( width )
, m_height ( height )
2019-01-24 10:30:29 +00:00
, m_scale_factor ( scale_factor )
2018-05-24 11:46:17 +00:00
{
}
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 ;
}
2019-01-24 10:30:29 +00:00
int Size : : get_scale_factor ( ) const
{
return m_scale_factor ;
}
void Size : : set_scale_factor ( int scale_factor )
{
m_scale_factor = scale_factor ;
}
2019-02-20 14:23:23 +00:00
# if !ENABLE_TEXTURES_FROM_SVG
2018-05-25 12:05:08 +00:00
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 ( ) ;
}
2018-05-25 13:56:14 +00:00
void GLCanvas3D : : Shader : : set_uniform ( const std : : string & name , float value ) const
{
if ( m_shader ! = nullptr )
m_shader - > set_uniform ( name . c_str ( ) , value ) ;
}
2018-06-21 06:37:04 +00:00
void GLCanvas3D : : Shader : : set_uniform ( const std : : string & name , const float * matrix ) const
{
if ( m_shader ! = nullptr )
m_shader - > set_uniform ( name . c_str ( ) , matrix ) ;
}
2018-05-29 12:34:45 +00:00
const GLShader * GLCanvas3D : : Shader : : get_shader ( ) const
2018-05-25 12:05:08 +00:00
{
return m_shader ;
}
void GLCanvas3D : : Shader : : _reset ( )
{
if ( m_shader ! = nullptr )
{
m_shader - > release ( ) ;
delete m_shader ;
m_shader = nullptr ;
}
}
2019-02-20 14:23:23 +00:00
# endif // !ENABLE_TEXTURES_FROM_SVG
2018-05-25 12:05:08 +00:00
2018-05-18 12:08:59 +00:00
GLCanvas3D : : LayersEditing : : LayersEditing ( )
2018-06-01 13:54:41 +00:00
: m_use_legacy_opengl ( false )
2018-05-25 12:05:08 +00:00
, m_enabled ( false )
, m_z_texture_id ( 0 )
2019-01-21 09:06:51 +00:00
, m_model_object ( nullptr )
, m_object_max_z ( 0.f )
2019-01-21 16:02:16 +00:00
, m_slicing_parameters ( nullptr )
2019-01-21 09:06:51 +00:00
, m_layer_height_profile_modified ( false )
2018-06-01 13:54:41 +00:00
, state ( Unknown )
, band_width ( 2.0f )
, strength ( 0.005f )
, last_object_id ( - 1 )
, last_z ( 0.0f )
2019-01-21 09:06:51 +00:00
, last_action ( LAYER_HEIGHT_EDIT_ACTION_INCREASE )
2018-05-18 12:08:59 +00:00
{
}
2018-05-24 11:46:17 +00:00
GLCanvas3D : : LayersEditing : : ~ LayersEditing ( )
{
2018-05-25 12:05:08 +00:00
if ( m_z_texture_id ! = 0 )
{
2019-03-27 13:42:09 +00:00
glsafe ( : : glDeleteTextures ( 1 , & m_z_texture_id ) ) ;
2018-05-25 12:05:08 +00:00
m_z_texture_id = 0 ;
}
2019-01-21 09:06:51 +00:00
delete m_slicing_parameters ;
2018-05-25 12:05:08 +00:00
}
2019-01-24 10:30:29 +00:00
const float GLCanvas3D : : LayersEditing : : THICKNESS_BAR_WIDTH = 70.0f ;
const float GLCanvas3D : : LayersEditing : : THICKNESS_RESET_BUTTON_HEIGHT = 22.0f ;
2018-05-25 12:05:08 +00:00
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 ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glGenTextures ( 1 , ( GLuint * ) & m_z_texture_id ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , m_z_texture_id ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR_MIPMAP_NEAREST ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 1 ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , 0 ) ) ;
2018-05-25 12:05:08 +00:00
return true ;
}
2019-01-22 11:14:26 +00:00
void GLCanvas3D : : LayersEditing : : set_config ( const DynamicPrintConfig * config )
{
m_config = config ;
delete m_slicing_parameters ;
m_slicing_parameters = nullptr ;
m_layers_texture . valid = false ;
}
2019-01-21 09:06:51 +00:00
void GLCanvas3D : : LayersEditing : : select_object ( const Model & model , int object_id )
{
2019-01-21 16:02:16 +00:00
const ModelObject * model_object_new = ( object_id > = 0 ) ? model . objects [ object_id ] : nullptr ;
2019-04-13 12:15:54 +00:00
// Maximum height of an object changes when the object gets rotated or scaled.
// Changing maximum height of an object will invalidate the layer heigth editing profile.
// m_model_object->raw_bounding_box() is cached, therefore it is cheap even if this method is called frequently.
2019-05-04 19:40:58 +00:00
float new_max_z = ( model_object_new = = nullptr ) ? 0.f : model_object_new - > raw_bounding_box ( ) . size ( ) . z ( ) ;
2019-04-13 12:15:54 +00:00
if ( m_model_object ! = model_object_new | | this - > last_object_id ! = object_id | | m_object_max_z ! = new_max_z | |
( model_object_new ! = nullptr & & m_model_object - > id ( ) ! = model_object_new - > id ( ) ) ) {
2019-01-21 09:06:51 +00:00
m_layer_height_profile . clear ( ) ;
m_layer_height_profile_modified = false ;
2019-01-21 16:02:16 +00:00
delete m_slicing_parameters ;
2019-04-13 12:15:54 +00:00
m_slicing_parameters = nullptr ;
2019-01-22 11:14:26 +00:00
m_layers_texture . valid = false ;
2019-04-13 12:15:54 +00:00
this - > last_object_id = object_id ;
m_model_object = model_object_new ;
m_object_max_z = new_max_z ;
2019-01-21 09:06:51 +00:00
}
}
2018-05-25 12:05:08 +00:00
bool GLCanvas3D : : LayersEditing : : is_allowed ( ) const
{
2019-01-21 09:06:51 +00:00
return ! m_use_legacy_opengl & & m_shader . is_initialized ( ) & & m_shader . get_shader ( ) - > shader_program_id > 0 & & m_z_texture_id > 0 ;
2018-05-25 12:05:08 +00:00
}
2018-05-25 14:28:24 +00:00
void GLCanvas3D : : LayersEditing : : set_use_legacy_opengl ( bool use_legacy_opengl )
2018-05-25 12:05:08 +00:00
{
2018-05-25 14:28:24 +00:00
m_use_legacy_opengl = use_legacy_opengl ;
2018-05-24 11:46:17 +00:00
}
2018-05-18 12:08:59 +00:00
bool GLCanvas3D : : LayersEditing : : is_enabled ( ) const
{
return m_enabled ;
}
2018-05-25 12:05:08 +00:00
void GLCanvas3D : : LayersEditing : : set_enabled ( bool enabled )
{
2018-05-25 14:28:24 +00:00
m_enabled = is_allowed ( ) & & enabled ;
2018-05-25 12:05:08 +00:00
}
2019-01-21 09:06:51 +00:00
void GLCanvas3D : : LayersEditing : : render_overlay ( const GLCanvas3D & canvas ) const
2018-05-24 11:46:17 +00:00
{
2018-05-25 13:56:14 +00:00
if ( ! m_enabled )
return ;
2018-05-30 13:18:45 +00:00
const Rect & bar_rect = get_bar_rect_viewport ( canvas ) ;
const Rect & reset_rect = get_reset_rect_viewport ( canvas ) ;
2018-05-25 13:56:14 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_DEPTH_TEST ) ) ;
2018-05-25 13:56:14 +00:00
// The viewport and camera are set to complete view and glOrtho(-$x / 2, $x / 2, -$y / 2, $y / 2, -$depth, $depth),
// where x, y is the window size divided by $self->_zoom.
2019-03-27 13:42:09 +00:00
glsafe ( : : glPushMatrix ( ) ) ;
glsafe ( : : glLoadIdentity ( ) ) ;
2018-05-25 13:56:14 +00:00
2018-05-24 11:46:17 +00:00
_render_tooltip_texture ( canvas , bar_rect , reset_rect ) ;
2018-06-13 07:12:16 +00:00
_render_reset_texture ( reset_rect ) ;
2019-01-21 09:06:51 +00:00
_render_active_object_annotations ( canvas , bar_rect ) ;
_render_profile ( bar_rect ) ;
2018-05-25 13:56:14 +00:00
// Revert the matrices.
2019-03-27 13:42:09 +00:00
glsafe ( : : glPopMatrix ( ) ) ;
2018-05-25 13:56:14 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_DEPTH_TEST ) ) ;
2018-05-24 11:46:17 +00:00
}
2018-05-28 12:10:02 +00:00
float GLCanvas3D : : LayersEditing : : get_cursor_z_relative ( const GLCanvas3D & canvas )
2018-05-28 11:43:29 +00:00
{
2019-04-01 09:08:26 +00:00
const Vec2d mouse_pos = canvas . get_local_mouse_position ( ) ;
2018-05-30 13:18:45 +00:00
const Rect & rect = get_bar_rect_screen ( canvas ) ;
2018-08-17 13:53:43 +00:00
float x = ( float ) mouse_pos ( 0 ) ;
float y = ( float ) mouse_pos ( 1 ) ;
2018-05-30 13:18:45 +00:00
float t = rect . get_top ( ) ;
float b = rect . get_bottom ( ) ;
2018-05-28 11:43:29 +00:00
2018-05-30 13:18:45 +00:00
return ( ( rect . get_left ( ) < = x ) & & ( x < = rect . get_right ( ) ) & & ( t < = y ) & & ( y < = b ) ) ?
2018-05-28 11:43:29 +00:00
// Inside the bar.
( b - y - 1.0f ) / ( b - t - 1.0f ) :
// Outside the bar.
- 1000.0f ;
}
2018-05-28 12:39:59 +00:00
bool GLCanvas3D : : LayersEditing : : bar_rect_contains ( const GLCanvas3D & canvas , float x , float y )
2018-05-25 12:05:08 +00:00
{
2018-05-30 13:18:45 +00:00
const Rect & rect = get_bar_rect_screen ( canvas ) ;
2018-05-28 12:39:59 +00:00
return ( rect . get_left ( ) < = x ) & & ( x < = rect . get_right ( ) ) & & ( rect . get_top ( ) < = y ) & & ( y < = rect . get_bottom ( ) ) ;
2018-05-25 12:05:08 +00:00
}
2018-05-28 12:39:59 +00:00
bool GLCanvas3D : : LayersEditing : : reset_rect_contains ( const GLCanvas3D & canvas , float x , float y )
2018-05-24 11:46:17 +00:00
{
2018-05-30 13:18:45 +00:00
const Rect & rect = get_reset_rect_screen ( canvas ) ;
2018-05-28 12:39:59 +00:00
return ( rect . get_left ( ) < = x ) & & ( x < = rect . get_right ( ) ) & & ( rect . get_top ( ) < = y ) & & ( y < = rect . get_bottom ( ) ) ;
}
2018-05-24 11:46:17 +00:00
2018-05-30 13:18:45 +00:00
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 ( ) ;
2019-01-24 10:30:29 +00:00
return Rect ( w - thickness_bar_width ( canvas ) , 0.0f , w , h - reset_button_height ( canvas ) ) ;
2018-05-30 13:18:45 +00:00
}
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 ( ) ;
2019-01-24 10:30:29 +00:00
return Rect ( w - thickness_bar_width ( canvas ) , h - reset_button_height ( canvas ) , w , h ) ;
2018-05-30 13:18:45 +00:00
}
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 ( ) ;
2019-04-01 08:00:10 +00:00
float zoom = canvas . get_camera ( ) . zoom ;
2018-05-30 13:18:45 +00:00
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
2019-01-24 10:30:29 +00:00
return Rect ( ( half_w - thickness_bar_width ( canvas ) ) * inv_zoom , half_h * inv_zoom , half_w * inv_zoom , ( - half_h + reset_button_height ( canvas ) ) * inv_zoom ) ;
2018-05-30 13:18:45 +00:00
}
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 ( ) ;
2019-04-01 08:00:10 +00:00
float zoom = canvas . get_camera ( ) . zoom ;
2018-05-30 13:18:45 +00:00
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
2019-01-24 10:30:29 +00:00
return Rect ( ( half_w - thickness_bar_width ( canvas ) ) * inv_zoom , ( - half_h + reset_button_height ( canvas ) ) * inv_zoom , half_w * inv_zoom , - half_h * inv_zoom ) ;
2018-05-30 13:18:45 +00:00
}
2018-05-28 12:39:59 +00:00
bool GLCanvas3D : : LayersEditing : : _is_initialized ( ) const
{
return m_shader . is_initialized ( ) ;
2018-05-24 11:46:17 +00:00
}
void GLCanvas3D : : LayersEditing : : _render_tooltip_texture ( const GLCanvas3D & canvas , const Rect & bar_rect , const Rect & reset_rect ) const
{
2019-01-24 10:30:29 +00:00
// TODO: do this with ImGui
2018-06-11 08:46:32 +00:00
if ( m_tooltip_texture . get_id ( ) = = 0 )
2018-05-24 11:46:17 +00:00
{
2018-06-11 08:46:32 +00:00
std : : string filename = resources_dir ( ) + " /icons/variable_layer_height_tooltip.png " ;
2018-06-14 08:37:28 +00:00
if ( ! m_tooltip_texture . load_from_file ( filename , false ) )
2018-05-24 11:46:17 +00:00
return ;
}
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
const float scale = canvas . get_canvas_size ( ) . get_scale_factor ( ) ;
# else
2019-02-04 20:41:10 +00:00
const float scale = canvas . get_wxglcanvas ( ) - > GetContentScaleFactor ( ) ;
2019-01-24 10:30:29 +00:00
# endif
2019-02-04 20:41:10 +00:00
const float width = ( float ) m_tooltip_texture . get_width ( ) * scale ;
const float height = ( float ) m_tooltip_texture . get_height ( ) * scale ;
2019-01-24 10:30:29 +00:00
2019-04-01 08:00:10 +00:00
float zoom = canvas . get_camera ( ) . zoom ;
2018-05-24 11:46:17 +00:00
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 ( ) ;
2019-01-24 10:30:29 +00:00
float l = bar_left - width * inv_zoom - gap ;
2018-05-24 11:46:17 +00:00
float r = bar_left - gap ;
2019-01-24 10:30:29 +00:00
float t = reset_bottom + height * inv_zoom + gap ;
2018-05-24 11:46:17 +00:00
float b = reset_bottom + gap ;
2018-06-13 07:12:16 +00:00
GLTexture : : render_texture ( m_tooltip_texture . get_id ( ) , l , r , b , t ) ;
2018-05-24 11:46:17 +00:00
}
2018-06-13 07:12:16 +00:00
void GLCanvas3D : : LayersEditing : : _render_reset_texture ( const Rect & reset_rect ) const
2018-05-24 11:46:17 +00:00
{
2018-06-11 08:46:32 +00:00
if ( m_reset_texture . get_id ( ) = = 0 )
2018-05-24 11:46:17 +00:00
{
2018-06-11 08:46:32 +00:00
std : : string filename = resources_dir ( ) + " /icons/variable_layer_height_reset.png " ;
2018-06-14 08:37:28 +00:00
if ( ! m_reset_texture . load_from_file ( filename , false ) )
2018-05-24 11:46:17 +00:00
return ;
}
2018-06-13 07:12:16 +00:00
GLTexture : : render_texture ( m_reset_texture . get_id ( ) , reset_rect . get_left ( ) , reset_rect . get_right ( ) , reset_rect . get_bottom ( ) , reset_rect . get_top ( ) ) ;
2018-05-24 11:46:17 +00:00
}
2019-01-21 09:06:51 +00:00
void GLCanvas3D : : LayersEditing : : _render_active_object_annotations ( const GLCanvas3D & canvas , const Rect & bar_rect ) const
2018-05-25 13:56:14 +00:00
{
m_shader . start_using ( ) ;
2019-01-21 09:06:51 +00:00
m_shader . set_uniform ( " z_to_texture_row " , float ( m_layers_texture . cells - 1 ) / ( float ( m_layers_texture . width ) * m_object_max_z ) ) ;
m_shader . set_uniform ( " z_texture_row_to_normalized " , 1.0f / ( float ) m_layers_texture . height ) ;
m_shader . set_uniform ( " z_cursor " , m_object_max_z * this - > get_cursor_z_relative ( canvas ) ) ;
2018-06-01 13:54:41 +00:00
m_shader . set_uniform ( " z_cursor_band_width " , band_width ) ;
2018-06-21 06:37:04 +00:00
// The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
m_shader . set_uniform ( " volume_world_matrix " , UNIT_MATRIX ) ;
2018-05-25 13:56:14 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , m_z_texture_id ) ) ;
2018-05-25 13:56:14 +00:00
// 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 ) ;
2019-01-21 13:41:59 +00:00
: : glNormal3f ( 0.0f , 0.0f , 1.0f ) ;
2018-05-25 13:56:14 +00:00
: : glVertex3f ( l , b , 0.0f ) ;
: : glVertex3f ( r , b , 0.0f ) ;
2019-01-21 09:06:51 +00:00
: : glVertex3f ( r , t , m_object_max_z ) ;
: : glVertex3f ( l , t , m_object_max_z ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnd ( ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , 0 ) ) ;
2018-05-25 13:56:14 +00:00
m_shader . stop_using ( ) ;
}
2019-01-21 09:06:51 +00:00
void GLCanvas3D : : LayersEditing : : _render_profile ( const Rect & bar_rect ) const
2018-05-24 13:17:01 +00:00
{
2019-01-21 09:06:51 +00:00
//FIXME show some kind of legend.
2018-05-24 13:17:01 +00:00
// Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region.
2019-01-21 16:02:16 +00:00
assert ( m_slicing_parameters ! = nullptr ) ;
float scale_x = bar_rect . get_width ( ) / ( float ) ( 1.12 * m_slicing_parameters - > max_layer_height ) ;
float scale_y = bar_rect . get_height ( ) / m_object_max_z ;
float x = bar_rect . get_left ( ) + ( float ) m_slicing_parameters - > layer_height * scale_x ;
2018-05-24 13:17:01 +00:00
// Baseline
2019-03-27 13:42:09 +00:00
glsafe ( : : glColor3f ( 0.0f , 0.0f , 0.0f ) ) ;
2018-05-24 13:17:01 +00:00
: : glBegin ( GL_LINE_STRIP ) ;
2019-01-21 16:02:16 +00:00
: : glVertex2f ( x , bar_rect . get_bottom ( ) ) ;
: : glVertex2f ( x , bar_rect . get_top ( ) ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnd ( ) ) ;
2018-05-24 13:17:01 +00:00
// Curve
2019-03-27 13:42:09 +00:00
glsafe ( : : glColor3f ( 0.0f , 0.0f , 1.0f ) ) ;
2019-01-21 09:06:51 +00:00
: : glBegin ( GL_LINE_STRIP ) ;
for ( unsigned int i = 0 ; i < m_layer_height_profile . size ( ) ; i + = 2 )
2019-01-21 16:02:16 +00:00
: : glVertex2f ( bar_rect . get_left ( ) + ( float ) m_layer_height_profile [ i + 1 ] * scale_x , bar_rect . get_bottom ( ) + ( float ) m_layer_height_profile [ i ] * scale_y ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnd ( ) ) ;
2019-01-21 09:06:51 +00:00
}
2018-05-24 13:17:01 +00:00
2019-01-21 09:06:51 +00:00
void GLCanvas3D : : LayersEditing : : render_volumes ( const GLCanvas3D & canvas , const GLVolumeCollection & volumes ) const
{
assert ( this - > is_allowed ( ) ) ;
assert ( this - > last_object_id ! = - 1 ) ;
GLint shader_id = m_shader . get_shader ( ) - > shader_program_id ;
assert ( shader_id > 0 ) ;
GLint current_program_id ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glGetIntegerv ( GL_CURRENT_PROGRAM , & current_program_id ) ) ;
2019-01-21 09:06:51 +00:00
if ( shader_id > 0 & & shader_id ! = current_program_id )
// The layer editing shader is not yet active. Activate it.
2019-03-27 13:42:09 +00:00
glsafe ( : : glUseProgram ( shader_id ) ) ;
2019-01-21 09:06:51 +00:00
else
// The layer editing shader was already active.
current_program_id = - 1 ;
2019-03-27 13:42:09 +00:00
GLint z_to_texture_row_id = : : glGetUniformLocation ( shader_id , " z_to_texture_row " ) ;
GLint z_texture_row_to_normalized_id = : : glGetUniformLocation ( shader_id , " z_texture_row_to_normalized " ) ;
GLint z_cursor_id = : : glGetUniformLocation ( shader_id , " z_cursor " ) ;
GLint z_cursor_band_width_id = : : glGetUniformLocation ( shader_id , " z_cursor_band_width " ) ;
GLint world_matrix_id = : : glGetUniformLocation ( shader_id , " volume_world_matrix " ) ;
glcheck ( ) ;
2019-01-21 09:06:51 +00:00
if ( z_to_texture_row_id ! = - 1 & & z_texture_row_to_normalized_id ! = - 1 & & z_cursor_id ! = - 1 & & z_cursor_band_width_id ! = - 1 & & world_matrix_id ! = - 1 )
{
const_cast < LayersEditing * > ( this ) - > generate_layer_height_texture ( ) ;
// Uniforms were resolved, go ahead using the layer editing shader.
2019-03-27 13:42:09 +00:00
glsafe ( : : glUniform1f ( z_to_texture_row_id , GLfloat ( m_layers_texture . cells - 1 ) / ( GLfloat ( m_layers_texture . width ) * GLfloat ( m_object_max_z ) ) ) ) ;
glsafe ( : : glUniform1f ( z_texture_row_to_normalized_id , GLfloat ( 1.0f / m_layers_texture . height ) ) ) ;
glsafe ( : : glUniform1f ( z_cursor_id , GLfloat ( m_object_max_z ) * GLfloat ( this - > get_cursor_z_relative ( canvas ) ) ) ) ;
glsafe ( : : glUniform1f ( z_cursor_band_width_id , GLfloat ( this - > band_width ) ) ) ;
2019-01-21 09:06:51 +00:00
// Initialize the layer height texture mapping.
GLsizei w = ( GLsizei ) m_layers_texture . width ;
GLsizei h = ( GLsizei ) m_layers_texture . height ;
GLsizei half_w = w / 2 ;
GLsizei half_h = h / 2 ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , m_z_texture_id ) ) ;
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , w , h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ) ;
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , 1 , GL_RGBA , half_w , half_h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ) ;
glsafe ( : : glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , w , h , GL_RGBA , GL_UNSIGNED_BYTE , m_layers_texture . data . data ( ) ) ) ;
glsafe ( : : glTexSubImage2D ( GL_TEXTURE_2D , 1 , 0 , 0 , half_w , half_h , GL_RGBA , GL_UNSIGNED_BYTE , m_layers_texture . data . data ( ) + m_layers_texture . width * m_layers_texture . height * 4 ) ) ;
2019-01-21 09:06:51 +00:00
for ( const GLVolume * glvolume : volumes . volumes ) {
// Render the object using the layer editing shader and texture.
if ( ! glvolume - > is_active | | glvolume - > composite_id . object_id ! = this - > last_object_id | | glvolume - > is_modifier )
continue ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glUniformMatrix4fv ( world_matrix_id , 1 , GL_FALSE , ( const GLfloat * ) glvolume - > world_matrix ( ) . cast < float > ( ) . data ( ) ) ) ;
2019-01-21 09:06:51 +00:00
glvolume - > render ( ) ;
}
// Revert back to the previous shader.
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
if ( current_program_id > 0 )
2019-03-27 13:42:09 +00:00
glsafe ( : : glUseProgram ( current_program_id ) ) ;
2019-01-21 09:06:51 +00:00
}
else
{
// Something went wrong. Just render the object.
assert ( false ) ;
for ( const GLVolume * glvolume : volumes . volumes ) {
// Render the object using the layer editing shader and texture.
if ( ! glvolume - > is_active | | glvolume - > composite_id . object_id ! = this - > last_object_id | | glvolume - > is_modifier )
continue ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glUniformMatrix4fv ( world_matrix_id , 1 , GL_FALSE , ( const GLfloat * ) glvolume - > world_matrix ( ) . cast < float > ( ) . data ( ) ) ) ;
2019-01-21 09:06:51 +00:00
glvolume - > render ( ) ;
}
}
}
void GLCanvas3D : : LayersEditing : : adjust_layer_height_profile ( )
{
2019-01-21 16:02:16 +00:00
this - > update_slicing_parameters ( ) ;
2019-01-21 09:06:51 +00:00
PrintObject : : update_layer_height_profile ( * m_model_object , * m_slicing_parameters , m_layer_height_profile ) ;
Slic3r : : adjust_layer_height_profile ( * m_slicing_parameters , m_layer_height_profile , this - > last_z , this - > strength , this - > band_width , this - > last_action ) ;
m_layer_height_profile_modified = true ;
m_layers_texture . valid = false ;
}
2019-01-23 13:00:03 +00:00
void GLCanvas3D : : LayersEditing : : reset_layer_height_profile ( GLCanvas3D & canvas )
2019-01-22 11:14:26 +00:00
{
const_cast < ModelObject * > ( m_model_object ) - > layer_height_profile . clear ( ) ;
m_layer_height_profile . clear ( ) ;
m_layers_texture . valid = false ;
2019-01-23 13:00:03 +00:00
canvas . post_event ( SimpleEvent ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS ) ) ;
2019-01-22 11:14:26 +00:00
}
2019-01-21 09:06:51 +00:00
void GLCanvas3D : : LayersEditing : : generate_layer_height_texture ( )
{
2019-01-21 16:02:16 +00:00
this - > update_slicing_parameters ( ) ;
// Always try to update the layer height profile.
2019-01-21 09:06:51 +00:00
bool update = ! m_layers_texture . valid ;
if ( PrintObject : : update_layer_height_profile ( * m_model_object , * m_slicing_parameters , m_layer_height_profile ) ) {
// Initialized to the default value.
m_layer_height_profile_modified = false ;
update = true ;
}
// Update if the layer height profile was changed, or when the texture is not valid.
if ( ! update & & ! m_layers_texture . data . empty ( ) & & m_layers_texture . cells > 0 )
// Texture is valid, don't update.
return ;
if ( m_layers_texture . data . empty ( ) ) {
m_layers_texture . width = 1024 ;
m_layers_texture . height = 1024 ;
m_layers_texture . levels = 2 ;
m_layers_texture . data . assign ( m_layers_texture . width * m_layers_texture . height * 5 , 0 ) ;
}
2019-02-22 09:01:34 +00:00
2019-01-21 09:06:51 +00:00
bool level_of_detail_2nd_level = true ;
m_layers_texture . cells = Slic3r : : generate_layer_height_texture (
* m_slicing_parameters ,
Slic3r : : generate_object_layers ( * m_slicing_parameters , m_layer_height_profile ) ,
m_layers_texture . data . data ( ) , m_layers_texture . height , m_layers_texture . width , level_of_detail_2nd_level ) ;
m_layers_texture . valid = true ;
}
2019-02-22 09:01:34 +00:00
2019-01-21 09:06:51 +00:00
void GLCanvas3D : : LayersEditing : : accept_changes ( GLCanvas3D & canvas )
{
if ( last_object_id > = 0 ) {
if ( m_layer_height_profile_modified ) {
const_cast < ModelObject * > ( m_model_object ) - > layer_height_profile = m_layer_height_profile ;
canvas . post_event ( SimpleEvent ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS ) ) ;
2019-03-04 13:52:08 +00:00
}
2018-06-13 07:12:16 +00:00
}
2019-01-21 09:06:51 +00:00
m_layer_height_profile_modified = false ;
2018-06-13 07:12:16 +00:00
}
2019-01-21 16:02:16 +00:00
void GLCanvas3D : : LayersEditing : : update_slicing_parameters ( )
2018-10-15 09:30:50 +00:00
{
2019-01-21 16:02:16 +00:00
if ( m_slicing_parameters = = nullptr ) {
m_slicing_parameters = new SlicingParameters ( ) ;
2019-04-13 12:15:54 +00:00
* m_slicing_parameters = PrintObject : : slicing_parameters ( * m_config , * m_model_object , m_object_max_z ) ;
2019-01-21 16:02:16 +00:00
}
2018-10-15 09:30:50 +00:00
}
2018-06-13 08:49:59 +00:00
2019-01-24 10:30:29 +00:00
float GLCanvas3D : : LayersEditing : : thickness_bar_width ( const GLCanvas3D & canvas )
2018-06-13 08:49:59 +00:00
{
2019-02-04 20:41:10 +00:00
return
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
2019-02-04 20:41:10 +00:00
canvas . get_canvas_size ( ) . get_scale_factor ( )
2019-02-26 08:56:23 +00:00
# else
2019-02-04 20:41:10 +00:00
canvas . get_wxglcanvas ( ) - > GetContentScaleFactor ( )
2019-01-24 10:30:29 +00:00
# endif
2019-02-04 20:41:10 +00:00
* THICKNESS_BAR_WIDTH ;
2018-06-13 08:49:59 +00:00
}
2018-06-13 07:12:16 +00:00
2019-01-24 10:30:29 +00:00
float GLCanvas3D : : LayersEditing : : reset_button_height ( const GLCanvas3D & canvas )
2018-12-17 12:20:57 +00:00
{
2019-02-04 20:41:10 +00:00
return
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
2019-02-04 20:41:10 +00:00
canvas . get_canvas_size ( ) . get_scale_factor ( )
2019-02-26 08:56:23 +00:00
# else
2019-02-04 20:41:10 +00:00
canvas . get_wxglcanvas ( ) - > GetContentScaleFactor ( )
2019-01-24 10:30:29 +00:00
# endif
2019-02-04 20:41:10 +00:00
* THICKNESS_RESET_BUTTON_HEIGHT ;
2018-12-17 12:20:57 +00:00
}
2019-01-24 10:30:29 +00:00
2018-06-01 13:54:41 +00:00
const Point GLCanvas3D : : Mouse : : Drag : : Invalid_2D_Point ( INT_MAX , INT_MAX ) ;
2018-08-21 15:43:05 +00:00
const Vec3d GLCanvas3D : : Mouse : : Drag : : Invalid_3D_Point ( DBL_MAX , DBL_MAX , DBL_MAX ) ;
2019-01-14 08:29:17 +00:00
const int GLCanvas3D : : Mouse : : Drag : : MoveThresholdPx = 5 ;
2018-05-23 11:56:54 +00:00
2018-06-01 13:54:41 +00:00
GLCanvas3D : : Mouse : : Drag : : Drag ( )
: start_position_2D ( Invalid_2D_Point )
, start_position_3D ( Invalid_3D_Point )
2018-06-21 06:37:04 +00:00
, move_volume_idx ( - 1 )
2019-01-14 08:29:17 +00:00
, move_requires_threshold ( false )
, move_start_threshold_position_2D ( Invalid_2D_Point )
2018-10-08 12:02:12 +00:00
{
}
2018-06-01 13:54:41 +00:00
GLCanvas3D : : Mouse : : Mouse ( )
: dragging ( false )
, position ( DBL_MAX , DBL_MAX )
2018-11-15 10:38:40 +00:00
, scene_position ( DBL_MAX , DBL_MAX , DBL_MAX )
2019-04-26 12:07:46 +00:00
, ignore_left_up ( false )
2019-02-26 08:56:23 +00:00
{
}
2019-03-01 10:00:34 +00:00
const unsigned char GLCanvas3D : : WarningTexture : : Background_Color [ 3 ] = { 120 , 120 , 120 } ; //{ 9, 91, 134 };
2018-07-19 11:18:19 +00:00
const unsigned char GLCanvas3D : : WarningTexture : : Opacity = 255 ;
2018-07-31 12:20:16 +00:00
GLCanvas3D : : WarningTexture : : WarningTexture ( )
: GUI : : GLTexture ( )
, m_original_width ( 0 )
, m_original_height ( 0 )
{
}
2019-02-20 11:09:45 +00:00
void GLCanvas3D : : WarningTexture : : activate ( WarningTexture : : Warning warning , bool state , const GLCanvas3D & canvas )
{
auto it = std : : find ( m_warnings . begin ( ) , m_warnings . end ( ) , warning ) ;
if ( state ) {
if ( it ! = m_warnings . end ( ) ) // this warning is already set to be shown
return ;
m_warnings . push_back ( warning ) ;
std : : sort ( m_warnings . begin ( ) , m_warnings . end ( ) ) ;
}
else {
if ( it = = m_warnings . end ( ) ) // deactivating something that is not active is an easy task
return ;
m_warnings . erase ( it ) ;
if ( m_warnings . empty ( ) ) { // nothing remains to be shown
reset ( ) ;
2019-04-16 10:11:48 +00:00
m_msg_text = " " ; // save information for rescaling
2019-02-20 11:09:45 +00:00
return ;
}
}
// Look at the end of our vector and generate proper texture.
std : : string text ;
2019-03-01 10:00:34 +00:00
bool red_colored = false ;
2019-02-20 11:09:45 +00:00
switch ( m_warnings . back ( ) ) {
2019-05-10 09:44:21 +00:00
case ObjectOutside : text = L ( " An object outside the print area was detected " ) ; break ;
case ToolpathOutside : text = L ( " A toolpath outside the print area was detected " ) ; break ;
2019-05-10 09:47:11 +00:00
case SlaSupportsOutside : text = L ( " SLA supports outside the print area were detected " ) ; break ;
2019-02-20 11:09:45 +00:00
case SomethingNotShown : text = L ( " Some objects are not visible when editing supports " ) ; break ;
2019-03-01 10:00:34 +00:00
case ObjectClashed : {
2019-05-10 09:44:21 +00:00
text = L ( " An object outside the print area was detected \n "
" Resolve the current problem to continue slicing " ) ;
2019-03-01 10:00:34 +00:00
red_colored = true ;
break ;
}
2019-02-20 11:09:45 +00:00
}
2019-03-01 10:00:34 +00:00
_generate ( text , canvas , red_colored ) ; // GUI::GLTexture::reset() is called at the beginning of generate(...)
2019-04-16 10:11:48 +00:00
// save information for rescaling
m_msg_text = text ;
m_is_colored_red = red_colored ;
2019-02-20 11:09:45 +00:00
}
2019-03-21 13:33:55 +00:00
# ifdef __WXMSW__
static bool is_font_cleartype ( const wxFont & font )
{
// Native font description: on MSW, it is a version number plus the content of LOGFONT, separated by semicolon.
wxString font_desc = font . GetNativeFontInfoDesc ( ) ;
// Find the quality field.
wxString sep ( " ; " ) ;
size_t startpos = 0 ;
for ( size_t i = 0 ; i < 12 ; + + i )
startpos = font_desc . find ( sep , startpos + 1 ) ;
+ + startpos ;
size_t endpos = font_desc . find ( sep , startpos ) ;
int quality = wxAtoi ( font_desc ( startpos , endpos - startpos ) ) ;
return quality = = CLEARTYPE_QUALITY ;
}
// ClearType produces renders, which are difficult to convert into an alpha blended OpenGL texture.
// Therefore it is better to disable it, though Vojtech found out, that the font returned with ClearType
// disabled is signifcantly thicker than the default ClearType font.
// This function modifies the font provided.
static void msw_disable_cleartype ( wxFont & font )
{
// Native font description: on MSW, it is a version number plus the content of LOGFONT, separated by semicolon.
wxString font_desc = font . GetNativeFontInfoDesc ( ) ;
// Find the quality field.
wxString sep ( " ; " ) ;
size_t startpos_weight = 0 ;
for ( size_t i = 0 ; i < 5 ; + + i )
startpos_weight = font_desc . find ( sep , startpos_weight + 1 ) ;
+ + startpos_weight ;
size_t endpos_weight = font_desc . find ( sep , startpos_weight ) ;
// Parse the weight field.
unsigned int weight = atoi ( font_desc ( startpos_weight , endpos_weight - startpos_weight ) ) ;
size_t startpos = endpos_weight ;
for ( size_t i = 0 ; i < 6 ; + + i )
startpos = font_desc . find ( sep , startpos + 1 ) ;
+ + startpos ;
size_t endpos = font_desc . find ( sep , startpos ) ;
int quality = wxAtoi ( font_desc ( startpos , endpos - startpos ) ) ;
if ( quality = = CLEARTYPE_QUALITY ) {
// Replace the weight with a smaller value to compensate the weight of non ClearType font.
wxString sweight = std : : to_string ( weight * 2 / 4 ) ;
size_t len_weight = endpos_weight - startpos_weight ;
wxString squality = std : : to_string ( ANTIALIASED_QUALITY ) ;
font_desc . replace ( startpos_weight , len_weight , sweight ) ;
font_desc . replace ( startpos + sweight . size ( ) - len_weight , endpos - startpos , squality ) ;
font . SetNativeFontInfo ( font_desc ) ;
wxString font_desc2 = font . GetNativeFontInfoDesc ( ) ;
}
wxString font_desc2 = font . GetNativeFontInfoDesc ( ) ;
}
# endif /* __WXMSW__ */
bool GLCanvas3D : : WarningTexture : : _generate ( const std : : string & msg_utf8 , const GLCanvas3D & canvas , const bool red_colored /* = false*/ )
2018-07-19 11:18:19 +00:00
{
reset ( ) ;
2019-03-21 13:33:55 +00:00
if ( msg_utf8 . empty ( ) )
2018-07-19 11:18:19 +00:00
return false ;
2019-05-09 17:24:21 +00:00
wxString msg = _ ( msg_utf8 ) ;
2019-03-21 13:33:55 +00:00
2018-07-19 11:18:19 +00:00
wxMemoryDC memDC ;
2019-04-26 11:07:31 +00:00
# ifdef __WXMSW__
// set scaled application normal font as default font
wxFont font = wxGetApp ( ) . normal_font ( ) ;
# else
2018-07-19 11:18:19 +00:00
// select default font
2019-01-24 10:30:29 +00:00
const float scale = canvas . get_canvas_size ( ) . get_scale_factor ( ) ;
2019-04-26 11:07:31 +00:00
wxFont font = wxSystemSettings : : GetFont ( wxSYS_DEFAULT_GUI_FONT ) . Scale ( scale ) ;
# endif
2019-04-16 10:11:48 +00:00
2018-07-31 12:20:16 +00:00
font . MakeLarger ( ) ;
2018-07-31 13:32:16 +00:00
font . MakeBold ( ) ;
2018-07-31 12:20:16 +00:00
memDC . SetFont ( font ) ;
2018-07-19 11:18:19 +00:00
// calculates texture size
wxCoord w , h ;
2019-03-01 10:00:34 +00:00
memDC . GetMultiLineTextExtent ( msg , & w , & h ) ;
2018-07-31 12:20:16 +00:00
m_original_width = ( int ) w ;
m_original_height = ( int ) h ;
2019-03-21 13:33:55 +00:00
m_width = ( int ) next_highest_power_of_2 ( ( uint32_t ) w ) ;
m_height = ( int ) next_highest_power_of_2 ( ( uint32_t ) h ) ;
2018-07-19 11:18:19 +00:00
// generates bitmap
wxBitmap bitmap ( m_width , m_height ) ;
memDC . SelectObject ( bitmap ) ;
2019-03-21 13:33:55 +00:00
memDC . SetBackground ( wxBrush ( * wxBLACK ) ) ;
2018-07-19 11:18:19 +00:00
memDC . Clear ( ) ;
// draw message
2019-03-21 13:33:55 +00:00
memDC . SetTextForeground ( * wxRED ) ;
memDC . DrawLabel ( msg , wxRect ( 0 , 0 , m_original_width , m_original_height ) , wxALIGN_CENTER ) ;
2018-07-19 11:18:19 +00:00
memDC . SelectObject ( wxNullBitmap ) ;
// Convert the bitmap into a linear data ready to be loaded into the GPU.
wxImage image = bitmap . ConvertToImage ( ) ;
// prepare buffer
std : : vector < unsigned char > data ( 4 * m_width * m_height , 0 ) ;
2019-03-21 13:33:55 +00:00
const unsigned char * src = image . GetData ( ) ;
2018-07-19 11:18:19 +00:00
for ( int h = 0 ; h < m_height ; + + h )
{
2019-03-21 13:33:55 +00:00
unsigned char * dst = data . data ( ) + 4 * h * m_width ;
2018-07-19 11:18:19 +00:00
for ( int w = 0 ; w < m_width ; + + w )
{
2019-03-21 13:33:55 +00:00
* dst + + = 255 ;
if ( red_colored ) {
* dst + + = 72 ; // 204
* dst + + = 65 ; // 204
} else {
* dst + + = 255 ;
* dst + + = 255 ;
}
* dst + + = ( unsigned char ) std : : min < int > ( 255 , * src ) ;
src + = 3 ;
2018-07-19 11:18:19 +00:00
}
}
// sends buffer to gpu
2019-03-27 13:42:09 +00:00
glsafe ( : : glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ) ;
glsafe ( : : glGenTextures ( 1 , & m_id ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , ( GLuint ) m_id ) ) ;
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ( ) ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 0 ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , 0 ) ) ;
2018-07-19 11:18:19 +00:00
return true ;
}
2018-07-31 12:20:16 +00:00
void GLCanvas3D : : WarningTexture : : render ( const GLCanvas3D & canvas ) const
{
2019-02-20 11:09:45 +00:00
if ( m_warnings . empty ( ) )
return ;
2018-07-31 12:32:59 +00:00
if ( ( m_id > 0 ) & & ( m_original_width > 0 ) & & ( m_original_height > 0 ) & & ( m_width > 0 ) & & ( m_height > 0 ) )
2018-07-31 12:20:16 +00:00
{
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_DEPTH_TEST ) ) ;
glsafe ( : : glPushMatrix ( ) ) ;
glsafe ( : : glLoadIdentity ( ) ) ;
2018-07-31 12:20:16 +00:00
const Size & cnv_size = canvas . get_canvas_size ( ) ;
2019-04-01 08:00:10 +00:00
float zoom = canvas . get_camera ( ) . zoom ;
2018-07-31 12:20:16 +00:00
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
float left = ( - 0.5f * ( float ) m_original_width ) * inv_zoom ;
float top = ( - 0.5f * ( float ) cnv_size . get_height ( ) + ( float ) m_original_height + 2.0f ) * inv_zoom ;
float right = left + ( float ) m_original_width * inv_zoom ;
float bottom = top - ( float ) m_original_height * inv_zoom ;
float uv_left = 0.0f ;
float uv_top = 0.0f ;
float uv_right = ( float ) m_original_width / ( float ) m_width ;
float uv_bottom = ( float ) m_original_height / ( float ) m_height ;
GLTexture : : Quad_UVs uvs ;
uvs . left_top = { uv_left , uv_top } ;
uvs . left_bottom = { uv_left , uv_bottom } ;
uvs . right_bottom = { uv_right , uv_bottom } ;
uvs . right_top = { uv_right , uv_top } ;
GLTexture : : render_sub_texture ( m_id , left , right , bottom , top , uvs ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glPopMatrix ( ) ) ;
glsafe ( : : glEnable ( GL_DEPTH_TEST ) ) ;
2018-07-31 12:20:16 +00:00
}
}
2019-04-26 11:07:31 +00:00
void GLCanvas3D : : WarningTexture : : msw_rescale ( const GLCanvas3D & canvas )
2019-04-16 10:11:48 +00:00
{
if ( m_msg_text . empty ( ) )
return ;
_generate ( m_msg_text , canvas , m_is_colored_red ) ;
}
2018-07-19 11:18:19 +00:00
const unsigned char GLCanvas3D : : LegendTexture : : Squares_Border_Color [ 3 ] = { 64 , 64 , 64 } ;
2018-12-13 10:13:58 +00:00
const unsigned char GLCanvas3D : : LegendTexture : : Default_Background_Color [ 3 ] = { ( unsigned char ) ( DEFAULT_BG_LIGHT_COLOR [ 0 ] * 255.0f ) , ( unsigned char ) ( DEFAULT_BG_LIGHT_COLOR [ 1 ] * 255.0f ) , ( unsigned char ) ( DEFAULT_BG_LIGHT_COLOR [ 2 ] * 255.0f ) } ;
const unsigned char GLCanvas3D : : LegendTexture : : Error_Background_Color [ 3 ] = { ( unsigned char ) ( ERROR_BG_LIGHT_COLOR [ 0 ] * 255.0f ) , ( unsigned char ) ( ERROR_BG_LIGHT_COLOR [ 1 ] * 255.0f ) , ( unsigned char ) ( ERROR_BG_LIGHT_COLOR [ 2 ] * 255.0f ) } ;
2018-07-19 11:18:19 +00:00
const unsigned char GLCanvas3D : : LegendTexture : : Opacity = 255 ;
2018-07-31 12:32:59 +00:00
GLCanvas3D : : LegendTexture : : LegendTexture ( )
: GUI : : GLTexture ( )
, m_original_width ( 0 )
, m_original_height ( 0 )
{
}
2019-01-29 14:11:29 +00:00
void GLCanvas3D : : LegendTexture : : fill_color_print_legend_values ( const GCodePreviewData & preview_data , const GLCanvas3D & canvas ,
std : : vector < std : : pair < double , double > > & cp_legend_values )
2018-07-19 11:18:19 +00:00
{
2018-11-28 11:32:43 +00:00
if ( preview_data . extrusion . view_type = = GCodePreviewData : : Extrusion : : ColorPrint )
{
2019-01-30 13:45:18 +00:00
auto & config = wxGetApp ( ) . preset_bundle - > project_config ;
2018-11-28 11:32:43 +00:00
const std : : vector < double > & color_print_values = config . option < ConfigOptionFloats > ( " colorprint_heights " ) - > values ;
const int values_cnt = color_print_values . size ( ) ;
if ( values_cnt > 0 ) {
auto print_zs = canvas . get_current_print_zs ( true ) ;
auto z = 0 ;
for ( auto i = 0 ; i < values_cnt ; + + i )
{
double prev_z = - 1.0 ;
for ( z ; z < print_zs . size ( ) ; + + z )
if ( fabs ( color_print_values [ i ] - print_zs [ z ] ) < EPSILON ) {
prev_z = print_zs [ z - 1 ] ;
break ;
}
if ( prev_z < 0 )
continue ;
cp_legend_values . push_back ( std : : pair < double , double > ( prev_z , color_print_values [ i ] ) ) ;
}
}
}
2019-01-29 14:11:29 +00:00
}
2019-03-05 12:57:41 +00:00
bool GLCanvas3D : : LegendTexture : : generate ( const GCodePreviewData & preview_data , const std : : vector < float > & tool_colors , const GLCanvas3D & canvas )
2019-01-29 14:11:29 +00:00
{
reset ( ) ;
// collects items to render
auto title = _ ( preview_data . get_legend_title ( ) ) ;
std : : vector < std : : pair < double , double > > cp_legend_values ;
fill_color_print_legend_values ( preview_data , canvas , cp_legend_values ) ;
const GCodePreviewData : : LegendItemsList & items = preview_data . get_legend_items ( tool_colors , cp_legend_values ) ;
2018-07-19 11:18:19 +00:00
unsigned int items_count = ( unsigned int ) items . size ( ) ;
if ( items_count = = 0 )
// nothing to render, return
return false ;
wxMemoryDC memDC ;
2018-12-13 10:13:58 +00:00
wxMemoryDC mask_memDC ;
2019-01-24 10:30:29 +00:00
// calculate scaling
2019-02-21 11:19:00 +00:00
const float scale_gl = canvas . get_canvas_size ( ) . get_scale_factor ( ) ;
const float scale = scale_gl * wxGetApp ( ) . em_unit ( ) * 0.1 ; // get scale from em_unit() value, because of get_scale_factor() return 1
2019-01-24 10:30:29 +00:00
const int scaled_square = std : : floor ( ( float ) Px_Square * scale ) ;
const int scaled_title_offset = Px_Title_Offset * scale ;
const int scaled_text_offset = Px_Text_Offset * scale ;
const int scaled_square_contour = Px_Square_Contour * scale ;
const int scaled_border = Px_Border * scale ;
2019-03-21 13:33:55 +00:00
# ifdef __WXMSW__
2019-04-26 11:07:31 +00:00
// set scaled application normal font as default font
wxFont font = wxGetApp ( ) . normal_font ( ) ;
2019-03-21 13:33:55 +00:00
// Disabling ClearType works, but the font returned is very different (much thicker) from the default.
// msw_disable_cleartype(font);
bool cleartype = is_font_cleartype ( font ) ;
# else
2019-04-26 11:07:31 +00:00
// select default font
wxFont font = wxSystemSettings : : GetFont ( wxSYS_DEFAULT_GUI_FONT ) . Scale ( scale_gl ) ;
2019-03-21 13:33:55 +00:00
bool cleartype = false ;
# endif /* __WXMSW__ */
2019-01-24 10:30:29 +00:00
memDC . SetFont ( font ) ;
mask_memDC . SetFont ( font ) ;
2018-07-19 11:18:19 +00:00
// calculates texture size
wxCoord w , h ;
memDC . GetTextExtent ( title , & w , & h ) ;
int title_width = ( int ) w ;
int title_height = ( int ) h ;
int max_text_width = 0 ;
int max_text_height = 0 ;
for ( const GCodePreviewData : : LegendItem & item : items )
{
memDC . GetTextExtent ( GUI : : from_u8 ( item . text ) , & w , & h ) ;
max_text_width = std : : max ( max_text_width , ( int ) w ) ;
max_text_height = std : : max ( max_text_height , ( int ) h ) ;
}
2019-01-24 10:30:29 +00:00
m_original_width = std : : max ( 2 * scaled_border + title_width , 2 * ( scaled_border + scaled_square_contour ) + scaled_square + scaled_text_offset + max_text_width ) ;
m_original_height = 2 * ( scaled_border + scaled_square_contour ) + title_height + scaled_title_offset + items_count * scaled_square ;
2018-07-19 11:18:19 +00:00
if ( items_count > 1 )
2019-01-24 10:30:29 +00:00
m_original_height + = ( items_count - 1 ) * scaled_square_contour ;
2018-07-31 12:32:59 +00:00
2019-03-21 13:33:55 +00:00
m_width = ( int ) next_highest_power_of_2 ( ( uint32_t ) m_original_width ) ;
m_height = ( int ) next_highest_power_of_2 ( ( uint32_t ) m_original_height ) ;
2018-07-19 11:18:19 +00:00
// generates bitmap
wxBitmap bitmap ( m_width , m_height ) ;
2018-12-13 10:13:58 +00:00
wxBitmap mask ( m_width , m_height ) ;
2018-07-19 11:18:19 +00:00
memDC . SelectObject ( bitmap ) ;
2018-12-13 10:13:58 +00:00
mask_memDC . SelectObject ( mask ) ;
2019-03-05 12:57:41 +00:00
memDC . SetBackground ( wxBrush ( * wxBLACK ) ) ;
2018-12-13 10:13:58 +00:00
mask_memDC . SetBackground ( wxBrush ( * wxBLACK ) ) ;
2018-07-19 11:18:19 +00:00
memDC . Clear ( ) ;
2018-12-13 10:13:58 +00:00
mask_memDC . Clear ( ) ;
2018-07-19 11:18:19 +00:00
// draw title
2019-03-05 12:57:41 +00:00
memDC . SetTextForeground ( * wxWHITE ) ;
2019-03-21 13:33:55 +00:00
mask_memDC . SetTextForeground ( * wxRED ) ;
2018-12-13 10:13:58 +00:00
2019-01-24 10:30:29 +00:00
int title_x = scaled_border ;
int title_y = scaled_border ;
2018-07-19 11:18:19 +00:00
memDC . DrawText ( title , title_x , title_y ) ;
2018-12-13 10:13:58 +00:00
mask_memDC . DrawText ( title , title_x , title_y ) ;
2018-07-19 11:18:19 +00:00
// draw icons contours as background
2019-01-24 10:30:29 +00:00
int squares_contour_x = scaled_border ;
int squares_contour_y = scaled_border + title_height + scaled_title_offset ;
int squares_contour_width = scaled_square + 2 * scaled_square_contour ;
int squares_contour_height = items_count * scaled_square + 2 * scaled_square_contour ;
2018-07-19 11:18:19 +00:00
if ( items_count > 1 )
2019-01-24 10:30:29 +00:00
squares_contour_height + = ( items_count - 1 ) * scaled_square_contour ;
2018-07-19 11:18:19 +00:00
wxColour color ( Squares_Border_Color [ 0 ] , Squares_Border_Color [ 1 ] , Squares_Border_Color [ 2 ] ) ;
wxPen pen ( color ) ;
wxBrush brush ( color ) ;
memDC . SetPen ( pen ) ;
memDC . SetBrush ( brush ) ;
memDC . DrawRectangle ( wxRect ( squares_contour_x , squares_contour_y , squares_contour_width , squares_contour_height ) ) ;
// draw items (colored icon + text)
2019-01-24 10:30:29 +00:00
int icon_x = squares_contour_x + scaled_square_contour ;
2018-07-19 11:18:19 +00:00
int icon_x_inner = icon_x + 1 ;
2019-01-24 10:30:29 +00:00
int icon_y = squares_contour_y + scaled_square_contour ;
int icon_y_step = scaled_square + scaled_square_contour ;
2018-07-19 11:18:19 +00:00
2019-01-24 10:30:29 +00:00
int text_x = icon_x + scaled_square + scaled_text_offset ;
int text_y_offset = ( scaled_square - max_text_height ) / 2 ;
2018-07-19 11:18:19 +00:00
2019-01-24 10:30:29 +00:00
int px_inner_square = scaled_square - 2 ;
2018-07-19 11:18:19 +00:00
for ( const GCodePreviewData : : LegendItem & item : items )
{
// draw darker icon perimeter
const std : : vector < unsigned char > & item_color_bytes = item . color . as_bytes ( ) ;
wxImage : : HSVValue dark_hsv = wxImage : : RGBtoHSV ( wxImage : : RGBValue ( item_color_bytes [ 0 ] , item_color_bytes [ 1 ] , item_color_bytes [ 2 ] ) ) ;
dark_hsv . value * = 0.75 ;
wxImage : : RGBValue dark_rgb = wxImage : : HSVtoRGB ( dark_hsv ) ;
color . Set ( dark_rgb . red , dark_rgb . green , dark_rgb . blue , item_color_bytes [ 3 ] ) ;
pen . SetColour ( color ) ;
brush . SetColour ( color ) ;
memDC . SetPen ( pen ) ;
memDC . SetBrush ( brush ) ;
2019-01-24 10:30:29 +00:00
memDC . DrawRectangle ( wxRect ( icon_x , icon_y , scaled_square , scaled_square ) ) ;
2018-07-19 11:18:19 +00:00
// draw icon interior
color . Set ( item_color_bytes [ 0 ] , item_color_bytes [ 1 ] , item_color_bytes [ 2 ] , item_color_bytes [ 3 ] ) ;
pen . SetColour ( color ) ;
brush . SetColour ( color ) ;
memDC . SetPen ( pen ) ;
memDC . SetBrush ( brush ) ;
memDC . DrawRectangle ( wxRect ( icon_x_inner , icon_y + 1 , px_inner_square , px_inner_square ) ) ;
// draw text
2018-12-13 10:13:58 +00:00
mask_memDC . DrawText ( GUI : : from_u8 ( item . text ) , text_x , icon_y + text_y_offset ) ;
2018-07-19 11:18:19 +00:00
// update y
icon_y + = icon_y_step ;
}
memDC . SelectObject ( wxNullBitmap ) ;
2018-12-13 10:13:58 +00:00
mask_memDC . SelectObject ( wxNullBitmap ) ;
2018-07-19 11:18:19 +00:00
// Convert the bitmap into a linear data ready to be loaded into the GPU.
wxImage image = bitmap . ConvertToImage ( ) ;
2018-12-13 10:13:58 +00:00
wxImage mask_image = mask . ConvertToImage ( ) ;
2018-07-19 11:18:19 +00:00
// prepare buffer
std : : vector < unsigned char > data ( 4 * m_width * m_height , 0 ) ;
2019-03-21 13:33:55 +00:00
const unsigned char * src_image = image . GetData ( ) ;
const unsigned char * src_mask = mask_image . GetData ( ) ;
for ( int h = 0 ; h < m_height ; + + h )
2018-07-19 11:18:19 +00:00
{
int hh = h * m_width ;
unsigned char * px_ptr = data . data ( ) + 4 * hh ;
for ( int w = 0 ; w < m_width ; + + w )
{
2019-03-21 13:33:55 +00:00
if ( w > = squares_contour_x & & w < squares_contour_x + squares_contour_width & &
h > = squares_contour_y & & h < squares_contour_y + squares_contour_height ) {
// Color palette, use the color verbatim.
* px_ptr + + = * src_image + + ;
* px_ptr + + = * src_image + + ;
* px_ptr + + = * src_image + + ;
* px_ptr + + = 255 ;
} else {
// Text or background
unsigned char alpha = * src_mask ;
// Compensate the white color for the 50% opacity reduction at the character edges.
//unsigned char color = (unsigned char)floor(alpha * 255.f / (128.f + 0.5f * alpha));
unsigned char color = alpha ;
* px_ptr + + = color ;
* px_ptr + + = color ; // *src_mask ++;
* px_ptr + + = color ; // *src_mask ++;
* px_ptr + + = 128 + ( alpha / 2 ) ; // (alpha > 0) ? 255 : 128;
src_image + = 3 ;
}
src_mask + = 3 ;
2018-07-19 11:18:19 +00:00
}
}
// sends buffer to gpu
2019-03-27 13:42:09 +00:00
glsafe ( : : glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ) ;
glsafe ( : : glGenTextures ( 1 , & m_id ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , ( GLuint ) m_id ) ) ;
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ( ) ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 0 ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , 0 ) ) ;
2018-07-19 11:18:19 +00:00
return true ;
}
2018-07-31 12:32:59 +00:00
void GLCanvas3D : : LegendTexture : : render ( const GLCanvas3D & canvas ) const
{
if ( ( m_id > 0 ) & & ( m_original_width > 0 ) & & ( m_original_height > 0 ) & & ( m_width > 0 ) & & ( m_height > 0 ) )
{
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_DEPTH_TEST ) ) ;
glsafe ( : : glPushMatrix ( ) ) ;
glsafe ( : : glLoadIdentity ( ) ) ;
2018-07-31 12:32:59 +00:00
const Size & cnv_size = canvas . get_canvas_size ( ) ;
2019-04-01 08:00:10 +00:00
float zoom = canvas . get_camera ( ) . zoom ;
2018-07-31 12:32:59 +00:00
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
float left = ( - 0.5f * ( float ) cnv_size . get_width ( ) ) * inv_zoom ;
float top = ( 0.5f * ( float ) cnv_size . get_height ( ) ) * inv_zoom ;
float right = left + ( float ) m_original_width * inv_zoom ;
float bottom = top - ( float ) m_original_height * inv_zoom ;
float uv_left = 0.0f ;
float uv_top = 0.0f ;
float uv_right = ( float ) m_original_width / ( float ) m_width ;
float uv_bottom = ( float ) m_original_height / ( float ) m_height ;
GLTexture : : Quad_UVs uvs ;
uvs . left_top = { uv_left , uv_top } ;
uvs . left_bottom = { uv_left , uv_bottom } ;
uvs . right_bottom = { uv_right , uv_bottom } ;
uvs . right_top = { uv_right , uv_top } ;
GLTexture : : render_sub_texture ( m_id , left , right , bottom , top , uvs ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glPopMatrix ( ) ) ;
glsafe ( : : glEnable ( GL_DEPTH_TEST ) ) ;
2018-07-31 12:32:59 +00:00
}
}
2018-12-06 09:38:19 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_INIT , SimpleEvent ) ;
2018-10-25 10:10:35 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS , SimpleEvent ) ;
2018-10-16 14:04:19 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_OBJECT_SELECT , SimpleEvent ) ;
2018-10-03 09:34:39 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_RIGHT_CLICK , Vec2dEvent ) ;
wxDEFINE_EVENT ( EVT_GLCANVAS_REMOVE_OBJECT , SimpleEvent ) ;
wxDEFINE_EVENT ( EVT_GLCANVAS_ARRANGE , SimpleEvent ) ;
2019-02-03 13:06:13 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_SELECT_ALL , SimpleEvent ) ;
2018-12-19 13:01:13 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_QUESTION_MARK , SimpleEvent ) ;
2018-10-18 13:09:41 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_INCREASE_INSTANCES , Event < int > ) ;
2018-10-09 15:14:59 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_INSTANCE_MOVED , SimpleEvent ) ;
2019-01-03 10:24:03 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_INSTANCE_ROTATED , SimpleEvent ) ;
wxDEFINE_EVENT ( EVT_GLCANVAS_INSTANCE_SCALED , SimpleEvent ) ;
2018-10-03 09:34:39 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_WIPETOWER_MOVED , Vec3dEvent ) ;
2019-04-26 13:34:26 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_WIPETOWER_ROTATED , Vec3dEvent ) ;
2018-10-03 09:34:39 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , Event < bool > ) ;
wxDEFINE_EVENT ( EVT_GLCANVAS_UPDATE_GEOMETRY , Vec3dsEvent < 2 > ) ;
2018-11-23 11:47:32 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED , SimpleEvent ) ;
2019-02-19 14:15:27 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_UPDATE_BED_SHAPE , SimpleEvent ) ;
2019-02-21 10:54:18 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_TAB , SimpleEvent ) ;
2019-03-27 13:16:38 +00:00
wxDEFINE_EVENT ( EVT_GLCANVAS_RESETGIZMOS , SimpleEvent ) ;
2018-10-03 09:34:39 +00:00
2019-03-07 10:49:00 +00:00
GLCanvas3D : : GLCanvas3D ( wxGLCanvas * canvas , Bed3D & bed , Camera & camera , GLToolbar & view_toolbar )
2018-05-09 08:47:04 +00:00
: m_canvas ( canvas )
2018-06-27 09:31:11 +00:00
, m_context ( nullptr )
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
, m_retina_helper ( nullptr )
# endif
2018-11-27 15:55:54 +00:00
, m_in_render ( false )
2019-03-07 10:49:00 +00:00
, m_bed ( bed )
, m_camera ( camera )
, m_view_toolbar ( view_toolbar )
2019-02-26 11:56:13 +00:00
# if ENABLE_SVG_ICONS
, m_toolbar ( GLToolbar : : Normal , " Top " )
# else
2018-12-17 09:55:14 +00:00
, m_toolbar ( GLToolbar : : Normal )
2019-02-26 11:56:13 +00:00
# endif // ENABLE_SVG_ICONS
2018-11-27 13:50:57 +00:00
, m_use_clipping_planes ( false )
2018-12-18 09:40:53 +00:00
, m_sidebar_field ( " " )
2018-05-23 09:14:49 +00:00
, m_config ( nullptr )
2018-11-22 14:29:59 +00:00
, m_process ( nullptr )
2018-06-07 09:18:28 +00:00
, m_model ( nullptr )
2018-05-09 08:47:04 +00:00
, m_dirty ( true )
2018-06-05 10:24:26 +00:00
, m_initialized ( false )
2018-06-04 10:26:39 +00:00
, m_use_VBOs ( false )
2018-05-14 12:14:19 +00:00
, m_apply_zoom_to_volumes_filter ( false )
2018-05-21 12:57:43 +00:00
, m_legend_texture_enabled ( false )
2018-05-22 07:02:42 +00:00
, m_picking_enabled ( false )
2018-05-31 11:51:50 +00:00
, m_moving_enabled ( false )
2018-07-27 07:38:39 +00:00
, m_dynamic_background_enabled ( false )
2018-05-23 13:35:11 +00:00
, m_multisample_allowed ( false )
2018-10-08 12:02:12 +00:00
, m_regenerate_volumes ( true )
2018-11-26 09:41:16 +00:00
, m_moving ( false )
2019-02-21 17:31:01 +00:00
, m_tab_down ( false )
2019-04-24 13:43:52 +00:00
, m_cursor_type ( Standard )
2018-06-06 08:16:58 +00:00
, m_color_by ( " volume " )
2018-06-08 07:40:00 +00:00
, m_reload_delayed ( false )
2019-02-01 15:12:00 +00:00
, m_render_sla_auxiliaries ( true )
2018-05-09 08:47:04 +00:00
{
2019-01-26 12:10:58 +00:00
if ( m_canvas ! = nullptr ) {
2018-10-25 07:35:08 +00:00
m_timer . SetOwner ( m_canvas ) ;
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
m_retina_helper . reset ( new RetinaHelper ( canvas ) ) ;
# endif
2018-06-27 09:31:11 +00:00
}
2018-10-08 12:02:12 +00:00
m_selection . set_volumes ( & m_volumes . volumes ) ;
2018-05-09 08:47:04 +00:00
}
2018-05-15 07:50:01 +00:00
GLCanvas3D : : ~ GLCanvas3D ( )
{
2018-06-11 13:13:13 +00:00
reset_volumes ( ) ;
2018-10-03 09:34:39 +00:00
}
2018-10-03 12:07:10 +00:00
void GLCanvas3D : : post_event ( wxEvent & & event )
2018-10-03 09:34:39 +00:00
{
2018-10-03 12:07:10 +00:00
event . SetEventObject ( m_canvas ) ;
2018-10-03 09:34:39 +00:00
wxPostEvent ( m_canvas , event ) ;
}
2018-06-27 09:31:11 +00:00
2018-05-25 12:05:08 +00:00
bool GLCanvas3D : : init ( bool useVBOs , bool use_legacy_opengl )
2018-05-23 07:57:44 +00:00
{
2018-06-05 10:24:26 +00:00
if ( m_initialized )
return true ;
2018-06-27 09:31:11 +00:00
if ( ( m_canvas = = nullptr ) | | ( m_context = = nullptr ) )
return false ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glClearColor ( 1.0f , 1.0f , 1.0f , 1.0f ) ) ;
glsafe ( : : glClearDepth ( 1.0f ) ) ;
2018-06-01 13:54:41 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glDepthFunc ( GL_LESS ) ) ;
2018-05-31 06:44:39 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_DEPTH_TEST ) ) ;
glsafe ( : : glEnable ( GL_CULL_FACE ) ) ;
glsafe ( : : glEnable ( GL_BLEND ) ) ;
glsafe ( : : glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ) ;
2018-05-23 13:35:11 +00:00
// Set antialiasing / multisampling
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_LINE_SMOOTH ) ) ;
glsafe ( : : glDisable ( GL_POLYGON_SMOOTH ) ) ;
2018-05-23 13:35:11 +00:00
// ambient lighting
GLfloat ambient [ 4 ] = { 0.3f , 0.3f , 0.3f , 1.0f } ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glLightModelfv ( GL_LIGHT_MODEL_AMBIENT , ambient ) ) ;
2018-05-23 13:35:11 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_LIGHT0 ) ) ;
glsafe ( : : glEnable ( GL_LIGHT1 ) ) ;
2018-05-23 13:35:11 +00:00
// light from camera
2018-06-11 08:46:32 +00:00
GLfloat specular_cam [ 4 ] = { 0.3f , 0.3f , 0.3f , 1.0f } ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glLightfv ( GL_LIGHT1 , GL_SPECULAR , specular_cam ) ) ;
2018-06-11 08:46:32 +00:00
GLfloat diffuse_cam [ 4 ] = { 0.2f , 0.2f , 0.2f , 1.0f } ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glLightfv ( GL_LIGHT1 , GL_DIFFUSE , diffuse_cam ) ) ;
2018-05-23 13:35:11 +00:00
2018-05-31 06:44:39 +00:00
// light from above
2018-06-11 08:46:32 +00:00
GLfloat specular_top [ 4 ] = { 0.2f , 0.2f , 0.2f , 1.0f } ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glLightfv ( GL_LIGHT0 , GL_SPECULAR , specular_top ) ) ;
2018-06-11 08:46:32 +00:00
GLfloat diffuse_top [ 4 ] = { 0.5f , 0.5f , 0.5f , 1.0f } ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glLightfv ( GL_LIGHT0 , GL_DIFFUSE , diffuse_top ) ) ;
2018-05-31 06:44:39 +00:00
2018-05-23 13:35:11 +00:00
// Enables Smooth Color Shading; try GL_FLAT for (lack of) fun.
2019-03-27 13:42:09 +00:00
glsafe ( : : glShadeModel ( GL_SMOOTH ) ) ;
2018-05-23 13:35:11 +00:00
// A handy trick -- have surface material mirror the color.
2019-03-27 13:42:09 +00:00
glsafe ( : : glColorMaterial ( GL_FRONT_AND_BACK , GL_AMBIENT_AND_DIFFUSE ) ) ;
glsafe ( : : glEnable ( GL_COLOR_MATERIAL ) ) ;
2018-05-23 13:35:11 +00:00
2018-06-01 13:54:41 +00:00
if ( m_multisample_allowed )
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_MULTISAMPLE ) ) ;
2018-05-23 13:35:11 +00:00
2018-05-23 07:57:44 +00:00
if ( useVBOs & & ! m_shader . init ( " gouraud.vs " , " gouraud.fs " ) )
return false ;
2019-03-28 08:12:32 +00:00
if ( m_toolbar . is_enabled ( ) & & useVBOs & & ! m_layers_editing . init ( " variable_layer_height.vs " , " variable_layer_height.fs " ) )
2018-05-25 12:05:08 +00:00
return false ;
2018-06-04 10:26:39 +00:00
m_use_VBOs = useVBOs ;
2018-06-04 11:15:28 +00:00
m_layers_editing . set_use_legacy_opengl ( use_legacy_opengl ) ;
2018-05-25 12:05:08 +00:00
2018-06-05 10:24:26 +00:00
// 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()
2018-06-11 13:13:13 +00:00
if ( ! m_volumes . empty ( ) )
m_volumes . finalize_geometry ( m_use_VBOs ) ;
2018-06-05 10:24:26 +00:00
2018-10-18 13:13:38 +00:00
if ( m_gizmos . is_enabled ( ) ) {
if ( ! m_gizmos . init ( * this ) ) {
2018-11-19 13:03:28 +00:00
std : : cout < < " Unable to initialize gizmos: please, check that all the required textures are available " < < std : : endl ;
return false ;
2018-10-18 13:13:38 +00:00
}
}
2018-06-13 07:12:16 +00:00
2018-07-23 11:49:48 +00:00
if ( ! _init_toolbar ( ) )
return false ;
2019-03-28 07:44:46 +00:00
if ( m_selection . is_enabled ( ) & & ! m_selection . init ( m_use_VBOs ) )
2018-12-19 13:44:37 +00:00
return false ;
2018-12-06 09:38:19 +00:00
post_event ( SimpleEvent ( EVT_GLCANVAS_INIT ) ) ;
2018-06-05 10:24:26 +00:00
m_initialized = true ;
2018-05-23 07:57:44 +00:00
return true ;
2018-05-15 07:50:01 +00:00
}
2018-06-22 14:06:37 +00:00
void GLCanvas3D : : set_as_dirty ( )
{
m_dirty = true ;
}
2018-06-11 11:48:02 +00:00
unsigned int GLCanvas3D : : get_volumes_count ( ) const
2018-05-14 12:14:19 +00:00
{
2018-06-11 13:13:13 +00:00
return ( unsigned int ) m_volumes . volumes . size ( ) ;
2018-05-14 12:14:19 +00:00
}
2018-05-18 12:08:59 +00:00
void GLCanvas3D : : reset_volumes ( )
{
2019-02-04 15:05:54 +00:00
if ( ! m_initialized )
return ;
2019-01-31 08:15:43 +00:00
_set_current ( ) ;
2018-06-25 13:17:13 +00:00
if ( ! m_volumes . empty ( ) )
2018-05-18 12:08:59 +00:00
{
2018-10-08 12:02:12 +00:00
m_selection . clear ( ) ;
2018-06-11 13:13:13 +00:00
m_volumes . release_geometry ( ) ;
m_volumes . clear ( ) ;
2018-06-01 13:54:41 +00:00
m_dirty = true ;
2018-05-18 12:08:59 +00:00
}
2018-07-27 06:49:58 +00:00
2019-02-20 11:09:45 +00:00
_set_warning_texture ( WarningTexture : : ObjectOutside , false ) ;
2018-05-18 12:08:59 +00:00
}
2018-12-04 12:55:25 +00:00
int GLCanvas3D : : check_volumes_outside_state ( ) const
{
ModelInstance : : EPrintVolumeState state ;
m_volumes . check_outside_state ( m_config , & state ) ;
return ( int ) state ;
}
2018-06-11 11:48:02 +00:00
2019-03-24 12:35:09 +00:00
void GLCanvas3D : : toggle_sla_auxiliaries_visibility ( bool visible , const ModelObject * mo , int instance_idx )
2019-02-01 15:12:00 +00:00
{
for ( GLVolume * vol : m_volumes . volumes ) {
2019-03-24 12:35:09 +00:00
if ( ( mo = = nullptr | | m_model - > objects [ vol - > composite_id . object_id ] = = mo )
& & ( instance_idx = = - 1 | | vol - > composite_id . instance_id = = instance_idx )
& & vol - > composite_id . volume_id < 0 )
vol - > is_active = visible ;
2019-02-01 15:12:00 +00:00
}
m_render_sla_auxiliaries = visible ;
}
2019-02-06 14:16:25 +00:00
void GLCanvas3D : : toggle_model_objects_visibility ( bool visible , const ModelObject * mo , int instance_idx )
2019-02-01 15:12:00 +00:00
{
for ( GLVolume * vol : m_volumes . volumes ) {
2019-02-06 16:20:54 +00:00
if ( ( mo = = nullptr | | m_model - > objects [ vol - > composite_id . object_id ] = = mo )
2019-03-20 13:04:20 +00:00
& & ( instance_idx = = - 1 | | vol - > composite_id . instance_id = = instance_idx ) ) {
2019-02-01 15:12:00 +00:00
vol - > is_active = visible ;
2019-03-20 13:04:20 +00:00
vol - > force_native_color = ( instance_idx ! = - 1 ) ;
}
2019-02-01 15:12:00 +00:00
}
if ( visible & & ! mo )
2019-03-24 12:35:09 +00:00
toggle_sla_auxiliaries_visibility ( true , mo , instance_idx ) ;
2019-02-20 11:09:45 +00:00
if ( ! mo & & ! visible & & ! m_model - > objects . empty ( ) & & ( m_model - > objects . size ( ) > 1 | | m_model - > objects . front ( ) - > instances . size ( ) > 1 ) )
_set_warning_texture ( WarningTexture : : SomethingNotShown , true ) ;
if ( ! mo & & visible )
_set_warning_texture ( WarningTexture : : SomethingNotShown , false ) ;
2019-02-01 15:12:00 +00:00
}
2019-01-21 09:06:51 +00:00
void GLCanvas3D : : set_config ( const DynamicPrintConfig * config )
2018-05-23 09:14:49 +00:00
{
2018-05-28 13:23:01 +00:00
m_config = config ;
2019-01-21 09:06:51 +00:00
m_layers_editing . set_config ( config ) ;
2018-05-23 09:14:49 +00:00
}
2018-11-22 14:29:59 +00:00
void GLCanvas3D : : set_process ( BackgroundSlicingProcess * process )
2018-05-23 09:14:49 +00:00
{
2018-11-22 14:29:59 +00:00
m_process = process ;
2018-11-13 16:45:44 +00:00
}
2018-06-07 09:18:28 +00:00
void GLCanvas3D : : set_model ( Model * model )
{
m_model = model ;
2018-10-08 12:02:12 +00:00
m_selection . set_model ( m_model ) ;
2018-06-07 09:18:28 +00:00
}
2019-02-19 14:15:27 +00:00
void GLCanvas3D : : bed_shape_changed ( )
{
2019-03-07 10:49:00 +00:00
m_camera . set_scene_box ( scene_bounding_box ( ) ) ;
m_camera . requires_zoom_to_bed = true ;
2019-02-19 14:15:27 +00:00
m_dirty = true ;
}
2018-05-09 08:47:04 +00:00
2018-06-06 10:36:52 +00:00
void GLCanvas3D : : set_color_by ( const std : : string & value )
{
m_color_by = value ;
}
2018-05-14 12:14:19 +00:00
BoundingBoxf3 GLCanvas3D : : volumes_bounding_box ( ) const
{
BoundingBoxf3 bb ;
2018-06-11 13:13:13 +00:00
for ( const GLVolume * volume : m_volumes . volumes )
2018-05-14 12:14:19 +00:00
{
2018-06-11 13:13:13 +00:00
if ( ! m_apply_zoom_to_volumes_filter | | ( ( volume ! = nullptr ) & & volume - > zoom_to_volumes ) )
bb . merge ( volume - > transformed_bounding_box ( ) ) ;
2018-05-14 12:14:19 +00:00
}
return bb ;
}
2018-12-07 15:23:04 +00:00
BoundingBoxf3 GLCanvas3D : : scene_bounding_box ( ) const
{
BoundingBoxf3 bb = volumes_bounding_box ( ) ;
2019-03-07 10:49:00 +00:00
bb . merge ( m_bed . get_bounding_box ( ) ) ;
2019-02-20 12:50:35 +00:00
2019-01-08 08:10:57 +00:00
if ( m_config ! = nullptr )
{
double h = m_config - > opt_float ( " max_print_height " ) ;
bb . min ( 2 ) = std : : min ( bb . min ( 2 ) , - h ) ;
bb . max ( 2 ) = std : : max ( bb . max ( 2 ) , h ) ;
}
2018-12-07 15:23:04 +00:00
return bb ;
}
2018-05-18 12:08:59 +00:00
bool GLCanvas3D : : is_layers_editing_enabled ( ) const
{
return m_layers_editing . is_enabled ( ) ;
}
2018-05-25 14:28:24 +00:00
bool GLCanvas3D : : is_layers_editing_allowed ( ) const
{
return m_layers_editing . is_allowed ( ) ;
}
2018-06-08 07:40:00 +00:00
bool GLCanvas3D : : is_reload_delayed ( ) const
{
return m_reload_delayed ;
}
2018-05-25 12:05:08 +00:00
void GLCanvas3D : : enable_layers_editing ( bool enable )
2018-05-23 13:35:11 +00:00
{
2018-05-25 12:05:08 +00:00
m_layers_editing . set_enabled ( enable ) ;
2019-01-31 13:25:11 +00:00
const Selection : : IndicesList & idxs = m_selection . get_volume_idxs ( ) ;
for ( unsigned int idx : idxs )
{
GLVolume * v = m_volumes . volumes [ idx ] ;
if ( v - > is_modifier )
v - > force_transparent = enable ;
}
2019-04-15 12:19:18 +00:00
set_as_dirty ( ) ;
2018-05-23 13:35:11 +00:00
}
2018-05-21 12:57:43 +00:00
void GLCanvas3D : : enable_legend_texture ( bool enable )
{
m_legend_texture_enabled = enable ;
}
2018-05-22 07:02:42 +00:00
void GLCanvas3D : : enable_picking ( bool enable )
{
m_picking_enabled = enable ;
2018-10-08 12:02:12 +00:00
m_selection . set_mode ( Selection : : Instance ) ;
2018-05-22 07:02:42 +00:00
}
2018-05-31 11:51:50 +00:00
void GLCanvas3D : : enable_moving ( bool enable )
{
m_moving_enabled = enable ;
}
2018-06-13 07:12:16 +00:00
void GLCanvas3D : : enable_gizmos ( bool enable )
{
m_gizmos . set_enabled ( enable ) ;
}
2019-03-28 07:44:46 +00:00
void GLCanvas3D : : enable_selection ( bool enable )
{
m_selection . set_enabled ( enable ) ;
}
2018-07-23 11:49:48 +00:00
void GLCanvas3D : : enable_toolbar ( bool enable )
{
m_toolbar . set_enabled ( enable ) ;
}
2018-07-27 07:38:39 +00:00
void GLCanvas3D : : enable_dynamic_background ( bool enable )
{
m_dynamic_background_enabled = enable ;
}
2018-05-23 13:35:11 +00:00
void GLCanvas3D : : allow_multisample ( bool allow )
{
m_multisample_allowed = allow ;
}
2018-05-15 08:32:38 +00:00
void GLCanvas3D : : zoom_to_bed ( )
{
2019-03-07 10:49:00 +00:00
_zoom_to_bounding_box ( m_bed . get_bounding_box ( ) ) ;
2018-05-15 08:32:38 +00:00
}
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 ;
}
2018-10-26 07:50:28 +00:00
void GLCanvas3D : : zoom_to_selection ( )
{
if ( ! m_selection . is_empty ( ) )
_zoom_to_bounding_box ( m_selection . get_bounding_box ( ) ) ;
}
2018-05-15 09:30:11 +00:00
void GLCanvas3D : : select_view ( const std : : string & direction )
{
2019-04-01 08:00:10 +00:00
if ( m_camera . select_view ( direction ) & & ( m_canvas ! = nullptr ) )
m_canvas - > Refresh ( ) ;
2018-05-15 09:30:11 +00:00
}
2018-05-29 13:07:06 +00:00
void GLCanvas3D : : update_volumes_colors_by_extruder ( )
{
2018-06-11 13:13:13 +00:00
if ( m_config ! = nullptr )
m_volumes . update_colors_by_extruder ( m_config ) ;
2018-05-29 13:07:06 +00:00
}
2018-06-04 10:26:39 +00:00
void GLCanvas3D : : render ( )
2018-05-29 11:54:34 +00:00
{
2018-11-27 15:55:54 +00:00
wxCHECK_RET ( ! m_in_render , " GLCanvas3D::render() called recursively " ) ;
m_in_render = true ;
Slic3r : : ScopeGuard in_render_guard ( [ this ] ( ) { m_in_render = false ; } ) ;
( void ) in_render_guard ;
2018-05-29 12:34:45 +00:00
if ( m_canvas = = nullptr )
return ;
2019-03-08 09:42:09 +00:00
# ifndef __WXMAC__
// on Mac this check causes flickering when changing view
2019-03-08 07:29:54 +00:00
if ( ! _is_shown_on_screen ( ) )
return ;
2019-03-08 09:42:09 +00:00
# endif // __WXMAC__
2018-06-04 10:26:39 +00:00
2018-06-25 13:17:13 +00:00
// ensures this canvas is current and initialized
2018-10-04 08:41:11 +00:00
if ( ! _set_current ( ) | | ! _3DScene : : init ( m_canvas ) )
2018-06-04 11:15:28 +00:00
return ;
2019-04-25 09:10:01 +00:00
# if ENABLE_RENDER_STATISTICS
auto start_time = std : : chrono : : high_resolution_clock : : now ( ) ;
# endif // ENABLE_RENDER_STATISTICS
2019-03-07 10:49:00 +00:00
if ( m_bed . get_shape ( ) . empty ( ) )
2019-03-18 09:10:11 +00:00
{
2019-02-19 14:15:27 +00:00
// this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE
post_event ( SimpleEvent ( EVT_GLCANVAS_UPDATE_BED_SHAPE ) ) ;
2019-03-18 09:10:11 +00:00
return ;
}
2019-02-05 09:18:40 +00:00
2019-03-07 10:49:00 +00:00
if ( m_camera . requires_zoom_to_bed )
2019-01-17 12:21:33 +00:00
{
zoom_to_bed ( ) ;
const Size & cnv_size = get_canvas_size ( ) ;
_resize ( ( unsigned int ) cnv_size . get_width ( ) , ( unsigned int ) cnv_size . get_height ( ) ) ;
2019-03-07 10:49:00 +00:00
m_camera . requires_zoom_to_bed = false ;
2019-01-17 12:21:33 +00:00
}
2018-06-04 10:26:39 +00:00
2019-04-01 08:00:10 +00:00
m_camera . apply_view_matrix ( ) ;
2018-06-04 10:26:39 +00:00
2018-06-11 08:46:32 +00:00
GLfloat position_cam [ 4 ] = { 1.0f , 0.0f , 1.0f , 0.0f } ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glLightfv ( GL_LIGHT1 , GL_POSITION , position_cam ) ) ;
2018-06-11 08:46:32 +00:00
GLfloat position_top [ 4 ] = { - 0.5f , - 0.5f , 1.0f , 0.0f } ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glLightfv ( GL_LIGHT0 , GL_POSITION , position_top ) ) ;
2018-06-04 10:26:39 +00:00
2019-03-07 10:49:00 +00:00
float theta = m_camera . get_theta ( ) ;
2019-01-25 10:35:28 +00:00
if ( theta > 180.f )
// absolute value of the rotation
theta = 360.f - theta ;
2018-11-26 09:56:07 +00:00
wxGetApp ( ) . imgui ( ) - > new_frame ( ) ;
2018-10-31 09:19:44 +00:00
2019-04-25 13:08:14 +00:00
if ( m_picking_enabled )
{
if ( m_rectangle_selection . is_dragging ( ) )
// picking pass using rectangle selection
_rectangular_selection_picking_pass ( ) ;
else
// regular picking pass
_picking_pass ( ) ;
}
2018-08-21 12:27:36 +00:00
2018-08-24 09:17:53 +00:00
// draw scene
2019-03-27 13:42:09 +00:00
glsafe ( : : glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ) ;
2018-08-24 09:17:53 +00:00
_render_background ( ) ;
2018-08-21 12:27:36 +00:00
2018-12-21 08:56:11 +00:00
// textured bed needs to be rendered after objects if the texture is transparent
2019-03-07 10:49:00 +00:00
bool early_bed_render = m_bed . is_custom ( ) | | ( theta < = 90.0f ) ;
if ( early_bed_render )
2018-06-11 09:40:11 +00:00
_render_bed ( theta ) ;
2018-09-10 10:08:57 +00:00
2018-11-14 09:43:52 +00:00
_render_objects ( ) ;
2018-11-28 14:13:25 +00:00
_render_sla_slices ( ) ;
2018-10-08 12:02:12 +00:00
_render_selection ( ) ;
2018-10-26 13:45:52 +00:00
2018-12-17 13:09:35 +00:00
_render_axes ( ) ;
2018-12-21 08:56:11 +00:00
if ( ! early_bed_render )
2018-06-11 09:40:11 +00:00
_render_bed ( theta ) ;
2018-08-24 09:17:53 +00:00
2018-12-18 11:35:49 +00:00
# if ENABLE_RENDER_SELECTION_CENTER
_render_selection_center ( ) ;
# endif // ENABLE_RENDER_SELECTION_CENTER
2018-11-15 10:38:40 +00:00
// we need to set the mouse's scene position here because the depth buffer
// could be invalidated by the following gizmo render methods
// this position is used later into on_mouse() to drag the objects
m_mouse . scene_position = _mouse_to_3d ( m_mouse . position . cast < int > ( ) ) ;
2019-02-15 15:15:18 +00:00
_render_current_gizmo ( ) ;
2018-12-19 13:44:37 +00:00
_render_selection_sidebar_hints ( ) ;
2018-10-26 07:50:28 +00:00
# if ENABLE_SHOW_CAMERA_TARGET
_render_camera_target ( ) ;
# endif // ENABLE_SHOW_CAMERA_TARGET
2018-08-24 09:17:53 +00:00
2019-04-25 07:10:03 +00:00
if ( m_picking_enabled & & m_rectangle_selection . is_dragging ( ) )
m_rectangle_selection . render ( * this ) ;
2018-08-24 09:17:53 +00:00
// draw overlays
2018-08-21 12:27:36 +00:00
_render_gizmos_overlay ( ) ;
2018-05-29 11:54:34 +00:00
_render_warning_texture ( ) ;
_render_legend_texture ( ) ;
2019-02-26 08:56:23 +00:00
# if !ENABLE_SVG_ICONS
2018-12-06 09:38:19 +00:00
_resize_toolbars ( ) ;
2019-02-26 08:56:23 +00:00
# endif // !ENABLE_SVG_ICONS
2018-07-23 11:49:48 +00:00
_render_toolbar ( ) ;
2018-12-06 09:38:19 +00:00
_render_view_toolbar ( ) ;
2019-04-15 12:19:18 +00:00
if ( ( m_layers_editing . last_object_id > = 0 ) & & ( m_layers_editing . object_max_z ( ) > 0.0f ) )
2019-01-21 09:06:51 +00:00
m_layers_editing . render_overlay ( * this ) ;
2018-05-29 12:34:45 +00:00
2019-04-25 09:10:01 +00:00
# if ENABLE_RENDER_STATISTICS
ImGuiWrapper & imgui = * wxGetApp ( ) . imgui ( ) ;
imgui . set_next_window_bg_alpha ( 0.5f ) ;
2019-04-25 09:50:30 +00:00
imgui . begin ( std : : string ( " Render statistics " ) , ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse ) ;
2019-04-25 09:10:01 +00:00
imgui . text ( " Last frame: " ) ;
ImGui : : SameLine ( ) ;
imgui . text ( std : : to_string ( m_render_stats . last_frame ) ) ;
ImGui : : SameLine ( ) ;
imgui . text ( " ms " ) ;
imgui . end ( ) ;
# endif // ENABLE_RENDER_STATISTICS
2018-11-26 09:56:07 +00:00
wxGetApp ( ) . imgui ( ) - > render ( ) ;
2018-10-31 09:19:44 +00:00
2018-05-29 12:34:45 +00:00
m_canvas - > SwapBuffers ( ) ;
2019-04-25 09:10:01 +00:00
# if ENABLE_RENDER_STATISTICS
auto end_time = std : : chrono : : high_resolution_clock : : now ( ) ;
m_render_stats . last_frame = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( end_time - start_time ) . count ( ) ;
# endif // ENABLE_RENDER_STATISTICS
2018-05-21 13:24:52 +00:00
}
2018-11-21 14:28:35 +00:00
void GLCanvas3D : : select_all ( )
{
m_selection . add_all ( ) ;
2018-12-04 07:38:31 +00:00
m_dirty = true ;
2018-11-21 14:28:35 +00:00
}
2018-11-14 07:53:56 +00:00
void GLCanvas3D : : delete_selected ( )
{
m_selection . erase ( ) ;
}
2018-11-21 11:27:20 +00:00
void GLCanvas3D : : ensure_on_bed ( unsigned int object_idx )
{
typedef std : : map < std : : pair < int , int > , double > InstancesToZMap ;
InstancesToZMap instances_min_z ;
for ( GLVolume * volume : m_volumes . volumes )
{
if ( ( volume - > object_idx ( ) = = object_idx ) & & ! volume - > is_modifier )
{
double min_z = volume - > transformed_convex_hull_bounding_box ( ) . min ( 2 ) ;
std : : pair < int , int > instance = std : : make_pair ( volume - > object_idx ( ) , volume - > instance_idx ( ) ) ;
InstancesToZMap : : iterator it = instances_min_z . find ( instance ) ;
if ( it = = instances_min_z . end ( ) )
it = instances_min_z . insert ( InstancesToZMap : : value_type ( instance , DBL_MAX ) ) . first ;
it - > second = std : : min ( it - > second , min_z ) ;
}
}
for ( GLVolume * volume : m_volumes . volumes )
{
std : : pair < int , int > instance = std : : make_pair ( volume - > object_idx ( ) , volume - > instance_idx ( ) ) ;
InstancesToZMap : : iterator it = instances_min_z . find ( instance ) ;
if ( it ! = instances_min_z . end ( ) )
volume - > set_instance_offset ( Z , volume - > get_instance_offset ( Z ) - it - > second ) ;
}
}
2018-06-04 13:42:34 +00:00
std : : vector < double > GLCanvas3D : : get_current_print_zs ( bool active_only ) const
{
2018-06-11 13:13:13 +00:00
return m_volumes . get_current_print_zs ( active_only ) ;
2018-06-04 13:42:34 +00:00
}
void GLCanvas3D : : set_toolpaths_range ( double low , double high )
{
2018-06-11 13:13:13 +00:00
m_volumes . set_range ( low , high ) ;
2018-06-04 13:42:34 +00:00
}
2018-06-06 08:16:58 +00:00
std : : vector < int > GLCanvas3D : : load_object ( const ModelObject & model_object , int obj_idx , std : : vector < int > instance_idxs )
{
if ( instance_idxs . empty ( ) )
{
for ( unsigned int i = 0 ; i < model_object . instances . size ( ) ; + + i )
{
instance_idxs . push_back ( i ) ;
}
}
2018-10-08 14:05:55 +00:00
return m_volumes . load_object ( & model_object , obj_idx , instance_idxs , m_color_by , m_use_VBOs & & m_initialized ) ;
2018-06-06 08:16:58 +00:00
}
2018-06-07 07:22:19 +00:00
std : : vector < int > GLCanvas3D : : load_object ( const Model & model , int obj_idx )
2018-06-06 08:16:58 +00:00
{
if ( ( 0 < = obj_idx ) & & ( obj_idx < ( int ) model . objects . size ( ) ) )
{
const ModelObject * model_object = model . objects [ obj_idx ] ;
if ( model_object ! = nullptr )
2018-06-07 07:22:19 +00:00
return load_object ( * model_object , obj_idx , std : : vector < int > ( ) ) ;
2018-06-06 08:16:58 +00:00
}
return std : : vector < int > ( ) ;
}
2018-10-18 13:50:51 +00:00
void GLCanvas3D : : mirror_selection ( Axis axis )
{
m_selection . mirror ( axis ) ;
2018-11-21 09:36:09 +00:00
do_mirror ( ) ;
2019-05-03 10:36:26 +00:00
wxGetApp ( ) . obj_manipul ( ) - > set_dirty ( ) ;
2018-10-18 13:50:51 +00:00
}
2018-11-16 17:28:50 +00:00
// Reload the 3D scene of
// 1) Model / ModelObjects / ModelInstances / ModelVolumes
// 2) Print bed
// 3) SLA support meshes for their respective ModelObjects / ModelInstances
// 4) Wipe tower preview
// 5) Out of bed collision status & message overlay (texture)
2018-11-21 12:52:46 +00:00
void GLCanvas3D : : reload_scene ( bool refresh_immediately , bool force_full_scene_refresh )
2018-06-08 07:40:00 +00:00
{
2018-06-11 13:13:13 +00:00
if ( ( m_canvas = = nullptr ) | | ( m_config = = nullptr ) | | ( m_model = = nullptr ) )
2018-06-08 07:40:00 +00:00
return ;
2018-06-25 13:17:13 +00:00
2019-02-04 15:05:54 +00:00
if ( m_initialized )
_set_current ( ) ;
2019-01-30 18:02:30 +00:00
2018-11-16 17:28:50 +00:00
struct ModelVolumeState {
ModelVolumeState ( const GLVolume * volume ) :
2018-11-22 12:20:13 +00:00
model_volume ( nullptr ) , geometry_id ( volume - > geometry_id ) , volume_idx ( - 1 ) { }
ModelVolumeState ( const ModelVolume * model_volume , const ModelID & instance_id , const GLVolume : : CompositeID & composite_id ) :
model_volume ( model_volume ) , geometry_id ( std : : make_pair ( model_volume - > id ( ) . id , instance_id . id ) ) , composite_id ( composite_id ) , volume_idx ( - 1 ) { }
2018-11-16 17:28:50 +00:00
ModelVolumeState ( const ModelID & volume_id , const ModelID & instance_id ) :
2018-11-22 12:20:13 +00:00
model_volume ( nullptr ) , geometry_id ( std : : make_pair ( volume_id . id , instance_id . id ) ) , volume_idx ( - 1 ) { }
2018-11-16 17:28:50 +00:00
bool new_geometry ( ) const { return this - > volume_idx = = size_t ( - 1 ) ; }
2018-11-22 12:20:13 +00:00
const ModelVolume * model_volume ;
2018-11-16 17:28:50 +00:00
// ModelID of ModelVolume + ModelID of ModelInstance
// or timestamp of an SLAPrintObjectStep + ModelID of ModelInstance
std : : pair < size_t , size_t > geometry_id ;
GLVolume : : CompositeID composite_id ;
// Volume index in the new GLVolume vector.
size_t volume_idx ;
} ;
std : : vector < ModelVolumeState > model_volume_state ;
std : : vector < ModelVolumeState > aux_volume_state ;
// SLA steps to pull the preview meshes for.
typedef std : : array < SLAPrintObjectStep , 2 > SLASteps ;
SLASteps sla_steps = { slaposSupportTree , slaposBasePool } ;
struct SLASupportState {
std : : array < PrintStateBase : : StateWithTimeStamp , std : : tuple_size < SLASteps > : : value > step ;
} ;
// State of the sla_steps for all SLAPrintObjects.
std : : vector < SLASupportState > sla_support_state ;
2018-06-25 13:17:13 +00:00
2019-04-26 15:28:31 +00:00
std : : vector < size_t > instance_ids_selected ;
2018-11-16 17:28:50 +00:00
std : : vector < size_t > map_glvolume_old_to_new ( m_volumes . volumes . size ( ) , size_t ( - 1 ) ) ;
std : : vector < GLVolume * > glvolumes_new ;
glvolumes_new . reserve ( m_volumes . volumes . size ( ) ) ;
auto model_volume_state_lower = [ ] ( const ModelVolumeState & m1 , const ModelVolumeState & m2 ) { return m1 . geometry_id < m2 . geometry_id ; } ;
2018-06-08 07:40:00 +00:00
2018-11-21 12:52:46 +00:00
m_reload_delayed = ! m_canvas - > IsShown ( ) & & ! refresh_immediately & & ! force_full_scene_refresh ;
2018-06-08 07:40:00 +00:00
2018-12-12 13:40:56 +00:00
PrinterTechnology printer_technology = m_process - > current_printer_technology ( ) ;
int volume_idx_wipe_tower_old = - 1 ;
2018-11-16 17:28:50 +00:00
2018-10-08 12:02:12 +00:00
if ( m_regenerate_volumes )
{
2018-11-16 17:28:50 +00:00
// Release invalidated volumes to conserve GPU memory in case of delayed refresh (see m_reload_delayed).
// First initialize model_volumes_new_sorted & model_instances_new_sorted.
for ( int object_idx = 0 ; object_idx < ( int ) m_model - > objects . size ( ) ; + + object_idx ) {
const ModelObject * model_object = m_model - > objects [ object_idx ] ;
for ( int instance_idx = 0 ; instance_idx < ( int ) model_object - > instances . size ( ) ; + + instance_idx ) {
const ModelInstance * model_instance = model_object - > instances [ instance_idx ] ;
for ( int volume_idx = 0 ; volume_idx < ( int ) model_object - > volumes . size ( ) ; + + volume_idx ) {
const ModelVolume * model_volume = model_object - > volumes [ volume_idx ] ;
2018-11-22 12:20:13 +00:00
model_volume_state . emplace_back ( model_volume , model_instance - > id ( ) , GLVolume : : CompositeID ( object_idx , volume_idx , instance_idx ) ) ;
2018-11-16 17:28:50 +00:00
}
}
}
if ( printer_technology = = ptSLA ) {
2018-11-22 14:29:59 +00:00
const SLAPrint * sla_print = this - > sla_print ( ) ;
2019-01-30 14:27:11 +00:00
# ifndef NDEBUG
2018-11-16 17:28:50 +00:00
// Verify that the SLAPrint object is synchronized with m_model.
2018-11-22 14:29:59 +00:00
check_model_ids_equal ( * m_model , sla_print - > model ( ) ) ;
2019-01-30 14:27:11 +00:00
# endif /* NDEBUG */
2018-11-22 14:29:59 +00:00
sla_support_state . reserve ( sla_print - > objects ( ) . size ( ) ) ;
for ( const SLAPrintObject * print_object : sla_print - > objects ( ) ) {
2018-11-16 17:28:50 +00:00
SLASupportState state ;
for ( size_t istep = 0 ; istep < sla_steps . size ( ) ; + + istep ) {
state . step [ istep ] = print_object - > step_state_with_timestamp ( sla_steps [ istep ] ) ;
2018-11-17 16:23:56 +00:00
if ( state . step [ istep ] . state = = PrintStateBase : : DONE ) {
if ( ! print_object - > has_mesh ( sla_steps [ istep ] ) )
// Consider the DONE step without a valid mesh as invalid for the purpose
// of mesh visualization.
state . step [ istep ] . state = PrintStateBase : : INVALID ;
else
for ( const ModelInstance * model_instance : print_object - > model_object ( ) - > instances )
aux_volume_state . emplace_back ( state . step [ istep ] . timestamp , model_instance - > id ( ) ) ;
}
2018-11-16 17:28:50 +00:00
}
sla_support_state . emplace_back ( state ) ;
}
}
std : : sort ( model_volume_state . begin ( ) , model_volume_state . end ( ) , model_volume_state_lower ) ;
std : : sort ( aux_volume_state . begin ( ) , aux_volume_state . end ( ) , model_volume_state_lower ) ;
// Release all ModelVolume based GLVolumes not found in the current Model.
for ( size_t volume_id = 0 ; volume_id < m_volumes . volumes . size ( ) ; + + volume_id ) {
GLVolume * volume = m_volumes . volumes [ volume_id ] ;
ModelVolumeState key ( volume ) ;
ModelVolumeState * mvs = nullptr ;
if ( volume - > volume_idx ( ) < 0 ) {
auto it = std : : lower_bound ( aux_volume_state . begin ( ) , aux_volume_state . end ( ) , key , model_volume_state_lower ) ;
if ( it ! = aux_volume_state . end ( ) & & it - > geometry_id = = key . geometry_id )
mvs = & ( * it ) ;
} else {
auto it = std : : lower_bound ( model_volume_state . begin ( ) , model_volume_state . end ( ) , key , model_volume_state_lower ) ;
if ( it ! = model_volume_state . end ( ) & & it - > geometry_id = = key . geometry_id )
mvs = & ( * it ) ;
}
2019-04-26 15:28:31 +00:00
// Emplace instance ID of the volume. Both the aux volumes and model volumes share the same instance ID.
// The wipe tower has its own wipe_tower_instance_id().
if ( m_selection . contains_volume ( volume_id ) )
instance_ids_selected . emplace_back ( volume - > geometry_id . second ) ;
2018-11-21 12:52:46 +00:00
if ( mvs = = nullptr | | force_full_scene_refresh ) {
2018-11-16 17:28:50 +00:00
// This GLVolume will be released.
2018-12-12 13:40:56 +00:00
if ( volume - > is_wipe_tower ) {
// There is only one wipe tower.
assert ( volume_idx_wipe_tower_old = = - 1 ) ;
volume_idx_wipe_tower_old = ( int ) volume_id ;
}
2018-11-16 17:28:50 +00:00
volume - > release_geometry ( ) ;
if ( ! m_reload_delayed )
delete volume ;
} else {
// This GLVolume will be reused.
2018-11-22 12:33:20 +00:00
volume - > set_sla_shift_z ( 0.0 ) ;
2018-11-16 17:28:50 +00:00
map_glvolume_old_to_new [ volume_id ] = glvolumes_new . size ( ) ;
mvs - > volume_idx = glvolumes_new . size ( ) ;
glvolumes_new . emplace_back ( volume ) ;
2018-11-22 12:20:13 +00:00
// Update color of the volume based on the current extruder.
2018-11-22 12:33:21 +00:00
if ( mvs - > model_volume ! = nullptr ) {
int extruder_id = mvs - > model_volume - > extruder_id ( ) ;
if ( extruder_id ! = - 1 )
volume - > extruder_id = extruder_id ;
2018-11-23 09:50:25 +00:00
2018-12-05 11:10:09 +00:00
volume - > is_modifier = ! mvs - > model_volume - > is_model_part ( ) ;
volume - > set_color_from_model_volume ( mvs - > model_volume ) ;
2018-11-23 09:50:25 +00:00
// updates volumes transformations
2018-12-04 10:35:04 +00:00
volume - > set_instance_transformation ( mvs - > model_volume - > get_object ( ) - > instances [ mvs - > composite_id . instance_id ] - > get_transformation ( ) ) ;
2018-11-23 09:50:25 +00:00
volume - > set_volume_transformation ( mvs - > model_volume - > get_transformation ( ) ) ;
}
2018-11-16 17:28:50 +00:00
}
2018-10-08 12:02:12 +00:00
}
2019-04-26 15:28:31 +00:00
sort_remove_duplicates ( instance_ids_selected ) ;
2018-10-08 12:02:12 +00:00
}
2018-11-16 17:28:50 +00:00
if ( m_reload_delayed )
return ;
2019-04-24 17:03:05 +00:00
bool update_object_list = false ;
2018-10-08 12:02:12 +00:00
if ( m_regenerate_volumes )
2018-06-08 07:40:00 +00:00
{
2019-04-24 17:03:05 +00:00
if ( m_volumes . volumes ! = glvolumes_new )
update_object_list = true ;
2018-11-16 17:28:50 +00:00
m_volumes . volumes = std : : move ( glvolumes_new ) ;
for ( unsigned int obj_idx = 0 ; obj_idx < ( unsigned int ) m_model - > objects . size ( ) ; + + obj_idx ) {
const ModelObject & model_object = * m_model - > objects [ obj_idx ] ;
for ( int volume_idx = 0 ; volume_idx < ( int ) model_object . volumes . size ( ) ; + + volume_idx ) {
const ModelVolume & model_volume = * model_object . volumes [ volume_idx ] ;
for ( int instance_idx = 0 ; instance_idx < ( int ) model_object . instances . size ( ) ; + + instance_idx ) {
const ModelInstance & model_instance = * model_object . instances [ instance_idx ] ;
ModelVolumeState key ( model_volume . id ( ) , model_instance . id ( ) ) ;
auto it = std : : lower_bound ( model_volume_state . begin ( ) , model_volume_state . end ( ) , key , model_volume_state_lower ) ;
assert ( it ! = model_volume_state . end ( ) & & it - > geometry_id = = key . geometry_id ) ;
if ( it - > new_geometry ( ) ) {
// New volume.
2019-01-21 09:06:51 +00:00
m_volumes . load_object_volume ( & model_object , obj_idx , volume_idx , instance_idx , m_color_by , m_use_VBOs & & m_initialized ) ;
2018-11-16 17:28:50 +00:00
m_volumes . volumes . back ( ) - > geometry_id = key . geometry_id ;
2019-04-24 17:03:05 +00:00
update_object_list = true ;
2018-11-16 17:28:50 +00:00
} else {
// Recycling an old GLVolume.
GLVolume & existing_volume = * m_volumes . volumes [ it - > volume_idx ] ;
assert ( existing_volume . geometry_id = = key . geometry_id ) ;
// Update the Object/Volume/Instance indices into the current Model.
2019-04-24 17:03:05 +00:00
if ( existing_volume . composite_id ! = it - > composite_id ) {
existing_volume . composite_id = it - > composite_id ;
update_object_list = true ;
}
2018-11-16 17:28:50 +00:00
}
}
}
}
if ( printer_technology = = ptSLA ) {
size_t idx = 0 ;
2018-11-22 14:29:59 +00:00
const SLAPrint * sla_print = this - > sla_print ( ) ;
2018-11-26 14:19:42 +00:00
std : : vector < double > shift_zs ( m_model - > objects . size ( ) , 0 ) ;
2019-04-08 16:09:31 +00:00
double relative_correction_z = sla_print - > relative_correction ( ) . z ( ) ;
if ( relative_correction_z < = EPSILON )
relative_correction_z = 1. ;
2018-11-26 14:19:42 +00:00
for ( const SLAPrintObject * print_object : sla_print - > objects ( ) ) {
2018-11-16 17:28:50 +00:00
SLASupportState & state = sla_support_state [ idx + + ] ;
const ModelObject * model_object = print_object - > model_object ( ) ;
// Find an index of the ModelObject
int object_idx ;
2018-11-17 16:23:56 +00:00
if ( std : : all_of ( state . step . begin ( ) , state . step . end ( ) , [ ] ( const PrintStateBase : : StateWithTimeStamp & state ) { return state . state ! = PrintStateBase : : DONE ; } ) )
2018-11-16 17:28:50 +00:00
continue ;
// There may be new SLA volumes added to the scene for this print_object.
// Find the object index of this print_object in the Model::objects list.
2018-11-22 14:29:59 +00:00
auto it = std : : find ( sla_print - > model ( ) . objects . begin ( ) , sla_print - > model ( ) . objects . end ( ) , model_object ) ;
assert ( it ! = sla_print - > model ( ) . objects . end ( ) ) ;
object_idx = it - sla_print - > model ( ) . objects . begin ( ) ;
2018-11-26 14:19:42 +00:00
// Cache the Z offset to be applied to all volumes with this object_idx.
2019-04-08 16:09:31 +00:00
shift_zs [ object_idx ] = print_object - > get_current_elevation ( ) / relative_correction_z ;
2018-11-16 17:28:50 +00:00
// Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene.
// pairs of <instance_idx, print_instance_idx>
std : : vector < std : : pair < size_t , size_t > > instances [ std : : tuple_size < SLASteps > : : value ] ;
for ( size_t print_instance_idx = 0 ; print_instance_idx < print_object - > instances ( ) . size ( ) ; + + print_instance_idx ) {
const SLAPrintObject : : Instance & instance = print_object - > instances ( ) [ print_instance_idx ] ;
// Find index of ModelInstance corresponding to this SLAPrintObject::Instance.
auto it = std : : find_if ( model_object - > instances . begin ( ) , model_object - > instances . end ( ) ,
[ & instance ] ( const ModelInstance * mi ) { return mi - > id ( ) = = instance . instance_id ; } ) ;
assert ( it ! = model_object - > instances . end ( ) ) ;
int instance_idx = it - model_object - > instances . begin ( ) ;
for ( size_t istep = 0 ; istep < sla_steps . size ( ) ; + + istep )
if ( state . step [ istep ] . state = = PrintStateBase : : DONE ) {
ModelVolumeState key ( state . step [ istep ] . timestamp , instance . instance_id . id ) ;
auto it = std : : lower_bound ( aux_volume_state . begin ( ) , aux_volume_state . end ( ) , key , model_volume_state_lower ) ;
assert ( it ! = aux_volume_state . end ( ) & & it - > geometry_id = = key . geometry_id ) ;
if ( it - > new_geometry ( ) )
instances [ istep ] . emplace_back ( std : : pair < size_t , size_t > ( instance_idx , print_instance_idx ) ) ;
else
// Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
2018-11-22 12:33:20 +00:00
m_volumes . volumes [ it - > volume_idx ] - > composite_id = GLVolume : : CompositeID ( object_idx , m_volumes . volumes [ it - > volume_idx ] - > volume_idx ( ) , instance_idx ) ;
2018-11-16 17:28:50 +00:00
}
}
2018-11-19 12:04:19 +00:00
// stores the current volumes count
size_t volumes_count = m_volumes . volumes . size ( ) ;
2018-11-19 11:20:44 +00:00
for ( size_t istep = 0 ; istep < sla_steps . size ( ) ; + + istep )
if ( ! instances [ istep ] . empty ( ) )
m_volumes . load_object_auxiliary ( print_object , object_idx , instances [ istep ] , sla_steps [ istep ] , state . step [ istep ] . timestamp , m_use_VBOs & & m_initialized ) ;
2018-11-16 17:28:50 +00:00
}
2018-11-26 14:19:42 +00:00
// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
for ( GLVolume * volume : m_volumes . volumes )
volume - > set_sla_shift_z ( shift_zs [ volume - > object_idx ( ) ] ) ;
2018-11-16 17:28:50 +00:00
}
2018-11-13 16:45:44 +00:00
if ( printer_technology = = ptFFF & & m_config - > has ( " nozzle_diameter " ) )
2018-10-08 12:02:12 +00:00
{
// Should the wipe tower be visualized ?
unsigned int extruders_count = ( unsigned int ) dynamic_cast < const ConfigOptionFloats * > ( m_config - > option ( " nozzle_diameter " ) ) - > values . size ( ) ;
2018-06-08 07:40:00 +00:00
2018-10-08 12:02:12 +00:00
bool semm = dynamic_cast < const ConfigOptionBool * > ( m_config - > option ( " single_extruder_multi_material " ) ) - > value ;
bool wt = dynamic_cast < const ConfigOptionBool * > ( m_config - > option ( " wipe_tower " ) ) - > value ;
bool co = dynamic_cast < const ConfigOptionBool * > ( m_config - > option ( " complete_objects " ) ) - > value ;
2018-06-08 07:40:00 +00:00
2018-10-08 12:02:12 +00:00
if ( ( extruders_count > 1 ) & & semm & & wt & & ! co )
{
// Height of a print (Show at least a slab)
double height = std : : max ( m_model - > bounding_box ( ) . max ( 2 ) , 10.0 ) ;
2018-06-08 07:40:00 +00:00
2018-10-08 12:02:12 +00:00
float x = dynamic_cast < const ConfigOptionFloat * > ( m_config - > option ( " wipe_tower_x " ) ) - > value ;
float y = dynamic_cast < const ConfigOptionFloat * > ( m_config - > option ( " wipe_tower_y " ) ) - > value ;
float w = dynamic_cast < const ConfigOptionFloat * > ( m_config - > option ( " wipe_tower_width " ) ) - > value ;
float a = dynamic_cast < const ConfigOptionFloat * > ( m_config - > option ( " wipe_tower_rotation_angle " ) ) - > value ;
2018-06-08 07:40:00 +00:00
2018-11-22 14:29:59 +00:00
const Print * print = m_process - > fff_print ( ) ;
float depth = print - > get_wipe_tower_depth ( ) ;
2019-03-13 09:46:50 +00:00
// Calculate wipe tower brim spacing.
const DynamicPrintConfig & print_config = wxGetApp ( ) . preset_bundle - > prints . get_edited_preset ( ) . config ;
double layer_height = print_config . opt_float ( " layer_height " ) ;
double first_layer_height = print_config . get_abs_value ( " first_layer_height " , layer_height ) ;
float brim_spacing = print - > config ( ) . nozzle_diameter . values [ 0 ] * 1.25f - first_layer_height * ( 1. - M_PI_4 ) ;
2018-11-22 14:29:59 +00:00
if ( ! print - > is_step_done ( psWipeTower ) )
2018-10-08 12:02:12 +00:00
depth = ( 900.f / w ) * ( float ) ( extruders_count - 1 ) ;
2018-12-12 13:40:56 +00:00
int volume_idx_wipe_tower_new = m_volumes . load_wipe_tower_preview (
1000 , x , y , w , depth , ( float ) height , a , m_use_VBOs & & m_initialized , ! print - > is_step_done ( psWipeTower ) ,
2019-03-13 09:46:50 +00:00
brim_spacing * 4.5f ) ;
2018-12-12 13:40:56 +00:00
if ( volume_idx_wipe_tower_old ! = - 1 )
map_glvolume_old_to_new [ volume_idx_wipe_tower_old ] = volume_idx_wipe_tower_new ;
2018-10-08 12:02:12 +00:00
}
2018-06-08 07:40:00 +00:00
}
2018-10-08 12:02:12 +00:00
update_volumes_colors_by_extruder ( ) ;
2018-11-16 17:28:50 +00:00
// Update selection indices based on the old/new GLVolumeCollection.
2019-04-26 15:28:31 +00:00
if ( m_selection . get_mode ( ) = = Selection : : Instance )
m_selection . instances_changed ( instance_ids_selected ) ;
else
m_selection . volumes_changed ( map_glvolume_old_to_new ) ;
2018-11-16 17:28:50 +00:00
}
2019-03-26 14:55:47 +00:00
m_gizmos . update_data ( * this ) ;
2019-05-10 09:29:21 +00:00
m_gizmos . refresh_on_off_state ( m_selection ) ;
2018-11-16 17:28:50 +00:00
// Update the toolbar
2019-04-24 17:03:05 +00:00
if ( update_object_list )
post_event ( SimpleEvent ( EVT_GLCANVAS_OBJECT_SELECT ) ) ;
2018-06-08 07:40:00 +00:00
// checks for geometry outside the print volume to render it accordingly
2018-06-11 13:13:13 +00:00
if ( ! m_volumes . empty ( ) )
2018-06-08 07:40:00 +00:00
{
2018-07-18 12:26:42 +00:00
ModelInstance : : EPrintVolumeState state ;
2019-03-01 10:00:34 +00:00
const bool contained_min_one = m_volumes . check_outside_state ( m_config , & state ) ;
_set_warning_texture ( WarningTexture : : ObjectClashed , state = = ModelInstance : : PVS_Partly_Outside ) ;
_set_warning_texture ( WarningTexture : : ObjectOutside , state = = ModelInstance : : PVS_Fully_Outside ) ;
post_event ( Event < bool > ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS ,
contained_min_one & & ! m_model - > objects . empty ( ) & & state ! = ModelInstance : : PVS_Partly_Outside ) ) ;
// #ys_FIXME_delete_after_testing
// bool contained = m_volumes.check_outside_state(m_config, &state);
// if (!contained)
// {
// _set_warning_texture(WarningTexture::ObjectOutside, true);
// post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, state == ModelInstance::PVS_Fully_Outside));
// }
// else
// {
// m_volumes.reset_outside_state();
// _set_warning_texture(WarningTexture::ObjectOutside, false);
// post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, !m_model->objects.empty()));
// }
2018-06-08 07:40:00 +00:00
}
else
{
2019-02-20 11:09:45 +00:00
_set_warning_texture ( WarningTexture : : ObjectOutside , false ) ;
2019-03-01 10:00:34 +00:00
_set_warning_texture ( WarningTexture : : ObjectClashed , false ) ;
2018-10-03 09:34:39 +00:00
post_event ( Event < bool > ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , false ) ) ;
2018-06-08 07:40:00 +00:00
}
2018-10-08 12:02:12 +00:00
// restore to default value
m_regenerate_volumes = true ;
2018-12-07 15:23:04 +00:00
2019-03-07 10:49:00 +00:00
m_camera . set_scene_box ( scene_bounding_box ( ) ) ;
2018-12-07 15:23:04 +00:00
2019-01-10 13:43:07 +00:00
if ( m_selection . is_empty ( ) )
2019-01-14 13:42:21 +00:00
{
// If no object is selected, deactivate the active gizmo, if any
// Otherwise it may be shown after cleaning the scene (if it was active while the objects were deleted)
2019-01-10 13:43:07 +00:00
m_gizmos . reset_all_states ( ) ;
2019-01-14 13:42:21 +00:00
// If no object is selected, reset the objects manipulator on the sidebar
// to force a reset of its cache
auto manip = wxGetApp ( ) . obj_manipul ( ) ;
if ( manip ! = nullptr )
2019-05-03 10:36:26 +00:00
manip - > set_dirty ( ) ;
2019-01-14 13:42:21 +00:00
}
2018-11-16 17:28:50 +00:00
// and force this canvas to be redrawn.
m_dirty = true ;
2018-06-08 07:40:00 +00:00
}
2018-06-05 08:56:55 +00:00
void GLCanvas3D : : load_gcode_preview ( const GCodePreviewData & preview_data , const std : : vector < std : : string > & str_tool_colors )
{
2018-11-22 14:29:59 +00:00
const Print * print = this - > fff_print ( ) ;
if ( ( m_canvas ! = nullptr ) & & ( print ! = nullptr ) )
2018-06-05 08:56:55 +00:00
{
2019-01-30 18:02:30 +00:00
_set_current ( ) ;
2018-12-13 10:13:58 +00:00
std : : vector < float > tool_colors = _parse_colors ( str_tool_colors ) ;
2018-06-11 13:13:13 +00:00
if ( m_volumes . empty ( ) )
2018-06-05 08:56:55 +00:00
{
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 ) ;
2018-12-13 10:13:58 +00:00
if ( ! m_volumes . empty ( ) )
2018-06-05 08:56:55 +00:00
{
// removes empty volumes
2018-06-11 13:13:13 +00:00
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 ( ) ) ;
2019-05-07 10:29:48 +00:00
_load_fff_shells ( ) ;
2018-06-05 08:56:55 +00:00
}
2018-07-24 11:39:17 +00:00
_update_toolpath_volumes_outside_state ( ) ;
2018-06-05 08:56:55 +00:00
}
_update_gcode_volumes_visibility ( preview_data ) ;
2019-05-07 10:29:48 +00:00
_show_warning_texture_if_needed ( WarningTexture : : ToolpathOutside ) ;
2018-12-13 10:13:58 +00:00
if ( m_volumes . empty ( ) )
reset_legend_texture ( ) ;
else
_generate_legend_texture ( preview_data , tool_colors ) ;
2018-07-24 11:39:17 +00:00
}
}
2018-11-26 14:16:35 +00:00
void GLCanvas3D : : load_sla_preview ( )
{
const SLAPrint * print = this - > sla_print ( ) ;
if ( ( m_canvas ! = nullptr ) & & ( print ! = nullptr ) )
{
2019-01-30 18:02:30 +00:00
_set_current ( ) ;
2019-05-07 10:29:48 +00:00
_load_sla_shells ( ) ;
_update_sla_shells_outside_state ( ) ;
_show_warning_texture_if_needed ( WarningTexture : : SlaSupportsOutside ) ;
2018-11-26 14:16:35 +00:00
}
}
2019-01-29 14:11:29 +00:00
void GLCanvas3D : : load_preview ( const std : : vector < std : : string > & str_tool_colors , const std : : vector < double > & color_print_values )
2018-07-24 11:39:17 +00:00
{
2018-11-22 14:29:59 +00:00
const Print * print = this - > fff_print ( ) ;
if ( print = = nullptr )
2018-07-24 11:39:17 +00:00
return ;
2019-01-30 18:02:30 +00:00
_set_current ( ) ;
2018-07-24 11:39:17 +00:00
_load_print_toolpaths ( ) ;
_load_wipe_tower_toolpaths ( str_tool_colors ) ;
2018-11-22 14:29:59 +00:00
for ( const PrintObject * object : print - > objects ( ) )
2018-07-24 11:39:17 +00:00
{
if ( object ! = nullptr )
2019-01-29 14:11:29 +00:00
_load_print_object_toolpaths ( * object , str_tool_colors , color_print_values ) ;
2018-06-05 08:56:55 +00:00
}
2018-07-27 10:08:33 +00:00
2018-07-27 06:49:58 +00:00
for ( GLVolume * volume : m_volumes . volumes )
{
volume - > is_extrusion_path = true ;
}
2018-07-27 10:08:33 +00:00
2018-07-24 11:39:17 +00:00
_update_toolpath_volumes_outside_state ( ) ;
2019-05-07 10:29:48 +00:00
_show_warning_texture_if_needed ( WarningTexture : : ToolpathOutside ) ;
2019-01-29 14:11:29 +00:00
if ( color_print_values . empty ( ) )
reset_legend_texture ( ) ;
else {
auto preview_data = GCodePreviewData ( ) ;
preview_data . extrusion . view_type = GCodePreviewData : : Extrusion : : ColorPrint ;
const std : : vector < float > tool_colors = _parse_colors ( str_tool_colors ) ;
_generate_legend_texture ( preview_data , tool_colors ) ;
}
2018-09-06 14:10:31 +00:00
}
2018-06-06 12:19:28 +00:00
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 ) ;
2019-02-15 14:35:32 +00:00
m_canvas - > Bind ( wxEVT_KEY_DOWN , & GLCanvas3D : : on_key , this ) ;
m_canvas - > Bind ( wxEVT_KEY_UP , & GLCanvas3D : : on_key , this ) ;
2018-06-06 12:19:28 +00:00
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 - > 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 ) ;
2019-02-15 14:35:32 +00:00
m_canvas - > Unbind ( wxEVT_KEY_DOWN , & GLCanvas3D : : on_key , this ) ;
m_canvas - > Unbind ( wxEVT_KEY_UP , & GLCanvas3D : : on_key , this ) ;
2018-06-06 12:19:28 +00:00
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 ) ;
2019-02-03 13:11:09 +00:00
m_canvas - > Unbind ( wxEVT_LEFT_UP , & GLCanvas3D : : on_mouse , this ) ;
2018-06-06 12:19:28 +00:00
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 ) ;
}
}
2018-05-14 12:47:13 +00:00
void GLCanvas3D : : on_size ( wxSizeEvent & evt )
{
2018-06-01 13:54:41 +00:00
m_dirty = true ;
2018-05-14 12:47:13 +00:00
}
void GLCanvas3D : : on_idle ( wxIdleEvent & evt )
{
2019-03-14 12:54:05 +00:00
m_dirty | = m_toolbar . update_items_state ( ) ;
m_dirty | = m_view_toolbar . update_items_state ( ) ;
2018-06-01 13:54:41 +00:00
if ( ! m_dirty )
2018-05-14 12:47:13 +00:00
return ;
2018-05-28 13:23:01 +00:00
_refresh_if_shown_on_screen ( ) ;
2018-05-14 12:47:13 +00:00
}
2018-05-15 14:09:04 +00:00
void GLCanvas3D : : on_char ( wxKeyEvent & evt )
{
2019-04-10 11:52:18 +00:00
if ( ! m_initialized )
return ;
2019-02-03 13:06:13 +00:00
// see include/wx/defs.h enum wxKeyCode
int keyCode = evt . GetKeyCode ( ) ;
2019-02-03 16:57:55 +00:00
int ctrlMask = wxMOD_CONTROL ;
2019-02-15 14:35:32 +00:00
auto imgui = wxGetApp ( ) . imgui ( ) ;
if ( imgui - > update_key_data ( evt ) ) {
render ( ) ;
2019-02-20 15:55:00 +00:00
return ;
2019-02-15 14:35:32 +00:00
}
2019-03-26 13:08:02 +00:00
if ( m_gizmos . on_char ( evt , * this ) )
return ;
2019-02-04 08:37:49 +00:00
//#ifdef __APPLE__
// ctrlMask |= wxMOD_RAW_CONTROL;
//#endif /* __APPLE__ */
2019-02-03 16:57:55 +00:00
if ( ( evt . GetModifiers ( ) & ctrlMask ) ! = 0 ) {
2019-02-03 13:06:13 +00:00
switch ( keyCode ) {
2019-04-10 08:16:04 +00:00
# ifdef __APPLE__
2019-02-04 14:12:24 +00:00
case ' a ' :
case ' A ' :
2019-04-10 08:16:04 +00:00
# else /* __APPLE__ */
2019-02-11 07:21:37 +00:00
case WXK_CONTROL_A :
2019-04-10 08:16:04 +00:00
# endif /* __APPLE__ */
2019-02-11 07:21:37 +00:00
post_event ( SimpleEvent ( EVT_GLCANVAS_SELECT_ALL ) ) ;
break ;
2019-04-16 11:47:37 +00:00
# ifdef __APPLE__
case ' c ' :
case ' C ' :
# else /* __APPLE__ */
case WXK_CONTROL_C :
# endif /* __APPLE__ */
post_event ( SimpleEvent ( EVT_GLTOOLBAR_COPY ) ) ;
break ;
# ifdef __APPLE__
case ' v ' :
case ' V ' :
# else /* __APPLE__ */
case WXK_CONTROL_V :
# endif /* __APPLE__ */
post_event ( SimpleEvent ( EVT_GLTOOLBAR_PASTE ) ) ;
break ;
2019-02-03 13:06:13 +00:00
# ifdef __APPLE__
case WXK_BACK : // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead.
2019-02-04 14:46:12 +00:00
# else /* __APPLE__ */
case WXK_DELETE :
2019-02-03 13:06:13 +00:00
# endif /* __APPLE__ */
2019-02-04 14:46:12 +00:00
post_event ( SimpleEvent ( EVT_GLTOOLBAR_DELETE_ALL ) ) ; break ;
default : evt . Skip ( ) ;
2019-02-03 13:06:13 +00:00
}
} else if ( evt . HasModifiers ( ) ) {
2018-05-15 14:09:04 +00:00
evt . Skip ( ) ;
2019-02-03 13:06:13 +00:00
} else {
switch ( keyCode )
2018-05-15 14:09:04 +00:00
{
2019-02-03 13:06:13 +00:00
# ifdef __APPLE__
case WXK_BACK : // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead.
2019-02-04 14:46:12 +00:00
# else /* __APPLE__ */
case WXK_DELETE :
2019-02-03 13:06:13 +00:00
# endif /* __APPLE__ */
2019-03-26 13:08:02 +00:00
post_event ( SimpleEvent ( EVT_GLTOOLBAR_DELETE ) ) ;
2019-02-11 15:29:03 +00:00
break ;
2019-02-04 14:46:12 +00:00
case ' 0 ' : { select_view ( " iso " ) ; break ; }
2019-02-03 13:06:13 +00:00
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 ; }
case ' + ' : { post_event ( Event < int > ( EVT_GLCANVAS_INCREASE_INSTANCES , + 1 ) ) ; break ; }
case ' - ' : { post_event ( Event < int > ( EVT_GLCANVAS_INCREASE_INSTANCES , - 1 ) ) ; break ; }
case ' ? ' : { post_event ( SimpleEvent ( EVT_GLCANVAS_QUESTION_MARK ) ) ; break ; }
case ' A ' :
2019-03-26 13:08:02 +00:00
case ' a ' : { post_event ( SimpleEvent ( EVT_GLCANVAS_ARRANGE ) ) ; break ; }
2019-02-03 13:06:13 +00:00
case ' B ' :
case ' b ' : { zoom_to_bed ( ) ; break ; }
case ' I ' :
case ' i ' : { set_camera_zoom ( 1.0f ) ; break ; }
case ' O ' :
case ' o ' : { set_camera_zoom ( - 1.0f ) ; break ; }
case ' Z ' :
case ' z ' : { m_selection . is_empty ( ) ? zoom_to_volumes ( ) : zoom_to_selection ( ) ; break ; }
2018-05-15 14:09:04 +00:00
default :
2019-02-03 13:06:13 +00:00
{
2019-03-26 13:08:02 +00:00
evt . Skip ( ) ;
2019-02-03 13:06:13 +00:00
break ;
}
2018-05-15 14:09:04 +00:00
}
}
}
2019-02-15 14:35:32 +00:00
void GLCanvas3D : : on_key ( wxKeyEvent & evt )
2019-02-11 07:21:37 +00:00
{
2019-02-21 17:31:01 +00:00
const int keyCode = evt . GetKeyCode ( ) ;
2019-02-15 14:35:32 +00:00
auto imgui = wxGetApp ( ) . imgui ( ) ;
if ( imgui - > update_key_data ( evt ) ) {
render ( ) ;
2019-03-26 13:38:30 +00:00
}
else
{
if ( ! m_gizmos . on_key ( evt , * this ) )
{
if ( evt . GetEventType ( ) = = wxEVT_KEY_UP ) {
if ( m_tab_down & & keyCode = = WXK_TAB & & ! evt . HasAnyModifiers ( ) ) {
// Enable switching between 3D and Preview with Tab
// m_canvas->HandleAsNavigationKey(evt); // XXX: Doesn't work in some cases / on Linux
post_event ( SimpleEvent ( EVT_GLCANVAS_TAB ) ) ;
}
2019-04-24 13:43:52 +00:00
else if ( keyCode = = WXK_SHIFT )
{
2019-04-25 07:10:03 +00:00
if ( m_picking_enabled & & m_rectangle_selection . is_dragging ( ) )
{
2019-04-25 10:31:55 +00:00
_update_selection_from_hover ( ) ;
2019-04-25 07:10:03 +00:00
m_rectangle_selection . stop_dragging ( ) ;
2019-04-26 12:07:46 +00:00
m_mouse . ignore_left_up = true ;
2019-04-25 07:10:03 +00:00
m_dirty = true ;
}
2019-04-29 06:26:08 +00:00
// set_cursor(Standard);
2019-04-24 13:43:52 +00:00
}
else if ( keyCode = = WXK_ALT )
{
2019-04-25 07:10:03 +00:00
if ( m_picking_enabled & & m_rectangle_selection . is_dragging ( ) )
{
2019-04-25 10:31:55 +00:00
_update_selection_from_hover ( ) ;
2019-04-25 07:10:03 +00:00
m_rectangle_selection . stop_dragging ( ) ;
2019-04-26 12:07:46 +00:00
m_mouse . ignore_left_up = true ;
2019-04-25 07:10:03 +00:00
m_dirty = true ;
}
2019-04-29 06:26:08 +00:00
// set_cursor(Standard);
2019-04-24 13:43:52 +00:00
}
2019-04-25 07:46:26 +00:00
else if ( keyCode = = WXK_CONTROL )
m_dirty = true ;
2019-03-26 13:38:30 +00:00
}
else if ( evt . GetEventType ( ) = = wxEVT_KEY_DOWN ) {
m_tab_down = keyCode = = WXK_TAB & & ! evt . HasAnyModifiers ( ) ;
2019-04-24 13:43:52 +00:00
if ( keyCode = = WXK_SHIFT )
{
2019-04-25 07:10:03 +00:00
if ( m_picking_enabled & & ( m_gizmos . get_current_type ( ) ! = GLGizmosManager : : SlaSupports ) )
2019-04-26 12:07:46 +00:00
{
m_mouse . ignore_left_up = false ;
2019-04-29 06:26:08 +00:00
// set_cursor(Cross);
2019-04-26 12:07:46 +00:00
}
2019-04-24 13:43:52 +00:00
}
else if ( keyCode = = WXK_ALT )
{
2019-04-25 07:10:03 +00:00
if ( m_picking_enabled & & ( m_gizmos . get_current_type ( ) ! = GLGizmosManager : : SlaSupports ) )
2019-04-26 12:07:46 +00:00
{
m_mouse . ignore_left_up = false ;
2019-04-29 06:26:08 +00:00
// set_cursor(Cross);
2019-04-26 12:07:46 +00:00
}
2019-04-24 13:43:52 +00:00
}
2019-04-25 07:46:26 +00:00
else if ( keyCode = = WXK_CONTROL )
m_dirty = true ;
2019-03-26 13:38:30 +00:00
}
2019-02-21 10:54:18 +00:00
}
2019-02-15 14:35:32 +00:00
}
2019-02-11 07:21:37 +00:00
2019-02-21 17:31:01 +00:00
if ( keyCode ! = WXK_TAB
& & keyCode ! = WXK_LEFT
& & keyCode ! = WXK_UP
& & keyCode ! = WXK_RIGHT
& & keyCode ! = WXK_DOWN ) {
evt . Skip ( ) ; // Needed to have EVT_CHAR generated as well
}
2019-02-11 07:21:37 +00:00
}
2018-05-28 13:23:01 +00:00
void GLCanvas3D : : on_mouse_wheel ( wxMouseEvent & evt )
{
2019-04-09 09:18:40 +00:00
if ( ! m_initialized )
return ;
2018-05-28 13:23:01 +00:00
// Ignore the wheel events if the middle button is pressed.
if ( evt . MiddleIsDown ( ) )
return ;
2019-02-06 07:44:06 +00:00
# if ENABLE_RETINA_GL
const float scale = m_retina_helper - > get_scale_factor ( ) ;
evt . SetX ( evt . GetX ( ) * scale ) ;
evt . SetY ( evt . GetY ( ) * scale ) ;
# endif
2018-05-28 13:23:01 +00:00
// Performs layers editing updates, if enabled
2018-06-13 11:14:17 +00:00
if ( is_layers_editing_enabled ( ) )
2018-05-28 13:23:01 +00:00
{
2018-10-09 13:56:34 +00:00
int object_idx_selected = m_selection . get_object_idx ( ) ;
2018-05-28 13:23:01 +00:00
if ( object_idx_selected ! = - 1 )
{
// A volume is selected. Test, whether hovering over a layer thickness bar.
2018-06-13 11:14:17 +00:00
if ( m_layers_editing . bar_rect_contains ( * this , ( float ) evt . GetX ( ) , ( float ) evt . GetY ( ) ) )
2018-05-28 13:23:01 +00:00
{
// Adjust the width of the selection.
2018-06-01 13:54:41 +00:00
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 ) ;
2018-05-28 13:23:01 +00:00
if ( m_canvas ! = nullptr )
m_canvas - > Refresh ( ) ;
return ;
}
}
}
2019-04-17 08:16:39 +00:00
// Inform gizmos about the event so they have the opportunity to react.
if ( m_gizmos . on_mouse_wheel ( evt , * this ) )
return ;
2018-05-28 13:23:01 +00:00
// Calculate the zoom delta and apply it to the current zoom factor
float zoom = ( float ) evt . GetWheelRotation ( ) / ( float ) evt . GetWheelDelta ( ) ;
2018-11-29 08:03:38 +00:00
set_camera_zoom ( zoom ) ;
2018-05-28 13:23:01 +00:00
}
2018-05-30 13:18:45 +00:00
void GLCanvas3D : : on_timer ( wxTimerEvent & evt )
{
2019-01-21 09:06:51 +00:00
if ( m_layers_editing . state = = LayersEditing : : Editing )
_perform_layer_editing_action ( ) ;
2018-05-30 13:18:45 +00:00
}
2019-02-25 16:09:44 +00:00
# ifndef NDEBUG
// #define SLIC3R_DEBUG_MOUSE_EVENTS
# endif
# ifdef SLIC3R_DEBUG_MOUSE_EVENTS
std : : string format_mouse_event_debug_message ( const wxMouseEvent & evt )
{
static int idx = 0 ;
char buf [ 2048 ] ;
std : : string out ;
sprintf ( buf , " Mouse Event %d - " , idx + + ) ;
out = buf ;
if ( evt . Entering ( ) )
out + = " Entering " ;
if ( evt . Leaving ( ) )
out + = " Leaving " ;
if ( evt . Dragging ( ) )
out + = " Dragging " ;
if ( evt . Moving ( ) )
out + = " Moving " ;
if ( evt . Magnify ( ) )
out + = " Magnify " ;
if ( evt . LeftDown ( ) )
out + = " LeftDown " ;
if ( evt . LeftUp ( ) )
out + = " LeftUp " ;
if ( evt . LeftDClick ( ) )
out + = " LeftDClick " ;
if ( evt . MiddleDown ( ) )
out + = " MiddleDown " ;
if ( evt . MiddleUp ( ) )
out + = " MiddleUp " ;
if ( evt . MiddleDClick ( ) )
out + = " MiddleDClick " ;
if ( evt . RightDown ( ) )
out + = " RightDown " ;
if ( evt . RightUp ( ) )
out + = " RightUp " ;
if ( evt . RightDClick ( ) )
out + = " RightDClick " ;
sprintf ( buf , " (%d, %d) " , evt . GetX ( ) , evt . GetY ( ) ) ;
out + = buf ;
return out ;
}
# endif /* SLIC3R_DEBUG_MOUSE_EVENTS */
2018-05-31 11:51:50 +00:00
void GLCanvas3D : : on_mouse ( wxMouseEvent & evt )
{
2019-03-26 11:30:17 +00:00
auto mouse_up_cleanup = [ this ] ( ) {
m_moving = false ;
m_mouse . drag . move_volume_idx = - 1 ;
m_mouse . set_start_position_3D_as_invalid ( ) ;
m_mouse . set_start_position_2D_as_invalid ( ) ;
m_mouse . dragging = false ;
2019-04-26 12:07:46 +00:00
m_mouse . ignore_left_up = false ;
2019-03-26 11:30:17 +00:00
m_dirty = true ;
if ( m_canvas - > HasCapture ( ) )
m_canvas - > ReleaseMouse ( ) ;
} ;
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
const float scale = m_retina_helper - > get_scale_factor ( ) ;
evt . SetX ( evt . GetX ( ) * scale ) ;
evt . SetY ( evt . GetY ( ) * scale ) ;
# endif
2019-02-22 15:16:04 +00:00
Point pos ( evt . GetX ( ) , evt . GetY ( ) ) ;
ImGuiWrapper * imgui = wxGetApp ( ) . imgui ( ) ;
2018-11-26 09:56:07 +00:00
if ( imgui - > update_mouse_data ( evt ) ) {
2019-02-22 15:16:04 +00:00
m_mouse . position = evt . Leaving ( ) ? Vec2d ( - 1.0 , - 1.0 ) : pos . cast < double > ( ) ;
2018-11-26 09:56:07 +00:00
render ( ) ;
2019-02-25 16:09:44 +00:00
# ifdef SLIC3R_DEBUG_MOUSE_EVENTS
printf ( ( format_mouse_event_debug_message ( evt ) + " - Consumed by ImGUI \n " ) . c_str ( ) ) ;
# endif /* SLIC3R_DEBUG_MOUSE_EVENTS */
return ;
2018-11-26 09:56:07 +00:00
}
2018-10-31 09:19:44 +00:00
2019-02-26 10:50:45 +00:00
# ifdef __WXMSW__
2019-02-25 16:09:44 +00:00
bool on_enter_workaround = false ;
2019-02-22 15:16:04 +00:00
if ( ! evt . Entering ( ) & & ! evt . Leaving ( ) & & m_mouse . position . x ( ) = = - 1.0 ) {
// Workaround for SPE-832: There seems to be a mouse event sent to the window before evt.Entering()
m_mouse . position = pos . cast < double > ( ) ;
render ( ) ;
2019-02-25 16:09:44 +00:00
# ifdef SLIC3R_DEBUG_MOUSE_EVENTS
printf ( ( format_mouse_event_debug_message ( evt ) + " - OnEnter workaround \n " ) . c_str ( ) ) ;
# endif /* SLIC3R_DEBUG_MOUSE_EVENTS */
on_enter_workaround = true ;
2019-02-26 10:50:45 +00:00
} else
# endif /* __WXMSW__ */
{
2019-02-25 16:09:44 +00:00
# ifdef SLIC3R_DEBUG_MOUSE_EVENTS
printf ( ( format_mouse_event_debug_message ( evt ) + " - other \n " ) . c_str ( ) ) ;
# endif /* SLIC3R_DEBUG_MOUSE_EVENTS */
}
2018-05-31 11:51:50 +00:00
2019-03-26 11:30:17 +00:00
if ( m_toolbar . on_mouse ( evt , * this ) )
{
if ( evt . LeftUp ( ) | | evt . MiddleUp ( ) | | evt . RightUp ( ) )
mouse_up_cleanup ( ) ;
m_mouse . set_start_position_3D_as_invalid ( ) ;
return ;
}
2019-03-14 12:54:05 +00:00
2019-03-26 11:30:17 +00:00
if ( m_view_toolbar . on_mouse ( evt , * this ) )
2019-03-19 08:21:27 +00:00
{
2019-03-26 11:30:17 +00:00
if ( evt . LeftUp ( ) | | evt . MiddleUp ( ) | | evt . RightUp ( ) )
mouse_up_cleanup ( ) ;
2019-03-19 08:21:27 +00:00
m_mouse . set_start_position_3D_as_invalid ( ) ;
return ;
}
2019-03-14 12:54:05 +00:00
2019-03-26 08:01:04 +00:00
if ( m_gizmos . on_mouse ( evt , * this ) )
2019-03-19 08:21:27 +00:00
{
2019-03-26 11:30:17 +00:00
if ( evt . LeftUp ( ) | | evt . MiddleUp ( ) | | evt . RightUp ( ) )
mouse_up_cleanup ( ) ;
2019-03-26 11:39:40 +00:00
2019-03-19 08:21:27 +00:00
m_mouse . set_start_position_3D_as_invalid ( ) ;
return ;
}
2019-03-14 12:54:05 +00:00
2019-01-31 09:50:16 +00:00
if ( m_picking_enabled )
_set_current ( ) ;
2018-10-09 13:56:34 +00:00
int selected_object_idx = m_selection . get_object_idx ( ) ;
2018-10-08 12:02:12 +00:00
int layer_editing_object_idx = is_layers_editing_enabled ( ) ? selected_object_idx : - 1 ;
2019-01-21 09:06:51 +00:00
m_layers_editing . select_object ( * m_model , layer_editing_object_idx ) ;
2018-05-31 11:51:50 +00:00
2019-01-14 08:29:17 +00:00
if ( m_mouse . drag . move_requires_threshold & & m_mouse . is_move_start_threshold_position_2D_defined ( ) & & m_mouse . is_move_threshold_met ( pos ) )
{
m_mouse . drag . move_requires_threshold = false ;
m_mouse . set_move_start_threshold_position_2D_as_invalid ( ) ;
}
2019-02-26 10:50:45 +00:00
if ( evt . ButtonDown ( ) & & wxWindow : : FindFocus ( ) ! = this - > m_canvas )
// Grab keyboard focus on any mouse click event.
m_canvas - > SetFocus ( ) ;
2018-05-31 11:51:50 +00:00
if ( evt . Entering ( ) )
{
2019-01-08 09:06:10 +00:00
//#if defined(__WXMSW__) || defined(__linux__)
// // On Windows and Linux needs focus in order to catch key events
// Set focus in order to remove it from sidebar fields
2019-01-02 16:49:23 +00:00
if ( m_canvas ! = nullptr ) {
// Only set focus, if the top level window of this canvas is active.
2019-02-06 11:23:57 +00:00
auto p = dynamic_cast < wxWindow * > ( evt . GetEventObject ( ) ) ;
2019-01-02 16:49:23 +00:00
while ( p - > GetParent ( ) )
p = p - > GetParent ( ) ;
auto * top_level_wnd = dynamic_cast < wxTopLevelWindow * > ( p ) ;
if ( top_level_wnd & & top_level_wnd - > IsActive ( ) )
m_canvas - > SetFocus ( ) ;
2019-03-05 09:54:03 +00:00
m_mouse . position = pos . cast < double > ( ) ;
2019-04-24 13:07:28 +00:00
// 1) forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while
2019-02-26 10:50:45 +00:00
// the context menu is shown, ensuring it to disappear if the mouse is outside any volume and to
// change the volume hover state if any is under the mouse
2019-03-05 09:54:03 +00:00
// 2) when switching between 3d view and preview the size of the canvas changes if the side panels are visible,
// so forces a resize to avoid multiple renders with different sizes (seen as flickering)
_refresh_if_shown_on_screen ( ) ;
2019-02-06 11:23:57 +00:00
}
2018-06-01 13:54:41 +00:00
m_mouse . set_start_position_2D_as_invalid ( ) ;
2019-01-08 09:06:10 +00:00
//#endif
2018-07-26 10:10:45 +00:00
}
2018-07-26 10:51:31 +00:00
else if ( evt . Leaving ( ) )
{
2018-07-30 07:09:14 +00:00
// to remove hover on objects when the mouse goes out of this canvas
2018-08-21 19:05:24 +00:00
m_mouse . position = Vec2d ( - 1.0 , - 1.0 ) ;
2018-07-30 07:09:14 +00:00
m_dirty = true ;
2018-07-26 10:51:31 +00:00
}
2018-05-31 11:51:50 +00:00
else if ( evt . LeftDown ( ) | | evt . RightDown ( ) )
{
// If user pressed left or right button we first check whether this happened
// on a volume or not.
2018-06-01 13:54:41 +00:00
m_layers_editing . state = LayersEditing : : Unknown ;
2018-08-17 13:53:43 +00:00
if ( ( layer_editing_object_idx ! = - 1 ) & & m_layers_editing . bar_rect_contains ( * this , pos ( 0 ) , pos ( 1 ) ) )
2018-05-31 11:51:50 +00:00
{
// A volume is selected and the mouse is inside the layer thickness bar.
// Start editing the layer height.
2018-06-01 13:54:41 +00:00
m_layers_editing . state = LayersEditing : : Editing ;
_perform_layer_editing_action ( & evt ) ;
2018-05-31 11:51:50 +00:00
}
2018-08-17 13:53:43 +00:00
else if ( ( layer_editing_object_idx ! = - 1 ) & & m_layers_editing . reset_rect_contains ( * this , pos ( 0 ) , pos ( 1 ) ) )
2018-05-31 11:51:50 +00:00
{
if ( evt . LeftDown ( ) )
{
2019-01-21 09:06:51 +00:00
// A volume is selected and the mouse is inside the reset button. Reset the ModelObject's layer height profile.
2019-01-23 13:00:03 +00:00
m_layers_editing . reset_layer_height_profile ( * this ) ;
2018-05-31 11:51:50 +00:00
// Index 2 means no editing, just wait for mouse up event.
2018-06-01 13:54:41 +00:00
m_layers_editing . state = LayersEditing : : Completed ;
m_dirty = true ;
2018-05-31 11:51:50 +00:00
}
}
2019-04-25 07:10:03 +00:00
else if ( evt . LeftDown ( ) & & ( evt . ShiftDown ( ) | | evt . AltDown ( ) ) & & m_picking_enabled )
{
if ( m_gizmos . get_current_type ( ) ! = GLGizmosManager : : SlaSupports )
{
m_rectangle_selection . start_dragging ( m_mouse . position , evt . ShiftDown ( ) ? GLSelectionRectangle : : Select : GLSelectionRectangle : : Deselect ) ;
m_dirty = true ;
}
}
2018-05-31 11:51:50 +00:00
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.
2019-04-24 13:07:28 +00:00
if ( m_picking_enabled & & ( ! m_hover_volume_idxs . empty ( ) | | ! is_layers_editing_enabled ( ) ) )
2018-05-31 11:51:50 +00:00
{
2019-04-24 13:07:28 +00:00
if ( evt . LeftDown ( ) & & ! m_hover_volume_idxs . empty ( ) )
2018-10-08 12:02:12 +00:00
{
2019-04-24 13:07:28 +00:00
int volume_idx = get_first_hover_volume_idx ( ) ;
bool already_selected = m_selection . contains_volume ( volume_idx ) ;
2019-04-08 06:30:28 +00:00
bool ctrl_down = evt . CmdDown ( ) ;
2018-10-30 10:18:15 +00:00
2019-01-03 10:24:03 +00:00
Selection : : IndicesList curr_idxs = m_selection . get_volume_idxs ( ) ;
2019-04-08 06:30:28 +00:00
if ( already_selected & & ctrl_down )
2019-04-24 13:07:28 +00:00
m_selection . remove ( volume_idx ) ;
2018-10-30 10:18:15 +00:00
else
{
2019-04-24 13:07:28 +00:00
m_selection . add ( volume_idx , ! ctrl_down , true ) ;
2019-01-14 08:29:17 +00:00
m_mouse . drag . move_requires_threshold = ! already_selected ;
if ( already_selected )
m_mouse . set_move_start_threshold_position_2D_as_invalid ( ) ;
else
m_mouse . drag . move_start_threshold_position_2D = pos ;
2018-10-30 10:18:15 +00:00
}
2018-10-08 12:02:12 +00:00
2019-04-24 13:07:28 +00:00
// propagate event through callback
2019-01-03 10:24:03 +00:00
if ( curr_idxs ! = m_selection . get_volume_idxs ( ) )
{
2019-05-09 08:09:33 +00:00
if ( m_selection . is_empty ( ) )
m_gizmos . reset_all_states ( ) ;
else
m_gizmos . refresh_on_off_state ( m_selection ) ;
2019-03-26 14:55:47 +00:00
m_gizmos . update_data ( * this ) ;
2019-01-03 10:24:03 +00:00
post_event ( SimpleEvent ( EVT_GLCANVAS_OBJECT_SELECT ) ) ;
m_dirty = true ;
}
2018-10-08 12:02:12 +00:00
}
2018-05-31 11:51:50 +00:00
}
2019-04-24 13:07:28 +00:00
if ( ! m_hover_volume_idxs . empty ( ) )
2018-05-31 11:51:50 +00:00
{
2018-10-08 12:02:12 +00:00
if ( evt . LeftDown ( ) & & m_moving_enabled & & ( m_mouse . drag . move_volume_idx = = - 1 ) )
2018-05-31 11:51:50 +00:00
{
// Only accept the initial position, if it is inside the volume bounding box.
2019-04-24 13:07:28 +00:00
int volume_idx = get_first_hover_volume_idx ( ) ;
BoundingBoxf3 volume_bbox = m_volumes . volumes [ volume_idx ] - > transformed_bounding_box ( ) ;
2018-05-31 11:51:50 +00:00
volume_bbox . offset ( 1.0 ) ;
2018-11-15 10:38:40 +00:00
if ( volume_bbox . contains ( m_mouse . scene_position ) )
2018-05-31 11:51:50 +00:00
{
// The dragging operation is initiated.
2019-04-24 13:07:28 +00:00
m_mouse . drag . move_volume_idx = volume_idx ;
2018-10-08 12:02:12 +00:00
m_selection . start_dragging ( ) ;
2018-11-15 10:38:40 +00:00
m_mouse . drag . start_position_3D = m_mouse . scene_position ;
2018-11-26 09:41:16 +00:00
m_moving = true ;
2018-05-31 11:51:50 +00:00
}
}
}
}
}
2019-03-26 11:39:40 +00:00
else if ( evt . Dragging ( ) & & evt . LeftIsDown ( ) & & ( m_layers_editing . state = = LayersEditing : : Unknown ) & & ( m_mouse . drag . move_volume_idx ! = - 1 ) )
2018-05-31 14:04:59 +00:00
{
2019-01-14 08:29:17 +00:00
if ( ! m_mouse . drag . move_requires_threshold )
{
m_mouse . dragging = true ;
2018-06-01 13:54:41 +00:00
2019-02-22 10:34:37 +00:00
Vec3d cur_pos = m_mouse . drag . start_position_3D ;
2019-01-14 08:29:17 +00:00
// we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag
2019-04-24 13:07:28 +00:00
if ( m_selection . contains_volume ( get_first_hover_volume_idx ( ) ) )
2019-02-22 10:34:37 +00:00
{
2019-03-07 10:49:00 +00:00
if ( m_camera . get_theta ( ) = = 90.0f )
2019-02-22 10:34:37 +00:00
{
// side view -> move selected volumes orthogonally to camera view direction
Linef3 ray = mouse_ray ( pos ) ;
Vec3d dir = ray . unit_vector ( ) ;
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
Vec3d inters = ray . a + ( m_mouse . drag . start_position_3D - ray . a ) . dot ( dir ) / dir . squaredNorm ( ) * dir ;
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - m_mouse . drag . start_position_3D ;
2019-04-01 08:00:10 +00:00
Vec3d camera_right = m_camera . get_dir_right ( ) ;
Vec3d camera_up = m_camera . get_dir_up ( ) ;
2019-02-22 10:34:37 +00:00
2019-03-06 11:09:20 +00:00
// finds projection of the vector along the camera axes
double projection_x = inters_vec . dot ( camera_right ) ;
double projection_z = inters_vec . dot ( camera_up ) ;
2019-02-22 10:34:37 +00:00
2019-03-06 11:09:20 +00:00
// apply offset
cur_pos = m_mouse . drag . start_position_3D + projection_x * camera_right + projection_z * camera_up ;
2019-02-22 10:34:37 +00:00
}
else
{
// Generic view
// Get new position at the same Z of the initial click point.
float z0 = 0.0f ;
float z1 = 1.0f ;
cur_pos = Linef3 ( _mouse_to_3d ( pos , & z0 ) , _mouse_to_3d ( pos , & z1 ) ) . intersect_plane ( m_mouse . drag . start_position_3D ( 2 ) ) ;
}
}
2018-05-31 14:04:59 +00:00
2019-01-14 08:29:17 +00:00
m_regenerate_volumes = false ;
m_selection . translate ( cur_pos - m_mouse . drag . start_position_3D ) ;
2019-05-03 10:36:26 +00:00
wxGetApp ( ) . obj_manipul ( ) - > set_dirty ( ) ;
2019-01-14 08:29:17 +00:00
m_dirty = true ;
}
2018-05-31 14:04:59 +00:00
}
2019-04-25 07:10:03 +00:00
else if ( evt . Dragging ( ) & & evt . LeftIsDown ( ) & & m_picking_enabled & & m_rectangle_selection . is_dragging ( ) )
{
m_rectangle_selection . dragging ( pos . cast < double > ( ) ) ;
m_dirty = true ;
}
2019-03-26 08:01:04 +00:00
else if ( evt . Dragging ( ) )
2018-05-31 14:04:59 +00:00
{
2018-06-01 13:54:41 +00:00
m_mouse . dragging = true ;
2018-06-13 11:14:17 +00:00
if ( ( m_layers_editing . state ! = LayersEditing : : Unknown ) & & ( layer_editing_object_idx ! = - 1 ) )
2018-05-31 14:04:59 +00:00
{
2018-06-01 13:54:41 +00:00
if ( m_layers_editing . state = = LayersEditing : : Editing )
_perform_layer_editing_action ( & evt ) ;
2018-05-31 14:04:59 +00:00
}
2019-03-01 11:23:33 +00:00
// do not process the dragging if the left mouse was set down in another canvas
2019-03-14 12:54:05 +00:00
else if ( evt . LeftIsDown ( ) )
2018-06-01 07:00:30 +00:00
{
// if dragging over blank area with left button, rotate
2019-04-24 13:07:28 +00:00
if ( m_hover_volume_idxs . empty ( ) & & m_mouse . is_start_position_3D_defined ( ) )
2018-06-01 07:00:30 +00:00
{
2018-08-21 15:43:05 +00:00
const Vec3d & orig = m_mouse . drag . start_position_3D ;
2019-04-30 13:09:25 +00:00
float sign = m_camera . inverted_phi ? - 1.0f : 1.0f ;
m_camera . phi + = sign * ( ( float ) pos ( 0 ) - ( float ) orig ( 0 ) ) * TRACKBALLSIZE ;
2019-03-07 10:49:00 +00:00
m_camera . set_theta ( m_camera . get_theta ( ) - ( ( float ) pos ( 1 ) - ( float ) orig ( 1 ) ) * TRACKBALLSIZE , wxGetApp ( ) . preset_bundle - > printers . get_edited_preset ( ) . printer_technology ( ) ! = ptSLA ) ;
2018-06-01 13:54:41 +00:00
m_dirty = true ;
2018-06-01 07:00:30 +00:00
}
2018-08-24 08:20:00 +00:00
m_mouse . drag . start_position_3D = Vec3d ( ( double ) pos ( 0 ) , ( double ) pos ( 1 ) , 0.0 ) ;
2018-06-01 07:00:30 +00:00
}
else if ( evt . MiddleIsDown ( ) | | evt . RightIsDown ( ) )
{
// If dragging over blank area with right button, pan.
2018-06-01 13:54:41 +00:00
if ( m_mouse . is_start_position_2D_defined ( ) )
2018-06-01 07:00:30 +00:00
{
// get point in model space at Z = 0
float z = 0.0f ;
2018-08-21 15:43:05 +00:00
const Vec3d & cur_pos = _mouse_to_3d ( pos , & z ) ;
Vec3d orig = _mouse_to_3d ( m_mouse . drag . start_position_2D , & z ) ;
2019-03-07 10:49:00 +00:00
m_camera . set_target ( m_camera . get_target ( ) + orig - cur_pos ) ;
2018-06-01 13:54:41 +00:00
m_dirty = true ;
2018-06-01 07:00:30 +00:00
}
2018-06-01 13:54:41 +00:00
m_mouse . drag . start_position_2D = pos ;
2018-06-01 07:00:30 +00:00
}
2018-05-31 14:04:59 +00:00
}
else if ( evt . LeftUp ( ) | | evt . MiddleUp ( ) | | evt . RightUp ( ) )
{
2018-06-01 13:54:41 +00:00
if ( m_layers_editing . state ! = LayersEditing : : Unknown )
2018-05-31 14:04:59 +00:00
{
2018-06-01 13:54:41 +00:00
m_layers_editing . state = LayersEditing : : Unknown ;
_stop_timer ( ) ;
2019-01-21 09:06:51 +00:00
m_layers_editing . accept_changes ( * this ) ;
2018-05-31 14:04:59 +00:00
}
2019-03-20 13:04:59 +00:00
else if ( ( m_mouse . drag . move_volume_idx ! = - 1 ) & & m_mouse . dragging )
2018-05-31 14:04:59 +00:00
{
2018-10-08 12:02:12 +00:00
m_regenerate_volumes = false ;
2018-11-21 09:36:09 +00:00
do_move ( ) ;
2019-05-03 10:36:26 +00:00
wxGetApp ( ) . obj_manipul ( ) - > set_dirty ( ) ;
2018-11-26 07:36:31 +00:00
// Let the platter know that the dragging finished, so a delayed refresh
// of the scene with the background processing data should be performed.
post_event ( SimpleEvent ( EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED ) ) ;
2018-05-31 14:04:59 +00:00
}
2019-04-25 07:10:03 +00:00
else if ( evt . LeftUp ( ) & & m_picking_enabled & & m_rectangle_selection . is_dragging ( ) )
{
2019-04-25 10:31:55 +00:00
if ( evt . ShiftDown ( ) | | evt . AltDown ( ) )
_update_selection_from_hover ( ) ;
2019-04-25 07:10:03 +00:00
m_rectangle_selection . stop_dragging ( ) ;
}
2019-04-26 12:07:46 +00:00
else if ( evt . LeftUp ( ) & & ! m_mouse . ignore_left_up & & ! m_mouse . dragging & & m_hover_volume_idxs . empty ( ) & & ! is_layers_editing_enabled ( ) )
2018-09-12 10:14:20 +00:00
{
2018-06-14 10:34:19 +00:00
// deselect and propagate event through callback
2019-03-26 11:30:17 +00:00
if ( ! evt . ShiftDown ( ) & & m_picking_enabled )
2018-06-14 10:34:19 +00:00
{
2018-10-08 12:02:12 +00:00
m_selection . clear ( ) ;
2018-11-07 11:11:34 +00:00
m_selection . set_mode ( Selection : : Instance ) ;
2019-05-03 10:36:26 +00:00
wxGetApp ( ) . obj_manipul ( ) - > set_dirty ( ) ;
2019-01-25 13:55:20 +00:00
m_gizmos . reset_all_states ( ) ;
2019-03-26 14:55:47 +00:00
m_gizmos . update_data ( * this ) ;
2019-01-25 13:55:20 +00:00
post_event ( SimpleEvent ( EVT_GLCANVAS_OBJECT_SELECT ) ) ;
2018-06-14 10:34:19 +00:00
}
2018-06-15 12:10:28 +00:00
}
2019-04-30 13:09:25 +00:00
else if ( evt . LeftUp ( ) & & m_mouse . dragging )
// Flips X mouse deltas if bed is upside down
m_camera . inverted_phi = ( m_camera . get_dir_up ( ) ( 2 ) < 0.0 ) ;
2019-03-18 09:47:01 +00:00
else if ( evt . RightUp ( ) )
{
m_mouse . position = pos . cast < double > ( ) ;
2019-04-24 13:07:28 +00:00
// forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while
2019-03-18 09:47:01 +00:00
// the context menu is already shown
render ( ) ;
2019-04-24 13:07:28 +00:00
if ( ! m_hover_volume_idxs . empty ( ) )
2019-03-18 09:47:01 +00:00
{
// if right clicking on volume, propagate event through callback (shows context menu)
2019-04-24 13:07:28 +00:00
int volume_idx = get_first_hover_volume_idx ( ) ;
if ( ! m_volumes . volumes [ volume_idx ] - > is_wipe_tower // no context menu for the wipe tower
2019-03-20 12:51:25 +00:00
& & m_gizmos . get_current_type ( ) ! = GLGizmosManager : : SlaSupports ) // disable context menu when the gizmo is open
2019-03-18 09:47:01 +00:00
{
// forces the selection of the volume
2019-04-24 13:07:28 +00:00
m_selection . add ( volume_idx ) ;
2019-03-26 11:30:17 +00:00
m_gizmos . refresh_on_off_state ( m_selection ) ;
2019-03-18 09:47:01 +00:00
post_event ( SimpleEvent ( EVT_GLCANVAS_OBJECT_SELECT ) ) ;
2019-03-26 14:55:47 +00:00
m_gizmos . update_data ( * this ) ;
2019-05-03 10:36:26 +00:00
wxGetApp ( ) . obj_manipul ( ) - > set_dirty ( ) ;
2019-03-18 09:47:01 +00:00
// forces a frame render to update the view before the context menu is shown
render ( ) ;
Vec2d logical_pos = pos . cast < double > ( ) ;
# if ENABLE_RETINA_GL
const float factor = m_retina_helper - > get_scale_factor ( ) ;
logical_pos = logical_pos . cwiseQuotient ( Vec2d ( factor , factor ) ) ;
# endif // ENABLE_RETINA_GL
post_event ( Vec2dEvent ( EVT_GLCANVAS_RIGHT_CLICK , logical_pos ) ) ;
}
}
}
2018-06-14 10:34:19 +00:00
2019-03-26 11:30:17 +00:00
mouse_up_cleanup ( ) ;
2018-05-31 14:04:59 +00:00
}
2018-05-31 11:51:50 +00:00
else if ( evt . Moving ( ) )
{
2018-12-06 09:38:19 +00:00
m_mouse . position = pos . cast < double > ( ) ;
std : : string tooltip = " " ;
2019-03-26 08:01:04 +00:00
if ( tooltip . empty ( ) )
tooltip = m_gizmos . get_tooltip ( ) ;
2018-12-06 09:38:19 +00:00
2019-03-14 12:54:05 +00:00
if ( tooltip . empty ( ) )
tooltip = m_toolbar . get_tooltip ( ) ;
if ( tooltip . empty ( ) )
tooltip = m_view_toolbar . get_tooltip ( ) ;
2018-12-06 09:38:19 +00:00
set_tooltip ( tooltip ) ;
2019-03-26 08:10:35 +00:00
// updates gizmos overlay
2019-03-26 08:01:04 +00:00
if ( m_selection . is_empty ( ) )
m_gizmos . reset_all_states ( ) ;
2018-05-31 11:51:50 +00:00
// Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over.
2018-06-01 13:54:41 +00:00
if ( m_picking_enabled )
m_dirty = true ;
2018-05-31 11:51:50 +00:00
}
else
evt . Skip ( ) ;
2019-02-25 16:09:44 +00:00
2019-02-26 10:50:45 +00:00
# ifdef __WXMSW__
2019-02-25 16:09:44 +00:00
if ( on_enter_workaround )
m_mouse . position = Vec2d ( - 1. , - 1. ) ;
2019-02-26 10:50:45 +00:00
# endif /* __WXMSW__ */
2018-05-31 11:51:50 +00:00
}
2018-06-04 10:26:39 +00:00
void GLCanvas3D : : on_paint ( wxPaintEvent & evt )
{
2019-01-30 18:48:26 +00:00
if ( m_initialized )
m_dirty = true ;
else
// Call render directly, so it gets initialized immediately, not from On Idle handler.
this - > render ( ) ;
2018-06-04 10:26:39 +00:00
}
2018-05-24 11:46:17 +00:00
Size GLCanvas3D : : get_canvas_size ( ) const
{
int w = 0 ;
int h = 0 ;
if ( m_canvas ! = nullptr )
m_canvas - > GetSize ( & w , & h ) ;
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
const float factor = m_retina_helper - > get_scale_factor ( ) ;
w * = factor ;
h * = factor ;
# else
2019-04-01 09:08:26 +00:00
const float factor = 1.0f ;
2019-01-24 10:30:29 +00:00
# endif
return Size ( w , h , factor ) ;
2018-05-24 11:46:17 +00:00
}
2019-04-01 09:08:26 +00:00
Vec2d GLCanvas3D : : get_local_mouse_position ( ) const
2018-05-25 13:56:14 +00:00
{
if ( m_canvas = = nullptr )
2019-04-01 09:08:26 +00:00
return Vec2d : : Zero ( ) ;
2018-05-25 13:56:14 +00:00
wxPoint mouse_pos = m_canvas - > ScreenToClient ( wxGetMousePosition ( ) ) ;
2019-04-01 09:08:26 +00:00
const double factor =
# if ENABLE_RETINA_GL
m_retina_helper - > get_scale_factor ( ) ;
# else
1.0 ;
# endif
return Vec2d ( factor * mouse_pos . x , factor * mouse_pos . y ) ;
2018-05-25 13:56:14 +00:00
}
2018-07-19 11:18:19 +00:00
void GLCanvas3D : : reset_legend_texture ( )
{
2019-02-04 15:05:54 +00:00
if ( m_legend_texture . get_id ( ) ! = 0 )
{
_set_current ( ) ;
m_legend_texture . reset ( ) ;
}
2018-07-19 11:18:19 +00:00
}
2018-10-25 07:35:08 +00:00
void GLCanvas3D : : set_tooltip ( const std : : string & tooltip ) const
2018-07-25 08:01:17 +00:00
{
if ( m_canvas ! = nullptr )
2018-10-25 07:35:08 +00:00
{
wxToolTip * t = m_canvas - > GetToolTip ( ) ;
if ( t ! = nullptr )
{
2019-01-16 10:10:24 +00:00
if ( tooltip . empty ( ) )
m_canvas - > UnsetToolTip ( ) ;
else
2019-05-09 15:18:03 +00:00
t - > SetTip ( wxString : : FromUTF8 ( tooltip . data ( ) ) ) ;
2018-10-25 07:35:08 +00:00
}
2019-02-08 16:36:13 +00:00
else if ( ! tooltip . empty ( ) ) // Avoid "empty" tooltips => unset of the empty tooltip leads to application crash under OSX
2019-05-09 15:18:03 +00:00
m_canvas - > SetToolTip ( wxString : : FromUTF8 ( tooltip . data ( ) ) ) ;
2018-10-25 07:35:08 +00:00
}
2018-07-25 08:01:17 +00:00
}
2018-10-18 13:13:38 +00:00
2018-11-21 09:36:09 +00:00
void GLCanvas3D : : do_move ( )
{
if ( m_model = = nullptr )
return ;
std : : set < std : : pair < int , int > > done ; // keeps track of modified instances
bool object_moved = false ;
Vec3d wipe_tower_origin = Vec3d : : Zero ( ) ;
Selection : : EMode selection_mode = m_selection . get_mode ( ) ;
for ( const GLVolume * v : m_volumes . volumes )
{
int object_idx = v - > object_idx ( ) ;
int instance_idx = v - > instance_idx ( ) ;
int volume_idx = v - > volume_idx ( ) ;
std : : pair < int , int > done_id ( object_idx , instance_idx ) ;
if ( ( 0 < = object_idx ) & & ( object_idx < ( int ) m_model - > objects . size ( ) ) )
{
done . insert ( done_id ) ;
// Move instances/volumes
ModelObject * model_object = m_model - > objects [ object_idx ] ;
if ( model_object ! = nullptr )
{
2019-01-03 10:24:03 +00:00
if ( selection_mode = = Selection : : Instance )
model_object - > instances [ instance_idx ] - > set_offset ( v - > get_instance_offset ( ) ) ;
else if ( selection_mode = = Selection : : Volume )
model_object - > volumes [ volume_idx ] - > set_offset ( v - > get_volume_offset ( ) ) ;
object_moved = true ;
model_object - > invalidate_bounding_box ( ) ;
2018-11-21 09:36:09 +00:00
}
}
else if ( object_idx = = 1000 )
// Move a wipe tower proxy.
wipe_tower_origin = v - > get_volume_offset ( ) ;
}
// Fixes sinking/flying instances
for ( const std : : pair < int , int > & i : done )
{
ModelObject * m = m_model - > objects [ i . first ] ;
Vec3d shift ( 0.0 , 0.0 , - m - > get_instance_min_z ( i . second ) ) ;
m_selection . translate ( i . first , i . second , shift ) ;
m - > translate_instance ( i . second , shift ) ;
}
if ( object_moved )
post_event ( SimpleEvent ( EVT_GLCANVAS_INSTANCE_MOVED ) ) ;
if ( wipe_tower_origin ! = Vec3d : : Zero ( ) )
post_event ( Vec3dEvent ( EVT_GLCANVAS_WIPETOWER_MOVED , std : : move ( wipe_tower_origin ) ) ) ;
}
void GLCanvas3D : : do_rotate ( )
{
if ( m_model = = nullptr )
return ;
std : : set < std : : pair < int , int > > done ; // keeps track of modified instances
Selection : : EMode selection_mode = m_selection . get_mode ( ) ;
for ( const GLVolume * v : m_volumes . volumes )
{
int object_idx = v - > object_idx ( ) ;
2019-04-26 13:34:26 +00:00
if ( object_idx = = 1000 ) { // the wipe tower
Vec3d offset = v - > get_volume_offset ( ) ;
post_event ( Vec3dEvent ( EVT_GLCANVAS_WIPETOWER_ROTATED , Vec3d ( offset ( 0 ) , offset ( 1 ) , v - > get_volume_rotation ( ) ( 2 ) ) ) ) ;
}
2018-11-21 09:36:09 +00:00
if ( ( object_idx < 0 ) | | ( ( int ) m_model - > objects . size ( ) < = object_idx ) )
continue ;
int instance_idx = v - > instance_idx ( ) ;
int volume_idx = v - > volume_idx ( ) ;
done . insert ( std : : pair < int , int > ( object_idx , instance_idx ) ) ;
// Rotate instances/volumes.
ModelObject * model_object = m_model - > objects [ object_idx ] ;
if ( model_object ! = nullptr )
{
if ( selection_mode = = Selection : : Instance )
{
model_object - > instances [ instance_idx ] - > set_rotation ( v - > get_instance_rotation ( ) ) ;
model_object - > instances [ instance_idx ] - > set_offset ( v - > get_instance_offset ( ) ) ;
}
else if ( selection_mode = = Selection : : Volume )
{
model_object - > volumes [ volume_idx ] - > set_rotation ( v - > get_volume_rotation ( ) ) ;
model_object - > volumes [ volume_idx ] - > set_offset ( v - > get_volume_offset ( ) ) ;
}
model_object - > invalidate_bounding_box ( ) ;
}
}
// Fixes sinking/flying instances
for ( const std : : pair < int , int > & i : done )
{
ModelObject * m = m_model - > objects [ i . first ] ;
Vec3d shift ( 0.0 , 0.0 , - m - > get_instance_min_z ( i . second ) ) ;
m_selection . translate ( i . first , i . second , shift ) ;
m - > translate_instance ( i . second , shift ) ;
}
2019-01-03 10:24:03 +00:00
if ( ! done . empty ( ) )
post_event ( SimpleEvent ( EVT_GLCANVAS_INSTANCE_ROTATED ) ) ;
2018-11-21 09:36:09 +00:00
}
void GLCanvas3D : : do_scale ( )
{
if ( m_model = = nullptr )
return ;
std : : set < std : : pair < int , int > > done ; // keeps track of modified instances
Selection : : EMode selection_mode = m_selection . get_mode ( ) ;
for ( const GLVolume * v : m_volumes . volumes )
{
int object_idx = v - > object_idx ( ) ;
if ( ( object_idx < 0 ) | | ( ( int ) m_model - > objects . size ( ) < = object_idx ) )
continue ;
int instance_idx = v - > instance_idx ( ) ;
int volume_idx = v - > volume_idx ( ) ;
done . insert ( std : : pair < int , int > ( object_idx , instance_idx ) ) ;
// Rotate instances/volumes
ModelObject * model_object = m_model - > objects [ object_idx ] ;
if ( model_object ! = nullptr )
{
if ( selection_mode = = Selection : : Instance )
{
model_object - > instances [ instance_idx ] - > set_scaling_factor ( v - > get_instance_scaling_factor ( ) ) ;
model_object - > instances [ instance_idx ] - > set_offset ( v - > get_instance_offset ( ) ) ;
}
else if ( selection_mode = = Selection : : Volume )
{
model_object - > instances [ instance_idx ] - > set_offset ( v - > get_instance_offset ( ) ) ;
model_object - > volumes [ volume_idx ] - > set_scaling_factor ( v - > get_volume_scaling_factor ( ) ) ;
model_object - > volumes [ volume_idx ] - > set_offset ( v - > get_volume_offset ( ) ) ;
}
model_object - > invalidate_bounding_box ( ) ;
}
}
// Fixes sinking/flying instances
for ( const std : : pair < int , int > & i : done )
{
ModelObject * m = m_model - > objects [ i . first ] ;
Vec3d shift ( 0.0 , 0.0 , - m - > get_instance_min_z ( i . second ) ) ;
m_selection . translate ( i . first , i . second , shift ) ;
m - > translate_instance ( i . second , shift ) ;
}
2019-01-03 10:24:03 +00:00
if ( ! done . empty ( ) )
post_event ( SimpleEvent ( EVT_GLCANVAS_INSTANCE_ROTATED ) ) ;
2018-11-21 09:36:09 +00:00
}
void GLCanvas3D : : do_flatten ( )
{
do_rotate ( ) ;
}
void GLCanvas3D : : do_mirror ( )
{
if ( m_model = = nullptr )
return ;
std : : set < std : : pair < int , int > > done ; // keeps track of modified instances
Selection : : EMode selection_mode = m_selection . get_mode ( ) ;
for ( const GLVolume * v : m_volumes . volumes )
{
int object_idx = v - > object_idx ( ) ;
if ( ( object_idx < 0 ) | | ( ( int ) m_model - > objects . size ( ) < = object_idx ) )
continue ;
int instance_idx = v - > instance_idx ( ) ;
int volume_idx = v - > volume_idx ( ) ;
done . insert ( std : : pair < int , int > ( object_idx , instance_idx ) ) ;
// Mirror instances/volumes
ModelObject * model_object = m_model - > objects [ object_idx ] ;
if ( model_object ! = nullptr )
{
if ( selection_mode = = Selection : : Instance )
model_object - > instances [ instance_idx ] - > set_mirror ( v - > get_instance_mirror ( ) ) ;
else if ( selection_mode = = Selection : : Volume )
model_object - > volumes [ volume_idx ] - > set_mirror ( v - > get_volume_mirror ( ) ) ;
2019-01-02 09:49:13 +00:00
2018-11-21 09:36:09 +00:00
model_object - > invalidate_bounding_box ( ) ;
}
}
2018-12-10 08:46:01 +00:00
// Fixes sinking/flying instances
for ( const std : : pair < int , int > & i : done )
{
ModelObject * m = m_model - > objects [ i . first ] ;
Vec3d shift ( 0.0 , 0.0 , - m - > get_instance_min_z ( i . second ) ) ;
m_selection . translate ( i . first , i . second , shift ) ;
m - > translate_instance ( i . second , shift ) ;
}
2018-11-21 09:36:09 +00:00
post_event ( SimpleEvent ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS ) ) ;
}
2018-11-29 08:03:38 +00:00
void GLCanvas3D : : set_camera_zoom ( float zoom )
{
zoom = std : : max ( std : : min ( zoom , 4.0f ) , - 4.0f ) / 10.0f ;
2019-04-01 08:00:10 +00:00
zoom = m_camera . zoom / ( 1.0f - zoom ) ;
2018-11-29 08:03:38 +00:00
// 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 )
2019-01-15 11:59:28 +00:00
zoom = std : : max ( zoom , zoom_min * 0.7f ) ;
2018-11-29 08:03:38 +00:00
2019-01-29 11:05:49 +00:00
// Don't allow to zoom too close to the scene.
zoom = std : : min ( zoom , 100.0f ) ;
2019-03-07 10:49:00 +00:00
m_camera . zoom = zoom ;
2018-11-29 08:03:38 +00:00
_refresh_if_shown_on_screen ( ) ;
}
2018-12-03 12:29:07 +00:00
void GLCanvas3D : : update_gizmos_on_off_state ( )
{
set_as_dirty ( ) ;
2019-03-26 14:55:47 +00:00
m_gizmos . update_data ( * this ) ;
2019-03-26 11:30:17 +00:00
m_gizmos . refresh_on_off_state ( get_selection ( ) ) ;
2018-12-03 12:29:07 +00:00
}
2018-12-18 09:40:53 +00:00
void GLCanvas3D : : handle_sidebar_focus_event ( const std : : string & opt_key , bool focus_on )
{
m_sidebar_field = focus_on ? opt_key : " " ;
if ( ! m_sidebar_field . empty ( ) )
{
m_gizmos . reset_all_states ( ) ;
m_dirty = true ;
}
}
2019-01-24 12:16:46 +00:00
void GLCanvas3D : : update_ui_from_settings ( )
{
# if ENABLE_RETINA_GL
const float orig_scaling = m_retina_helper - > get_scale_factor ( ) ;
const bool use_retina = wxGetApp ( ) . app_config - > get ( " use_retina_opengl " ) = = " 1 " ;
BOOST_LOG_TRIVIAL ( debug ) < < " GLCanvas3D: Use Retina OpenGL: " < < use_retina ;
m_retina_helper - > set_use_retina ( use_retina ) ;
const float new_scaling = m_retina_helper - > get_scale_factor ( ) ;
if ( new_scaling ! = orig_scaling ) {
BOOST_LOG_TRIVIAL ( debug ) < < " GLCanvas3D: Scaling factor: " < < new_scaling ;
2019-03-07 10:49:00 +00:00
m_camera . zoom / = orig_scaling ;
m_camera . zoom * = new_scaling ;
2019-01-24 12:16:46 +00:00
_refresh_if_shown_on_screen ( ) ;
}
# endif
}
2019-04-29 12:32:02 +00:00
arr : : WipeTowerInfo GLCanvas3D : : get_wipe_tower_info ( ) const
{
arr : : WipeTowerInfo wti ;
for ( const GLVolume * vol : m_volumes . volumes ) {
if ( vol - > is_wipe_tower ) {
wti . is_wipe_tower = true ;
wti . pos = Vec2d ( m_config - > opt_float ( " wipe_tower_x " ) ,
m_config - > opt_float ( " wipe_tower_y " ) ) ;
wti . rotation = ( M_PI / 180. ) * m_config - > opt_float ( " wipe_tower_rotation_angle " ) ;
const BoundingBoxf3 & bb = vol - > bounding_box ;
wti . bb_size = Vec2d ( bb . size ( ) ( 0 ) , bb . size ( ) ( 1 ) ) ;
break ;
}
}
return wti ;
}
void GLCanvas3D : : arrange_wipe_tower ( const arr : : WipeTowerInfo & wti ) const
{
if ( wti . is_wipe_tower ) {
DynamicPrintConfig cfg ;
cfg . opt < ConfigOptionFloat > ( " wipe_tower_x " , true ) - > value = wti . pos ( 0 ) ;
cfg . opt < ConfigOptionFloat > ( " wipe_tower_y " , true ) - > value = wti . pos ( 1 ) ;
cfg . opt < ConfigOptionFloat > ( " wipe_tower_rotation_angle " , true ) - > value = ( 180. / M_PI ) * wti . rotation ;
wxGetApp ( ) . get_tab ( Preset : : TYPE_PRINT ) - > load_config ( cfg ) ;
}
}
2019-03-26 11:30:17 +00:00
Linef3 GLCanvas3D : : mouse_ray ( const Point & mouse_pos )
{
float z0 = 0.0f ;
float z1 = 1.0f ;
return Linef3 ( _mouse_to_3d ( mouse_pos , & z0 ) , _mouse_to_3d ( mouse_pos , & z1 ) ) ;
}
2019-04-12 13:31:33 +00:00
double GLCanvas3D : : get_size_proportional_to_max_bed_size ( double factor ) const
{
return factor * m_bed . get_bounding_box ( ) . max_size ( ) ;
}
2019-04-24 13:43:52 +00:00
void GLCanvas3D : : set_cursor ( ECursorType type )
{
if ( ( m_canvas ! = nullptr ) & & ( m_cursor_type ! = type ) )
{
switch ( type )
{
case Standard : { m_canvas - > SetCursor ( * wxSTANDARD_CURSOR ) ; break ; }
case Cross : { m_canvas - > SetCursor ( * wxCROSS_CURSOR ) ; break ; }
}
m_cursor_type = type ;
}
}
2019-04-24 23:45:00 +00:00
void GLCanvas3D : : msw_rescale ( )
2019-04-16 10:11:48 +00:00
{
2019-04-26 11:07:31 +00:00
m_warning_texture . msw_rescale ( * this ) ;
2019-04-16 10:11:48 +00:00
}
2018-06-12 07:18:25 +00:00
bool GLCanvas3D : : _is_shown_on_screen ( ) const
{
2018-06-27 10:05:23 +00:00
return ( m_canvas ! = nullptr ) ? m_canvas - > IsShownOnScreen ( ) : false ;
2018-06-12 07:18:25 +00:00
}
2018-07-23 11:49:48 +00:00
bool GLCanvas3D : : _init_toolbar ( )
{
if ( ! m_toolbar . is_enabled ( ) )
return true ;
2019-02-26 08:56:23 +00:00
# if !ENABLE_SVG_ICONS
2018-12-17 09:55:14 +00:00
ItemsIconsTexture : : Metadata icons_data ;
icons_data . filename = " toolbar.png " ;
2019-02-22 09:01:34 +00:00
icons_data . icon_size = 37 ;
2019-02-26 08:56:23 +00:00
# endif // !ENABLE_SVG_ICONS
2018-12-19 13:44:37 +00:00
2018-12-17 09:55:14 +00:00
BackgroundTexture : : Metadata background_data ;
background_data . filename = " toolbar_background.png " ;
background_data . left = 16 ;
background_data . top = 16 ;
background_data . right = 16 ;
background_data . bottom = 16 ;
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
if ( ! m_toolbar . init ( background_data ) )
# else
2018-12-17 09:55:14 +00:00
if ( ! m_toolbar . init ( icons_data , background_data ) )
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2018-07-31 10:25:00 +00:00
{
// unable to init the toolbar texture, disable it
m_toolbar . set_enabled ( false ) ;
return true ;
}
2019-03-08 13:35:33 +00:00
# if ENABLE_SVG_ICONS
m_toolbar . set_icons_size ( 40 ) ;
# endif // ENABLE_SVG_ICONS
2018-07-31 10:25:00 +00:00
// m_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
m_toolbar . set_layout_type ( GLToolbar : : Layout : : Horizontal ) ;
2018-12-17 09:55:14 +00:00
m_toolbar . set_layout_orientation ( GLToolbar : : Layout : : Top ) ;
m_toolbar . set_border ( 5.0f ) ;
2018-07-31 10:25:00 +00:00
m_toolbar . set_separator_size ( 5 ) ;
m_toolbar . set_gap_size ( 2 ) ;
GLToolbarItem : : Data item ;
2018-07-23 11:49:48 +00:00
item . name = " add " ;
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
2019-02-26 09:40:00 +00:00
item . icon_filename = " add.svg " ;
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Add... " ) ) + " [ " + GUI : : shortkey_ctrl_prefix ( ) + " I] " ;
2018-07-31 10:25:00 +00:00
item . sprite_id = 0 ;
2019-03-14 12:54:05 +00:00
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_ADD ) ) ; } ;
2018-07-23 11:49:48 +00:00
if ( ! m_toolbar . add_item ( item ) )
return false ;
item . name = " delete " ;
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
2019-02-26 09:40:00 +00:00
item . icon_filename = " remove.svg " ;
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Delete " ) ) + " [Del] " ;
2018-07-31 10:25:00 +00:00
item . sprite_id = 1 ;
2019-03-14 12:54:05 +00:00
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_DELETE ) ) ; } ;
item . enabled_state_callback = [ ] ( ) - > bool { return wxGetApp ( ) . plater ( ) - > can_delete ( ) ; } ;
2018-07-23 11:49:48 +00:00
if ( ! m_toolbar . add_item ( item ) )
return false ;
item . name = " deleteall " ;
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
2019-02-26 09:40:00 +00:00
item . icon_filename = " delete_all.svg " ;
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Delete all " ) ) + " [ " + GUI : : shortkey_ctrl_prefix ( ) + " Del] " ;
2018-07-31 10:25:00 +00:00
item . sprite_id = 2 ;
2019-03-14 12:54:05 +00:00
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_DELETE_ALL ) ) ; } ;
item . enabled_state_callback = [ ] ( ) - > bool { return wxGetApp ( ) . plater ( ) - > can_delete_all ( ) ; } ;
2018-07-23 11:49:48 +00:00
if ( ! m_toolbar . add_item ( item ) )
return false ;
item . name = " arrange " ;
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
2019-02-26 09:40:00 +00:00
item . icon_filename = " arrange.svg " ;
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Arrange " ) ) + " [A] " ;
2018-07-31 10:25:00 +00:00
item . sprite_id = 3 ;
2019-03-14 12:54:05 +00:00
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_ARRANGE ) ) ; } ;
item . enabled_state_callback = [ ] ( ) - > bool { return wxGetApp ( ) . plater ( ) - > can_arrange ( ) ; } ;
2018-07-23 11:49:48 +00:00
if ( ! m_toolbar . add_item ( item ) )
return false ;
if ( ! m_toolbar . add_separator ( ) )
return false ;
2019-04-11 11:20:34 +00:00
item . name = " copy " ;
# if ENABLE_SVG_ICONS
item . icon_filename = " copy.svg " ;
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Copy " ) ) + " [ " + GUI : : shortkey_ctrl_prefix ( ) + " C] " ;
2019-04-11 11:20:34 +00:00
item . sprite_id = 4 ;
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_COPY ) ) ; } ;
item . enabled_state_callback = [ ] ( ) - > bool { return wxGetApp ( ) . plater ( ) - > can_copy ( ) ; } ;
if ( ! m_toolbar . add_item ( item ) )
return false ;
item . name = " paste " ;
# if ENABLE_SVG_ICONS
item . icon_filename = " paste.svg " ;
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Paste " ) ) + " [ " + GUI : : shortkey_ctrl_prefix ( ) + " V] " ;
2019-04-11 11:20:34 +00:00
item . sprite_id = 5 ;
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_PASTE ) ) ; } ;
item . enabled_state_callback = [ ] ( ) - > bool { return wxGetApp ( ) . plater ( ) - > can_paste ( ) ; } ;
if ( ! m_toolbar . add_item ( item ) )
return false ;
if ( ! m_toolbar . add_separator ( ) )
return false ;
2018-07-23 11:49:48 +00:00
item . name = " more " ;
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
2019-02-26 09:40:00 +00:00
item . icon_filename = " instance_add.svg " ;
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Add instance " ) ) + " [+] " ;
2019-04-11 11:20:34 +00:00
item . sprite_id = 6 ;
2019-03-14 12:54:05 +00:00
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_MORE ) ) ; } ;
item . visibility_callback = [ ] ( ) - > bool { return wxGetApp ( ) . get_mode ( ) ! = comSimple ; } ;
item . enabled_state_callback = [ ] ( ) - > bool { return wxGetApp ( ) . plater ( ) - > can_increase_instances ( ) ; } ;
2018-07-23 11:49:48 +00:00
if ( ! m_toolbar . add_item ( item ) )
return false ;
item . name = " fewer " ;
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
2019-02-26 09:40:00 +00:00
item . icon_filename = " instance_remove.svg " ;
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Remove instance " ) ) + " [-] " ;
2019-04-11 11:20:34 +00:00
item . sprite_id = 7 ;
2019-03-14 12:54:05 +00:00
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_FEWER ) ) ; } ;
item . visibility_callback = [ ] ( ) - > bool { return wxGetApp ( ) . get_mode ( ) ! = comSimple ; } ;
item . enabled_state_callback = [ ] ( ) - > bool { return wxGetApp ( ) . plater ( ) - > can_decrease_instances ( ) ; } ;
2018-07-23 11:49:48 +00:00
if ( ! m_toolbar . add_item ( item ) )
return false ;
if ( ! m_toolbar . add_separator ( ) )
return false ;
2018-10-24 10:55:38 +00:00
item . name = " splitobjects " ;
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
2019-02-26 09:40:00 +00:00
item . icon_filename = " split_objects.svg " ;
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Split to objects " ) ) ;
2019-04-11 11:20:34 +00:00
item . sprite_id = 8 ;
2019-03-14 12:54:05 +00:00
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_SPLIT_OBJECTS ) ) ; } ;
item . visibility_callback = GLToolbarItem : : Default_Visibility_Callback ;
item . enabled_state_callback = [ ] ( ) - > bool { return wxGetApp ( ) . plater ( ) - > can_split_to_objects ( ) ; } ;
2018-10-24 10:55:38 +00:00
if ( ! m_toolbar . add_item ( item ) )
return false ;
item . name = " splitvolumes " ;
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
2019-02-26 09:40:00 +00:00
item . icon_filename = " split_parts.svg " ;
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Split to parts " ) ) ;
2019-04-11 11:20:34 +00:00
item . sprite_id = 9 ;
2019-03-14 12:54:05 +00:00
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_SPLIT_VOLUMES ) ) ; } ;
item . visibility_callback = [ ] ( ) - > bool { return wxGetApp ( ) . get_mode ( ) ! = comSimple ; } ;
item . enabled_state_callback = [ ] ( ) - > bool { return wxGetApp ( ) . plater ( ) - > can_split_to_volumes ( ) ; } ;
2018-07-23 11:49:48 +00:00
if ( ! m_toolbar . add_item ( item ) )
return false ;
if ( ! m_toolbar . add_separator ( ) )
return false ;
item . name = " layersediting " ;
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
2019-04-30 13:43:19 +00:00
item . icon_filename = " layers_white.svg " ;
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-05-09 15:18:03 +00:00
item . tooltip = _utf8 ( L ( " Layers editing " ) ) ;
2019-04-11 11:20:34 +00:00
item . sprite_id = 10 ;
2018-07-27 10:08:33 +00:00
item . is_toggable = true ;
2019-03-14 12:54:05 +00:00
item . action_callback = [ this ] ( ) { if ( m_canvas ! = nullptr ) wxPostEvent ( m_canvas , SimpleEvent ( EVT_GLTOOLBAR_LAYERSEDITING ) ) ; } ;
2019-04-05 07:51:58 +00:00
item . visibility_callback = [ this ] ( ) - > bool { return m_process - > current_printer_technology ( ) = = ptFFF ; } ;
2019-03-14 12:54:05 +00:00
item . enabled_state_callback = [ ] ( ) - > bool { return wxGetApp ( ) . plater ( ) - > can_layers_editing ( ) ; } ;
2018-07-23 11:49:48 +00:00
if ( ! m_toolbar . add_item ( item ) )
return false ;
return true ;
}
2018-10-04 08:41:11 +00:00
bool GLCanvas3D : : _set_current ( )
{
2019-04-09 08:55:32 +00:00
if ( _is_shown_on_screen ( ) & & ( m_context ! = nullptr ) ) {
2018-10-04 08:41:11 +00:00
return m_canvas - > SetCurrent ( * m_context ) ;
2019-04-09 08:55:32 +00:00
}
2018-10-04 08:41:11 +00:00
return false ;
}
2018-06-01 13:54:41 +00:00
void GLCanvas3D : : _resize ( unsigned int w , unsigned int h )
2018-05-30 13:18:45 +00:00
{
2018-06-25 13:17:13 +00:00
if ( ( m_canvas = = nullptr ) & & ( m_context = = nullptr ) )
2018-06-01 13:54:41 +00:00
return ;
2018-05-30 13:18:45 +00:00
2019-04-01 12:12:05 +00:00
auto * imgui = wxGetApp ( ) . imgui ( ) ;
imgui - > set_display_size ( ( float ) w , ( float ) h ) ;
2019-04-02 11:26:22 +00:00
const float font_size = 1.5f * wxGetApp ( ) . em_unit ( ) ;
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
2019-04-02 11:26:22 +00:00
imgui - > set_scaling ( font_size , 1.0f , m_retina_helper - > get_scale_factor ( ) ) ;
2019-02-04 20:41:10 +00:00
# else
2019-04-02 11:26:22 +00:00
imgui - > set_scaling ( font_size , m_canvas - > GetContentScaleFactor ( ) , 1.0f ) ;
2019-02-04 20:41:10 +00:00
# endif
2018-10-31 09:19:44 +00:00
2018-06-25 13:17:13 +00:00
// ensures that this canvas is current
2018-10-04 08:41:11 +00:00
_set_current ( ) ;
2019-04-01 08:00:10 +00:00
m_camera . apply_viewport ( 0 , 0 , w , h ) ;
2018-06-01 13:54:41 +00:00
const BoundingBoxf3 & bbox = _max_bounding_box ( ) ;
2019-03-07 10:49:00 +00:00
switch ( m_camera . type )
2018-06-01 13:54:41 +00:00
{
case Camera : : Ortho :
{
float w2 = w ;
float h2 = h ;
2019-04-01 08:00:10 +00:00
float two_zoom = 2.0f * m_camera . zoom ;
2018-06-01 13:54:41 +00:00
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
2019-05-10 12:43:35 +00:00
// Set at least some minimum depth in case the bounding box is empty to avoid an OpenGL driver error.
float depth = std : : max ( 1.f , 5.0f * ( float ) bbox . max_size ( ) ) ;
2019-04-01 08:00:10 +00:00
m_camera . apply_ortho_projection ( - w2 , w2 , - h2 , h2 , - depth , depth ) ;
2018-06-01 13:54:41 +00:00
break ;
}
2018-06-04 10:26:39 +00:00
// 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;
// }
2018-06-01 13:54:41 +00:00
default :
{
throw std : : runtime_error ( " Invalid camera type. " ) ;
break ;
}
}
m_dirty = false ;
2018-05-30 13:18:45 +00:00
}
2018-06-01 13:54:41 +00:00
BoundingBoxf3 GLCanvas3D : : _max_bounding_box ( ) const
2018-05-30 13:18:45 +00:00
{
2019-02-19 14:15:27 +00:00
BoundingBoxf3 bb = volumes_bounding_box ( ) ;
2019-03-07 10:49:00 +00:00
bb . merge ( m_bed . get_bounding_box ( ) ) ;
2019-02-19 14:15:27 +00:00
return bb ;
2018-05-30 13:18:45 +00:00
}
2018-05-14 12:14:19 +00:00
void GLCanvas3D : : _zoom_to_bounding_box ( const BoundingBoxf3 & bbox )
{
2018-05-15 07:50:01 +00:00
// Calculate the zoom factor needed to adjust viewport to bounding box.
float zoom = _get_zoom_to_bounding_box_factor ( bbox ) ;
if ( zoom > 0.0f )
{
2019-03-07 10:49:00 +00:00
m_camera . zoom = zoom ;
// center view around bounding box center
m_camera . set_target ( bbox . center ( ) ) ;
2019-01-17 12:21:33 +00:00
m_dirty = true ;
2018-05-15 07:50:01 +00:00
}
2018-05-14 12:14:19 +00:00
}
2018-05-15 07:50:01 +00:00
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
2018-05-31 06:44:39 +00:00
// we need the view matrix, we let opengl calculate it (same as done in render())
2019-04-01 08:00:10 +00:00
m_camera . apply_view_matrix ( ) ;
2018-05-15 07:50:01 +00:00
2019-04-01 08:00:10 +00:00
Vec3d right = m_camera . get_dir_right ( ) ;
Vec3d up = m_camera . get_dir_up ( ) ;
Vec3d forward = m_camera . get_dir_forward ( ) ;
2018-05-15 07:50:01 +00:00
2018-08-21 15:43:05 +00:00
Vec3d bb_min = bbox . min ;
Vec3d bb_max = bbox . max ;
Vec3d bb_center = bbox . center ( ) ;
2018-05-15 07:50:01 +00:00
// bbox vertices in world space
2018-08-21 15:43:05 +00:00
std : : vector < Vec3d > vertices ;
2018-05-15 07:50:01 +00:00
vertices . reserve ( 8 ) ;
vertices . push_back ( bb_min ) ;
2018-08-17 13:53:43 +00:00
vertices . emplace_back ( bb_max ( 0 ) , bb_min ( 1 ) , bb_min ( 2 ) ) ;
vertices . emplace_back ( bb_max ( 0 ) , bb_max ( 1 ) , bb_min ( 2 ) ) ;
vertices . emplace_back ( bb_min ( 0 ) , bb_max ( 1 ) , bb_min ( 2 ) ) ;
vertices . emplace_back ( bb_min ( 0 ) , bb_min ( 1 ) , bb_max ( 2 ) ) ;
vertices . emplace_back ( bb_max ( 0 ) , bb_min ( 1 ) , bb_max ( 2 ) ) ;
2018-05-15 07:50:01 +00:00
vertices . push_back ( bb_max ) ;
2018-08-17 13:53:43 +00:00
vertices . emplace_back ( bb_min ( 0 ) , bb_max ( 1 ) , bb_max ( 2 ) ) ;
2018-05-15 07:50:01 +00:00
2018-08-24 08:20:00 +00:00
double max_x = 0.0 ;
double max_y = 0.0 ;
2018-05-15 07:50:01 +00:00
// margin factor to give some empty space around the bbox
2018-08-24 08:20:00 +00:00
double margin_factor = 1.25 ;
2018-05-15 07:50:01 +00:00
2018-12-21 08:56:11 +00:00
for ( const Vec3d & v : vertices )
2018-05-15 07:50:01 +00:00
{
// project vertex on the plane perpendicular to camera forward axis
2018-08-21 15:43:05 +00:00
Vec3d pos ( v ( 0 ) - bb_center ( 0 ) , v ( 1 ) - bb_center ( 1 ) , v ( 2 ) - bb_center ( 2 ) ) ;
Vec3d proj_on_plane = pos - pos . dot ( forward ) * forward ;
2018-05-15 07:50:01 +00:00
// calculates vertex coordinate along camera xy axes
2018-08-24 08:20:00 +00:00
double x_on_plane = proj_on_plane . dot ( right ) ;
double y_on_plane = proj_on_plane . dot ( up ) ;
2018-05-15 07:50:01 +00:00
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 ;
2018-05-24 11:46:17 +00:00
const Size & cnv_size = get_canvas_size ( ) ;
2018-08-24 08:20:00 +00:00
return ( float ) std : : min ( ( double ) cnv_size . get_width ( ) / max_x , ( double ) cnv_size . get_height ( ) / max_y ) ;
2018-05-15 07:50:01 +00:00
}
2018-05-28 13:23:01 +00:00
void GLCanvas3D : : _refresh_if_shown_on_screen ( )
{
2018-06-12 07:18:25 +00:00
if ( _is_shown_on_screen ( ) )
2018-05-28 13:23:01 +00:00
{
const Size & cnv_size = get_canvas_size ( ) ;
2018-06-01 13:54:41 +00:00
_resize ( ( unsigned int ) cnv_size . get_width ( ) , ( unsigned int ) cnv_size . get_height ( ) ) ;
2018-11-02 09:31:10 +00:00
// Because of performance problems on macOS, where PaintEvents are not delivered
// frequently enough, we call render() here directly when we can.
2019-01-17 12:21:33 +00:00
render ( ) ;
2018-05-28 13:23:01 +00:00
}
}
2018-05-29 11:54:34 +00:00
void GLCanvas3D : : _picking_pass ( ) const
{
2019-04-25 11:41:00 +00:00
if ( m_picking_enabled & & ! m_mouse . dragging & & ( m_mouse . position ! = Vec2d ( DBL_MAX , DBL_MAX ) ) )
2018-05-29 11:54:34 +00:00
{
2019-04-24 13:07:28 +00:00
m_hover_volume_idxs . clear ( ) ;
2018-05-29 11:54:34 +00:00
// Render the object for picking.
// FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing.
// Better to use software ray - casting on a bounding - box hierarchy.
2018-06-01 13:54:41 +00:00
if ( m_multisample_allowed )
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_MULTISAMPLE ) ) ;
2018-05-29 11:54:34 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_BLEND ) ) ;
glsafe ( : : glEnable ( GL_DEPTH_TEST ) ) ;
2018-05-29 11:54:34 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ) ;
2018-05-29 11:54:34 +00:00
2019-03-25 11:01:02 +00:00
m_camera_clipping_plane = m_gizmos . get_sla_clipping_plane ( ) ;
2019-04-12 14:08:40 +00:00
if ( m_camera_clipping_plane . is_active ( ) ) {
2019-04-11 13:44:32 +00:00
: : glClipPlane ( GL_CLIP_PLANE0 , ( GLdouble * ) m_camera_clipping_plane . get_data ( ) ) ;
: : glEnable ( GL_CLIP_PLANE0 ) ;
}
2019-04-10 09:20:09 +00:00
_render_volumes_for_picking ( ) ;
2019-04-12 14:08:40 +00:00
if ( m_camera_clipping_plane . is_active ( ) )
2019-04-11 13:44:32 +00:00
: : glDisable ( GL_CLIP_PLANE0 ) ;
2019-03-25 11:01:02 +00:00
2018-10-15 09:30:50 +00:00
m_gizmos . render_current_gizmo_for_picking_pass ( m_selection ) ;
2018-05-29 11:54:34 +00:00
2018-06-01 13:54:41 +00:00
if ( m_multisample_allowed )
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_MULTISAMPLE ) ) ;
2018-05-29 11:54:34 +00:00
2018-07-30 07:09:14 +00:00
int volume_id = - 1 ;
2018-05-29 11:54:34 +00:00
2018-07-30 07:09:14 +00:00
GLubyte color [ 4 ] = { 0 , 0 , 0 , 0 } ;
const Size & cnv_size = get_canvas_size ( ) ;
2019-04-25 11:41:00 +00:00
bool inside = ( 0 < = m_mouse . position ( 0 ) ) & & ( m_mouse . position ( 0 ) < cnv_size . get_width ( ) ) & & ( 0 < = m_mouse . position ( 1 ) ) & & ( m_mouse . position ( 1 ) < cnv_size . get_height ( ) ) ;
2018-07-30 07:09:14 +00:00
if ( inside )
{
2019-04-25 11:41:00 +00:00
glsafe ( : : glReadPixels ( m_mouse . position ( 0 ) , cnv_size . get_height ( ) - m_mouse . position ( 1 ) - 1 , 1 , 1 , GL_RGBA , GL_UNSIGNED_BYTE , ( void * ) color ) ) ;
2019-04-25 07:46:26 +00:00
volume_id = color [ 0 ] + ( color [ 1 ] < < 8 ) + ( color [ 2 ] < < 16 ) ;
2018-07-30 07:09:14 +00:00
}
if ( ( 0 < = volume_id ) & & ( volume_id < ( int ) m_volumes . volumes . size ( ) ) )
2018-05-29 11:54:34 +00:00
{
2019-04-24 13:07:28 +00:00
m_hover_volume_idxs . push_back ( volume_id ) ;
2018-06-14 13:32:26 +00:00
m_gizmos . set_hover_id ( - 1 ) ;
2018-05-29 11:54:34 +00:00
}
2018-06-14 13:32:26 +00:00
else
2019-03-15 11:07:25 +00:00
m_gizmos . set_hover_id ( inside & & volume_id < = GLGizmoBase : : BASE_ID ? ( GLGizmoBase : : BASE_ID - volume_id ) : - 1 ) ;
2018-06-13 08:49:59 +00:00
2018-10-08 12:02:12 +00:00
_update_volumes_hover_state ( ) ;
2018-05-29 11:54:34 +00:00
}
}
2019-04-25 07:46:26 +00:00
void GLCanvas3D : : _rectangular_selection_picking_pass ( ) const
{
m_gizmos . set_hover_id ( - 1 ) ;
std : : set < int > idxs ;
if ( m_picking_enabled )
{
if ( m_multisample_allowed )
glsafe ( : : glDisable ( GL_MULTISAMPLE ) ) ;
glsafe ( : : glDisable ( GL_BLEND ) ) ;
glsafe ( : : glEnable ( GL_DEPTH_TEST ) ) ;
glsafe ( : : glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ) ;
_render_volumes_for_picking ( ) ;
if ( m_multisample_allowed )
glsafe ( : : glEnable ( GL_MULTISAMPLE ) ) ;
2019-04-29 06:31:32 +00:00
int width = std : : max ( ( int ) m_rectangle_selection . get_width ( ) , 1 ) ;
int height = std : : max ( ( int ) m_rectangle_selection . get_height ( ) , 1 ) ;
2019-04-25 07:46:26 +00:00
int px_count = width * height ;
2019-04-29 06:31:32 +00:00
int left = ( int ) m_rectangle_selection . get_left ( ) ;
int top = get_canvas_size ( ) . get_height ( ) - ( int ) m_rectangle_selection . get_top ( ) ;
if ( ( left > = 0 ) & & ( top > = 0 ) )
2019-04-25 07:46:26 +00:00
{
# define USE_PARALLEL 1
# if USE_PARALLEL
2019-04-29 06:31:32 +00:00
struct Pixel
{
std : : array < GLubyte , 4 > data ;
int id ( ) const { return data [ 0 ] + ( data [ 1 ] < < 8 ) + ( data [ 2 ] < < 16 ) ; }
} ;
2019-04-25 07:46:26 +00:00
2019-04-29 06:31:32 +00:00
std : : vector < Pixel > frame ( px_count ) ;
glsafe ( : : glReadPixels ( left , top , width , height , GL_RGBA , GL_UNSIGNED_BYTE , ( void * ) frame . data ( ) ) ) ;
2019-04-25 07:46:26 +00:00
2019-04-29 06:31:32 +00:00
tbb : : spin_mutex mutex ;
tbb : : parallel_for ( tbb : : blocked_range < size_t > ( 0 , frame . size ( ) , ( size_t ) width ) ,
[ this , & frame , & idxs , & mutex ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t i = range . begin ( ) ; i < range . end ( ) ; + + i )
{
int volume_id = frame [ i ] . id ( ) ;
if ( ( 0 < = volume_id ) & & ( volume_id < ( int ) m_volumes . volumes . size ( ) ) )
2019-04-25 07:46:26 +00:00
{
2019-04-29 06:31:32 +00:00
mutex . lock ( ) ;
idxs . insert ( volume_id ) ;
mutex . unlock ( ) ;
2019-04-25 07:46:26 +00:00
}
}
2019-04-29 06:31:32 +00:00
}
) ;
2019-04-25 07:46:26 +00:00
# else
2019-04-29 06:31:32 +00:00
std : : vector < GLubyte > frame ( 4 * px_count ) ;
glsafe ( : : glReadPixels ( left , top , width , height , GL_RGBA , GL_UNSIGNED_BYTE , ( void * ) frame . data ( ) ) ) ;
2019-04-25 07:46:26 +00:00
2019-04-29 06:31:32 +00:00
for ( int i = 0 ; i < px_count ; + + i )
{
int px_id = 4 * i ;
int volume_id = frame [ px_id ] + ( frame [ px_id + 1 ] < < 8 ) + ( frame [ px_id + 2 ] < < 16 ) ;
if ( ( 0 < = volume_id ) & & ( volume_id < ( int ) m_volumes . volumes . size ( ) ) )
idxs . insert ( volume_id ) ;
2019-04-25 07:46:26 +00:00
}
2019-04-29 06:31:32 +00:00
# endif // USE_PARALLEL
2019-04-25 07:46:26 +00:00
}
}
m_hover_volume_idxs . assign ( idxs . begin ( ) , idxs . end ( ) ) ;
_update_volumes_hover_state ( ) ;
}
2018-05-29 11:54:34 +00:00
void GLCanvas3D : : _render_background ( ) const
{
2019-03-27 13:42:09 +00:00
glsafe ( : : glPushMatrix ( ) ) ;
glsafe ( : : glLoadIdentity ( ) ) ;
glsafe ( : : glMatrixMode ( GL_PROJECTION ) ) ;
glsafe ( : : glPushMatrix ( ) ) ;
glsafe ( : : glLoadIdentity ( ) ) ;
2018-05-29 11:54:34 +00:00
2018-12-13 10:13:58 +00:00
// Draws a bottom to top gradient over the complete screen.
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_DEPTH_TEST ) ) ;
2018-05-29 11:54:34 +00:00
: : glBegin ( GL_QUADS ) ;
2018-12-12 09:38:07 +00:00
if ( m_dynamic_background_enabled & & _is_any_volume_outside ( ) )
: : glColor3fv ( ERROR_BG_DARK_COLOR ) ;
else
: : glColor3fv ( DEFAULT_BG_DARK_COLOR ) ;
2018-07-27 07:38:39 +00:00
: : glVertex2f ( - 1.0f , - 1.0f ) ;
: : glVertex2f ( 1.0f , - 1.0f ) ;
if ( m_dynamic_background_enabled & & _is_any_volume_outside ( ) )
2018-12-12 09:38:07 +00:00
: : glColor3fv ( ERROR_BG_LIGHT_COLOR ) ;
2018-07-27 07:38:39 +00:00
else
2018-12-12 09:38:07 +00:00
: : glColor3fv ( DEFAULT_BG_LIGHT_COLOR ) ;
2018-07-27 07:38:39 +00:00
: : glVertex2f ( 1.0f , 1.0f ) ;
: : glVertex2f ( - 1.0f , 1.0f ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnd ( ) ) ;
2018-05-29 11:54:34 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_DEPTH_TEST ) ) ;
2018-05-29 11:54:34 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glPopMatrix ( ) ) ;
glsafe ( : : glMatrixMode ( GL_MODELVIEW ) ) ;
glsafe ( : : glPopMatrix ( ) ) ;
2018-05-29 11:54:34 +00:00
}
2018-06-11 08:46:32 +00:00
void GLCanvas3D : : _render_bed ( float theta ) const
2018-05-29 11:54:34 +00:00
{
2019-01-24 14:44:00 +00:00
float scale_factor = 1.0 ;
# if ENABLE_RETINA_GL
scale_factor = m_retina_helper - > get_scale_factor ( ) ;
2019-03-07 10:49:00 +00:00
# endif // ENABLE_RETINA_GL
m_bed . render ( theta , m_use_VBOs , scale_factor ) ;
2018-05-29 11:54:34 +00:00
}
2018-12-17 13:09:35 +00:00
void GLCanvas3D : : _render_axes ( ) const
2018-05-29 11:54:34 +00:00
{
2019-03-07 10:49:00 +00:00
m_bed . render_axes ( ) ;
2018-05-29 11:54:34 +00:00
}
2019-03-11 09:35:23 +00:00
2019-03-20 07:48:42 +00:00
2018-06-04 10:26:39 +00:00
void GLCanvas3D : : _render_objects ( ) const
2018-05-29 11:54:34 +00:00
{
2018-06-11 13:13:13 +00:00
if ( m_volumes . empty ( ) )
2018-05-29 11:54:34 +00:00
return ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_LIGHTING ) ) ;
glsafe ( : : glEnable ( GL_DEPTH_TEST ) ) ;
2018-05-29 11:54:34 +00:00
2019-03-25 11:01:02 +00:00
m_camera_clipping_plane = m_gizmos . get_sla_clipping_plane ( ) ;
2019-03-20 07:48:42 +00:00
2018-12-20 12:20:21 +00:00
if ( m_use_VBOs )
2018-05-29 11:54:34 +00:00
{
2018-06-01 13:54:41 +00:00
if ( m_picking_enabled )
2018-05-29 11:54:34 +00:00
{
2019-01-21 09:06:51 +00:00
// Update the layer editing selection to the first object selected, update the current object maximum Z.
const_cast < LayersEditing & > ( m_layers_editing ) . select_object ( * m_model , this - > is_layers_editing_enabled ( ) ? m_selection . get_object_idx ( ) : - 1 ) ;
2018-05-29 11:54:34 +00:00
2019-03-07 10:49:00 +00:00
if ( m_config ! = nullptr )
2019-02-19 14:15:27 +00:00
{
2019-03-07 10:49:00 +00:00
const BoundingBoxf3 & bed_bb = m_bed . get_bounding_box ( ) ;
2019-02-19 14:15:27 +00:00
m_volumes . set_print_box ( ( float ) bed_bb . min ( 0 ) , ( float ) bed_bb . min ( 1 ) , 0.0f , ( float ) bed_bb . max ( 0 ) , ( float ) bed_bb . max ( 1 ) , ( float ) m_config - > opt_float ( " max_print_height " ) ) ;
m_volumes . check_outside_state ( m_config , nullptr ) ;
}
2018-05-29 11:54:34 +00:00
}
2018-11-27 13:50:57 +00:00
if ( m_use_clipping_planes )
2018-11-28 14:13:25 +00:00
m_volumes . set_z_range ( - m_clipping_planes [ 0 ] . get_data ( ) [ 3 ] , m_clipping_planes [ 1 ] . get_data ( ) [ 3 ] ) ;
2018-11-27 13:50:57 +00:00
else
m_volumes . set_z_range ( - FLT_MAX , FLT_MAX ) ;
2019-03-25 11:01:02 +00:00
m_volumes . set_clipping_plane ( m_camera_clipping_plane . get_data ( ) ) ;
2018-06-01 13:54:41 +00:00
m_shader . start_using ( ) ;
2019-04-16 06:50:46 +00:00
if ( m_picking_enabled & & ! m_gizmos . is_dragging ( ) & & m_layers_editing . is_enabled ( ) & & ( m_layers_editing . last_object_id ! = - 1 ) & & ( m_layers_editing . object_max_z ( ) > 0.0f ) ) {
2019-04-15 12:19:18 +00:00
int object_id = m_layers_editing . last_object_id ;
2019-04-01 08:00:10 +00:00
m_volumes . render_VBOs ( GLVolumeCollection : : Opaque , false , m_camera . get_view_matrix ( ) , [ object_id ] ( const GLVolume & volume ) {
2019-01-21 09:06:51 +00:00
// Which volume to paint without the layer height profile shader?
2019-04-01 08:00:10 +00:00
return volume . is_active & & ( volume . is_modifier | | volume . composite_id . object_id ! = object_id ) ;
2019-01-21 09:06:51 +00:00
} ) ;
// Let LayersEditing handle rendering of the active object using the layer height profile shader.
m_layers_editing . render_volumes ( * this , this - > m_volumes ) ;
} else {
// do not cull backfaces to show broken geometry, if any
2019-04-01 08:00:10 +00:00
m_volumes . render_VBOs ( GLVolumeCollection : : Opaque , m_picking_enabled , m_camera . get_view_matrix ( ) , [ this ] ( const GLVolume & volume ) {
2019-02-06 16:20:54 +00:00
return ( m_render_sla_auxiliaries | | volume . composite_id . volume_id > = 0 ) ;
} ) ;
2019-01-21 09:06:51 +00:00
}
2019-04-01 08:00:10 +00:00
m_volumes . render_VBOs ( GLVolumeCollection : : Transparent , false , m_camera . get_view_matrix ( ) ) ;
2018-06-01 13:54:41 +00:00
m_shader . stop_using ( ) ;
2018-05-29 11:54:34 +00:00
}
else
{
2019-03-25 11:01:02 +00:00
: : glClipPlane ( GL_CLIP_PLANE0 , ( GLdouble * ) m_camera_clipping_plane . get_data ( ) ) ;
: : glEnable ( GL_CLIP_PLANE0 ) ;
2018-11-27 13:50:57 +00:00
if ( m_use_clipping_planes )
{
2019-04-08 08:50:10 +00:00
glsafe ( : : glClipPlane ( GL_CLIP_PLANE1 , ( GLdouble * ) m_clipping_planes [ 0 ] . get_data ( ) ) ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_CLIP_PLANE1 ) ) ;
2019-04-08 08:50:10 +00:00
glsafe ( : : glClipPlane ( GL_CLIP_PLANE2 , ( GLdouble * ) m_clipping_planes [ 1 ] . get_data ( ) ) ) ;
glsafe ( : : glEnable ( GL_CLIP_PLANE2 ) ) ;
2018-11-27 13:50:57 +00:00
}
2019-03-25 11:01:02 +00:00
2018-11-27 13:50:57 +00:00
2018-12-21 08:56:11 +00:00
// do not cull backfaces to show broken geometry, if any
2019-04-01 08:00:10 +00:00
m_volumes . render_legacy ( GLVolumeCollection : : Opaque , m_picking_enabled , m_camera . get_view_matrix ( ) , [ this ] ( const GLVolume & volume ) {
return ( m_render_sla_auxiliaries | | volume . composite_id . volume_id > = 0 ) ;
} ) ;
m_volumes . render_legacy ( GLVolumeCollection : : Transparent , false , m_camera . get_view_matrix ( ) ) ;
2018-11-27 13:50:57 +00:00
2019-03-25 11:01:02 +00:00
: : glDisable ( GL_CLIP_PLANE0 ) ;
2018-11-27 13:50:57 +00:00
if ( m_use_clipping_planes )
{
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_CLIP_PLANE1 ) ) ;
2019-04-08 08:50:10 +00:00
glsafe ( : : glDisable ( GL_CLIP_PLANE2 ) ) ;
2018-11-27 13:50:57 +00:00
}
2018-05-29 11:54:34 +00:00
}
2019-03-25 11:01:02 +00:00
m_camera_clipping_plane = ClippingPlane : : ClipsNothing ( ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_LIGHTING ) ) ;
2018-05-29 11:54:34 +00:00
}
2018-10-08 12:02:12 +00:00
void GLCanvas3D : : _render_selection ( ) const
{
2019-01-24 14:44:00 +00:00
float scale_factor = 1.0 ;
# if ENABLE_RETINA_GL
scale_factor = m_retina_helper - > get_scale_factor ( ) ;
# endif
2018-11-06 09:31:19 +00:00
if ( ! m_gizmos . is_running ( ) )
2019-01-24 14:44:00 +00:00
m_selection . render ( scale_factor ) ;
2018-10-08 12:02:12 +00:00
}
2018-12-18 11:35:49 +00:00
# if ENABLE_RENDER_SELECTION_CENTER
void GLCanvas3D : : _render_selection_center ( ) const
{
if ( ! m_gizmos . is_running ( ) )
m_selection . render_center ( ) ;
}
# endif // ENABLE_RENDER_SELECTION_CENTER
2018-05-29 11:54:34 +00:00
void GLCanvas3D : : _render_warning_texture ( ) const
{
2018-07-31 12:20:16 +00:00
m_warning_texture . render ( * this ) ;
2018-05-29 11:54:34 +00:00
}
void GLCanvas3D : : _render_legend_texture ( ) const
{
if ( ! m_legend_texture_enabled )
return ;
2018-07-31 12:32:59 +00:00
m_legend_texture . render ( * this ) ;
2018-05-29 11:54:34 +00:00
}
2019-04-10 09:20:09 +00:00
void GLCanvas3D : : _render_volumes_for_picking ( ) const
2018-07-24 11:39:17 +00:00
{
static const GLfloat INV_255 = 1.0f / 255.0f ;
// do not cull backfaces to show broken geometry, if any
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_CULL_FACE ) ) ;
2018-07-24 11:39:17 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_BLEND ) ) ;
glsafe ( : : glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ) ;
2018-07-24 11:39:17 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnableClientState ( GL_VERTEX_ARRAY ) ) ;
glsafe ( : : glEnableClientState ( GL_NORMAL_ARRAY ) ) ;
2018-07-24 11:39:17 +00:00
2019-04-10 09:20:09 +00:00
const Transform3d & view_matrix = m_camera . get_view_matrix ( ) ;
GLVolumeWithIdAndZList to_render = volumes_to_render ( m_volumes . volumes , GLVolumeCollection : : Opaque , view_matrix ) ;
for ( const GLVolumeWithIdAndZ & volume : to_render )
2018-07-24 11:39:17 +00:00
{
2019-04-10 09:20:09 +00:00
// Object picking mode. Render the object with a color encoding the object index.
unsigned int r = ( volume . second . first & 0x000000FF ) > > 0 ;
unsigned int g = ( volume . second . first & 0x0000FF00 ) > > 8 ;
unsigned int b = ( volume . second . first & 0x00FF0000 ) > > 16 ;
glsafe ( : : glColor3f ( ( GLfloat ) r * INV_255 , ( GLfloat ) g * INV_255 , ( GLfloat ) b * INV_255 ) ) ;
2018-07-24 11:39:17 +00:00
2019-04-10 09:20:09 +00:00
if ( ! volume . first - > disabled & & ( ( volume . first - > composite_id . volume_id > = 0 ) | | m_render_sla_auxiliaries ) )
volume . first - > render ( ) ;
}
to_render = volumes_to_render ( m_volumes . volumes , GLVolumeCollection : : Transparent , view_matrix ) ;
for ( const GLVolumeWithIdAndZ & volume : to_render )
{
// Object picking mode. Render the object with a color encoding the object index.
unsigned int r = ( volume . second . first & 0x000000FF ) > > 0 ;
unsigned int g = ( volume . second . first & 0x0000FF00 ) > > 8 ;
unsigned int b = ( volume . second . first & 0x00FF0000 ) > > 16 ;
glsafe ( : : glColor3f ( ( GLfloat ) r * INV_255 , ( GLfloat ) g * INV_255 , ( GLfloat ) b * INV_255 ) ) ;
2018-11-07 11:11:34 +00:00
2019-04-10 09:20:09 +00:00
if ( ! volume . first - > disabled & & ( ( volume . first - > composite_id . volume_id > = 0 ) | | m_render_sla_auxiliaries ) )
volume . first - > render ( ) ;
2018-07-24 11:39:17 +00:00
}
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisableClientState ( GL_NORMAL_ARRAY ) ) ;
glsafe ( : : glDisableClientState ( GL_VERTEX_ARRAY ) ) ;
glsafe ( : : glDisable ( GL_BLEND ) ) ;
2018-07-24 11:39:17 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_CULL_FACE ) ) ;
2018-07-24 11:39:17 +00:00
}
2018-08-21 12:27:36 +00:00
void GLCanvas3D : : _render_current_gizmo ( ) const
{
2018-10-15 09:30:50 +00:00
m_gizmos . render_current_gizmo ( m_selection ) ;
2018-08-21 12:27:36 +00:00
}
void GLCanvas3D : : _render_gizmos_overlay ( ) const
2018-07-24 11:39:17 +00:00
{
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
m_gizmos . set_overlay_scale ( m_retina_helper - > get_scale_factor ( ) ) ;
2019-02-04 20:41:10 +00:00
# else
2019-04-16 10:11:48 +00:00
// m_gizmos.set_overlay_scale(m_canvas->GetContentScaleFactor());
m_gizmos . set_overlay_scale ( wxGetApp ( ) . em_unit ( ) * 0.1f ) ; //! #ys_FIXME_experiment
2019-02-04 20:41:10 +00:00
# endif /* __WXMSW__ */
2018-11-26 09:56:07 +00:00
m_gizmos . render_overlay ( * this , m_selection ) ;
2018-07-24 11:39:17 +00:00
}
2018-07-23 11:49:48 +00:00
void GLCanvas3D : : _render_toolbar ( ) const
{
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
# if ENABLE_RETINA_GL
m_toolbar . set_scale ( m_retina_helper - > get_scale_factor ( ) ) ;
# else
2019-04-16 10:11:48 +00:00
// m_toolbar.set_scale(m_canvas->GetContentScaleFactor());
m_toolbar . set_scale ( wxGetApp ( ) . em_unit ( ) * 0.1f ) ; //! #ys_FIXME_experiment
2019-02-26 08:56:23 +00:00
# endif // ENABLE_RETINA_GL
Size cnv_size = get_canvas_size ( ) ;
2019-04-01 08:00:10 +00:00
float zoom = m_camera . zoom ;
2019-02-26 08:56:23 +00:00
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
GLToolbar : : Layout : : EOrientation orientation = m_toolbar . get_layout_orientation ( ) ;
float top = 0.0f ;
float left = 0.0f ;
switch ( m_toolbar . get_layout_type ( ) )
{
default :
case GLToolbar : : Layout : : Horizontal :
{
// centers the toolbar on the top edge of the 3d scene
if ( orientation = = GLToolbar : : Layout : : Top )
{
top = 0.5f * ( float ) cnv_size . get_height ( ) * inv_zoom ;
left = - 0.5f * m_toolbar . get_width ( ) * inv_zoom ;
}
else
{
2019-03-07 10:49:00 +00:00
top = ( - 0.5f * ( float ) cnv_size . get_height ( ) + m_view_toolbar . get_height ( ) ) * inv_zoom ;
2019-02-26 08:56:23 +00:00
left = - 0.5f * m_toolbar . get_width ( ) * inv_zoom ;
}
break ;
}
case GLToolbar : : Layout : : Vertical :
{
// centers the toolbar on the right edge of the 3d scene
if ( orientation = = GLToolbar : : Layout : : Left )
{
top = 0.5f * m_toolbar . get_height ( ) * inv_zoom ;
left = ( - 0.5f * ( float ) cnv_size . get_width ( ) ) * inv_zoom ;
}
else
{
top = 0.5f * m_toolbar . get_height ( ) * inv_zoom ;
left = ( 0.5f * ( float ) cnv_size . get_width ( ) - m_toolbar . get_width ( ) ) * inv_zoom ;
}
break ;
}
}
m_toolbar . set_position ( top , left ) ;
# else
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
m_toolbar . set_icons_scale ( m_retina_helper - > get_scale_factor ( ) ) ;
2019-02-04 20:41:10 +00:00
# else
m_toolbar . set_icons_scale ( m_canvas - > GetContentScaleFactor ( ) ) ;
# endif /* __WXMSW__ */
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-02-04 20:41:10 +00:00
2018-12-17 09:55:14 +00:00
m_toolbar . render ( * this ) ;
2018-07-23 11:49:48 +00:00
}
2018-12-06 09:38:19 +00:00
void GLCanvas3D : : _render_view_toolbar ( ) const
{
2019-02-26 08:56:23 +00:00
# if ENABLE_SVG_ICONS
# if ENABLE_RETINA_GL
2019-03-07 10:49:00 +00:00
m_view_toolbar . set_scale ( m_retina_helper - > get_scale_factor ( ) ) ;
2019-02-26 08:56:23 +00:00
# else
2019-04-16 10:11:48 +00:00
// m_view_toolbar.set_scale(m_canvas->GetContentScaleFactor());
m_view_toolbar . set_scale ( wxGetApp ( ) . em_unit ( ) * 0.1f ) ; //! #ys_FIXME_experiment
2019-02-26 08:56:23 +00:00
# endif // ENABLE_RETINA_GL
2019-03-07 10:49:00 +00:00
Size cnv_size = get_canvas_size ( ) ;
2019-04-01 08:00:10 +00:00
float zoom = m_camera . zoom ;
2019-03-07 10:49:00 +00:00
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
2019-02-26 08:56:23 +00:00
2019-03-07 10:49:00 +00:00
// places the toolbar on the bottom-left corner of the 3d scene
float top = ( - 0.5f * ( float ) cnv_size . get_height ( ) + m_view_toolbar . get_height ( ) ) * inv_zoom ;
float left = - 0.5f * ( float ) cnv_size . get_width ( ) * inv_zoom ;
m_view_toolbar . set_position ( top , left ) ;
2019-02-26 08:56:23 +00:00
# else
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
2019-03-07 10:49:00 +00:00
m_view_toolbar . set_icons_scale ( m_retina_helper - > get_scale_factor ( ) ) ;
2019-02-04 20:41:10 +00:00
# else
2019-03-07 10:49:00 +00:00
m_view_toolbar . set_icons_scale ( m_canvas - > GetContentScaleFactor ( ) ) ;
2019-02-04 20:41:10 +00:00
# endif /* __WXMSW__ */
2019-02-26 08:56:23 +00:00
# endif // ENABLE_SVG_ICONS
2019-03-07 10:49:00 +00:00
m_view_toolbar . render ( * this ) ;
2018-12-06 09:38:19 +00:00
}
2018-10-26 07:50:28 +00:00
# if ENABLE_SHOW_CAMERA_TARGET
void GLCanvas3D : : _render_camera_target ( ) const
{
double half_length = 5.0 ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_DEPTH_TEST ) ) ;
2018-10-26 07:50:28 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glLineWidth ( 2.0f ) ) ;
2018-10-26 07:50:28 +00:00
: : glBegin ( GL_LINES ) ;
2019-03-07 10:49:00 +00:00
const Vec3d & target = m_camera . get_target ( ) ;
2018-12-07 15:23:04 +00:00
// draw line for x axis
: : glColor3f ( 1.0f , 0.0f , 0.0f ) ;
: : glVertex3d ( target ( 0 ) - half_length , target ( 1 ) , target ( 2 ) ) ;
: : glVertex3d ( target ( 0 ) + half_length , target ( 1 ) , target ( 2 ) ) ;
// draw line for y axis
: : glColor3f ( 0.0f , 1.0f , 0.0f ) ;
: : glVertex3d ( target ( 0 ) , target ( 1 ) - half_length , target ( 2 ) ) ;
: : glVertex3d ( target ( 0 ) , target ( 1 ) + half_length , target ( 2 ) ) ;
2018-12-18 12:07:50 +00:00
// draw line for z axis
2018-12-07 15:23:04 +00:00
: : glColor3f ( 0.0f , 0.0f , 1.0f ) ;
: : glVertex3d ( target ( 0 ) , target ( 1 ) , target ( 2 ) - half_length ) ;
: : glVertex3d ( target ( 0 ) , target ( 1 ) , target ( 2 ) + half_length ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnd ( ) ) ;
2018-10-26 07:50:28 +00:00
}
# endif // ENABLE_SHOW_CAMERA_TARGET
2018-11-28 14:13:25 +00:00
void GLCanvas3D : : _render_sla_slices ( ) const
{
if ( ! m_use_clipping_planes | | wxGetApp ( ) . preset_bundle - > printers . get_edited_preset ( ) . printer_technology ( ) ! = ptSLA )
return ;
const SLAPrint * print = this - > sla_print ( ) ;
2018-12-10 11:59:49 +00:00
const PrintObjects & print_objects = print - > objects ( ) ;
if ( print_objects . empty ( ) )
2018-11-28 14:13:25 +00:00
// nothing to render, return
return ;
double clip_min_z = - m_clipping_planes [ 0 ] . get_data ( ) [ 3 ] ;
double clip_max_z = m_clipping_planes [ 1 ] . get_data ( ) [ 3 ] ;
2018-12-10 11:59:49 +00:00
for ( unsigned int i = 0 ; i < ( unsigned int ) print_objects . size ( ) ; + + i )
2018-11-28 14:13:25 +00:00
{
2018-12-10 11:59:49 +00:00
const SLAPrintObject * obj = print_objects [ i ] ;
2018-11-28 14:13:25 +00:00
2019-04-02 10:13:45 +00:00
if ( ! obj - > is_step_done ( slaposSliceSupports ) )
continue ;
2019-01-24 18:08:58 +00:00
SlaCap : : ObjectIdToTrianglesMap : : iterator it_caps_bottom = m_sla_caps [ 0 ] . triangles . find ( i ) ;
SlaCap : : ObjectIdToTrianglesMap : : iterator it_caps_top = m_sla_caps [ 1 ] . triangles . find ( i ) ;
2018-12-10 11:59:49 +00:00
{
2019-01-24 18:08:58 +00:00
if ( it_caps_bottom = = m_sla_caps [ 0 ] . triangles . end ( ) )
it_caps_bottom = m_sla_caps [ 0 ] . triangles . emplace ( i , SlaCap : : Triangles ( ) ) . first ;
2019-03-22 20:26:58 +00:00
if ( ! m_sla_caps [ 0 ] . matches ( clip_min_z ) ) {
m_sla_caps [ 0 ] . z = clip_min_z ;
2019-01-24 18:08:58 +00:00
it_caps_bottom - > second . object . clear ( ) ;
it_caps_bottom - > second . supports . clear ( ) ;
2018-12-10 11:59:49 +00:00
}
2019-01-24 18:08:58 +00:00
if ( it_caps_top = = m_sla_caps [ 1 ] . triangles . end ( ) )
it_caps_top = m_sla_caps [ 1 ] . triangles . emplace ( i , SlaCap : : Triangles ( ) ) . first ;
2019-03-22 20:26:58 +00:00
if ( ! m_sla_caps [ 1 ] . matches ( clip_max_z ) ) {
m_sla_caps [ 1 ] . z = clip_max_z ;
2019-01-24 18:08:58 +00:00
it_caps_top - > second . object . clear ( ) ;
it_caps_top - > second . supports . clear ( ) ;
2018-11-28 14:13:25 +00:00
}
2018-12-10 11:59:49 +00:00
}
2019-01-24 18:08:58 +00:00
Pointf3s & bottom_obj_triangles = it_caps_bottom - > second . object ;
Pointf3s & bottom_sup_triangles = it_caps_bottom - > second . supports ;
Pointf3s & top_obj_triangles = it_caps_top - > second . object ;
Pointf3s & top_sup_triangles = it_caps_top - > second . supports ;
2018-11-28 14:13:25 +00:00
2019-03-25 18:02:05 +00:00
if ( ( bottom_obj_triangles . empty ( ) | | bottom_sup_triangles . empty ( ) | | top_obj_triangles . empty ( ) | | top_sup_triangles . empty ( ) ) & &
2019-04-02 10:13:45 +00:00
! obj - > get_slice_index ( ) . empty ( ) )
2018-12-10 11:59:49 +00:00
{
2019-03-26 09:38:50 +00:00
double layer_height = print - > default_object_config ( ) . layer_height . value ;
2019-03-22 20:26:58 +00:00
double initial_layer_height = print - > material_config ( ) . initial_layer_height . value ;
2019-04-02 11:47:49 +00:00
bool left_handed = obj - > is_left_handed ( ) ;
2019-03-26 10:13:28 +00:00
2019-03-25 18:02:05 +00:00
coord_t key_zero = obj - > get_slice_index ( ) . front ( ) . print_level ( ) ;
2019-03-26 09:38:50 +00:00
// Slice at the center of the slab starting at clip_min_z will be rendered for the lower plane.
2019-03-26 10:13:28 +00:00
coord_t key_low = coord_t ( ( clip_min_z - initial_layer_height + layer_height ) / SCALING_FACTOR ) + key_zero ;
2019-03-26 09:38:50 +00:00
// Slice at the center of the slab ending at clip_max_z will be rendered for the upper plane.
2019-03-25 18:02:05 +00:00
coord_t key_high = coord_t ( ( clip_max_z - initial_layer_height ) / SCALING_FACTOR ) + key_zero ;
2019-03-26 10:13:28 +00:00
const SliceRecord & slice_low = obj - > closest_slice_to_print_level ( key_low , coord_t ( SCALED_EPSILON ) ) ;
const SliceRecord & slice_high = obj - > closest_slice_to_print_level ( key_high , coord_t ( SCALED_EPSILON ) ) ;
2019-03-26 09:38:50 +00:00
// Offset to avoid OpenGL Z fighting between the object's horizontal surfaces and the triangluated surfaces of the cuts.
2019-03-26 10:13:28 +00:00
double plane_shift_z = 0.002 ;
2019-03-26 09:38:50 +00:00
2019-03-26 15:45:04 +00:00
if ( slice_low . is_valid ( ) ) {
2019-03-26 09:57:45 +00:00
const ExPolygons & obj_bottom = slice_low . get_slice ( soModel ) ;
const ExPolygons & sup_bottom = slice_low . get_slice ( soSupport ) ;
2019-03-22 20:26:58 +00:00
// calculate model bottom cap
if ( bottom_obj_triangles . empty ( ) & & ! obj_bottom . empty ( ) )
2019-04-02 11:47:49 +00:00
bottom_obj_triangles = triangulate_expolygons_3d ( obj_bottom , clip_min_z - plane_shift_z , ! left_handed ) ;
2019-03-22 20:26:58 +00:00
// calculate support bottom cap
if ( bottom_sup_triangles . empty ( ) & & ! sup_bottom . empty ( ) )
2019-04-02 11:47:49 +00:00
bottom_sup_triangles = triangulate_expolygons_3d ( sup_bottom , clip_min_z - plane_shift_z , ! left_handed ) ;
2019-03-22 20:26:58 +00:00
}
2019-03-26 15:45:04 +00:00
if ( slice_high . is_valid ( ) ) {
2019-03-26 09:57:45 +00:00
const ExPolygons & obj_top = slice_high . get_slice ( soModel ) ;
const ExPolygons & sup_top = slice_high . get_slice ( soSupport ) ;
2019-03-22 20:26:58 +00:00
// calculate model top cap
if ( top_obj_triangles . empty ( ) & & ! obj_top . empty ( ) )
2019-04-02 11:47:49 +00:00
top_obj_triangles = triangulate_expolygons_3d ( obj_top , clip_max_z + plane_shift_z , left_handed ) ;
2019-03-22 20:26:58 +00:00
// calculate support top cap
if ( top_sup_triangles . empty ( ) & & ! sup_top . empty ( ) )
2019-04-02 11:47:49 +00:00
top_sup_triangles = triangulate_expolygons_3d ( sup_top , clip_max_z + plane_shift_z , left_handed ) ;
2019-03-22 20:26:58 +00:00
}
2018-12-10 11:59:49 +00:00
}
2018-11-29 10:11:39 +00:00
2018-12-10 11:59:49 +00:00
if ( ! bottom_obj_triangles . empty ( ) | | ! top_obj_triangles . empty ( ) | | ! bottom_sup_triangles . empty ( ) | | ! top_sup_triangles . empty ( ) )
{
2019-04-02 11:47:49 +00:00
for ( const SLAPrintObject : : Instance & inst : obj - > instances ( ) )
2018-11-29 10:11:39 +00:00
{
2019-03-27 13:42:09 +00:00
glsafe ( : : glPushMatrix ( ) ) ;
2019-04-02 13:13:26 +00:00
glsafe ( : : glTranslated ( unscale < double > ( inst . shift . x ( ) ) , unscale < double > ( inst . shift . y ( ) ) , 0 ) ) ;
glsafe ( : : glRotatef ( Geometry : : rad2deg ( inst . rotation ) , 0.0 , 0.0 , 1.0 ) ) ;
2019-04-02 11:47:49 +00:00
if ( obj - > is_left_handed ( ) )
// The polygons are mirrored by X.
2019-04-02 13:13:26 +00:00
glsafe ( : : glScalef ( - 1.0 , 1.0 , 1.0 ) ) ;
glsafe ( : : glEnableClientState ( GL_VERTEX_ARRAY ) ) ;
2019-04-03 06:38:32 +00:00
glsafe ( : : glColor3f ( 1.0f , 0.37f , 0.0f ) ) ;
2019-04-02 16:04:23 +00:00
if ( ! bottom_obj_triangles . empty ( ) ) {
2019-04-03 06:38:32 +00:00
glsafe ( : : glVertexPointer ( 3 , GL_DOUBLE , 0 , ( GLdouble * ) bottom_obj_triangles . front ( ) . data ( ) ) ) ;
glsafe ( : : glDrawArrays ( GL_TRIANGLES , 0 , bottom_obj_triangles . size ( ) ) ) ;
2019-04-02 16:04:23 +00:00
}
if ( ! top_obj_triangles . empty ( ) ) {
2019-04-03 06:38:32 +00:00
glsafe ( : : glVertexPointer ( 3 , GL_DOUBLE , 0 , ( GLdouble * ) top_obj_triangles . front ( ) . data ( ) ) ) ;
glsafe ( : : glDrawArrays ( GL_TRIANGLES , 0 , top_obj_triangles . size ( ) ) ) ;
2019-04-02 16:04:23 +00:00
}
2019-04-02 13:13:26 +00:00
glsafe ( : : glColor3f ( 1.0f , 0.0f , 0.37f ) ) ;
2019-04-02 16:04:23 +00:00
if ( ! bottom_sup_triangles . empty ( ) ) {
2019-04-03 06:38:32 +00:00
glsafe ( : : glVertexPointer ( 3 , GL_DOUBLE , 0 , ( GLdouble * ) bottom_sup_triangles . front ( ) . data ( ) ) ) ;
glsafe ( : : glDrawArrays ( GL_TRIANGLES , 0 , bottom_sup_triangles . size ( ) ) ) ;
2019-04-02 16:04:23 +00:00
}
if ( ! top_sup_triangles . empty ( ) ) {
2019-04-03 06:38:32 +00:00
glsafe ( : : glVertexPointer ( 3 , GL_DOUBLE , 0 , ( GLdouble * ) top_sup_triangles . front ( ) . data ( ) ) ) ;
glsafe ( : : glDrawArrays ( GL_TRIANGLES , 0 , top_sup_triangles . size ( ) ) ) ;
2019-04-02 16:04:23 +00:00
}
2019-04-02 13:13:26 +00:00
glsafe ( : : glDisableClientState ( GL_VERTEX_ARRAY ) ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glPopMatrix ( ) ) ;
2018-11-28 14:13:25 +00:00
}
}
}
}
2018-12-19 13:44:37 +00:00
void GLCanvas3D : : _render_selection_sidebar_hints ( ) const
{
if ( m_use_VBOs )
m_shader . start_using ( ) ;
m_selection . render_sidebar_hints ( m_sidebar_field ) ;
if ( m_use_VBOs )
m_shader . stop_using ( ) ;
}
2019-04-26 13:34:26 +00:00
2018-10-08 12:02:12 +00:00
void GLCanvas3D : : _update_volumes_hover_state ( ) const
{
for ( GLVolume * v : m_volumes . volumes )
{
2019-04-25 11:35:24 +00:00
v - > hover = GLVolume : : HS_None ;
2018-10-08 12:02:12 +00:00
}
2019-04-24 13:07:28 +00:00
if ( m_hover_volume_idxs . empty ( ) )
2018-10-08 12:02:12 +00:00
return ;
2019-04-26 11:37:34 +00:00
bool ctrl_pressed = wxGetKeyState ( WXK_CONTROL ) ; // additive select/deselect
bool shift_pressed = wxGetKeyState ( WXK_SHIFT ) ; // select by rectangle
bool alt_pressed = wxGetKeyState ( WXK_ALT ) ; // deselect by rectangle
2019-04-25 07:46:26 +00:00
2019-04-26 11:37:34 +00:00
if ( alt_pressed & & ( shift_pressed | | ctrl_pressed ) )
{
// illegal combinations of keys
m_hover_volume_idxs . clear ( ) ;
return ;
}
bool selection_modifiers_only = m_selection . is_empty ( ) | | m_selection . is_any_modifier ( ) ;
bool hover_modifiers_only = true ;
for ( int i : m_hover_volume_idxs )
{
if ( ! m_volumes . volumes [ i ] - > is_modifier )
{
hover_modifiers_only = false ;
break ;
}
}
std : : set < std : : pair < int , int > > hover_instances ;
2019-04-25 07:46:26 +00:00
for ( int i : m_hover_volume_idxs )
2018-10-08 12:02:12 +00:00
{
2019-04-26 11:37:34 +00:00
const GLVolume & v = * m_volumes . volumes [ i ] ;
hover_instances . insert ( std : : make_pair ( v . object_idx ( ) , v . instance_idx ( ) ) ) ;
}
bool hover_from_single_instance = hover_instances . size ( ) = = 1 ;
2018-10-08 12:02:12 +00:00
2019-04-26 11:37:34 +00:00
if ( hover_modifiers_only & & ! hover_from_single_instance )
{
// do not allow to select volumes from different instances
m_hover_volume_idxs . clear ( ) ;
return ;
}
for ( int i : m_hover_volume_idxs )
{
GLVolume & volume = * m_volumes . volumes [ i ] ;
if ( volume . hover ! = GLVolume : : HS_None )
continue ;
bool deselect = volume . selected & & ( ( ctrl_pressed & & ! shift_pressed ) | | alt_pressed ) ;
// (volume->is_modifier && !selection_modifiers_only && !is_ctrl_pressed) -> allows hovering on selected modifiers belonging to selection of type Instance
bool select = ( ! volume . selected | | ( volume . is_modifier & & ! selection_modifiers_only & & ! ctrl_pressed ) ) & & ! alt_pressed ;
2018-10-08 12:02:12 +00:00
2019-04-25 07:46:26 +00:00
if ( select | | deselect )
2018-10-08 12:02:12 +00:00
{
2019-04-26 11:37:34 +00:00
bool as_volume =
volume . is_modifier & & hover_from_single_instance & & ! ctrl_pressed & &
(
( ! deselect ) | |
( deselect & & ! m_selection . is_single_full_instance ( ) & & ( volume . object_idx ( ) = = m_selection . get_object_idx ( ) ) & & ( volume . instance_idx ( ) = = m_selection . get_instance_idx ( ) ) )
) ;
if ( as_volume )
2019-04-25 07:46:26 +00:00
{
if ( deselect )
2019-04-26 11:37:34 +00:00
volume . hover = GLVolume : : HS_Deselect ;
2019-04-25 07:46:26 +00:00
else
2019-04-26 11:37:34 +00:00
volume . hover = GLVolume : : HS_Select ;
2019-04-25 07:46:26 +00:00
}
else
{
2019-04-26 11:37:34 +00:00
int object_idx = volume . object_idx ( ) ;
int instance_idx = volume . instance_idx ( ) ;
2019-04-25 07:46:26 +00:00
for ( GLVolume * v : m_volumes . volumes )
{
if ( ( v - > object_idx ( ) = = object_idx ) & & ( v - > instance_idx ( ) = = instance_idx ) )
{
if ( deselect )
2019-04-25 11:35:24 +00:00
v - > hover = GLVolume : : HS_Deselect ;
2019-04-25 07:46:26 +00:00
else
2019-04-25 11:35:24 +00:00
v - > hover = GLVolume : : HS_Select ;
2019-04-25 07:46:26 +00:00
}
}
}
2018-10-08 12:02:12 +00:00
}
}
}
2018-07-24 11:39:17 +00:00
void GLCanvas3D : : _perform_layer_editing_action ( wxMouseEvent * evt )
{
int object_idx_selected = m_layers_editing . last_object_id ;
if ( object_idx_selected = = - 1 )
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 ( ) ;
2019-01-21 09:06:51 +00:00
m_layers_editing . last_z = m_layers_editing . object_max_z ( ) * ( b - evt - > GetY ( ) - 1.0f ) / ( b - rect . get_top ( ) ) ;
m_layers_editing . last_action =
evt - > ShiftDown ( ) ? ( evt - > RightIsDown ( ) ? LAYER_HEIGHT_EDIT_ACTION_SMOOTH : LAYER_HEIGHT_EDIT_ACTION_REDUCE ) :
( evt - > RightIsDown ( ) ? LAYER_HEIGHT_EDIT_ACTION_INCREASE : LAYER_HEIGHT_EDIT_ACTION_DECREASE ) ;
2018-07-24 11:39:17 +00:00
}
2019-01-21 09:06:51 +00:00
m_layers_editing . adjust_layer_height_profile ( ) ;
2018-07-24 11:39:17 +00:00
_refresh_if_shown_on_screen ( ) ;
// Automatic action on mouse down with the same coordinate.
_start_timer ( ) ;
}
2018-08-21 15:43:05 +00:00
Vec3d GLCanvas3D : : _mouse_to_3d ( const Point & mouse_pos , float * z )
2018-07-24 11:39:17 +00:00
{
if ( m_canvas = = nullptr )
2018-08-21 15:43:05 +00:00
return Vec3d ( DBL_MAX , DBL_MAX , DBL_MAX ) ;
2018-07-24 11:39:17 +00:00
2019-04-01 08:00:10 +00:00
const std : : array < int , 4 > & viewport = m_camera . get_viewport ( ) ;
const Transform3d & modelview_matrix = m_camera . get_view_matrix ( ) ;
const Transform3d & projection_matrix = m_camera . get_projection_matrix ( ) ;
2018-07-24 11:39:17 +00:00
2018-08-17 16:07:45 +00:00
GLint y = viewport [ 3 ] - ( GLint ) mouse_pos ( 1 ) ;
2018-07-24 11:39:17 +00:00
GLfloat mouse_z ;
if ( z = = nullptr )
2019-03-27 13:42:09 +00:00
glsafe ( : : glReadPixels ( ( GLint ) mouse_pos ( 0 ) , y , 1 , 1 , GL_DEPTH_COMPONENT , GL_FLOAT , ( void * ) & mouse_z ) ) ;
2018-07-24 11:39:17 +00:00
else
mouse_z = * z ;
GLdouble out_x , out_y , out_z ;
2019-04-01 08:00:10 +00:00
: : gluUnProject ( ( GLdouble ) mouse_pos ( 0 ) , ( GLdouble ) y , ( GLdouble ) mouse_z , ( GLdouble * ) modelview_matrix . data ( ) , ( GLdouble * ) projection_matrix . data ( ) , ( GLint * ) viewport . data ( ) , & out_x , & out_y , & out_z ) ;
2018-08-24 08:20:00 +00:00
return Vec3d ( ( double ) out_x , ( double ) out_y , ( double ) out_z ) ;
2018-07-24 11:39:17 +00:00
}
2018-08-21 15:43:05 +00:00
Vec3d GLCanvas3D : : _mouse_to_bed_3d ( const Point & mouse_pos )
2018-08-20 08:23:17 +00:00
{
return mouse_ray ( mouse_pos ) . intersect_plane ( 0.0 ) ;
}
2018-07-24 11:39:17 +00:00
void GLCanvas3D : : _start_timer ( )
{
2018-10-25 07:35:08 +00:00
m_timer . Start ( 100 , wxTIMER_CONTINUOUS ) ;
2018-07-24 11:39:17 +00:00
}
void GLCanvas3D : : _stop_timer ( )
{
2018-10-25 07:35:08 +00:00
m_timer . Stop ( ) ;
2018-07-24 11:39:17 +00:00
}
void GLCanvas3D : : _load_print_toolpaths ( )
2018-06-01 13:54:41 +00:00
{
2018-11-22 14:29:59 +00:00
const Print * print = this - > fff_print ( ) ;
if ( print = = nullptr )
2018-07-24 11:39:17 +00:00
return ;
2018-06-01 13:54:41 +00:00
2018-11-22 14:29:59 +00:00
if ( ! print - > is_step_done ( psSkirt ) | | ! print - > is_step_done ( psBrim ) )
2018-07-24 11:39:17 +00:00
return ;
2018-06-01 13:54:41 +00:00
2018-11-22 14:29:59 +00:00
if ( ! print - > has_skirt ( ) & & ( print - > config ( ) . brim_width . value = = 0 ) )
2018-07-24 11:39:17 +00:00
return ;
2018-06-01 13:54:41 +00:00
2018-07-24 11:39:17 +00:00
const float color [ ] = { 0.5f , 1.0f , 0.5f , 1.0f } ; // greenish
2018-06-01 13:54:41 +00:00
2018-07-24 11:39:17 +00:00
// number of skirt layers
size_t total_layer_count = 0 ;
2018-11-22 14:29:59 +00:00
for ( const PrintObject * print_object : print - > objects ( ) )
2018-06-01 13:54:41 +00:00
{
2018-07-24 11:39:17 +00:00
total_layer_count = std : : max ( total_layer_count , print_object - > total_layer_count ( ) ) ;
2018-06-01 13:54:41 +00:00
}
2018-11-22 14:29:59 +00:00
size_t skirt_height = print - > has_infinite_skirt ( ) ? total_layer_count : std : : min < size_t > ( print - > config ( ) . skirt_height . value , total_layer_count ) ;
if ( ( skirt_height = = 0 ) & & ( print - > config ( ) . brim_width . value > 0 ) )
2018-07-24 11:39:17 +00:00
skirt_height = 1 ;
2018-06-01 13:54:41 +00:00
2018-07-24 11:39:17 +00:00
// get first skirt_height layers (maybe this should be moved to a PrintObject method?)
2018-11-22 14:29:59 +00:00
const PrintObject * object0 = print - > objects ( ) . front ( ) ;
2018-07-24 11:39:17 +00:00
std : : vector < float > print_zs ;
print_zs . reserve ( skirt_height * 2 ) ;
2018-09-12 09:59:02 +00:00
for ( size_t i = 0 ; i < std : : min ( skirt_height , object0 - > layers ( ) . size ( ) ) ; + + i )
2018-07-24 11:39:17 +00:00
{
2018-09-12 09:59:02 +00:00
print_zs . push_back ( float ( object0 - > layers ( ) [ i ] - > print_z ) ) ;
2018-07-24 11:39:17 +00:00
}
//FIXME why there are support layers?
2018-09-12 09:59:02 +00:00
for ( size_t i = 0 ; i < std : : min ( skirt_height , object0 - > support_layers ( ) . size ( ) ) ; + + i )
2018-07-24 11:39:17 +00:00
{
2018-09-12 09:59:02 +00:00
print_zs . push_back ( float ( object0 - > support_layers ( ) [ i ] - > print_z ) ) ;
2018-07-24 11:39:17 +00:00
}
sort_remove_duplicates ( print_zs ) ;
if ( print_zs . size ( ) > skirt_height )
print_zs . erase ( print_zs . begin ( ) + skirt_height , print_zs . end ( ) ) ;
2018-06-01 13:54:41 +00:00
2018-07-24 11:39:17 +00:00
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 )
2018-11-22 14:29:59 +00:00
_3DScene : : extrusionentity_to_verts ( print - > brim ( ) , print_zs [ i ] , Point ( 0 , 0 ) , volume ) ;
2018-06-22 10:21:43 +00:00
2018-11-22 14:29:59 +00:00
_3DScene : : extrusionentity_to_verts ( print - > skirt ( ) , print_zs [ i ] , Point ( 0 , 0 ) , volume ) ;
2018-07-24 11:39:17 +00:00
}
volume . bounding_box = volume . indexed_vertex_array . bounding_box ( ) ;
volume . indexed_vertex_array . finalize_geometry ( m_use_VBOs & & m_initialized ) ;
2018-06-01 13:54:41 +00:00
}
2019-01-29 14:11:29 +00:00
void GLCanvas3D : : _load_print_object_toolpaths ( const PrintObject & print_object , const std : : vector < std : : string > & str_tool_colors , const std : : vector < double > & color_print_values )
2018-06-13 07:12:16 +00:00
{
2018-07-24 11:39:17 +00:00
std : : vector < float > tool_colors = _parse_colors ( str_tool_colors ) ;
2018-06-13 07:12:16 +00:00
2018-07-24 11:39:17 +00:00
struct Ctxt
{
const Points * shifted_copies ;
std : : vector < const Layer * > layers ;
bool has_perimeters ;
bool has_infill ;
bool has_support ;
const std : : vector < float > * tool_colors ;
2019-01-29 14:11:29 +00:00
const std : : vector < double > * color_print_values ;
2018-06-01 13:54:41 +00:00
2018-07-24 11:39:17 +00:00
// 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 ; }
2018-05-30 13:18:45 +00:00
2018-07-24 11:39:17 +00:00
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
2018-05-30 13:18:45 +00:00
2018-07-24 11:39:17 +00:00
// 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
{
2019-01-30 11:10:26 +00:00
return this - > color_by_color_print ( ) ? 0 : this - > color_by_tool ( ) ? std : : min < int > ( this - > number_tools ( ) - 1 , std : : max < int > ( extruder - 1 , 0 ) ) : feature ;
2018-07-24 11:39:17 +00:00
}
2019-01-29 14:11:29 +00:00
// For coloring by a color_print(M600), return a parsed color.
bool color_by_color_print ( ) const { return color_print_values ! = nullptr ; }
const float * color_print_by_layer_idx ( const size_t layer_idx ) const
{
2019-01-30 13:45:18 +00:00
auto it = std : : lower_bound ( color_print_values - > begin ( ) , color_print_values - > end ( ) , layers [ layer_idx ] - > print_z + EPSILON ) ;
2019-01-30 11:10:26 +00:00
return color_tool ( ( it - color_print_values - > begin ( ) ) % number_tools ( ) ) ;
2019-01-29 14:11:29 +00:00
}
2018-07-24 11:39:17 +00:00
} ctxt ;
2018-05-30 13:18:45 +00:00
2019-01-09 14:07:10 +00:00
ctxt . has_perimeters = print_object . is_step_done ( posPerimeters ) ;
ctxt . has_infill = print_object . is_step_done ( posInfill ) ;
ctxt . has_support = print_object . is_step_done ( posSupportMaterial ) ;
ctxt . tool_colors = tool_colors . empty ( ) ? nullptr : & tool_colors ;
2019-01-29 14:11:29 +00:00
ctxt . color_print_values = color_print_values . empty ( ) ? nullptr : & color_print_values ;
2019-01-09 14:07:10 +00:00
2018-09-12 09:59:02 +00:00
ctxt . shifted_copies = & print_object . copies ( ) ;
2018-05-30 13:18:45 +00:00
2018-07-24 11:39:17 +00:00
// order layers by print_z
2019-01-09 14:07:10 +00:00
{
size_t nlayers = 0 ;
if ( ctxt . has_perimeters | | ctxt . has_infill )
nlayers = print_object . layers ( ) . size ( ) ;
if ( ctxt . has_support )
nlayers + = print_object . support_layers ( ) . size ( ) ;
ctxt . layers . reserve ( nlayers ) ;
}
if ( ctxt . has_perimeters | | ctxt . has_infill )
for ( const Layer * layer : print_object . layers ( ) )
ctxt . layers . push_back ( layer ) ;
if ( ctxt . has_support )
for ( const Layer * layer : print_object . support_layers ( ) )
ctxt . layers . push_back ( layer ) ;
2018-07-24 11:39:17 +00:00
std : : sort ( ctxt . layers . begin ( ) , ctxt . layers . end ( ) , [ ] ( const Layer * l1 , const Layer * l2 ) { return l1 - > print_z < l2 - > print_z ; } ) ;
2018-05-30 13:18:45 +00:00
2018-07-24 11:39:17 +00:00
// Maximum size of an allocation block: 32MB / sizeof(float)
BOOST_LOG_TRIVIAL ( debug ) < < " Loading print object toolpaths in parallel - start " ;
//FIXME Improve the heuristics for a grain size.
2019-01-29 14:11:29 +00:00
size_t grain_size = ctxt . color_by_color_print ( ) ? size_t ( 1 ) : std : : max ( ctxt . layers . size ( ) / 16 , size_t ( 1 ) ) ;
2018-07-24 11:39:17 +00:00
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 ( ) ;
m_volumes . volumes . emplace_back ( volume ) ;
new_volume_mutex . unlock ( ) ;
return volume ;
} ;
const size_t volumes_cnt_initial = m_volumes . volumes . size ( ) ;
std : : vector < GLVolumeCollection > volumes_per_thread ( ctxt . layers . size ( ) ) ;
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 0 , ctxt . layers . size ( ) , grain_size ) ,
[ & ctxt , & new_volume ] ( const tbb : : blocked_range < size_t > & range ) {
2018-10-08 12:02:12 +00:00
GLVolumePtrs vols ;
2019-01-29 14:11:29 +00:00
if ( ctxt . color_by_color_print ( ) )
vols . emplace_back ( new_volume ( ctxt . color_print_by_layer_idx ( range . begin ( ) ) ) ) ;
else if ( ctxt . color_by_tool ( ) ) {
2018-07-24 11:39:17 +00:00
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 & copy : * ctxt . shifted_copies ) {
2018-09-12 09:59:02 +00:00
for ( const LayerRegion * layerm : layer - > regions ( ) ) {
2018-07-24 11:39:17 +00:00
if ( ctxt . has_perimeters )
_3DScene : : extrusionentity_to_verts ( layerm - > perimeters , float ( layer - > print_z ) , copy ,
2018-09-12 09:59:02 +00:00
* vols [ ctxt . volume_idx ( layerm - > region ( ) - > config ( ) . perimeter_extruder . value , 0 ) ] ) ;
2018-07-24 11:39:17 +00:00
if ( ctxt . has_infill ) {
for ( const ExtrusionEntity * ee : layerm - > fills . entities ) {
// fill represents infill extrusions of a single island.
const auto * fill = dynamic_cast < const ExtrusionEntityCollection * > ( 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 ( ) ) ?
2018-09-12 09:59:02 +00:00
layerm - > region ( ) - > config ( ) . solid_infill_extruder :
layerm - > region ( ) - > config ( ) . infill_extruder ,
2018-07-24 11:39:17 +00:00
1 ) ] ) ;
}
}
}
if ( ctxt . has_support ) {
const SupportLayer * support_layer = dynamic_cast < const SupportLayer * > ( 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 ) ?
2018-09-12 09:59:02 +00:00
support_layer - > object ( ) - > config ( ) . support_material_extruder :
support_layer - > object ( ) - > config ( ) . support_material_interface_extruder ,
2018-07-24 11:39:17 +00:00
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 ( ) ) ;
}
2018-05-30 13:18:45 +00:00
}
}
2018-07-24 11:39:17 +00:00
for ( GLVolume * vol : vols ) {
vol - > bounding_box = vol - > indexed_vertex_array . bounding_box ( ) ;
vol - > indexed_vertex_array . shrink_to_fit ( ) ;
}
} ) ;
2018-05-30 13:18:45 +00:00
2018-07-24 11:39:17 +00:00
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 ) ;
2018-05-30 13:18:45 +00:00
2018-07-24 11:39:17 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Loading print object toolpaths in parallel - end " ;
2018-06-01 13:54:41 +00:00
}
2018-07-24 11:39:17 +00:00
void GLCanvas3D : : _load_wipe_tower_toolpaths ( const std : : vector < std : : string > & str_tool_colors )
2018-05-31 11:51:50 +00:00
{
2018-11-22 14:29:59 +00:00
const Print * print = this - > fff_print ( ) ;
if ( ( print = = nullptr ) | | print - > wipe_tower_data ( ) . tool_changes . empty ( ) )
2018-07-24 11:39:17 +00:00
return ;
2018-05-31 11:51:50 +00:00
2018-11-22 14:29:59 +00:00
if ( ! print - > is_step_done ( psWipeTower ) )
2018-07-24 11:39:17 +00:00
return ;
2018-05-31 11:51:50 +00:00
2018-07-24 11:39:17 +00:00
std : : vector < float > tool_colors = _parse_colors ( str_tool_colors ) ;
2018-06-15 12:10:28 +00:00
2018-07-24 11:39:17 +00:00
struct Ctxt
{
const Print * print ;
const std : : vector < float > * tool_colors ;
2018-08-02 11:29:39 +00:00
WipeTower : : xy wipe_tower_pos ;
float wipe_tower_angle ;
2018-06-01 13:54:41 +00:00
2018-07-24 11:39:17 +00:00
// 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 ; }
2018-06-01 13:54:41 +00:00
2018-07-24 11:39:17 +00:00
static const float * color_support ( ) { static float color [ 4 ] = { 0.5f , 1.0f , 0.5f , 1.f } ; return color ; } // greenish
2018-06-13 11:14:17 +00:00
2018-07-24 11:39:17 +00:00
// 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
2018-06-13 11:14:17 +00:00
{
2018-07-24 11:39:17 +00:00
return this - > color_by_tool ( ) ? std : : min < int > ( this - > number_tools ( ) - 1 , std : : max < int > ( tool , 0 ) ) : feature ;
2018-06-13 11:14:17 +00:00
}
2018-07-24 11:39:17 +00:00
const std : : vector < WipeTower : : ToolChangeResult > & tool_change ( size_t idx ) {
2018-09-12 09:59:02 +00:00
const auto & tool_changes = print - > wipe_tower_data ( ) . tool_changes ;
2018-07-24 11:39:17 +00:00
return priming . empty ( ) ?
2018-09-12 09:59:02 +00:00
( ( idx = = tool_changes . size ( ) ) ? final : tool_changes [ idx ] ) :
( ( idx = = 0 ) ? priming : ( idx = = tool_changes . size ( ) + 1 ) ? final : tool_changes [ idx - 1 ] ) ;
2018-07-24 11:39:17 +00:00
}
std : : vector < WipeTower : : ToolChangeResult > priming ;
std : : vector < WipeTower : : ToolChangeResult > final ;
} ctxt ;
2018-06-21 06:37:04 +00:00
2018-11-22 14:29:59 +00:00
ctxt . print = print ;
2018-07-24 11:39:17 +00:00
ctxt . tool_colors = tool_colors . empty ( ) ? nullptr : & tool_colors ;
2018-11-22 14:29:59 +00:00
if ( print - > wipe_tower_data ( ) . priming & & print - > config ( ) . single_extruder_multi_material_priming )
ctxt . priming . emplace_back ( * print - > wipe_tower_data ( ) . priming . get ( ) ) ;
if ( print - > wipe_tower_data ( ) . final_purge )
ctxt . final . emplace_back ( * print - > wipe_tower_data ( ) . final_purge . get ( ) ) ;
2018-07-24 11:39:17 +00:00
2018-09-12 09:59:02 +00:00
ctxt . wipe_tower_angle = ctxt . print - > config ( ) . wipe_tower_rotation_angle . value / 180.f * PI ;
ctxt . wipe_tower_pos = WipeTower : : xy ( ctxt . print - > config ( ) . wipe_tower_x . value , ctxt . print - > config ( ) . wipe_tower_y . value ) ;
2018-08-02 11:29:39 +00:00
2018-07-24 11:39:17 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Loading wipe tower toolpaths in parallel - start " ;
//FIXME Improve the heuristics for a grain size.
2018-11-22 14:29:59 +00:00
size_t n_items = print - > wipe_tower_data ( ) . tool_changes . size ( ) + ( ctxt . priming . empty ( ) ? 0 : 1 ) ;
2018-07-24 11:39:17 +00:00
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 ( ) ;
m_volumes . volumes . emplace_back ( volume ) ;
new_volume_mutex . unlock ( ) ;
return volume ;
} ;
const size_t volumes_cnt_initial = m_volumes . volumes . size ( ) ;
std : : vector < GLVolumeCollection > volumes_per_thread ( n_items ) ;
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 0 , n_items , grain_size ) ,
[ & ctxt , & new_volume ] ( const tbb : : blocked_range < size_t > & range ) {
// Bounding box of this slab of a wipe tower.
2018-10-08 12:02:12 +00:00
GLVolumePtrs vols ;
2018-07-24 11:39:17 +00:00
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 < WipeTower : : ToolChangeResult > & 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 ;
2018-06-21 06:37:04 +00:00
}
2018-07-24 11:39:17 +00:00
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 < double > widths ;
std : : vector < double > heights ;
lines . reserve ( n_lines ) ;
widths . reserve ( n_lines ) ;
heights . assign ( n_lines , extrusions . layer_height ) ;
2018-08-02 11:29:39 +00:00
WipeTower : : Extrusion e_prev = extrusions . extrusions [ i - 1 ] ;
2018-06-13 11:14:17 +00:00
2018-08-02 11:29:39 +00:00
if ( ! extrusions . priming ) { // wipe tower extrusions describe the wipe tower at the origin with no rotation
e_prev . pos . rotate ( ctxt . wipe_tower_angle ) ;
e_prev . pos . translate ( ctxt . wipe_tower_pos ) ;
}
2018-06-21 06:37:04 +00:00
2018-07-24 11:39:17 +00:00
for ( ; i < j ; + + i ) {
2018-08-02 11:29:39 +00:00
WipeTower : : Extrusion e = extrusions . extrusions [ i ] ;
2018-07-24 11:39:17 +00:00
assert ( e . width > 0.f ) ;
2018-08-02 11:29:39 +00:00
if ( ! extrusions . priming ) {
e . pos . rotate ( ctxt . wipe_tower_angle ) ;
e . pos . translate ( ctxt . wipe_tower_pos ) ;
}
2018-07-24 11:39:17 +00:00
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 ) ;
2018-08-02 11:29:39 +00:00
e_prev = e ;
2018-07-24 11:39:17 +00:00
}
_3DScene : : thick_lines_to_verts ( lines , widths , heights , lines . front ( ) . a = = lines . back ( ) . b , extrusions . print_z ,
* vols [ ctxt . volume_idx ( e . tool , 0 ) ] ) ;
2018-06-21 06:37:04 +00:00
}
}
}
2018-07-24 11:39:17 +00:00
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 " ;
2018-06-21 06:37:04 +00:00
}
2018-06-05 08:56:55 +00:00
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 ;
}
void GLCanvas3D : : _load_gcode_extrusion_paths ( const GCodePreviewData & preview_data , const std : : vector < float > & 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 ;
2018-11-26 15:07:09 +00:00
case GCodePreviewData : : Extrusion : : ColorPrint :
return ( float ) path . cp_color_id ;
2018-06-13 07:26:58 +00:00
default :
return 0.0f ;
2018-06-05 08:56:55 +00:00
}
return 0.0f ;
}
static GCodePreviewData : : Color path_color ( const GCodePreviewData & data , const std : : vector < float > & 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 ;
}
2018-11-26 15:07:09 +00:00
case GCodePreviewData : : Extrusion : : ColorPrint :
{
2019-01-29 14:11:29 +00:00
const size_t color_cnt = tool_colors . size ( ) / 4 ;
2018-11-26 15:07:09 +00:00
int val = int ( value ) ;
2019-01-29 14:11:29 +00:00
while ( val > = color_cnt )
val - = color_cnt ;
2018-11-26 15:07:09 +00:00
2019-01-29 14:11:29 +00:00
GCodePreviewData : : Color color ;
: : memcpy ( ( void * ) color . rgba , ( const void * ) ( tool_colors . data ( ) + val * 4 ) , 4 * sizeof ( float ) ) ;
2018-11-26 15:07:09 +00:00
return color ;
}
2018-06-13 07:26:58 +00:00
default :
return GCodePreviewData : : Color : : Dummy ;
2018-06-05 08:56:55 +00:00
}
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 < Filter > FiltersList ;
2018-06-11 13:13:13 +00:00
size_t initial_volumes_count = m_volumes . volumes . size ( ) ;
2018-06-05 08:56:55 +00:00
// 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 )
{
2018-06-11 13:13:13 +00:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex : : Extrusion , ( unsigned int ) filter . role , ( unsigned int ) m_volumes . volumes . size ( ) ) ;
2018-06-05 08:56:55 +00:00
GLVolume * volume = new GLVolume ( Helper : : path_color ( preview_data , tool_colors , filter . value ) . rgba ) ;
if ( volume ! = nullptr )
{
filter . volume = volume ;
2018-07-27 06:49:58 +00:00
volume - > is_extrusion_path = true ;
2018-06-11 13:13:13 +00:00
m_volumes . volumes . emplace_back ( volume ) ;
2018-06-05 08:56:55 +00:00
}
else
{
// an error occourred - restore to previous state and return
m_gcode_preview_volume_index . first_volumes . pop_back ( ) ;
2018-06-11 13:13:13 +00:00
if ( initial_volumes_count ! = m_volumes . volumes . size ( ) )
2018-06-05 08:56:55 +00:00
{
2018-10-08 12:02:12 +00:00
GLVolumePtrs : : iterator begin = m_volumes . volumes . begin ( ) + initial_volumes_count ;
GLVolumePtrs : : iterator end = m_volumes . volumes . end ( ) ;
for ( GLVolumePtrs : : iterator it = begin ; it < end ; + + it )
2018-06-05 08:56:55 +00:00
{
GLVolume * volume = * it ;
delete volume ;
}
2018-06-11 13:13:13 +00:00
m_volumes . volumes . erase ( begin , end ) ;
2018-06-05 08:56:55 +00:00
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
2018-06-11 13:13:13 +00:00
if ( m_volumes . volumes . size ( ) > initial_volumes_count )
2018-06-05 08:56:55 +00:00
{
2018-06-11 13:13:13 +00:00
for ( size_t i = initial_volumes_count ; i < m_volumes . volumes . size ( ) ; + + i )
2018-06-05 08:56:55 +00:00
{
2018-06-11 13:13:13 +00:00
GLVolume * volume = m_volumes . volumes [ i ] ;
2018-06-05 08:56:55 +00:00
volume - > bounding_box = volume - > indexed_vertex_array . bounding_box ( ) ;
2018-06-05 10:24:26 +00:00
volume - > indexed_vertex_array . finalize_geometry ( m_use_VBOs & & m_initialized ) ;
2018-06-05 08:56:55 +00:00
}
}
}
void GLCanvas3D : : _load_gcode_travel_paths ( const GCodePreviewData & preview_data , const std : : vector < float > & tool_colors )
{
2018-06-11 13:13:13 +00:00
size_t initial_volumes_count = m_volumes . volumes . size ( ) ;
2018-06-05 08:56:55 +00:00
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
2018-06-11 13:13:13 +00:00
if ( initial_volumes_count ! = m_volumes . volumes . size ( ) )
2018-06-05 08:56:55 +00:00
{
2018-10-08 12:02:12 +00:00
GLVolumePtrs : : iterator begin = m_volumes . volumes . begin ( ) + initial_volumes_count ;
GLVolumePtrs : : iterator end = m_volumes . volumes . end ( ) ;
for ( GLVolumePtrs : : iterator it = begin ; it < end ; + + it )
2018-06-05 08:56:55 +00:00
{
GLVolume * volume = * it ;
delete volume ;
}
2018-06-11 13:13:13 +00:00
m_volumes . volumes . erase ( begin , end ) ;
2018-06-05 08:56:55 +00:00
}
return ;
}
// finalize volumes and sends geometry to gpu
2018-06-11 13:13:13 +00:00
if ( m_volumes . volumes . size ( ) > initial_volumes_count )
2018-06-05 08:56:55 +00:00
{
2018-06-11 13:13:13 +00:00
for ( size_t i = initial_volumes_count ; i < m_volumes . volumes . size ( ) ; + + i )
2018-06-05 08:56:55 +00:00
{
2018-06-11 13:13:13 +00:00
GLVolume * volume = m_volumes . volumes [ i ] ;
2018-06-05 08:56:55 +00:00
volume - > bounding_box = volume - > indexed_vertex_array . bounding_box ( ) ;
2018-06-05 10:24:26 +00:00
volume - > indexed_vertex_array . finalize_geometry ( m_use_VBOs & & m_initialized ) ;
2018-06-05 08:56:55 +00:00
}
}
}
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 < Type > 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 ;
2018-06-11 13:13:13 +00:00
m_volumes . volumes . emplace_back ( volume ) ;
2018-06-05 08:56:55 +00:00
}
}
// 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 ( ) )
{
2018-08-21 15:43:05 +00:00
type - > volume - > print_zs . push_back ( unscale < double > ( polyline . polyline . bounding_box ( ) . min ( 2 ) ) ) ;
2018-06-05 08:56:55 +00:00
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 < Feedrate > 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 ;
2018-06-11 13:13:13 +00:00
m_volumes . volumes . emplace_back ( volume ) ;
2018-06-05 08:56:55 +00:00
}
}
// 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 ( ) )
{
2018-08-21 15:43:05 +00:00
feedrate - > volume - > print_zs . push_back ( unscale < double > ( polyline . polyline . bounding_box ( ) . min ( 2 ) ) ) ;
2018-06-05 08:56:55 +00:00
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 < float > & 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 < Tool > 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 )
{
2019-04-29 09:13:32 +00:00
// tool.value could be invalid (as it was with https://github.com/prusa3d/Slic3r/issues/2179), we better check
if ( tool . value > = tool_colors . size ( ) )
continue ;
2018-06-05 08:56:55 +00:00
GLVolume * volume = new GLVolume ( tool_colors . data ( ) + tool . value * 4 ) ;
if ( volume = = nullptr )
return false ;
else
{
tool . volume = volume ;
2018-06-11 13:13:13 +00:00
m_volumes . volumes . emplace_back ( volume ) ;
2018-06-05 08:56:55 +00:00
}
}
// 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 ) ) ;
2019-04-29 09:13:32 +00:00
if ( tool ! = tools . end ( ) & & tool - > volume ! = nullptr )
2018-06-05 08:56:55 +00:00
{
2018-08-21 15:43:05 +00:00
tool - > volume - > print_zs . push_back ( unscale < double > ( polyline . polyline . bounding_box ( ) . min ( 2 ) ) ) ;
2018-06-05 08:56:55 +00:00
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 )
{
2018-06-11 13:13:13 +00:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex : : Retraction , 0 , ( unsigned int ) m_volumes . volumes . size ( ) ) ;
2018-06-05 08:56:55 +00:00
// nothing to render, return
if ( preview_data . retraction . positions . empty ( ) )
return ;
GLVolume * volume = new GLVolume ( preview_data . retraction . color . rgba ) ;
if ( volume ! = nullptr )
{
2018-06-11 13:13:13 +00:00
m_volumes . volumes . emplace_back ( volume ) ;
2018-06-05 08:56:55 +00:00
GCodePreviewData : : Retraction : : PositionsList copy ( preview_data . retraction . positions ) ;
2018-08-17 13:53:43 +00:00
std : : sort ( copy . begin ( ) , copy . end ( ) , [ ] ( const GCodePreviewData : : Retraction : : Position & p1 , const GCodePreviewData : : Retraction : : Position & p2 ) { return p1 . position ( 2 ) < p2 . position ( 2 ) ; } ) ;
2018-06-05 08:56:55 +00:00
for ( const GCodePreviewData : : Retraction : : Position & position : copy )
{
2018-08-21 15:43:05 +00:00
volume - > print_zs . push_back ( unscale < double > ( position . position ( 2 ) ) ) ;
2018-06-05 08:56:55 +00:00
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 ( ) ;
2018-06-05 10:24:26 +00:00
volume - > indexed_vertex_array . finalize_geometry ( m_use_VBOs & & m_initialized ) ;
2018-06-05 08:56:55 +00:00
}
}
void GLCanvas3D : : _load_gcode_unretractions ( const GCodePreviewData & preview_data )
{
2018-06-11 13:13:13 +00:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex : : Unretraction , 0 , ( unsigned int ) m_volumes . volumes . size ( ) ) ;
2018-06-05 08:56:55 +00:00
// nothing to render, return
if ( preview_data . unretraction . positions . empty ( ) )
return ;
GLVolume * volume = new GLVolume ( preview_data . unretraction . color . rgba ) ;
if ( volume ! = nullptr )
{
2018-06-11 13:13:13 +00:00
m_volumes . volumes . emplace_back ( volume ) ;
2018-06-05 08:56:55 +00:00
GCodePreviewData : : Retraction : : PositionsList copy ( preview_data . unretraction . positions ) ;
2018-08-17 13:53:43 +00:00
std : : sort ( copy . begin ( ) , copy . end ( ) , [ ] ( const GCodePreviewData : : Retraction : : Position & p1 , const GCodePreviewData : : Retraction : : Position & p2 ) { return p1 . position ( 2 ) < p2 . position ( 2 ) ; } ) ;
2018-06-05 08:56:55 +00:00
for ( const GCodePreviewData : : Retraction : : Position & position : copy )
{
2018-08-21 15:43:05 +00:00
volume - > print_zs . push_back ( unscale < double > ( position . position ( 2 ) ) ) ;
2018-06-05 08:56:55 +00:00
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 ( ) ;
2018-06-05 10:24:26 +00:00
volume - > indexed_vertex_array . finalize_geometry ( m_use_VBOs & & m_initialized ) ;
2018-06-05 08:56:55 +00:00
}
}
2019-05-07 10:29:48 +00:00
void GLCanvas3D : : _load_fff_shells ( )
2018-06-05 08:56:55 +00:00
{
2018-06-11 13:13:13 +00:00
size_t initial_volumes_count = m_volumes . volumes . size ( ) ;
2018-06-05 08:56:55 +00:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex : : Shell , 0 , ( unsigned int ) initial_volumes_count ) ;
2018-11-22 14:29:59 +00:00
const Print * print = this - > fff_print ( ) ;
if ( print - > objects ( ) . empty ( ) )
2018-06-05 08:56:55 +00:00
// nothing to render, return
return ;
// adds objects' volumes
2018-11-26 14:16:35 +00:00
int object_id = 0 ;
2018-11-22 14:29:59 +00:00
for ( const PrintObject * obj : print - > objects ( ) )
2018-06-05 08:56:55 +00:00
{
2018-09-14 07:28:00 +00:00
const ModelObject * model_obj = obj - > model_object ( ) ;
2018-06-05 08:56:55 +00:00
std : : vector < int > instance_ids ( model_obj - > instances . size ( ) ) ;
2018-06-13 07:26:58 +00:00
for ( int i = 0 ; i < ( int ) model_obj - > instances . size ( ) ; + + i )
2018-06-05 08:56:55 +00:00
{
instance_ids [ i ] = i ;
}
2018-10-08 14:05:55 +00:00
m_volumes . load_object ( model_obj , object_id , instance_ids , " object " , m_use_VBOs & & m_initialized ) ;
2018-06-05 08:56:55 +00:00
+ + object_id ;
}
2018-11-12 14:36:40 +00:00
if ( wxGetApp ( ) . preset_bundle - > printers . get_edited_preset ( ) . printer_technology ( ) = = ptFFF ) {
// adds wipe tower's volume
2018-11-22 14:29:59 +00:00
double max_z = print - > objects ( ) [ 0 ] - > model_object ( ) - > get_model ( ) - > bounding_box ( ) . max ( 2 ) ;
const PrintConfig & config = print - > config ( ) ;
2018-11-12 14:36:40 +00:00
unsigned int extruders_count = config . nozzle_diameter . size ( ) ;
if ( ( extruders_count > 1 ) & & config . single_extruder_multi_material & & config . wipe_tower & & ! config . complete_objects ) {
2018-11-22 14:29:59 +00:00
float depth = print - > get_wipe_tower_depth ( ) ;
2019-03-13 09:46:50 +00:00
// Calculate wipe tower brim spacing.
const DynamicPrintConfig & print_config = wxGetApp ( ) . preset_bundle - > prints . get_edited_preset ( ) . config ;
double layer_height = print_config . opt_float ( " layer_height " ) ;
double first_layer_height = print_config . get_abs_value ( " first_layer_height " , layer_height ) ;
float brim_spacing = print - > config ( ) . nozzle_diameter . values [ 0 ] * 1.25f - first_layer_height * ( 1. - M_PI_4 ) ;
2018-11-22 14:29:59 +00:00
if ( ! print - > is_step_done ( psWipeTower ) )
2018-11-12 14:36:40 +00:00
depth = ( 900.f / config . wipe_tower_width ) * ( float ) ( extruders_count - 1 ) ;
m_volumes . load_wipe_tower_preview ( 1000 , config . wipe_tower_x , config . wipe_tower_y , config . wipe_tower_width , depth , max_z , config . wipe_tower_rotation_angle ,
2019-03-13 09:46:50 +00:00
m_use_VBOs & & m_initialized , ! print - > is_step_done ( psWipeTower ) , brim_spacing * 4.5f ) ;
2018-11-12 14:36:40 +00:00
}
2018-06-05 08:56:55 +00:00
}
}
2019-05-07 10:29:48 +00:00
void GLCanvas3D : : _load_sla_shells ( )
2018-11-26 14:16:35 +00:00
{
2019-04-02 11:47:49 +00:00
//FIXME use reload_scene
# if 1
2018-11-26 14:16:35 +00:00
const SLAPrint * print = this - > sla_print ( ) ;
if ( print - > objects ( ) . empty ( ) )
// nothing to render, return
return ;
2019-05-07 10:29:48 +00:00
auto add_volume = [ this ] ( const SLAPrintObject & object , int volume_id , const SLAPrintObject : : Instance & instance ,
const TriangleMesh & mesh , const float color [ 4 ] , bool outside_printer_detection_enabled ) {
2019-04-08 16:09:31 +00:00
m_volumes . volumes . emplace_back ( new GLVolume ( color ) ) ;
GLVolume & v = * m_volumes . volumes . back ( ) ;
v . indexed_vertex_array . load_mesh ( mesh , m_use_VBOs ) ;
v . shader_outside_printer_detection_enabled = outside_printer_detection_enabled ;
2019-05-07 10:29:48 +00:00
v . composite_id . volume_id = volume_id ;
2019-04-08 16:09:31 +00:00
v . set_instance_offset ( unscale ( instance . shift ( 0 ) , instance . shift ( 1 ) , 0 ) ) ;
v . set_instance_rotation ( Vec3d ( 0.0 , 0.0 , ( double ) instance . rotation ) ) ;
v . set_instance_mirror ( X , object . is_left_handed ( ) ? - 1. : 1. ) ;
} ;
2018-11-26 14:16:35 +00:00
// adds objects' volumes
for ( const SLAPrintObject * obj : print - > objects ( ) )
2019-04-08 16:09:31 +00:00
if ( obj - > is_step_done ( slaposSliceSupports ) ) {
unsigned int initial_volumes_count = ( unsigned int ) m_volumes . volumes . size ( ) ;
for ( const SLAPrintObject : : Instance & instance : obj - > instances ( ) ) {
2019-05-07 10:29:48 +00:00
add_volume ( * obj , 0 , instance , obj - > transformed_mesh ( ) , GLVolume : : MODEL_COLOR [ 0 ] , true ) ;
2019-04-08 16:09:31 +00:00
// Set the extruder_id and volume_id to achieve the same color as in the 3D scene when
// through the update_volumes_colors_by_extruder() call.
m_volumes . volumes . back ( ) - > extruder_id = obj - > model_object ( ) - > volumes . front ( ) - > extruder_id ( ) ;
if ( obj - > is_step_done ( slaposSupportTree ) & & obj - > has_mesh ( slaposSupportTree ) )
2019-05-07 10:29:48 +00:00
add_volume ( * obj , - int ( slaposSupportTree ) , instance , obj - > support_mesh ( ) , GLVolume : : SLA_SUPPORT_COLOR , true ) ;
2019-04-08 16:09:31 +00:00
if ( obj - > is_step_done ( slaposBasePool ) & & obj - > has_mesh ( slaposBasePool ) )
2019-05-07 10:29:48 +00:00
add_volume ( * obj , - int ( slaposBasePool ) , instance , obj - > pad_mesh ( ) , GLVolume : : SLA_PAD_COLOR , true ) ;
2018-11-26 14:16:35 +00:00
}
2019-04-08 16:09:31 +00:00
double shift_z = obj - > get_current_elevation ( ) ;
for ( unsigned int i = initial_volumes_count ; i < m_volumes . volumes . size ( ) ; + + i ) {
2018-11-26 14:16:35 +00:00
GLVolume & v = * m_volumes . volumes [ i ] ;
2019-04-08 16:09:31 +00:00
// finalize volumes and sends geometry to gpu
2018-11-26 14:16:35 +00:00
v . bounding_box = v . indexed_vertex_array . bounding_box ( ) ;
v . indexed_vertex_array . finalize_geometry ( m_use_VBOs ) ;
2019-04-08 16:09:31 +00:00
// apply shift z
v . set_sla_shift_z ( shift_z ) ;
2018-11-26 14:16:35 +00:00
}
}
update_volumes_colors_by_extruder ( ) ;
2019-04-02 11:47:49 +00:00
# else
this - > reload_scene ( true , true ) ;
# endif
2018-11-26 14:16:35 +00:00
}
2018-06-05 08:56:55 +00:00
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 )
{
2018-10-08 12:02:12 +00:00
GLVolumePtrs : : iterator begin = m_volumes . volumes . begin ( ) + m_gcode_preview_volume_index . first_volumes [ i ] . id ;
GLVolumePtrs : : iterator end = ( i + 1 < size ) ? m_volumes . volumes . begin ( ) + m_gcode_preview_volume_index . first_volumes [ i + 1 ] . id : m_volumes . volumes . end ( ) ;
for ( GLVolumePtrs : : iterator it = begin ; it ! = end ; + + it )
2018-06-05 08:56:55 +00:00
{
GLVolume * volume = * it ;
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 ;
}
}
}
}
}
2018-07-24 11:39:17 +00:00
void GLCanvas3D : : _update_toolpath_volumes_outside_state ( )
{
// tolerance to avoid false detection at bed edges
2018-08-24 08:20:00 +00:00
static const double tolerance_x = 0.05 ;
static const double tolerance_y = 0.05 ;
2018-07-24 11:39:17 +00:00
BoundingBoxf3 print_volume ;
if ( m_config ! = nullptr )
{
const ConfigOptionPoints * opt = dynamic_cast < const ConfigOptionPoints * > ( m_config - > option ( " bed_shape " ) ) ;
if ( opt ! = nullptr )
{
BoundingBox bed_box_2D = get_extents ( Polygon : : new_scale ( opt - > values ) ) ;
2018-08-21 15:43:05 +00:00
print_volume = BoundingBoxf3 ( Vec3d ( unscale < double > ( bed_box_2D . min ( 0 ) ) - tolerance_x , unscale < double > ( bed_box_2D . min ( 1 ) ) - tolerance_y , 0.0 ) , Vec3d ( unscale < double > ( bed_box_2D . max ( 0 ) ) + tolerance_x , unscale < double > ( bed_box_2D . max ( 1 ) ) + tolerance_y , m_config - > opt_float ( " max_print_height " ) ) ) ;
2018-07-24 11:39:17 +00:00
// Allow the objects to protrude below the print bed
2018-08-17 16:07:45 +00:00
print_volume . min ( 2 ) = - 1e10 ;
2018-07-24 11:39:17 +00:00
}
}
for ( GLVolume * volume : m_volumes . volumes )
{
2018-07-27 06:49:58 +00:00
volume - > is_outside = ( ( print_volume . radius ( ) > 0.0 ) & & volume - > is_extrusion_path ) ? ! print_volume . contains ( volume - > bounding_box ) : false ;
2018-07-24 11:39:17 +00:00
}
}
2019-05-07 10:29:48 +00:00
void GLCanvas3D : : _update_sla_shells_outside_state ( )
{
// tolerance to avoid false detection at bed edges
static const double tolerance_x = 0.05 ;
static const double tolerance_y = 0.05 ;
BoundingBoxf3 print_volume ;
if ( m_config ! = nullptr )
{
const ConfigOptionPoints * opt = dynamic_cast < const ConfigOptionPoints * > ( m_config - > option ( " bed_shape " ) ) ;
if ( opt ! = nullptr )
{
BoundingBox bed_box_2D = get_extents ( Polygon : : new_scale ( opt - > values ) ) ;
print_volume = BoundingBoxf3 ( Vec3d ( unscale < double > ( bed_box_2D . min ( 0 ) ) - tolerance_x , unscale < double > ( bed_box_2D . min ( 1 ) ) - tolerance_y , 0.0 ) , Vec3d ( unscale < double > ( bed_box_2D . max ( 0 ) ) + tolerance_x , unscale < double > ( bed_box_2D . max ( 1 ) ) + tolerance_y , m_config - > opt_float ( " max_print_height " ) ) ) ;
// Allow the objects to protrude below the print bed
print_volume . min ( 2 ) = - 1e10 ;
}
}
for ( GLVolume * volume : m_volumes . volumes )
{
2019-05-07 11:50:58 +00:00
volume - > is_outside = ( ( print_volume . radius ( ) > 0.0 ) & & volume - > is_sla_support ( ) ) ? ! print_volume . contains ( volume - > transformed_convex_hull_bounding_box ( ) ) : false ;
2019-05-07 10:29:48 +00:00
}
}
void GLCanvas3D : : _show_warning_texture_if_needed ( WarningTexture : : Warning warning )
2018-07-24 11:39:17 +00:00
{
2019-01-31 08:15:43 +00:00
_set_current ( ) ;
2019-05-07 10:29:48 +00:00
_set_warning_texture ( warning , _is_any_volume_outside ( ) ) ;
2018-07-24 11:39:17 +00:00
}
2018-06-05 12:09:36 +00:00
std : : vector < float > GLCanvas3D : : _parse_colors ( const std : : vector < std : : string > & colors )
{
2018-06-08 09:37:07 +00:00
static const float INV_255 = 1.0f / 255.0f ;
2018-06-05 12:09:36 +00:00
std : : vector < float > output ( colors . size ( ) * 4 , 1.0f ) ;
2018-06-08 09:37:07 +00:00
for ( size_t i = 0 ; i < colors . size ( ) ; + + i )
{
2018-06-05 12:09:36 +00:00
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 ;
2018-06-08 09:37:07 +00:00
output [ i * 4 + j ] = float ( digit1 * 16 + digit2 ) * INV_255 ;
2018-06-05 12:09:36 +00:00
}
}
}
return output ;
}
2018-07-19 11:18:19 +00:00
void GLCanvas3D : : _generate_legend_texture ( const GCodePreviewData & preview_data , const std : : vector < float > & tool_colors )
{
2019-03-05 12:57:41 +00:00
m_legend_texture . generate ( preview_data , tool_colors , * this ) ;
2018-07-19 11:18:19 +00:00
}
2019-02-20 11:09:45 +00:00
void GLCanvas3D : : _set_warning_texture ( WarningTexture : : Warning warning , bool state )
2018-07-19 11:18:19 +00:00
{
2019-02-20 11:09:45 +00:00
m_warning_texture . activate ( warning , state , * this ) ;
2018-07-19 11:18:19 +00:00
}
2018-07-27 07:38:39 +00:00
bool GLCanvas3D : : _is_any_volume_outside ( ) const
{
for ( const GLVolume * volume : m_volumes . volumes )
{
if ( ( volume ! = nullptr ) & & volume - > is_outside )
return true ;
}
return false ;
}
2019-02-26 08:56:23 +00:00
# if !ENABLE_SVG_ICONS
2018-12-06 09:38:19 +00:00
void GLCanvas3D : : _resize_toolbars ( ) const
2018-07-31 10:25:00 +00:00
{
Size cnv_size = get_canvas_size ( ) ;
float zoom = get_camera_zoom ( ) ;
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
m_toolbar . set_icons_scale ( m_retina_helper - > get_scale_factor ( ) ) ;
2019-02-04 20:41:10 +00:00
# else
m_toolbar . set_icons_scale ( m_canvas - > GetContentScaleFactor ( ) ) ;
# endif /* __WXMSW__ */
2019-01-24 10:30:29 +00:00
2018-12-17 09:55:14 +00:00
GLToolbar : : Layout : : EOrientation orientation = m_toolbar . get_layout_orientation ( ) ;
2018-07-31 10:25:00 +00:00
switch ( m_toolbar . get_layout_type ( ) )
{
default :
case GLToolbar : : Layout : : Horizontal :
{
// centers the toolbar on the top edge of the 3d scene
2018-12-17 09:55:14 +00:00
float top , left ;
if ( orientation = = GLToolbar : : Layout : : Top )
{
top = 0.5f * ( float ) cnv_size . get_height ( ) * inv_zoom ;
left = - 0.5f * m_toolbar . get_width ( ) * inv_zoom ;
}
else
{
2019-03-07 10:49:00 +00:00
top = ( - 0.5f * ( float ) cnv_size . get_height ( ) + m_view_toolbar . get_height ( ) ) * inv_zoom ;
2018-12-17 09:55:14 +00:00
left = - 0.5f * m_toolbar . get_width ( ) * inv_zoom ;
}
2018-07-31 10:25:00 +00:00
m_toolbar . set_position ( top , left ) ;
break ;
}
case GLToolbar : : Layout : : Vertical :
{
// centers the toolbar on the right edge of the 3d scene
2018-12-17 09:55:14 +00:00
float top , left ;
if ( orientation = = GLToolbar : : Layout : : Left )
{
top = 0.5f * m_toolbar . get_height ( ) * inv_zoom ;
left = ( - 0.5f * ( float ) cnv_size . get_width ( ) ) * inv_zoom ;
}
else
{
top = 0.5f * m_toolbar . get_height ( ) * inv_zoom ;
left = ( 0.5f * ( float ) cnv_size . get_width ( ) - m_toolbar . get_width ( ) ) * inv_zoom ;
}
2018-07-31 10:25:00 +00:00
m_toolbar . set_position ( top , left ) ;
break ;
}
}
2018-12-06 09:38:19 +00:00
if ( m_view_toolbar ! = nullptr )
{
2019-01-24 10:30:29 +00:00
# if ENABLE_RETINA_GL
2019-03-07 10:49:00 +00:00
m_view_toolbar . set_icons_scale ( m_retina_helper - > get_scale_factor ( ) ) ;
2019-02-04 20:41:10 +00:00
# else
2019-03-07 10:49:00 +00:00
m_view_toolbar . set_icons_scale ( m_canvas - > GetContentScaleFactor ( ) ) ;
2019-02-04 20:41:10 +00:00
# endif /* __WXMSW__ */
2019-01-24 10:30:29 +00:00
2018-12-17 09:55:14 +00:00
// places the toolbar on the bottom-left corner of the 3d scene
2019-03-07 10:49:00 +00:00
float top = ( - 0.5f * ( float ) cnv_size . get_height ( ) + m_view_toolbar . get_height ( ) ) * inv_zoom ;
2018-12-06 09:38:19 +00:00
float left = - 0.5f * ( float ) cnv_size . get_width ( ) * inv_zoom ;
2019-03-07 10:49:00 +00:00
m_view_toolbar . set_position ( top , left ) ;
2018-12-06 09:38:19 +00:00
}
2018-07-31 10:25:00 +00:00
}
2019-02-26 08:56:23 +00:00
# endif // !ENABLE_SVG_ICONS
2018-07-31 10:25:00 +00:00
2019-04-25 10:31:55 +00:00
void GLCanvas3D : : _update_selection_from_hover ( )
{
2019-04-26 11:37:34 +00:00
bool ctrl_pressed = wxGetKeyState ( WXK_CONTROL ) ;
2019-04-25 10:31:55 +00:00
if ( m_hover_volume_idxs . empty ( ) )
2019-04-26 11:37:34 +00:00
{
if ( ! ctrl_pressed & & ( m_rectangle_selection . get_state ( ) = = GLSelectionRectangle : : Select ) )
m_selection . clear ( ) ;
2019-04-25 10:31:55 +00:00
return ;
2019-04-26 11:37:34 +00:00
}
2019-04-25 10:31:55 +00:00
GLSelectionRectangle : : EState state = m_rectangle_selection . get_state ( ) ;
2019-04-26 11:37:34 +00:00
bool hover_modifiers_only = true ;
for ( int i : m_hover_volume_idxs )
{
if ( ! m_volumes . volumes [ i ] - > is_modifier )
{
hover_modifiers_only = false ;
break ;
}
}
2019-04-25 10:31:55 +00:00
2019-04-26 11:37:34 +00:00
if ( ( state = = GLSelectionRectangle : : Select ) & & ! ctrl_pressed )
2019-04-25 10:31:55 +00:00
m_selection . clear ( ) ;
2019-04-26 11:37:34 +00:00
for ( int i : m_hover_volume_idxs )
2019-04-25 10:31:55 +00:00
{
if ( state = = GLSelectionRectangle : : Select )
2019-04-26 11:37:34 +00:00
{
if ( hover_modifiers_only )
{
const GLVolume & v = * m_volumes . volumes [ i ] ;
m_selection . add_volume ( v . object_idx ( ) , v . volume_idx ( ) , v . instance_idx ( ) , false ) ;
}
else
m_selection . add ( i , false ) ;
}
2019-04-25 10:31:55 +00:00
else
2019-04-26 11:37:34 +00:00
m_selection . remove ( i ) ;
2019-04-25 10:31:55 +00:00
}
2019-05-09 08:09:33 +00:00
if ( m_selection . is_empty ( ) )
m_gizmos . reset_all_states ( ) ;
else
m_gizmos . refresh_on_off_state ( m_selection ) ;
2019-04-25 10:31:55 +00:00
m_gizmos . update_data ( * this ) ;
post_event ( SimpleEvent ( EVT_GLCANVAS_OBJECT_SELECT ) ) ;
m_dirty = true ;
}
2018-11-22 14:29:59 +00:00
const Print * GLCanvas3D : : fff_print ( ) const
{
return ( m_process = = nullptr ) ? nullptr : m_process - > fff_print ( ) ;
}
const SLAPrint * GLCanvas3D : : sla_print ( ) const
{
return ( m_process = = nullptr ) ? nullptr : m_process - > sla_print ( ) ;
}
2018-05-09 08:47:04 +00:00
} // namespace GUI
} // namespace Slic3r