2018-05-09 08:47:04 +00:00
# include "GLCanvas3D.hpp"
2018-05-14 12:14:19 +00:00
# include "../../slic3r/GUI/3DScene.hpp"
2018-05-23 07:57:44 +00:00
# include "../../slic3r/GUI/GLShader.hpp"
2018-06-08 07:40:00 +00:00
# include "../../slic3r/GUI/GUI.hpp"
2018-06-11 08:46:32 +00:00
# include "../../slic3r/GUI/PresetBundle.hpp"
2018-05-15 13:38:25 +00:00
# include "../../libslic3r/ClipperUtils.hpp"
2018-05-23 09:14:49 +00:00
# include "../../libslic3r/PrintConfig.hpp"
2018-05-24 13:17:01 +00:00
# include "../../libslic3r/Print.hpp"
2018-06-05 08:56:55 +00:00
# include "../../libslic3r/GCode/PreviewData.hpp"
2018-05-14 12:14:19 +00:00
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-05-24 11:46:17 +00:00
# include <wx/image.h>
2018-05-30 13:18:45 +00:00
# include <wx/timer.h>
2018-05-09 08:47:04 +00:00
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-05-09 08:47:04 +00:00
2018-06-01 07:00:30 +00:00
static const float TRACKBALLSIZE = 0.8f ;
2018-05-14 10:08:23 +00:00
static const float GIMBALL_LOCK_THETA_MAX = 180.0f ;
2018-05-15 13:38:25 +00:00
static const float GROUND_Z = - 0.02f ;
2018-05-14 10:08:23 +00:00
2018-05-15 09:30:11 +00:00
// phi / theta angles to orient the camera.
static const float VIEW_DEFAULT [ 2 ] = { 45.0f , 45.0f } ;
static const float VIEW_LEFT [ 2 ] = { 90.0f , 90.0f } ;
static const float VIEW_RIGHT [ 2 ] = { - 90.0f , 90.0f } ;
static const float VIEW_TOP [ 2 ] = { 0.0f , 0.0f } ;
static const float VIEW_BOTTOM [ 2 ] = { 0.0f , 180.0f } ;
static const float VIEW_FRONT [ 2 ] = { 0.0f , 90.0f } ;
static const float VIEW_REAR [ 2 ] = { 180.0f , 90.0f } ;
2018-05-24 11:46:17 +00:00
static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f ;
static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f ;
2018-05-09 08:47:04 +00:00
namespace Slic3r {
namespace GUI {
2018-06-11 08:46:32 +00:00
bool GeometryBuffer : : set_from_triangles ( const Polygons & triangles , float z , bool generate_tex_coords )
2018-05-15 13:38:25 +00:00
{
2018-06-11 08:46:32 +00:00
m_vertices . clear ( ) ;
m_tex_coords . clear ( ) ;
2018-05-15 13:38:25 +00:00
2018-06-11 08:46:32 +00:00
unsigned int v_size = 9 * ( unsigned int ) triangles . size ( ) ;
unsigned int t_size = 6 * ( unsigned int ) triangles . size ( ) ;
if ( v_size = = 0 )
2018-05-15 13:38:25 +00:00
return false ;
2018-06-11 08:46:32 +00:00
m_vertices = std : : vector < float > ( v_size , 0.0f ) ;
if ( generate_tex_coords )
m_tex_coords = std : : vector < float > ( t_size , 0.0f ) ;
2018-05-15 13:38:25 +00:00
2018-06-11 08:46:32 +00:00
float min_x = ( float ) unscale ( triangles [ 0 ] . points [ 0 ] . x ) ;
float min_y = ( float ) unscale ( triangles [ 0 ] . points [ 0 ] . y ) ;
float max_x = min_x ;
float max_y = min_y ;
unsigned int v_coord = 0 ;
unsigned int t_coord = 0 ;
2018-05-15 13:38:25 +00:00
for ( const Polygon & t : triangles )
{
for ( unsigned int v = 0 ; v < 3 ; + + v )
{
const Point & p = t . points [ v ] ;
2018-06-11 08:46:32 +00:00
float x = ( float ) unscale ( p . x ) ;
float y = ( float ) unscale ( p . y ) ;
m_vertices [ v_coord + + ] = x ;
m_vertices [ v_coord + + ] = y ;
m_vertices [ v_coord + + ] = z ;
if ( generate_tex_coords )
{
m_tex_coords [ t_coord + + ] = x ;
m_tex_coords [ t_coord + + ] = y ;
min_x = std : : min ( min_x , x ) ;
max_x = std : : max ( max_x , x ) ;
min_y = std : : min ( min_y , y ) ;
max_y = std : : max ( max_y , y ) ;
}
}
}
if ( generate_tex_coords )
{
float size_x = max_x - min_x ;
float size_y = max_y - min_y ;
if ( ( size_x ! = 0.0f ) & & ( size_y ! = 0.0f ) )
{
float inv_size_x = 1.0f / size_x ;
float inv_size_y = - 1.0f / size_y ;
for ( unsigned int i = 0 ; i < m_tex_coords . size ( ) ; i + = 2 )
{
m_tex_coords [ i ] * = inv_size_x ;
m_tex_coords [ i + 1 ] * = inv_size_y ;
}
2018-05-15 13:38:25 +00:00
}
}
return true ;
}
bool GeometryBuffer : : set_from_lines ( const Lines & lines , float z )
{
2018-06-11 08:46:32 +00:00
m_vertices . clear ( ) ;
m_tex_coords . clear ( ) ;
2018-05-15 13:38:25 +00:00
unsigned int size = 6 * ( unsigned int ) lines . size ( ) ;
if ( size = = 0 )
return false ;
2018-06-11 08:46:32 +00:00
m_vertices = std : : vector < float > ( size , 0.0f ) ;
2018-05-15 13:38:25 +00:00
unsigned int coord = 0 ;
for ( const Line & l : lines )
{
2018-06-11 08:46:32 +00:00
m_vertices [ coord + + ] = ( float ) unscale ( l . a . x ) ;
m_vertices [ coord + + ] = ( float ) unscale ( l . a . y ) ;
m_vertices [ coord + + ] = z ;
m_vertices [ coord + + ] = ( float ) unscale ( l . b . x ) ;
m_vertices [ coord + + ] = ( float ) unscale ( l . b . y ) ;
m_vertices [ coord + + ] = z ;
2018-05-15 13:38:25 +00:00
}
return true ;
}
2018-06-11 08:46:32 +00:00
const float * GeometryBuffer : : get_vertices ( ) const
2018-05-15 13:38:25 +00:00
{
2018-06-11 08:46:32 +00:00
return m_vertices . data ( ) ;
2018-05-15 13:38:25 +00:00
}
2018-06-11 08:46:32 +00:00
const float * GeometryBuffer : : get_tex_coords ( ) const
2018-05-15 13:38:25 +00:00
{
2018-06-11 08:46:32 +00:00
return m_tex_coords . data ( ) ;
}
unsigned int GeometryBuffer : : get_vertices_count ( ) const
{
return ( unsigned int ) m_vertices . size ( ) / 3 ;
2018-05-15 13:38:25 +00:00
}
2018-05-24 11:46:17 +00:00
Size : : Size ( )
: m_width ( 0 )
, m_height ( 0 )
{
}
Size : : Size ( int width , int height )
: m_width ( width )
, m_height ( height )
{
}
int Size : : get_width ( ) const
{
return m_width ;
}
void Size : : set_width ( int width )
{
m_width = width ;
}
int Size : : get_height ( ) const
{
return m_height ;
}
void Size : : set_height ( int height )
{
m_height = height ;
}
Rect : : Rect ( )
: m_left ( 0.0f )
, m_top ( 0.0f )
, m_right ( 0.0f )
, m_bottom ( 0.0f )
{
}
Rect : : Rect ( float left , float top , float right , float bottom )
: m_left ( left )
, m_top ( top )
, m_right ( right )
, m_bottom ( bottom )
{
}
float Rect : : get_left ( ) const
{
return m_left ;
}
void Rect : : set_left ( float left )
{
m_left = left ;
}
float Rect : : get_top ( ) const
{
return m_top ;
}
void Rect : : set_top ( float top )
{
m_top = top ;
}
float Rect : : get_right ( ) const
{
return m_right ;
}
void Rect : : set_right ( float right )
{
m_right = right ;
}
float Rect : : get_bottom ( ) const
{
return m_bottom ;
}
void Rect : : set_bottom ( float bottom )
{
m_bottom = bottom ;
}
2018-06-11 08:46:32 +00:00
GLCanvas3D : : GLTextureData : : GLTextureData ( )
: m_id ( 0 )
, m_width ( 0 )
, m_height ( 0 )
, m_source ( " " )
{
}
GLCanvas3D : : GLTextureData : : ~ GLTextureData ( )
{
reset ( ) ;
}
bool GLCanvas3D : : GLTextureData : : load_from_file ( const std : : string & filename )
{
reset ( ) ;
// Load a PNG with an alpha channel.
wxImage image ;
if ( ! image . LoadFile ( filename , wxBITMAP_TYPE_PNG ) )
{
reset ( ) ;
return false ;
}
m_width = image . GetWidth ( ) ;
m_height = image . GetHeight ( ) ;
int n_pixels = m_width * m_height ;
if ( n_pixels < = 0 )
{
reset ( ) ;
return false ;
}
// Get RGB & alpha raw data from wxImage, pack them into an array.
unsigned char * img_rgb = image . GetData ( ) ;
if ( img_rgb = = nullptr )
{
reset ( ) ;
return false ;
}
unsigned char * img_alpha = image . GetAlpha ( ) ;
std : : vector < unsigned char > data ( n_pixels * 4 , 0 ) ;
for ( int i = 0 ; i < n_pixels ; + + i )
{
int data_id = i * 4 ;
int img_id = i * 3 ;
data [ data_id + 0 ] = img_rgb [ img_id + 0 ] ;
data [ data_id + 1 ] = img_rgb [ img_id + 1 ] ;
data [ data_id + 2 ] = img_rgb [ img_id + 2 ] ;
data [ data_id + 3 ] = ( img_alpha ! = nullptr ) ? img_alpha [ i ] : 255 ;
}
// sends data to gpu
: : glGenTextures ( 1 , & m_id ) ;
: : glBindTexture ( GL_TEXTURE_2D , m_id ) ;
: : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
: : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
: : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 1 ) ;
: : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA8 , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ( ) ) ;
: : glBindTexture ( GL_TEXTURE_2D , 0 ) ;
m_source = filename ;
return true ;
}
void GLCanvas3D : : GLTextureData : : reset ( )
{
if ( m_id ! = 0 )
: : glDeleteTextures ( 1 , & m_id ) ;
m_id = 0 ;
m_width = 0 ;
m_height = 0 ;
m_source = " " ;
}
unsigned int GLCanvas3D : : GLTextureData : : get_id ( ) const
{
return m_id ;
}
int GLCanvas3D : : GLTextureData : : get_width ( ) const
{
return m_width ;
}
int GLCanvas3D : : GLTextureData : : get_height ( ) const
{
return m_height ;
}
const std : : string & GLCanvas3D : : GLTextureData : : get_source ( ) const
{
return m_source ;
}
2018-05-09 08:47:04 +00:00
GLCanvas3D : : Camera : : Camera ( )
2018-06-01 13:54:41 +00:00
: type ( Ortho )
, zoom ( 1.0f )
, phi ( 45.0f )
2018-06-04 10:26:39 +00:00
// , distance(0.0f)
2018-06-01 13:54:41 +00:00
, target ( 0.0 , 0.0 , 0.0 )
2018-05-14 10:08:23 +00:00
, m_theta ( 45.0f )
{
}
2018-05-14 09:31:58 +00:00
std : : string GLCanvas3D : : Camera : : get_type_as_string ( ) const
{
2018-06-01 13:54:41 +00:00
switch ( type )
2018-05-14 09:31:58 +00:00
{
default :
2018-06-01 13:54:41 +00:00
case Unknown :
2018-05-14 09:31:58 +00:00
return " unknown " ;
2018-06-04 10:26:39 +00:00
// case Perspective:
// return "perspective";
2018-06-01 13:54:41 +00:00
case Ortho :
2018-05-14 09:31:58 +00:00
return " ortho " ;
} ;
}
2018-05-14 10:08:23 +00:00
float GLCanvas3D : : Camera : : get_theta ( ) const
{
return m_theta ;
}
void GLCanvas3D : : Camera : : set_theta ( float theta )
{
2018-06-01 13:54:41 +00:00
m_theta = clamp ( 0.0f , GIMBALL_LOCK_THETA_MAX , theta ) ;
2018-05-14 10:08:23 +00:00
}
2018-06-11 08:46:32 +00:00
GLCanvas3D : : Bed : : Bed ( )
: m_type ( Custom )
{
}
2018-06-11 09:40:11 +00:00
bool GLCanvas3D : : Bed : : is_prusa ( ) const
{
return ( m_type = = MK2 ) | | ( m_type = = MK3 ) ;
}
bool GLCanvas3D : : Bed : : is_custom ( ) const
{
return m_type = = Custom ;
}
2018-05-14 12:14:19 +00:00
const Pointfs & GLCanvas3D : : Bed : : get_shape ( ) const
{
return m_shape ;
}
void GLCanvas3D : : Bed : : set_shape ( const Pointfs & shape )
{
m_shape = shape ;
2018-06-11 08:46:32 +00:00
m_type = _detect_type ( ) ;
2018-05-15 13:38:25 +00:00
2018-05-14 12:14:19 +00:00
_calc_bounding_box ( ) ;
2018-05-15 13:38:25 +00:00
ExPolygon poly ;
for ( const Pointf & p : m_shape )
{
poly . contour . append ( Point ( scale_ ( p . x ) , scale_ ( p . y ) ) ) ;
}
_calc_triangles ( poly ) ;
const BoundingBox & bed_bbox = poly . contour . bounding_box ( ) ;
_calc_gridlines ( poly , bed_bbox ) ;
2018-06-11 08:46:32 +00:00
m_polygon = offset_ex ( poly . contour , ( float ) bed_bbox . radius ( ) * 1.7f , jtRound , scale_ ( 0.5 ) ) [ 0 ] . contour ;
2018-05-14 12:14:19 +00:00
}
const BoundingBoxf3 & GLCanvas3D : : Bed : : get_bounding_box ( ) const
{
return m_bounding_box ;
}
2018-05-31 14:04:59 +00:00
bool GLCanvas3D : : Bed : : contains ( const Point & point ) const
{
return m_polygon . contains ( point ) ;
}
Point GLCanvas3D : : Bed : : point_projection ( const Point & point ) const
{
return m_polygon . point_projection ( point ) ;
}
2018-06-11 08:46:32 +00:00
void GLCanvas3D : : Bed : : render ( float theta ) const
2018-05-15 13:38:25 +00:00
{
2018-06-11 08:46:32 +00:00
switch ( m_type )
2018-05-15 13:38:25 +00:00
{
2018-06-11 08:46:32 +00:00
case MK2 :
{
_render_mk2 ( theta ) ;
break ;
}
case MK3 :
{
_render_mk3 ( theta ) ;
break ;
}
default :
case Custom :
{
_render_custom ( ) ;
break ;
}
2018-05-15 13:38:25 +00:00
}
}
2018-05-14 12:14:19 +00:00
void GLCanvas3D : : Bed : : _calc_bounding_box ( )
{
m_bounding_box = BoundingBoxf3 ( ) ;
for ( const Pointf & p : m_shape )
{
m_bounding_box . merge ( Pointf3 ( p . x , p . y , 0.0 ) ) ;
}
}
2018-05-15 13:38:25 +00:00
void GLCanvas3D : : Bed : : _calc_triangles ( const ExPolygon & poly )
{
Polygons triangles ;
poly . triangulate ( & triangles ) ;
2018-06-11 08:46:32 +00:00
if ( ! m_triangles . set_from_triangles ( triangles , GROUND_Z , m_type ! = Custom ) )
2018-05-15 13:38:25 +00:00
printf ( " Unable to create bed triangles \n " ) ;
}
void GLCanvas3D : : Bed : : _calc_gridlines ( const ExPolygon & poly , const BoundingBox & bed_bbox )
{
Polylines axes_lines ;
for ( coord_t x = bed_bbox . min . x ; x < = bed_bbox . max . x ; x + = scale_ ( 10.0 ) )
{
Polyline line ;
line . append ( Point ( x , bed_bbox . min . y ) ) ;
line . append ( Point ( x , bed_bbox . max . y ) ) ;
axes_lines . push_back ( line ) ;
}
for ( coord_t y = bed_bbox . min . y ; y < = bed_bbox . max . y ; y + = scale_ ( 10.0 ) )
{
Polyline line ;
line . append ( Point ( bed_bbox . min . x , y ) ) ;
line . append ( Point ( bed_bbox . max . x , y ) ) ;
axes_lines . push_back ( line ) ;
}
// clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped
Lines gridlines = to_lines ( intersection_pl ( axes_lines , offset ( poly , SCALED_EPSILON ) ) ) ;
// append bed contours
Lines contour_lines = to_lines ( poly ) ;
std : : copy ( contour_lines . begin ( ) , contour_lines . end ( ) , std : : back_inserter ( gridlines ) ) ;
if ( ! m_gridlines . set_from_lines ( gridlines , GROUND_Z ) )
printf ( " Unable to create bed grid lines \n " ) ;
}
2018-06-11 08:46:32 +00:00
GLCanvas3D : : Bed : : EType GLCanvas3D : : Bed : : _detect_type ( ) const
{
EType type = Custom ;
const PresetBundle * bundle = get_preset_bundle ( ) ;
if ( bundle ! = nullptr )
{
const Preset & curr = bundle - > printers . get_selected_preset ( ) ;
if ( curr . config . has ( " bed_shape " ) & & _are_equal ( m_shape , dynamic_cast < const ConfigOptionPoints * > ( curr . config . option ( " bed_shape " ) ) - > values ) )
{
if ( ( curr . vendor ! = nullptr ) & & ( curr . vendor - > name = = " Prusa Research " ) )
{
if ( boost : : contains ( curr . name , " MK2 " ) )
type = MK2 ;
else if ( boost : : contains ( curr . name , " MK3 " ) )
type = MK3 ;
}
}
}
return type ;
}
void GLCanvas3D : : Bed : : _render_mk2 ( float theta ) const
{
std : : string filename = resources_dir ( ) + " /icons/bed/mk2_top.png " ;
if ( ( m_top_texture . get_id ( ) = = 0 ) | | ( m_top_texture . get_source ( ) ! = filename ) )
{
if ( ! m_top_texture . load_from_file ( filename ) )
{
_render_custom ( ) ;
return ;
}
}
filename = resources_dir ( ) + " /icons/bed/mk2_bottom.png " ;
if ( ( m_bottom_texture . get_id ( ) = = 0 ) | | ( m_bottom_texture . get_source ( ) ! = filename ) )
{
if ( ! m_bottom_texture . load_from_file ( filename ) )
{
_render_custom ( ) ;
return ;
}
}
_render_prusa ( theta ) ;
}
void GLCanvas3D : : Bed : : _render_mk3 ( float theta ) const
{
std : : string filename = resources_dir ( ) + " /icons/bed/mk3_top.png " ;
if ( ( m_top_texture . get_id ( ) = = 0 ) | | ( m_top_texture . get_source ( ) ! = filename ) )
{
if ( ! m_top_texture . load_from_file ( filename ) )
{
_render_custom ( ) ;
return ;
}
}
filename = resources_dir ( ) + " /icons/bed/mk3_bottom.png " ;
if ( ( m_bottom_texture . get_id ( ) = = 0 ) | | ( m_bottom_texture . get_source ( ) ! = filename ) )
{
if ( ! m_bottom_texture . load_from_file ( filename ) )
{
_render_custom ( ) ;
return ;
}
}
_render_prusa ( theta ) ;
}
void GLCanvas3D : : Bed : : _render_prusa ( float theta ) const
{
unsigned int triangles_vcount = m_triangles . get_vertices_count ( ) ;
if ( triangles_vcount > 0 )
{
: : glEnable ( GL_DEPTH_TEST ) ;
: : glEnable ( GL_BLEND ) ;
: : glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
: : glEnable ( GL_TEXTURE_2D ) ;
: : glEnableClientState ( GL_VERTEX_ARRAY ) ;
: : glEnableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
if ( theta > 90.0f )
: : glFrontFace ( GL_CW ) ;
: : glColor4f ( 1.0f , 1.0f , 1.0f , 1.0f ) ;
: : glBindTexture ( GL_TEXTURE_2D , ( theta < = 90.0f ) ? ( GLuint ) m_top_texture . get_id ( ) : ( GLuint ) m_bottom_texture . get_id ( ) ) ;
: : glVertexPointer ( 3 , GL_FLOAT , 0 , ( GLvoid * ) m_triangles . get_vertices ( ) ) ;
: : glTexCoordPointer ( 2 , GL_FLOAT , 0 , ( GLvoid * ) m_triangles . get_tex_coords ( ) ) ;
: : glDrawArrays ( GL_TRIANGLES , 0 , ( GLsizei ) triangles_vcount ) ;
if ( theta > 90.0f )
: : glFrontFace ( GL_CCW ) ;
: : glBindTexture ( GL_TEXTURE_2D , 0 ) ;
: : glDisableClientState ( GL_TEXTURE_COORD_ARRAY ) ;
: : glDisableClientState ( GL_VERTEX_ARRAY ) ;
: : glDisable ( GL_TEXTURE_2D ) ;
: : glDisable ( GL_BLEND ) ;
}
}
void GLCanvas3D : : Bed : : _render_custom ( ) const
{
m_top_texture . reset ( ) ;
m_bottom_texture . reset ( ) ;
unsigned int triangles_vcount = m_triangles . get_vertices_count ( ) ;
if ( triangles_vcount > 0 )
{
: : glEnable ( GL_LIGHTING ) ;
2018-06-11 09:40:11 +00:00
: : glDisable ( GL_DEPTH_TEST ) ;
2018-06-11 08:46:32 +00:00
: : glEnable ( GL_BLEND ) ;
: : glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
: : glEnableClientState ( GL_VERTEX_ARRAY ) ;
: : glColor4f ( 0.8f , 0.6f , 0.5f , 0.4f ) ;
: : glNormal3d ( 0.0f , 0.0f , 1.0f ) ;
: : glVertexPointer ( 3 , GL_FLOAT , 0 , ( GLvoid * ) m_triangles . get_vertices ( ) ) ;
: : glDrawArrays ( GL_TRIANGLES , 0 , ( GLsizei ) triangles_vcount ) ;
// draw grid
unsigned int gridlines_vcount = m_gridlines . get_vertices_count ( ) ;
2018-06-11 09:40:11 +00:00
// we need depth test for grid, otherwise it would disappear when looking the object from below
: : glEnable ( GL_DEPTH_TEST ) ;
2018-06-11 08:46:32 +00:00
: : glLineWidth ( 3.0f ) ;
: : glColor4f ( 0.2f , 0.2f , 0.2f , 0.4f ) ;
: : glVertexPointer ( 3 , GL_FLOAT , 0 , ( GLvoid * ) m_gridlines . get_vertices ( ) ) ;
: : glDrawArrays ( GL_LINES , 0 , ( GLsizei ) gridlines_vcount ) ;
: : glDisableClientState ( GL_VERTEX_ARRAY ) ;
: : glDisable ( GL_BLEND ) ;
}
}
bool GLCanvas3D : : Bed : : _are_equal ( const Pointfs & bed_1 , const Pointfs & bed_2 )
{
if ( bed_1 . size ( ) ! = bed_2 . size ( ) )
return false ;
for ( unsigned int i = 0 ; i < ( unsigned int ) bed_1 . size ( ) ; + + i )
{
if ( bed_1 [ i ] ! = bed_2 [ i ] )
return false ;
}
return true ;
}
2018-05-18 11:02:47 +00:00
GLCanvas3D : : Axes : : Axes ( )
2018-06-01 13:54:41 +00:00
: length ( 0.0f )
2018-05-18 11:02:47 +00:00
{
}
2018-05-21 13:57:03 +00:00
void GLCanvas3D : : Axes : : render ( ) const
2018-05-18 11:02:47 +00:00
{
2018-05-21 13:24:52 +00:00
: : glDisable ( GL_LIGHTING ) ;
2018-05-18 11:02:47 +00:00
// disable depth testing so that axes are not covered by ground
: : glDisable ( GL_DEPTH_TEST ) ;
: : glLineWidth ( 2.0f ) ;
: : glBegin ( GL_LINES ) ;
// draw line for x axis
: : glColor3f ( 1.0f , 0.0f , 0.0f ) ;
2018-06-01 13:54:41 +00:00
: : glVertex3f ( ( GLfloat ) origin . x , ( GLfloat ) origin . y , ( GLfloat ) origin . z ) ;
: : glVertex3f ( ( GLfloat ) origin . x + length , ( GLfloat ) origin . y , ( GLfloat ) origin . z ) ;
// draw line for y axis
2018-05-18 11:02:47 +00:00
: : glColor3f ( 0.0f , 1.0f , 0.0f ) ;
2018-06-01 13:54:41 +00:00
: : glVertex3f ( ( GLfloat ) origin . x , ( GLfloat ) origin . y , ( GLfloat ) origin . z ) ;
: : glVertex3f ( ( GLfloat ) origin . x , ( GLfloat ) origin . y + length , ( GLfloat ) origin . z ) ;
2018-05-18 11:02:47 +00:00
: : glEnd ( ) ;
// draw line for Z axis
// (re-enable depth test so that axis is correctly shown when objects are behind it)
: : glEnable ( GL_DEPTH_TEST ) ;
: : glBegin ( GL_LINES ) ;
: : glColor3f ( 0.0f , 0.0f , 1.0f ) ;
2018-06-01 13:54:41 +00:00
: : glVertex3f ( ( GLfloat ) origin . x , ( GLfloat ) origin . y , ( GLfloat ) origin . z ) ;
: : glVertex3f ( ( GLfloat ) origin . x , ( GLfloat ) origin . y , ( GLfloat ) origin . z + length ) ;
2018-05-18 11:02:47 +00:00
: : glEnd ( ) ;
}
2018-05-18 09:05:48 +00:00
GLCanvas3D : : CuttingPlane : : CuttingPlane ( )
: m_z ( - 1.0f )
{
}
bool GLCanvas3D : : CuttingPlane : : set ( float z , const ExPolygons & polygons )
{
m_z = z ;
// grow slices in order to display them better
ExPolygons expolygons = offset_ex ( polygons , scale_ ( 0.1 ) ) ;
Lines lines = to_lines ( expolygons ) ;
return m_lines . set_from_lines ( lines , m_z ) ;
}
2018-05-21 13:57:03 +00:00
void GLCanvas3D : : CuttingPlane : : render ( const BoundingBoxf3 & bb ) const
2018-05-21 13:24:52 +00:00
{
: : glDisable ( GL_LIGHTING ) ;
_render_plane ( bb ) ;
_render_contour ( ) ;
}
2018-05-21 13:57:03 +00:00
void GLCanvas3D : : CuttingPlane : : _render_plane ( const BoundingBoxf3 & bb ) const
2018-05-18 09:05:48 +00:00
{
if ( m_z > = 0.0f )
{
: : glDisable ( GL_CULL_FACE ) ;
: : glEnable ( GL_BLEND ) ;
: : glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
float margin = 20.0f ;
float min_x = bb . min . x - margin ;
float max_x = bb . max . x + margin ;
float min_y = bb . min . y - margin ;
float max_y = bb . max . y + margin ;
: : glBegin ( GL_QUADS ) ;
: : glColor4f ( 0.8f , 0.8f , 0.8f , 0.5f ) ;
: : glVertex3f ( min_x , min_y , m_z ) ;
: : glVertex3f ( max_x , min_y , m_z ) ;
: : glVertex3f ( max_x , max_y , m_z ) ;
: : glVertex3f ( min_x , max_y , m_z ) ;
: : glEnd ( ) ;
: : glEnable ( GL_CULL_FACE ) ;
: : glDisable ( GL_BLEND ) ;
}
}
2018-05-21 13:57:03 +00:00
void GLCanvas3D : : CuttingPlane : : _render_contour ( ) const
2018-05-18 09:05:48 +00:00
{
: : glEnableClientState ( GL_VERTEX_ARRAY ) ;
if ( m_z > = 0.0f )
{
2018-06-11 08:46:32 +00:00
unsigned int lines_vcount = m_lines . get_vertices_count ( ) ;
2018-05-18 09:05:48 +00:00
: : glLineWidth ( 2.0f ) ;
: : glColor3f ( 0.0f , 0.0f , 0.0f ) ;
2018-06-11 08:46:32 +00:00
: : glVertexPointer ( 3 , GL_FLOAT , 0 , ( GLvoid * ) m_lines . get_vertices ( ) ) ;
2018-05-18 09:05:48 +00:00
: : glDrawArrays ( GL_LINES , 0 , ( GLsizei ) lines_vcount ) ;
}
: : glDisableClientState ( GL_VERTEX_ARRAY ) ;
}
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-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 ;
}
}
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 )
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 )
, last_action ( 0 )
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 )
{
: : glDeleteTextures ( 1 , & m_z_texture_id ) ;
m_z_texture_id = 0 ;
}
}
bool GLCanvas3D : : LayersEditing : : init ( const std : : string & vertex_shader_filename , const std : : string & fragment_shader_filename )
{
if ( ! m_shader . init ( vertex_shader_filename , fragment_shader_filename ) )
return false ;
: : glGenTextures ( 1 , ( GLuint * ) & m_z_texture_id ) ;
: : glBindTexture ( GL_TEXTURE_2D , m_z_texture_id ) ;
: : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP ) ;
: : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP ) ;
: : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
: : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR_MIPMAP_NEAREST ) ;
: : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 1 ) ;
: : glBindTexture ( GL_TEXTURE_2D , 0 ) ;
return true ;
}
bool GLCanvas3D : : LayersEditing : : is_allowed ( ) const
{
2018-06-04 11:15:28 +00:00
return ! m_use_legacy_opengl & & m_shader . is_initialized ( ) ;
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
}
unsigned int GLCanvas3D : : LayersEditing : : get_z_texture_id ( ) const
{
return m_z_texture_id ;
}
2018-05-25 13:56:14 +00:00
void GLCanvas3D : : LayersEditing : : render ( const GLCanvas3D & canvas , const PrintObject & print_object , const GLVolume & volume ) 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
: : glDisable ( GL_DEPTH_TEST ) ;
// The viewport and camera are set to complete view and glOrtho(-$x / 2, $x / 2, -$y / 2, $y / 2, -$depth, $depth),
// where x, y is the window size divided by $self->_zoom.
: : glPushMatrix ( ) ;
: : glLoadIdentity ( ) ;
2018-05-24 11:46:17 +00:00
_render_tooltip_texture ( canvas , bar_rect , reset_rect ) ;
_render_reset_texture ( canvas , reset_rect ) ;
2018-05-25 13:56:14 +00:00
_render_active_object_annotations ( canvas , volume , print_object , bar_rect ) ;
2018-05-24 13:17:01 +00:00
_render_profile ( print_object , bar_rect ) ;
2018-05-25 13:56:14 +00:00
// Revert the matrices.
2018-06-11 08:46:32 +00:00
: : glPopMatrix ( ) ;
2018-05-25 13:56:14 +00:00
2018-06-11 08:46:32 +00:00
: : glEnable ( GL_DEPTH_TEST ) ;
2018-05-24 11:46:17 +00:00
}
2018-06-01 13:54:41 +00:00
int GLCanvas3D : : LayersEditing : : get_shader_program_id ( ) const
2018-05-25 12:05:08 +00:00
{
2018-06-01 13:54:41 +00:00
const GLShader * shader = m_shader . get_shader ( ) ;
return ( shader ! = nullptr ) ? shader - > shader_program_id : - 1 ;
2018-05-25 12:05:08 +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
{
const Point & mouse_pos = canvas . get_local_mouse_position ( ) ;
2018-05-30 13:18:45 +00:00
const Rect & rect = get_bar_rect_screen ( canvas ) ;
2018-05-28 11:43:29 +00:00
float x = ( float ) mouse_pos . x ;
float y = ( float ) mouse_pos . y ;
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:10:02 +00:00
int GLCanvas3D : : LayersEditing : : get_first_selected_object_id ( const GLVolumeCollection & volumes , unsigned int objects_count )
{
for ( const GLVolume * vol : volumes . volumes )
{
if ( ( vol ! = nullptr ) & & vol - > selected )
{
int object_id = vol - > select_group_id / 1000000 ;
// Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy.
if ( object_id < 10000 )
return ( object_id > = ( int ) objects_count ) ? - 1 : object_id ;
}
}
return - 1 ;
}
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 ( ) ;
return Rect ( w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH , 0.0f , w , h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT ) ;
}
Rect GLCanvas3D : : LayersEditing : : get_reset_rect_screen ( const GLCanvas3D & canvas )
{
const Size & cnv_size = canvas . get_canvas_size ( ) ;
float w = ( float ) cnv_size . get_width ( ) ;
float h = ( float ) cnv_size . get_height ( ) ;
return Rect ( w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH , h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT , w , h ) ;
}
Rect GLCanvas3D : : LayersEditing : : get_bar_rect_viewport ( const GLCanvas3D & canvas )
{
const Size & cnv_size = canvas . get_canvas_size ( ) ;
float half_w = 0.5f * ( float ) cnv_size . get_width ( ) ;
float half_h = 0.5f * ( float ) cnv_size . get_height ( ) ;
float zoom = canvas . get_camera_zoom ( ) ;
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
return Rect ( ( half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH ) * inv_zoom , half_h * inv_zoom , half_w * inv_zoom , ( - half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT ) * inv_zoom ) ;
}
Rect GLCanvas3D : : LayersEditing : : get_reset_rect_viewport ( const GLCanvas3D & canvas )
{
const Size & cnv_size = canvas . get_canvas_size ( ) ;
float half_w = 0.5f * ( float ) cnv_size . get_width ( ) ;
float half_h = 0.5f * ( float ) cnv_size . get_height ( ) ;
float zoom = canvas . get_camera_zoom ( ) ;
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
return Rect ( ( half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH ) * inv_zoom , ( - half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT ) * inv_zoom , half_w * inv_zoom , - half_h * inv_zoom ) ;
}
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
{
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 " ;
if ( ! m_tooltip_texture . load_from_file ( filename ) )
2018-05-24 11:46:17 +00:00
return ;
}
float zoom = canvas . get_camera_zoom ( ) ;
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
float gap = 10.0f * inv_zoom ;
float bar_left = bar_rect . get_left ( ) ;
float reset_bottom = reset_rect . get_bottom ( ) ;
2018-06-11 08:46:32 +00:00
float l = bar_left - ( float ) m_tooltip_texture . get_width ( ) * inv_zoom - gap ;
2018-05-24 11:46:17 +00:00
float r = bar_left - gap ;
2018-06-11 08:46:32 +00:00
float t = reset_bottom + ( float ) m_tooltip_texture . get_height ( ) * inv_zoom + gap ;
2018-05-24 11:46:17 +00:00
float b = reset_bottom + gap ;
2018-06-11 08:46:32 +00:00
canvas . render_texture ( m_tooltip_texture . get_id ( ) , l , r , b , t ) ;
2018-05-24 11:46:17 +00:00
}
void GLCanvas3D : : LayersEditing : : _render_reset_texture ( const GLCanvas3D & canvas , const Rect & reset_rect ) const
{
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 " ;
if ( ! m_reset_texture . load_from_file ( filename ) )
2018-05-24 11:46:17 +00:00
return ;
}
2018-06-11 08:46:32 +00:00
canvas . 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
}
2018-05-25 13:56:14 +00:00
void GLCanvas3D : : LayersEditing : : _render_active_object_annotations ( const GLCanvas3D & canvas , const GLVolume & volume , const PrintObject & print_object , const Rect & bar_rect ) const
{
float max_z = print_object . model_object ( ) - > bounding_box ( ) . max . z ;
m_shader . start_using ( ) ;
m_shader . set_uniform ( " z_to_texture_row " , ( float ) volume . layer_height_texture_z_to_row_id ( ) ) ;
m_shader . set_uniform ( " z_texture_row_to_normalized " , 1.0f / ( float ) volume . layer_height_texture_height ( ) ) ;
2018-05-28 11:43:29 +00:00
m_shader . set_uniform ( " z_cursor " , max_z * get_cursor_z_relative ( canvas ) ) ;
2018-06-01 13:54:41 +00:00
m_shader . set_uniform ( " z_cursor_band_width " , band_width ) ;
2018-05-25 13:56:14 +00:00
GLsizei w = ( GLsizei ) volume . layer_height_texture_width ( ) ;
GLsizei h = ( GLsizei ) volume . layer_height_texture_height ( ) ;
GLsizei half_w = w / 2 ;
GLsizei half_h = h / 2 ;
: : glBindTexture ( GL_TEXTURE_2D , m_z_texture_id ) ;
: : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA8 , w , h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ;
: : glTexImage2D ( GL_TEXTURE_2D , 1 , GL_RGBA8 , half_w , half_h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ;
: : glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , w , h , GL_RGBA , GL_UNSIGNED_BYTE , volume . layer_height_texture_data_ptr_level0 ( ) ) ;
: : glTexSubImage2D ( GL_TEXTURE_2D , 1 , 0 , 0 , half_w , half_h , GL_RGBA , GL_UNSIGNED_BYTE , volume . layer_height_texture_data_ptr_level1 ( ) ) ;
// Render the color bar
float l = bar_rect . get_left ( ) ;
float r = bar_rect . get_right ( ) ;
float t = bar_rect . get_top ( ) ;
float b = bar_rect . get_bottom ( ) ;
: : glBegin ( GL_QUADS ) ;
: : glVertex3f ( l , b , 0.0f ) ;
: : glVertex3f ( r , b , 0.0f ) ;
: : glVertex3f ( r , t , max_z ) ;
: : glVertex3f ( l , t , max_z ) ;
: : glEnd ( ) ;
: : glBindTexture ( GL_TEXTURE_2D , 0 ) ;
m_shader . stop_using ( ) ;
}
2018-05-24 13:17:01 +00:00
void GLCanvas3D : : LayersEditing : : _render_profile ( const PrintObject & print_object , const Rect & bar_rect ) const
{
// FIXME show some kind of legend.
// Get a maximum layer height value.
// FIXME This is a duplicate code of Slicing.cpp.
double layer_height_max = DBL_MAX ;
const PrintConfig & print_config = print_object . print ( ) - > config ;
const std : : vector < double > & nozzle_diameters = dynamic_cast < const ConfigOptionFloats * > ( print_config . option ( " nozzle_diameter " ) ) - > values ;
const std : : vector < double > & layer_heights_min = dynamic_cast < const ConfigOptionFloats * > ( print_config . option ( " min_layer_height " ) ) - > values ;
const std : : vector < double > & layer_heights_max = dynamic_cast < const ConfigOptionFloats * > ( print_config . option ( " max_layer_height " ) ) - > values ;
for ( unsigned int i = 0 ; i < ( unsigned int ) nozzle_diameters . size ( ) ; + + i )
{
double lh_min = ( layer_heights_min [ i ] = = 0.0 ) ? 0.07 : std : : max ( 0.01 , layer_heights_min [ i ] ) ;
double lh_max = ( layer_heights_max [ i ] = = 0.0 ) ? ( 0.75 * nozzle_diameters [ i ] ) : layer_heights_max [ i ] ;
layer_height_max = std : : min ( layer_height_max , std : : max ( lh_min , lh_max ) ) ;
}
// Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region.
layer_height_max * = 1.12 ;
coordf_t max_z = unscale ( print_object . size . z ) ;
double layer_height = dynamic_cast < const ConfigOptionFloat * > ( print_object . config . option ( " layer_height " ) ) - > value ;
float l = bar_rect . get_left ( ) ;
float w = bar_rect . get_right ( ) - l ;
float b = bar_rect . get_bottom ( ) ;
float t = bar_rect . get_top ( ) ;
float h = t - b ;
float scale_x = w / ( float ) layer_height_max ;
float scale_y = h / ( float ) max_z ;
float x = l + ( float ) layer_height * scale_x ;
// Baseline
: : glColor3f ( 0.0f , 0.0f , 0.0f ) ;
: : glBegin ( GL_LINE_STRIP ) ;
: : glVertex2f ( x , b ) ;
: : glVertex2f ( x , t ) ;
: : glEnd ( ) ;
// Curve
const ModelObject * model_object = print_object . model_object ( ) ;
if ( model_object - > layer_height_profile_valid )
{
const std : : vector < coordf_t > & profile = model_object - > layer_height_profile ;
: : glColor3f ( 0.0f , 0.0f , 1.0f ) ;
: : glBegin ( GL_LINE_STRIP ) ;
for ( unsigned int i = 0 ; i < profile . size ( ) ; i + = 2 )
{
: : glVertex2f ( l + ( float ) profile [ i + 1 ] * scale_x , b + ( float ) profile [ i ] * scale_y ) ;
}
: : glEnd ( ) ;
}
}
2018-06-01 13:54:41 +00:00
const Point GLCanvas3D : : Mouse : : Drag : : Invalid_2D_Point ( INT_MAX , INT_MAX ) ;
const Pointf3 GLCanvas3D : : Mouse : : Drag : : Invalid_3D_Point ( DBL_MAX , DBL_MAX , DBL_MAX ) ;
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 )
, volume_idx ( - 1 )
2018-05-23 11:56:54 +00:00
{
}
2018-06-01 13:54:41 +00:00
GLCanvas3D : : Mouse : : Mouse ( )
: dragging ( false )
, position ( DBL_MAX , DBL_MAX )
2018-06-01 07:00:30 +00:00
{
}
2018-06-01 13:54:41 +00:00
void GLCanvas3D : : Mouse : : set_start_position_2D_as_invalid ( )
2018-05-31 11:51:50 +00:00
{
2018-06-01 13:54:41 +00:00
drag . start_position_2D = Drag : : Invalid_2D_Point ;
2018-05-31 11:51:50 +00:00
}
2018-06-01 13:54:41 +00:00
void GLCanvas3D : : Mouse : : set_start_position_3D_as_invalid ( )
2018-05-31 11:51:50 +00:00
{
2018-06-01 13:54:41 +00:00
drag . start_position_3D = Drag : : Invalid_3D_Point ;
2018-05-31 11:51:50 +00:00
}
2018-06-01 13:54:41 +00:00
bool GLCanvas3D : : Mouse : : is_start_position_2D_defined ( ) const
2018-05-31 11:51:50 +00:00
{
2018-06-01 13:54:41 +00:00
return ( drag . start_position_2D ! = Drag : : Invalid_2D_Point ) ;
2018-05-31 11:51:50 +00:00
}
2018-06-01 13:54:41 +00:00
bool GLCanvas3D : : Mouse : : is_start_position_3D_defined ( ) const
2018-05-31 11:51:50 +00:00
{
2018-06-01 13:54:41 +00:00
return ( drag . start_position_3D ! = Drag : : Invalid_3D_Point ) ;
2018-05-31 11:51:50 +00:00
}
2018-05-09 08:47:04 +00:00
GLCanvas3D : : GLCanvas3D ( wxGLCanvas * canvas , wxGLContext * context )
: m_canvas ( canvas )
, m_context ( context )
2018-05-30 13:18:45 +00:00
, m_timer ( nullptr )
2018-05-23 09:14:49 +00:00
, m_config ( nullptr )
2018-05-28 13:23:01 +00:00
, m_print ( 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-06-05 08:56:55 +00:00
, m_force_zoom_to_bed_enabled ( false )
2018-05-14 12:14:19 +00:00
, m_apply_zoom_to_volumes_filter ( false )
2018-05-23 13:35:11 +00:00
, m_hover_volume_id ( - 1 )
2018-05-21 12:40:09 +00:00
, m_warning_texture_enabled ( 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-05-25 12:05:08 +00:00
, m_shader_enabled ( false )
2018-05-23 13:35:11 +00:00
, m_multisample_allowed ( false )
2018-06-06 08:16:58 +00:00
, m_color_by ( " volume " )
, m_select_by ( " object " )
, m_drag_by ( " instance " )
2018-06-08 07:40:00 +00:00
, m_reload_delayed ( false )
2018-05-09 08:47:04 +00:00
{
2018-05-30 13:18:45 +00:00
if ( m_canvas ! = nullptr )
m_timer = new wxTimer ( m_canvas ) ;
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-06-06 12:19:28 +00:00
2018-05-30 13:18:45 +00:00
if ( m_timer ! = nullptr )
{
delete m_timer ;
m_timer = nullptr ;
}
2018-05-15 07:50:01 +00:00
_deregister_callbacks ( ) ;
2018-05-23 07:57:44 +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 ;
std : : cout < < " init: " < < ( void * ) m_canvas < < " ( " < < ( void * ) this < < " ) " < < std : : endl ;
2018-05-31 06:44:39 +00:00
: : glClearColor ( 1.0f , 1.0f , 1.0f , 1.0f ) ;
2018-05-23 13:35:11 +00:00
: : glClearDepth ( 1.0f ) ;
2018-06-01 13:54:41 +00:00
2018-05-31 06:44:39 +00:00
: : glDepthFunc ( GL_LESS ) ;
: : glEnable ( GL_DEPTH_TEST ) ;
2018-05-23 13:35:11 +00:00
: : glEnable ( GL_CULL_FACE ) ;
: : glEnable ( GL_BLEND ) ;
: : glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
// Set antialiasing / multisampling
: : glDisable ( GL_LINE_SMOOTH ) ;
: : glDisable ( GL_POLYGON_SMOOTH ) ;
// ambient lighting
GLfloat ambient [ 4 ] = { 0.3f , 0.3f , 0.3f , 1.0f } ;
: : glLightModelfv ( GL_LIGHT_MODEL_AMBIENT , ambient ) ;
: : glEnable ( GL_LIGHT0 ) ;
: : glEnable ( GL_LIGHT1 ) ;
// light from camera
2018-06-11 08:46:32 +00:00
GLfloat specular_cam [ 4 ] = { 0.3f , 0.3f , 0.3f , 1.0f } ;
: : glLightfv ( GL_LIGHT1 , GL_SPECULAR , specular_cam ) ;
GLfloat diffuse_cam [ 4 ] = { 0.2f , 0.2f , 0.2f , 1.0f } ;
: : glLightfv ( GL_LIGHT1 , GL_DIFFUSE , diffuse_cam ) ;
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 } ;
: : glLightfv ( GL_LIGHT0 , GL_SPECULAR , specular_top ) ;
GLfloat diffuse_top [ 4 ] = { 0.5f , 0.5f , 0.5f , 1.0f } ;
: : glLightfv ( GL_LIGHT0 , GL_DIFFUSE , diffuse_top ) ;
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.
: : glShadeModel ( GL_SMOOTH ) ;
// A handy trick -- have surface material mirror the color.
: : glColorMaterial ( GL_FRONT_AND_BACK , GL_AMBIENT_AND_DIFFUSE ) ;
: : glEnable ( GL_COLOR_MATERIAL ) ;
2018-06-01 13:54:41 +00:00
if ( m_multisample_allowed )
2018-05-23 13:35:11 +00:00
: : glEnable ( GL_MULTISAMPLE ) ;
2018-05-23 07:57:44 +00:00
if ( useVBOs & & ! m_shader . init ( " gouraud.vs " , " gouraud.fs " ) )
return false ;
2018-05-25 12:05:08 +00:00
if ( useVBOs & & ! m_layers_editing . init ( " variable_layer_height.vs " , " variable_layer_height.fs " ) )
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
m_initialized = true ;
2018-05-23 07:57:44 +00:00
return true ;
2018-05-15 07:50:01 +00:00
}
2018-05-18 12:08:59 +00:00
bool GLCanvas3D : : set_current ( )
2018-05-09 08:47:04 +00:00
{
if ( ( m_canvas ! = nullptr ) & & ( m_context ! = nullptr ) )
2018-05-18 12:08:59 +00:00
{
2018-05-09 08:47:04 +00:00
m_canvas - > SetCurrent ( * m_context ) ;
2018-05-18 12:08:59 +00:00
return true ;
}
return false ;
2018-05-09 08:47:04 +00:00
}
2018-05-14 12:14:19 +00:00
bool GLCanvas3D : : is_shown_on_screen ( ) const
{
return ( m_canvas ! = nullptr ) ? m_canvas - > IsShownOnScreen ( ) : false ;
}
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 ( )
{
2018-06-11 13:13:13 +00:00
if ( set_current ( ) )
2018-05-18 12:08:59 +00:00
{
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-05-25 07:03:55 +00:00
void GLCanvas3D : : deselect_volumes ( )
{
2018-06-11 13:13:13 +00:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-25 07:03:55 +00:00
{
2018-06-11 13:13:13 +00:00
if ( vol ! = nullptr )
vol - > selected = false ;
2018-05-25 07:03:55 +00:00
}
}
void GLCanvas3D : : select_volume ( unsigned int id )
{
2018-06-11 13:13:13 +00:00
if ( id < ( unsigned int ) m_volumes . volumes . size ( ) )
2018-05-25 07:03:55 +00:00
{
2018-06-11 13:13:13 +00:00
GLVolume * vol = m_volumes . volumes [ id ] ;
2018-05-25 07:03:55 +00:00
if ( vol ! = nullptr )
vol - > selected = true ;
}
}
2018-06-08 07:40:00 +00:00
void GLCanvas3D : : update_volumes_selection ( const std : : vector < int > & selections )
{
if ( m_model = = nullptr )
return ;
for ( unsigned int obj_idx = 0 ; obj_idx < ( unsigned int ) m_model - > objects . size ( ) ; + + obj_idx )
{
2018-06-11 13:49:04 +00:00
if ( ( selections [ obj_idx ] = = 1 ) & & ( obj_idx < ( unsigned int ) m_objects_volumes_idxs . size ( ) ) )
2018-06-08 07:40:00 +00:00
{
const std : : vector < int > & volume_idxs = m_objects_volumes_idxs [ obj_idx ] ;
for ( int v : volume_idxs )
{
select_volume ( v ) ;
}
}
}
}
2018-06-11 11:48:02 +00:00
bool GLCanvas3D : : check_volumes_outside_state ( const DynamicPrintConfig * config ) const
{
2018-06-11 13:13:13 +00:00
return m_volumes . check_outside_state ( config ) ;
2018-06-11 11:48:02 +00:00
}
bool GLCanvas3D : : move_volume_up ( unsigned int id )
{
2018-06-11 13:13:13 +00:00
if ( ( id > 0 ) & & ( id < ( unsigned int ) m_volumes . volumes . size ( ) ) )
2018-06-11 11:48:02 +00:00
{
2018-06-11 13:13:13 +00:00
std : : swap ( m_volumes . volumes [ id - 1 ] , m_volumes . volumes [ id ] ) ;
std : : swap ( m_volumes . volumes [ id - 1 ] - > composite_id , m_volumes . volumes [ id ] - > composite_id ) ;
std : : swap ( m_volumes . volumes [ id - 1 ] - > select_group_id , m_volumes . volumes [ id ] - > select_group_id ) ;
std : : swap ( m_volumes . volumes [ id - 1 ] - > drag_group_id , m_volumes . volumes [ id ] - > drag_group_id ) ;
return true ;
2018-06-11 11:48:02 +00:00
}
2018-06-11 13:13:13 +00:00
2018-06-11 11:48:02 +00:00
return false ;
}
bool GLCanvas3D : : move_volume_down ( unsigned int id )
{
2018-06-11 13:13:13 +00:00
if ( ( id > = 0 ) & & ( id + 1 < ( unsigned int ) m_volumes . volumes . size ( ) ) )
2018-06-11 11:48:02 +00:00
{
2018-06-11 13:13:13 +00:00
std : : swap ( m_volumes . volumes [ id + 1 ] , m_volumes . volumes [ id ] ) ;
std : : swap ( m_volumes . volumes [ id + 1 ] - > composite_id , m_volumes . volumes [ id ] - > composite_id ) ;
std : : swap ( m_volumes . volumes [ id + 1 ] - > select_group_id , m_volumes . volumes [ id ] - > select_group_id ) ;
std : : swap ( m_volumes . volumes [ id + 1 ] - > drag_group_id , m_volumes . volumes [ id ] - > drag_group_id ) ;
return true ;
2018-06-11 11:48:02 +00:00
}
return false ;
}
2018-06-08 07:40:00 +00:00
void GLCanvas3D : : set_objects_selections ( const std : : vector < int > & selections )
{
m_objects_selections = selections ;
}
2018-05-28 13:23:01 +00:00
void GLCanvas3D : : set_config ( DynamicPrintConfig * config )
2018-05-23 09:14:49 +00:00
{
2018-05-28 13:23:01 +00:00
m_config = config ;
2018-05-23 09:14:49 +00:00
}
2018-05-28 13:23:01 +00:00
void GLCanvas3D : : set_print ( Print * print )
2018-05-23 09:14:49 +00:00
{
2018-05-28 13:23:01 +00:00
m_print = print ;
2018-05-23 09:14:49 +00:00
}
2018-06-07 09:18:28 +00:00
void GLCanvas3D : : set_model ( Model * model )
{
m_model = model ;
}
2018-05-14 12:14:19 +00:00
void GLCanvas3D : : set_bed_shape ( const Pointfs & shape )
{
m_bed . set_shape ( shape ) ;
2018-05-18 11:02:47 +00:00
// Set the origin and size for painting of the coordinate system axes.
2018-06-01 13:54:41 +00:00
m_axes . origin = Pointf3 ( 0.0 , 0.0 , ( coordf_t ) GROUND_Z ) ;
set_axes_length ( 0.3f * ( float ) m_bed . get_bounding_box ( ) . max_size ( ) ) ;
2018-05-14 12:14:19 +00:00
}
2018-05-15 13:38:25 +00:00
void GLCanvas3D : : set_auto_bed_shape ( )
2018-05-15 09:07:32 +00:00
{
2018-05-15 13:38:25 +00:00
// draw a default square bed around object center
const BoundingBoxf3 & bbox = volumes_bounding_box ( ) ;
coordf_t max_size = bbox . max_size ( ) ;
const Pointf3 & center = bbox . center ( ) ;
2018-05-15 09:07:32 +00:00
2018-05-15 13:38:25 +00:00
Pointfs bed_shape ;
bed_shape . reserve ( 4 ) ;
bed_shape . emplace_back ( center . x - max_size , center . y - max_size ) ;
bed_shape . emplace_back ( center . x + max_size , center . y - max_size ) ;
bed_shape . emplace_back ( center . x + max_size , center . y + max_size ) ;
bed_shape . emplace_back ( center . x - max_size , center . y + max_size ) ;
set_bed_shape ( bed_shape ) ;
// Set the origin for painting of the coordinate system axes.
2018-06-01 13:54:41 +00:00
m_axes . origin = Pointf3 ( center . x , center . y , ( coordf_t ) GROUND_Z ) ;
2018-05-18 11:02:47 +00:00
}
void GLCanvas3D : : set_axes_length ( float length )
{
2018-06-01 13:54:41 +00:00
m_axes . length = length ;
2018-05-09 08:47:04 +00:00
}
2018-05-18 09:05:48 +00:00
void GLCanvas3D : : set_cutting_plane ( float z , const ExPolygons & polygons )
{
m_cutting_plane . set ( z , polygons ) ;
}
2018-06-06 10:36:52 +00:00
void GLCanvas3D : : set_color_by ( const std : : string & value )
{
m_color_by = value ;
}
void GLCanvas3D : : set_select_by ( const std : : string & value )
{
m_select_by = value ;
}
void GLCanvas3D : : set_drag_by ( const std : : string & value )
{
m_drag_by = value ;
}
2018-05-09 08:47:04 +00:00
float GLCanvas3D : : get_camera_zoom ( ) const
{
2018-06-01 13:54:41 +00:00
return m_camera . zoom ;
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-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-04 12:28:59 +00:00
bool GLCanvas3D : : is_shader_enabled ( ) const
{
return m_shader_enabled ;
}
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 ) ;
2018-05-23 13:35:11 +00:00
}
2018-05-21 12:40:09 +00:00
void GLCanvas3D : : enable_warning_texture ( bool enable )
{
m_warning_texture_enabled = enable ;
}
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-05-31 11:51:50 +00:00
void GLCanvas3D : : enable_moving ( bool enable )
{
m_moving_enabled = enable ;
}
2018-05-23 07:57:44 +00:00
void GLCanvas3D : : enable_shader ( bool enable )
{
2018-05-25 12:05:08 +00:00
m_shader_enabled = enable ;
2018-05-23 07:57:44 +00:00
}
2018-06-05 08:56:55 +00:00
void GLCanvas3D : : enable_force_zoom_to_bed ( bool enable )
{
m_force_zoom_to_bed_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 ( )
{
2018-06-01 13:54:41 +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-05-15 09:30:11 +00:00
void GLCanvas3D : : select_view ( const std : : string & direction )
{
const float * dir_vec = nullptr ;
if ( direction = = " iso " )
dir_vec = VIEW_DEFAULT ;
else if ( direction = = " left " )
dir_vec = VIEW_LEFT ;
else if ( direction = = " right " )
dir_vec = VIEW_RIGHT ;
else if ( direction = = " top " )
dir_vec = VIEW_TOP ;
else if ( direction = = " bottom " )
dir_vec = VIEW_BOTTOM ;
else if ( direction = = " front " )
dir_vec = VIEW_FRONT ;
else if ( direction = = " rear " )
dir_vec = VIEW_REAR ;
if ( ( dir_vec ! = nullptr ) & & ! empty ( volumes_bounding_box ( ) ) )
{
2018-06-01 13:54:41 +00:00
m_camera . phi = dir_vec [ 0 ] ;
2018-05-15 09:30:11 +00:00
m_camera . set_theta ( dir_vec [ 1 ] ) ;
m_on_viewport_changed_callback . call ( ) ;
if ( m_canvas ! = nullptr )
m_canvas - > Refresh ( ) ;
}
}
2018-05-29 13:36:09 +00:00
void GLCanvas3D : : set_viewport_from_scene ( const GLCanvas3D & other )
{
2018-06-01 13:54:41 +00:00
m_camera . phi = other . m_camera . phi ;
m_camera . set_theta ( other . m_camera . get_theta ( ) ) ;
m_camera . target = other . m_camera . target ;
m_camera . zoom = other . m_camera . zoom ;
m_dirty = true ;
2018-05-29 13:36:09 +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-05-29 12:34:45 +00:00
if ( m_canvas = = nullptr )
return ;
2018-06-04 10:26:39 +00:00
if ( ! is_shown_on_screen ( ) )
return ;
2018-06-05 08:56:55 +00:00
// ensures that the proper context is selected and that this canvas is initialized
if ( ! set_current ( ) | | ! _3DScene : : init ( m_canvas ) )
2018-06-04 11:15:28 +00:00
return ;
2018-06-05 08:56:55 +00:00
if ( m_force_zoom_to_bed_enabled )
_force_zoom_to_bed ( ) ;
2018-06-04 10:26:39 +00:00
2018-05-31 06:44:39 +00:00
_camera_tranform ( ) ;
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 } ;
: : glLightfv ( GL_LIGHT1 , GL_POSITION , position_cam ) ;
GLfloat position_top [ 4 ] = { - 0.5f , - 0.5f , 1.0f , 0.0f } ;
: : glLightfv ( GL_LIGHT0 , GL_POSITION , position_top ) ;
2018-06-04 10:26:39 +00:00
2018-06-11 09:40:11 +00:00
float theta = m_camera . get_theta ( ) ;
bool is_custom_bed = m_bed . is_custom ( ) ;
2018-05-29 11:54:34 +00:00
_picking_pass ( ) ;
_render_background ( ) ;
2018-06-11 09:40:11 +00:00
// untextured bed needs to be rendered before objects
if ( is_custom_bed )
{
_render_bed ( theta ) ;
_render_axes ( ) ;
}
2018-06-04 10:26:39 +00:00
_render_objects ( ) ;
2018-06-11 09:40:11 +00:00
// textured bed needs to be rendered after objects
if ( ! is_custom_bed )
{
_render_bed ( theta ) ;
_render_axes ( ) ;
}
2018-05-29 11:54:34 +00:00
_render_cutting_plane ( ) ;
_render_warning_texture ( ) ;
_render_legend_texture ( ) ;
_render_layer_editing_overlay ( ) ;
2018-05-29 12:34:45 +00:00
m_canvas - > SwapBuffers ( ) ;
2018-05-21 13:24:52 +00:00
}
2018-05-21 13:57:03 +00:00
void GLCanvas3D : : render_texture ( unsigned int tex_id , float left , float right , float bottom , float top ) const
2018-05-21 12:40:09 +00:00
{
: : glColor4f ( 1.0f , 1.0f , 1.0f , 1.0f ) ;
: : glDisable ( GL_LIGHTING ) ;
: : glEnable ( GL_BLEND ) ;
: : glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
: : glEnable ( GL_TEXTURE_2D ) ;
: : glBindTexture ( GL_TEXTURE_2D , ( GLuint ) tex_id ) ;
: : glBegin ( GL_QUADS ) ;
: : glTexCoord2d ( 0.0f , 1.0f ) ; glVertex3f ( left , bottom , 0.0f ) ;
: : glTexCoord2d ( 1.0f , 1.0f ) ; glVertex3f ( right , bottom , 0.0f ) ;
: : glTexCoord2d ( 1.0f , 0.0f ) ; glVertex3f ( right , top , 0.0f ) ;
: : glTexCoord2d ( 0.0f , 0.0f ) ; glVertex3f ( left , top , 0.0f ) ;
: : glEnd ( ) ;
: : glBindTexture ( GL_TEXTURE_2D , 0 ) ;
: : glDisable ( GL_TEXTURE_2D ) ;
: : glDisable ( GL_BLEND ) ;
: : glEnable ( GL_LIGHTING ) ;
}
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-06-11 13:13:13 +00:00
return m_volumes . load_object ( & model_object , obj_idx , instance_idxs , m_color_by , m_select_by , m_drag_by , m_use_VBOs & & m_initialized ) ;
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-06-08 07:40:00 +00:00
void GLCanvas3D : : reload_scene ( bool force )
{
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 ;
reset_volumes ( ) ;
set_bed_shape ( dynamic_cast < const ConfigOptionPoints * > ( m_config - > option ( " bed_shape " ) ) - > values ) ;
if ( ! m_canvas - > IsShown ( ) & & ! force )
{
m_reload_delayed = true ;
return ;
}
m_reload_delayed = false ;
m_objects_volumes_idxs . clear ( ) ;
for ( unsigned int obj_idx = 0 ; obj_idx < ( unsigned int ) m_model - > objects . size ( ) ; + + obj_idx )
{
m_objects_volumes_idxs . push_back ( load_object ( * m_model , obj_idx ) ) ;
}
update_volumes_selection ( m_objects_selections ) ;
if ( m_config - > has ( " nozzle_diameter " ) )
{
// Should the wipe tower be visualized ?
unsigned int extruders_count = ( unsigned int ) dynamic_cast < const ConfigOptionFloats * > ( m_config - > option ( " nozzle_diameter " ) ) - > values . size ( ) ;
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 ;
if ( ( extruders_count > 1 ) & & semm & & wt & & ! co )
{
// Height of a print (Show at least a slab)
coordf_t height = std : : max ( m_model - > bounding_box ( ) . max . z , 10.0 ) ;
float x = dynamic_cast < 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-11 13:13:13 +00:00
m_volumes . load_wipe_tower_preview ( 1000 , x , y , w , 15.0f * ( float ) ( extruders_count - 1 ) , ( float ) height , a , m_use_VBOs & & m_initialized ) ;
2018-06-08 07:40:00 +00:00
}
}
update_volumes_colors_by_extruder ( ) ;
// 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-06-11 13:13:13 +00:00
bool contained = m_volumes . check_outside_state ( m_config ) ;
2018-06-08 07:40:00 +00:00
if ( ! contained )
{
enable_warning_texture ( true ) ;
_3DScene : : generate_warning_texture ( L ( " Detected object outside print volume " ) ) ;
m_on_enable_action_buttons_callback . call ( false ) ;
}
else
{
enable_warning_texture ( false ) ;
2018-06-11 13:13:13 +00:00
m_volumes . reset_outside_state ( ) ;
2018-06-08 07:40:00 +00:00
_3DScene : : reset_warning_texture ( ) ;
m_on_enable_action_buttons_callback . call ( ! m_model - > objects . empty ( ) ) ;
}
}
else
{
enable_warning_texture ( false ) ;
_3DScene : : reset_warning_texture ( ) ;
}
}
2018-06-05 12:09:36 +00:00
void GLCanvas3D : : load_print_toolpaths ( )
{
2018-06-11 13:13:13 +00:00
if ( m_print = = nullptr )
2018-06-05 12:09:36 +00:00
return ;
if ( ! m_print - > state . is_done ( psSkirt ) | | ! m_print - > state . is_done ( psBrim ) )
return ;
if ( ! m_print - > has_skirt ( ) & & ( m_print - > config . brim_width . value = = 0 ) )
return ;
const float color [ ] = { 0.5f , 1.0f , 0.5f , 1.0f } ; // greenish
// number of skirt layers
size_t total_layer_count = 0 ;
for ( const PrintObject * print_object : m_print - > objects )
{
total_layer_count = std : : max ( total_layer_count , print_object - > total_layer_count ( ) ) ;
}
size_t skirt_height = m_print - > has_infinite_skirt ( ) ? total_layer_count : std : : min < size_t > ( m_print - > config . skirt_height . value , total_layer_count ) ;
if ( ( skirt_height = = 0 ) & & ( m_print - > config . brim_width . value > 0 ) )
skirt_height = 1 ;
// get first skirt_height layers (maybe this should be moved to a PrintObject method?)
const PrintObject * object0 = m_print - > objects . front ( ) ;
std : : vector < float > print_zs ;
print_zs . reserve ( skirt_height * 2 ) ;
for ( size_t i = 0 ; i < std : : min ( skirt_height , object0 - > layers . size ( ) ) ; + + i )
{
print_zs . push_back ( float ( object0 - > layers [ i ] - > print_z ) ) ;
}
//FIXME why there are support layers?
for ( size_t i = 0 ; i < std : : min ( skirt_height , object0 - > support_layers . size ( ) ) ; + + i )
{
print_zs . push_back ( float ( object0 - > support_layers [ i ] - > print_z ) ) ;
}
sort_remove_duplicates ( print_zs ) ;
if ( print_zs . size ( ) > skirt_height )
print_zs . erase ( print_zs . begin ( ) + skirt_height , print_zs . end ( ) ) ;
2018-06-11 13:13:13 +00:00
m_volumes . volumes . emplace_back ( new GLVolume ( color ) ) ;
GLVolume & volume = * m_volumes . volumes . back ( ) ;
2018-06-05 12:09:36 +00:00
for ( size_t i = 0 ; i < skirt_height ; + + i ) {
volume . print_zs . push_back ( print_zs [ i ] ) ;
volume . offsets . push_back ( volume . indexed_vertex_array . quad_indices . size ( ) ) ;
volume . offsets . push_back ( volume . indexed_vertex_array . triangle_indices . size ( ) ) ;
if ( i = = 0 )
_3DScene : : extrusionentity_to_verts ( m_print - > brim , print_zs [ i ] , Point ( 0 , 0 ) , volume ) ;
_3DScene : : extrusionentity_to_verts ( m_print - > skirt , print_zs [ i ] , Point ( 0 , 0 ) , volume ) ;
}
volume . bounding_box = volume . indexed_vertex_array . bounding_box ( ) ;
volume . indexed_vertex_array . finalize_geometry ( m_use_VBOs & & m_initialized ) ;
}
void GLCanvas3D : : load_print_object_toolpaths ( const PrintObject & print_object , const std : : vector < std : : string > & str_tool_colors )
{
std : : vector < float > tool_colors = _parse_colors ( str_tool_colors ) ;
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 ;
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max ( ) { return 131072 ; } // 3.15MB
// static const size_t alloc_size_max () { return 65536; } // 1.57MB
// static const size_t alloc_size_max () { return 32768; } // 786kB
static const size_t alloc_size_reserve ( ) { return alloc_size_max ( ) * 2 ; }
static const float * color_perimeters ( ) { static float color [ 4 ] = { 1.0f , 1.0f , 0.0f , 1.f } ; return color ; } // yellow
static const float * color_infill ( ) { static float color [ 4 ] = { 1.0f , 0.5f , 0.5f , 1.f } ; return color ; } // redish
static const float * color_support ( ) { static float color [ 4 ] = { 0.5f , 1.0f , 0.5f , 1.f } ; return color ; } // greenish
// For cloring by a tool, return a parsed color.
bool color_by_tool ( ) const { return tool_colors ! = nullptr ; }
size_t number_tools ( ) const { return this - > color_by_tool ( ) ? tool_colors - > size ( ) / 4 : 0 ; }
const float * color_tool ( size_t tool ) const { return tool_colors - > data ( ) + tool * 4 ; }
int volume_idx ( int extruder , int feature ) const
{
return this - > color_by_tool ( ) ? std : : min < int > ( this - > number_tools ( ) - 1 , std : : max < int > ( extruder - 1 , 0 ) ) : feature ;
}
} ctxt ;
ctxt . shifted_copies = & print_object . _shifted_copies ;
// order layers by print_z
ctxt . layers . reserve ( print_object . layers . size ( ) + print_object . support_layers . size ( ) ) ;
for ( const Layer * layer : print_object . layers )
ctxt . layers . push_back ( layer ) ;
for ( const Layer * layer : print_object . support_layers )
ctxt . layers . push_back ( layer ) ;
std : : sort ( ctxt . layers . begin ( ) , ctxt . layers . end ( ) , [ ] ( const Layer * l1 , const Layer * l2 ) { return l1 - > print_z < l2 - > print_z ; } ) ;
// Maximum size of an allocation block: 32MB / sizeof(float)
ctxt . has_perimeters = print_object . state . is_done ( posPerimeters ) ;
ctxt . has_infill = print_object . state . is_done ( posInfill ) ;
ctxt . has_support = print_object . state . is_done ( posSupportMaterial ) ;
ctxt . tool_colors = tool_colors . empty ( ) ? nullptr : & tool_colors ;
BOOST_LOG_TRIVIAL ( debug ) < < " Loading print object toolpaths in parallel - start " ;
//FIXME Improve the heuristics for a grain size.
size_t grain_size = std : : max ( ctxt . layers . size ( ) / 16 , size_t ( 1 ) ) ;
tbb : : spin_mutex new_volume_mutex ;
auto new_volume = [ this , & new_volume_mutex ] ( const float * color ) - > GLVolume * {
auto * volume = new GLVolume ( color ) ;
new_volume_mutex . lock ( ) ;
volume - > outside_printer_detection_enabled = false ;
2018-06-11 13:13:13 +00:00
m_volumes . volumes . emplace_back ( volume ) ;
2018-06-05 12:09:36 +00:00
new_volume_mutex . unlock ( ) ;
return volume ;
} ;
2018-06-11 13:13:13 +00:00
const size_t volumes_cnt_initial = m_volumes . volumes . size ( ) ;
2018-06-05 12:09:36 +00:00
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 ) {
std : : vector < GLVolume * > vols ;
if ( ctxt . color_by_tool ( ) ) {
for ( size_t i = 0 ; i < ctxt . number_tools ( ) ; + + i )
vols . emplace_back ( new_volume ( ctxt . color_tool ( i ) ) ) ;
}
else
vols = { new_volume ( ctxt . color_perimeters ( ) ) , new_volume ( ctxt . color_infill ( ) ) , new_volume ( ctxt . color_support ( ) ) } ;
for ( GLVolume * vol : vols )
vol - > indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ( ) ) ;
for ( size_t idx_layer = range . begin ( ) ; idx_layer < range . end ( ) ; + + idx_layer ) {
const Layer * layer = ctxt . layers [ idx_layer ] ;
for ( size_t i = 0 ; i < vols . size ( ) ; + + i ) {
GLVolume & vol = * vols [ i ] ;
if ( vol . print_zs . empty ( ) | | vol . print_zs . back ( ) ! = layer - > print_z ) {
vol . print_zs . push_back ( layer - > print_z ) ;
vol . offsets . push_back ( vol . indexed_vertex_array . quad_indices . size ( ) ) ;
vol . offsets . push_back ( vol . indexed_vertex_array . triangle_indices . size ( ) ) ;
}
}
for ( const Point & copy : * ctxt . shifted_copies ) {
for ( const LayerRegion * layerm : layer - > regions ) {
if ( ctxt . has_perimeters )
_3DScene : : extrusionentity_to_verts ( layerm - > perimeters , float ( layer - > print_z ) , copy ,
* vols [ ctxt . volume_idx ( layerm - > region ( ) - > config . perimeter_extruder . value , 0 ) ] ) ;
if ( ctxt . has_infill ) {
for ( const ExtrusionEntity * ee : layerm - > fills . entities ) {
// fill represents infill extrusions of a single island.
const auto * fill = dynamic_cast < 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 ( ) ) ?
layerm - > region ( ) - > config . solid_infill_extruder :
layerm - > region ( ) - > config . infill_extruder ,
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 ) ?
support_layer - > object ( ) - > config . support_material_extruder :
support_layer - > object ( ) - > config . support_material_interface_extruder ,
2 ) ] ) ;
}
}
}
for ( size_t i = 0 ; i < vols . size ( ) ; + + i ) {
GLVolume & vol = * vols [ i ] ;
if ( vol . indexed_vertex_array . vertices_and_normals_interleaved . size ( ) / 6 > ctxt . alloc_size_max ( ) ) {
// Store the vertex arrays and restart their containers,
vols [ i ] = new_volume ( vol . color ) ;
GLVolume & vol_new = * vols [ i ] ;
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new . indexed_vertex_array = std : : move ( vol . indexed_vertex_array ) ;
// Copy the content back to the old GLVolume.
vol . indexed_vertex_array = vol_new . indexed_vertex_array ;
// Finalize a bounding box of the old GLVolume.
vol . bounding_box = vol . indexed_vertex_array . bounding_box ( ) ;
// Clear the buffers, but keep them pre-allocated.
vol_new . indexed_vertex_array . clear ( ) ;
// Just make sure that clear did not clear the reserved memory.
vol_new . indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ( ) ) ;
}
}
}
for ( GLVolume * vol : vols ) {
vol - > bounding_box = vol - > indexed_vertex_array . bounding_box ( ) ;
vol - > indexed_vertex_array . shrink_to_fit ( ) ;
}
} ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " Loading print object toolpaths in parallel - finalizing results " ;
// Remove empty volumes from the newly added volumes.
2018-06-11 13:13:13 +00:00
m_volumes . volumes . erase (
std : : remove_if ( m_volumes . volumes . begin ( ) + volumes_cnt_initial , m_volumes . volumes . end ( ) ,
2018-06-05 12:09:36 +00:00
[ ] ( const GLVolume * volume ) { return volume - > empty ( ) ; } ) ,
2018-06-11 13:13:13 +00:00
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-06-05 12:09:36 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Loading print object toolpaths in parallel - end " ;
}
void GLCanvas3D : : load_wipe_tower_toolpaths ( const std : : vector < std : : string > & str_tool_colors )
{
2018-06-11 13:13:13 +00:00
if ( ( m_print = = nullptr ) | | m_print - > m_wipe_tower_tool_changes . empty ( ) )
2018-06-05 12:09:36 +00:00
return ;
if ( ! m_print - > state . is_done ( psWipeTower ) )
return ;
std : : vector < float > tool_colors = _parse_colors ( str_tool_colors ) ;
struct Ctxt
{
const Print * print ;
const std : : vector < float > * tool_colors ;
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max ( ) { return 131072 ; } // 3.15MB
static const size_t alloc_size_reserve ( ) { return alloc_size_max ( ) * 2 ; }
static const float * color_support ( ) { static float color [ 4 ] = { 0.5f , 1.0f , 0.5f , 1.f } ; return color ; } // greenish
// For cloring by a tool, return a parsed color.
bool color_by_tool ( ) const { return tool_colors ! = nullptr ; }
size_t number_tools ( ) const { return this - > color_by_tool ( ) ? tool_colors - > size ( ) / 4 : 0 ; }
const float * color_tool ( size_t tool ) const { return tool_colors - > data ( ) + tool * 4 ; }
int volume_idx ( int tool , int feature ) const
{
return this - > color_by_tool ( ) ? std : : min < int > ( this - > number_tools ( ) - 1 , std : : max < int > ( tool , 0 ) ) : feature ;
}
const std : : vector < WipeTower : : ToolChangeResult > & tool_change ( size_t idx ) {
return priming . empty ( ) ?
( ( idx = = print - > m_wipe_tower_tool_changes . size ( ) ) ? final : print - > m_wipe_tower_tool_changes [ idx ] ) :
( ( idx = = 0 ) ? priming : ( idx = = print - > m_wipe_tower_tool_changes . size ( ) + 1 ) ? final : print - > m_wipe_tower_tool_changes [ idx - 1 ] ) ;
}
std : : vector < WipeTower : : ToolChangeResult > priming ;
std : : vector < WipeTower : : ToolChangeResult > final ;
} ctxt ;
ctxt . print = m_print ;
ctxt . tool_colors = tool_colors . empty ( ) ? nullptr : & tool_colors ;
if ( m_print - > m_wipe_tower_priming )
ctxt . priming . emplace_back ( * m_print - > m_wipe_tower_priming . get ( ) ) ;
if ( m_print - > m_wipe_tower_final_purge )
ctxt . final . emplace_back ( * m_print - > m_wipe_tower_final_purge . get ( ) ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " Loading wipe tower toolpaths in parallel - start " ;
//FIXME Improve the heuristics for a grain size.
size_t n_items = m_print - > m_wipe_tower_tool_changes . size ( ) + ( ctxt . priming . empty ( ) ? 0 : 1 ) ;
size_t grain_size = std : : max ( n_items / 128 , size_t ( 1 ) ) ;
tbb : : spin_mutex new_volume_mutex ;
auto new_volume = [ this , & new_volume_mutex ] ( const float * color ) - > GLVolume * {
auto * volume = new GLVolume ( color ) ;
new_volume_mutex . lock ( ) ;
volume - > outside_printer_detection_enabled = false ;
2018-06-11 13:13:13 +00:00
m_volumes . volumes . emplace_back ( volume ) ;
2018-06-05 12:09:36 +00:00
new_volume_mutex . unlock ( ) ;
return volume ;
} ;
2018-06-11 13:13:13 +00:00
const size_t volumes_cnt_initial = m_volumes . volumes . size ( ) ;
2018-06-05 12:09:36 +00:00
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.
std : : vector < GLVolume * > vols ;
if ( ctxt . color_by_tool ( ) ) {
for ( size_t i = 0 ; i < ctxt . number_tools ( ) ; + + i )
vols . emplace_back ( new_volume ( ctxt . color_tool ( i ) ) ) ;
}
else
vols = { new_volume ( ctxt . color_support ( ) ) } ;
for ( GLVolume * volume : vols )
volume - > indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ( ) ) ;
for ( size_t idx_layer = range . begin ( ) ; idx_layer < range . end ( ) ; + + idx_layer ) {
const std : : vector < 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 ;
}
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 ) ;
for ( ; i < j ; + + i ) {
const WipeTower : : Extrusion & e = extrusions . extrusions [ i ] ;
assert ( e . width > 0.f ) ;
const WipeTower : : Extrusion & e_prev = * ( & e - 1 ) ;
lines . emplace_back ( Point : : new_scale ( e_prev . pos . x , e_prev . pos . y ) , Point : : new_scale ( e . pos . x , e . pos . y ) ) ;
widths . emplace_back ( e . width ) ;
}
_3DScene : : thick_lines_to_verts ( lines , widths , heights , lines . front ( ) . a = = lines . back ( ) . b , extrusions . print_z ,
* vols [ ctxt . volume_idx ( e . tool , 0 ) ] ) ;
}
}
}
for ( size_t i = 0 ; i < vols . size ( ) ; + + i ) {
GLVolume & vol = * vols [ i ] ;
if ( vol . indexed_vertex_array . vertices_and_normals_interleaved . size ( ) / 6 > ctxt . alloc_size_max ( ) ) {
// Store the vertex arrays and restart their containers,
vols [ i ] = new_volume ( vol . color ) ;
GLVolume & vol_new = * vols [ i ] ;
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new . indexed_vertex_array = std : : move ( vol . indexed_vertex_array ) ;
// Copy the content back to the old GLVolume.
vol . indexed_vertex_array = vol_new . indexed_vertex_array ;
// Finalize a bounding box of the old GLVolume.
vol . bounding_box = vol . indexed_vertex_array . bounding_box ( ) ;
// Clear the buffers, but keep them pre-allocated.
vol_new . indexed_vertex_array . clear ( ) ;
// Just make sure that clear did not clear the reserved memory.
vol_new . indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ( ) ) ;
}
}
for ( GLVolume * vol : vols ) {
vol - > bounding_box = vol - > indexed_vertex_array . bounding_box ( ) ;
vol - > indexed_vertex_array . shrink_to_fit ( ) ;
}
} ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " Loading wipe tower toolpaths in parallel - finalizing results " ;
// Remove empty volumes from the newly added volumes.
2018-06-11 13:13:13 +00:00
m_volumes . volumes . erase (
std : : remove_if ( m_volumes . volumes . begin ( ) + volumes_cnt_initial , m_volumes . volumes . end ( ) ,
2018-06-05 12:09:36 +00:00
[ ] ( const GLVolume * volume ) { return volume - > empty ( ) ; } ) ,
2018-06-11 13:13:13 +00:00
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-06-05 12:09:36 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Loading wipe tower toolpaths in parallel - end " ;
}
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-06-11 13:13:13 +00:00
if ( ( m_canvas ! = nullptr ) & & ( m_print ! = nullptr ) )
2018-06-05 08:56:55 +00:00
{
2018-06-05 10:24:26 +00:00
// ensures that the proper context is selected
if ( ! set_current ( ) )
2018-06-05 08:56:55 +00:00
return ;
2018-06-11 13:13:13 +00:00
if ( m_volumes . empty ( ) )
2018-06-05 08:56:55 +00:00
{
std : : vector < float > tool_colors = _parse_colors ( str_tool_colors ) ;
m_gcode_preview_volume_index . reset ( ) ;
_load_gcode_extrusion_paths ( preview_data , tool_colors ) ;
_load_gcode_travel_paths ( preview_data , tool_colors ) ;
_load_gcode_retractions ( preview_data ) ;
_load_gcode_unretractions ( preview_data ) ;
2018-06-11 13:13:13 +00:00
if ( m_volumes . empty ( ) )
2018-06-05 08:56:55 +00:00
_3DScene : : reset_legend_texture ( ) ;
else
{
_3DScene : : generate_legend_texture ( preview_data , tool_colors ) ;
// 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 ( ) ) ;
2018-06-05 08:56:55 +00:00
_load_shells ( ) ;
}
}
_update_gcode_volumes_visibility ( preview_data ) ;
}
}
2018-05-15 07:50:01 +00:00
void GLCanvas3D : : register_on_viewport_changed_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_viewport_changed_callback . register_callback ( callback ) ;
}
2018-05-31 11:51:50 +00:00
void GLCanvas3D : : register_on_double_click_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_double_click_callback . register_callback ( callback ) ;
}
void GLCanvas3D : : register_on_right_click_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_right_click_callback . register_callback ( callback ) ;
}
2018-06-08 09:37:07 +00:00
void GLCanvas3D : : register_on_select_object_callback ( void * callback )
2018-05-31 11:51:50 +00:00
{
if ( callback ! = nullptr )
2018-06-08 09:37:07 +00:00
m_on_select_object_callback . register_callback ( callback ) ;
2018-05-31 11:51:50 +00:00
}
2018-05-31 14:04:59 +00:00
void GLCanvas3D : : register_on_model_update_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_model_update_callback . register_callback ( callback ) ;
}
2018-06-07 07:22:19 +00:00
void GLCanvas3D : : register_on_remove_object_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_remove_object_callback . register_callback ( callback ) ;
}
void GLCanvas3D : : register_on_arrange_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_arrange_callback . register_callback ( callback ) ;
}
void GLCanvas3D : : register_on_rotate_object_left_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_rotate_object_left_callback . register_callback ( callback ) ;
}
void GLCanvas3D : : register_on_rotate_object_right_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_rotate_object_right_callback . register_callback ( callback ) ;
}
void GLCanvas3D : : register_on_scale_object_uniformly_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_scale_object_uniformly_callback . register_callback ( callback ) ;
}
void GLCanvas3D : : register_on_increase_objects_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_increase_objects_callback . register_callback ( callback ) ;
}
void GLCanvas3D : : register_on_decrease_objects_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_decrease_objects_callback . register_callback ( callback ) ;
}
2018-06-07 09:18:28 +00:00
void GLCanvas3D : : register_on_instance_moved_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_instance_moved_callback . register_callback ( callback ) ;
}
void GLCanvas3D : : register_on_wipe_tower_moved_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_wipe_tower_moved_callback . register_callback ( callback ) ;
}
void GLCanvas3D : : register_on_enable_action_buttons_callback ( void * callback )
{
if ( callback ! = nullptr )
m_on_enable_action_buttons_callback . register_callback ( callback ) ;
}
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 ) ;
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 ) ;
2018-06-07 07:22:19 +00:00
m_canvas - > Bind ( wxEVT_KEY_DOWN , & GLCanvas3D : : on_key_down , this ) ;
2018-06-06 12:19:28 +00:00
}
}
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 ) ;
m_canvas - > Unbind ( wxEVT_MOUSEWHEEL , & GLCanvas3D : : on_mouse_wheel , this ) ;
m_canvas - > Unbind ( wxEVT_TIMER , & GLCanvas3D : : on_timer , this ) ;
m_canvas - > Unbind ( wxEVT_LEFT_DOWN , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_LEFT_UP , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_MIDDLE_DOWN , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_MIDDLE_UP , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_RIGHT_DOWN , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_RIGHT_UP , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_MOTION , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_ENTER_WINDOW , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_LEAVE_WINDOW , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_LEFT_DCLICK , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_MIDDLE_DCLICK , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_RIGHT_DCLICK , & GLCanvas3D : : on_mouse , this ) ;
m_canvas - > Unbind ( wxEVT_PAINT , & GLCanvas3D : : on_paint , this ) ;
2018-06-07 07:22:19 +00:00
m_canvas - > Unbind ( wxEVT_KEY_DOWN , & GLCanvas3D : : on_key_down , this ) ;
2018-06-06 12:19:28 +00:00
}
}
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 )
{
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 )
{
if ( evt . HasModifiers ( ) )
evt . Skip ( ) ;
else
{
int keyCode = evt . GetKeyCode ( ) ;
switch ( keyCode - 48 )
{
// numerical input
case 0 : { select_view ( " iso " ) ; break ; }
case 1 : { select_view ( " top " ) ; break ; }
case 2 : { select_view ( " bottom " ) ; break ; }
case 3 : { select_view ( " front " ) ; break ; }
case 4 : { select_view ( " rear " ) ; break ; }
case 5 : { select_view ( " left " ) ; break ; }
case 6 : { select_view ( " right " ) ; break ; }
default :
{
// text input
switch ( keyCode )
{
2018-06-07 07:22:19 +00:00
// key +
case 43 : { m_on_increase_objects_callback . call ( ) ; break ; }
// key -
case 45 : { m_on_decrease_objects_callback . call ( ) ; break ; }
// key A/a
case 65 :
case 97 : { m_on_arrange_callback . call ( ) ; break ; }
2018-05-15 14:09:04 +00:00
// key B/b
case 66 :
2018-05-31 11:51:50 +00:00
case 98 : { zoom_to_bed ( ) ; break ; }
2018-06-07 07:22:19 +00:00
// key L/l
case 76 :
case 108 : { m_on_rotate_object_left_callback . call ( ) ; break ; }
// key R/r
case 82 :
case 114 : { m_on_rotate_object_right_callback . call ( ) ; break ; }
// key S/s
case 83 :
case 115 : { m_on_scale_object_uniformly_callback . call ( ) ; break ; }
2018-05-15 14:09:04 +00:00
// key Z/z
case 90 :
case 122 : { zoom_to_volumes ( ) ; break ; }
2018-06-07 07:22:19 +00:00
default :
{
evt . Skip ( ) ;
break ;
}
2018-05-15 14:09:04 +00:00
}
}
}
}
}
2018-05-28 13:23:01 +00:00
void GLCanvas3D : : on_mouse_wheel ( wxMouseEvent & evt )
{
// Ignore the wheel events if the middle button is pressed.
if ( evt . MiddleIsDown ( ) )
return ;
// Performs layers editing updates, if enabled
if ( is_layers_editing_enabled ( ) & & ( m_print ! = nullptr ) )
{
2018-06-01 13:54:41 +00:00
int object_idx_selected = _get_layers_editing_first_selected_object_id ( ( unsigned int ) m_print - > objects . size ( ) ) ;
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-01 13:54:41 +00:00
if ( _bar_rect_contains ( ( 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 ;
}
}
}
// Calculate the zoom delta and apply it to the current zoom factor
float zoom = ( float ) evt . GetWheelRotation ( ) / ( float ) evt . GetWheelDelta ( ) ;
zoom = std : : max ( std : : min ( zoom , 4.0f ) , - 4.0f ) / 10.0f ;
zoom = get_camera_zoom ( ) / ( 1.0f - zoom ) ;
// Don't allow to zoom too far outside the scene.
2018-06-01 13:54:41 +00:00
float zoom_min = _get_zoom_to_bounding_box_factor ( _max_bounding_box ( ) ) ;
2018-05-28 13:23:01 +00:00
if ( zoom_min > 0.0f )
2018-06-11 09:40:11 +00:00
zoom = std : : max ( zoom , zoom_min * 0.8f ) ;
2018-05-28 13:23:01 +00:00
2018-06-01 13:54:41 +00:00
m_camera . zoom = zoom ;
2018-05-28 13:23:01 +00:00
m_on_viewport_changed_callback . call ( ) ;
_refresh_if_shown_on_screen ( ) ;
}
2018-05-30 13:18:45 +00:00
void GLCanvas3D : : on_timer ( wxTimerEvent & evt )
{
2018-06-01 13:54:41 +00:00
if ( m_layers_editing . state ! = LayersEditing : : Editing )
2018-05-30 13:18:45 +00:00
return ;
_perform_layer_editing_action ( ) ;
}
2018-05-31 11:51:50 +00:00
void GLCanvas3D : : on_mouse ( wxMouseEvent & evt )
{
Point pos ( evt . GetX ( ) , evt . GetY ( ) ) ;
2018-06-01 13:54:41 +00:00
int selected_object_idx = ( is_layers_editing_enabled ( ) & & ( m_print ! = nullptr ) ) ? _get_layers_editing_first_selected_object_id ( m_print - > objects . size ( ) ) : - 1 ;
m_layers_editing . last_object_id = selected_object_idx ;
2018-05-31 11:51:50 +00:00
if ( evt . Entering ( ) )
{
2018-06-01 07:18:10 +00:00
# if defined(__WXMSW__) || defined(__linux__)
2018-05-31 11:51:50 +00:00
// On Windows and Linux needs focus in order to catch key events
2018-06-01 13:54:41 +00:00
if ( m_canvas ! = nullptr )
m_canvas - > SetFocus ( ) ;
m_mouse . set_start_position_2D_as_invalid ( ) ;
2018-05-31 11:51:50 +00:00
# endif
}
else if ( evt . LeftDClick ( ) )
m_on_double_click_callback . call ( ) ;
else if ( evt . LeftDown ( ) | | evt . RightDown ( ) )
{
// If user pressed left or right button we first check whether this happened
// on a volume or not.
2018-06-01 13:54:41 +00:00
int volume_idx = m_hover_volume_id ;
m_layers_editing . state = LayersEditing : : Unknown ;
if ( ( selected_object_idx ! = - 1 ) & & _bar_rect_contains ( pos . x , pos . y ) )
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-06-01 13:54:41 +00:00
else if ( ( selected_object_idx ! = - 1 ) & & _reset_rect_contains ( pos . x , pos . y ) )
2018-05-31 11:51:50 +00:00
{
if ( evt . LeftDown ( ) )
{
// A volume is selected and the mouse is inside the reset button.
m_print - > get_object ( selected_object_idx ) - > reset_layer_height_profile ( ) ;
// Index 2 means no editing, just wait for mouse up event.
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
}
}
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.
2018-06-01 13:54:41 +00:00
if ( m_picking_enabled & & ( ( volume_idx ! = - 1 ) | | ! is_layers_editing_enabled ( ) ) )
2018-05-31 11:51:50 +00:00
{
deselect_volumes ( ) ;
select_volume ( volume_idx ) ;
if ( volume_idx ! = - 1 )
{
2018-06-11 13:13:13 +00:00
int group_id = m_volumes . volumes [ volume_idx ] - > select_group_id ;
2018-05-31 11:51:50 +00:00
if ( group_id ! = - 1 )
{
2018-06-11 13:13:13 +00:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-31 11:51:50 +00:00
{
if ( ( vol ! = nullptr ) & & ( vol - > select_group_id = = group_id ) )
vol - > selected = true ;
}
}
}
2018-06-01 13:54:41 +00:00
m_dirty = true ;
2018-05-31 11:51:50 +00:00
}
// propagate event through callback
2018-06-08 09:37:07 +00:00
if ( m_picking_enabled )
_on_select ( volume_idx ) ;
2018-05-31 11:51:50 +00:00
// The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate pos x, y,
// an converts the screen space coordinate to unscaled object space.
Pointf3 pos3d = ( volume_idx = = - 1 ) ? Pointf3 ( DBL_MAX , DBL_MAX ) : _mouse_to_3d ( pos ) ;
if ( volume_idx ! = - 1 )
{
2018-06-01 13:54:41 +00:00
if ( evt . LeftDown ( ) & & m_moving_enabled )
2018-05-31 11:51:50 +00:00
{
// Only accept the initial position, if it is inside the volume bounding box.
2018-06-11 13:13:13 +00:00
BoundingBoxf3 volume_bbox = m_volumes . volumes [ volume_idx ] - > transformed_bounding_box ( ) ;
2018-05-31 11:51:50 +00:00
volume_bbox . offset ( 1.0 ) ;
if ( volume_bbox . contains ( pos3d ) )
{
// The dragging operation is initiated.
2018-06-01 13:54:41 +00:00
m_mouse . drag . volume_idx = volume_idx ;
m_mouse . drag . start_position_3D = pos3d ;
2018-05-31 11:51:50 +00:00
// Remember the shift to to the object center.The object center will later be used
// to limit the object placement close to the bed.
2018-06-01 13:54:41 +00:00
m_mouse . drag . volume_center_offset = pos3d . vector_to ( volume_bbox . center ( ) ) ;
2018-05-31 11:51:50 +00:00
}
}
else if ( evt . RightDown ( ) )
{
// if right clicking on volume, propagate event through callback
2018-06-11 13:13:13 +00:00
if ( m_volumes . volumes [ volume_idx ] - > hover )
2018-05-31 11:51:50 +00:00
m_on_right_click_callback . call ( pos . x , pos . y ) ;
}
}
}
}
2018-06-01 13:54:41 +00:00
else if ( evt . Dragging ( ) & & evt . LeftIsDown ( ) & & ( m_layers_editing . state = = LayersEditing : : Unknown ) & & ( m_mouse . drag . volume_idx ! = - 1 ) )
2018-05-31 14:04:59 +00:00
{
2018-06-01 13:54:41 +00:00
m_mouse . dragging = true ;
2018-05-31 14:04:59 +00:00
// Get new position at the same Z of the initial click point.
float z0 = 0.0f ;
float z1 = 1.0f ;
2018-06-01 13:54:41 +00:00
Pointf3 cur_pos = Linef3 ( _mouse_to_3d ( pos , & z0 ) , _mouse_to_3d ( pos , & z1 ) ) . intersect_plane ( m_mouse . drag . start_position_3D . z ) ;
2018-05-31 14:04:59 +00:00
// Clip the new position, so the object center remains close to the bed.
2018-06-01 13:54:41 +00:00
cur_pos . translate ( m_mouse . drag . volume_center_offset ) ;
2018-05-31 14:04:59 +00:00
Point cur_pos2 ( scale_ ( cur_pos . x ) , scale_ ( cur_pos . y ) ) ;
if ( ! m_bed . contains ( cur_pos2 ) )
{
Point ip = m_bed . point_projection ( cur_pos2 ) ;
cur_pos . x = unscale ( ip . x ) ;
cur_pos . y = unscale ( ip . y ) ;
}
2018-06-01 13:54:41 +00:00
cur_pos . translate ( m_mouse . drag . volume_center_offset . negative ( ) ) ;
2018-05-31 14:04:59 +00:00
// Calculate the translation vector.
2018-06-01 13:54:41 +00:00
Vectorf3 vector = m_mouse . drag . start_position_3D . vector_to ( cur_pos ) ;
2018-05-31 14:04:59 +00:00
// Get the volume being dragged.
2018-06-11 13:13:13 +00:00
GLVolume * volume = m_volumes . volumes [ m_mouse . drag . volume_idx ] ;
2018-05-31 14:04:59 +00:00
// Get all volumes belonging to the same group, if any.
std : : vector < GLVolume * > volumes ;
if ( volume - > drag_group_id = = - 1 )
volumes . push_back ( volume ) ;
else
{
2018-06-11 13:13:13 +00:00
for ( GLVolume * v : m_volumes . volumes )
2018-05-31 14:04:59 +00:00
{
if ( ( v ! = nullptr ) & & ( v - > drag_group_id = = volume - > drag_group_id ) )
volumes . push_back ( v ) ;
}
}
// Apply new temporary volume origin and ignore Z.
for ( GLVolume * v : volumes )
{
v - > origin . translate ( vector . x , vector . y , 0.0 ) ;
}
2018-06-01 13:54:41 +00:00
m_mouse . drag . start_position_3D = cur_pos ;
m_dirty = true ;
2018-05-31 14:04:59 +00:00
}
else if ( evt . Dragging ( ) )
{
2018-06-01 13:54:41 +00:00
m_mouse . dragging = true ;
if ( ( m_layers_editing . state ! = LayersEditing : : Unknown ) & & ( selected_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
}
2018-06-01 07:00:30 +00:00
else if ( evt . LeftIsDown ( ) )
{
// if dragging over blank area with left button, rotate
2018-06-01 13:54:41 +00:00
if ( m_mouse . is_start_position_3D_defined ( ) )
2018-06-01 07:00:30 +00:00
{
2018-06-01 13:54:41 +00:00
const Pointf3 & orig = m_mouse . drag . start_position_3D ;
m_camera . phi + = ( ( ( float ) pos . x - ( float ) orig . x ) * TRACKBALLSIZE ) ;
m_camera . set_theta ( m_camera . get_theta ( ) - ( ( float ) pos . y - ( float ) orig . y ) * TRACKBALLSIZE ) ;
2018-06-01 07:00:30 +00:00
m_on_viewport_changed_callback . call ( ) ;
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_3D = Pointf3 ( ( coordf_t ) pos . x , ( coordf_t ) pos . y , 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 ;
const Pointf3 & cur_pos = _mouse_to_3d ( pos , & z ) ;
2018-06-01 13:54:41 +00:00
Pointf3 orig = _mouse_to_3d ( m_mouse . drag . start_position_2D , & z ) ;
Pointf3 camera_target = m_camera . target ;
2018-06-01 07:00:30 +00:00
camera_target . translate ( orig . vector_to ( cur_pos ) . negative ( ) ) ;
2018-06-01 13:54:41 +00:00
m_camera . target = camera_target ;
2018-06-01 07:00:30 +00:00
m_on_viewport_changed_callback . call ( ) ;
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 ( ) ;
2018-05-31 14:04:59 +00:00
if ( selected_object_idx ! = - 1 )
m_on_model_update_callback . call ( ) ;
}
2018-06-01 13:54:41 +00:00
else if ( ( m_mouse . drag . volume_idx ! = - 1 ) & & m_mouse . dragging )
2018-05-31 14:04:59 +00:00
{
// get all volumes belonging to the same group, if any
std : : vector < int > volume_idxs ;
2018-06-01 13:54:41 +00:00
int vol_id = m_mouse . drag . volume_idx ;
2018-06-11 13:13:13 +00:00
int group_id = m_volumes . volumes [ vol_id ] - > drag_group_id ;
2018-05-31 14:04:59 +00:00
if ( group_id = = - 1 )
volume_idxs . push_back ( vol_id ) ;
else
{
2018-06-11 13:13:13 +00:00
for ( int i = 0 ; i < m_volumes . volumes . size ( ) ; + + i )
2018-05-31 14:04:59 +00:00
{
2018-06-11 13:13:13 +00:00
if ( m_volumes . volumes [ i ] - > drag_group_id = = group_id )
2018-05-31 14:04:59 +00:00
volume_idxs . push_back ( i ) ;
}
}
2018-06-07 09:18:28 +00:00
_on_move ( volume_idxs ) ;
2018-05-31 14:04:59 +00:00
}
2018-06-01 13:54:41 +00:00
m_mouse . drag . volume_idx = - 1 ;
m_mouse . set_start_position_3D_as_invalid ( ) ;
m_mouse . set_start_position_2D_as_invalid ( ) ;
m_mouse . dragging = false ;
2018-05-31 14:04:59 +00:00
}
2018-05-31 11:51:50 +00:00
else if ( evt . Moving ( ) )
{
2018-06-01 13:54:41 +00:00
m_mouse . position = Pointf ( ( coordf_t ) pos . x , ( coordf_t ) pos . y ) ;
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 ( ) ;
}
2018-06-04 10:26:39 +00:00
void GLCanvas3D : : on_paint ( wxPaintEvent & evt )
{
render ( ) ;
}
2018-06-07 07:22:19 +00:00
void GLCanvas3D : : on_key_down ( wxKeyEvent & evt )
{
if ( evt . HasModifiers ( ) )
evt . Skip ( ) ;
else
{
int key = evt . GetKeyCode ( ) ;
if ( key = = WXK_DELETE )
m_on_remove_object_callback . call ( ) ;
else
evt . Skip ( ) ;
}
}
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 ) ;
return Size ( w , h ) ;
}
2018-05-25 13:56:14 +00:00
Point GLCanvas3D : : get_local_mouse_position ( ) const
{
if ( m_canvas = = nullptr )
return Point ( ) ;
wxPoint mouse_pos = m_canvas - > ScreenToClient ( wxGetMousePosition ( ) ) ;
2018-05-28 11:43:29 +00:00
return Point ( mouse_pos . x , mouse_pos . y ) ;
2018-05-25 13:56:14 +00:00
}
2018-06-05 08:56:55 +00:00
void GLCanvas3D : : _force_zoom_to_bed ( )
2018-06-04 12:38:41 +00:00
{
2018-06-04 10:26:39 +00:00
zoom_to_bed ( ) ;
2018-06-05 08:56:55 +00:00
m_force_zoom_to_bed_enabled = false ;
2018-06-04 10:26:39 +00:00
}
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-01 13:54:41 +00:00
if ( m_context = = nullptr )
return ;
2018-05-30 13:18:45 +00:00
2018-06-01 13:54:41 +00:00
set_current ( ) ;
: : glViewport ( 0 , 0 , w , h ) ;
: : glMatrixMode ( GL_PROJECTION ) ;
: : glLoadIdentity ( ) ;
const BoundingBoxf3 & bbox = _max_bounding_box ( ) ;
switch ( m_camera . type )
{
case Camera : : Ortho :
{
float w2 = w ;
float h2 = h ;
float two_zoom = 2.0f * get_camera_zoom ( ) ;
if ( two_zoom ! = 0.0f )
{
float inv_two_zoom = 1.0f / two_zoom ;
w2 * = inv_two_zoom ;
h2 * = inv_two_zoom ;
}
// FIXME: calculate a tighter value for depth will improve z-fighting
float depth = 5.0f * ( float ) bbox . max_size ( ) ;
: : glOrtho ( - w2 , w2 , - h2 , h2 , - depth , depth ) ;
break ;
}
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 ;
}
}
: : glMatrixMode ( GL_MODELVIEW ) ;
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
{
2018-06-01 13:54:41 +00:00
BoundingBoxf3 bb = m_bed . get_bounding_box ( ) ;
bb . merge ( volumes_bounding_box ( ) ) ;
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 )
{
2018-06-01 13:54:41 +00:00
m_camera . zoom = zoom ;
2018-05-15 07:50:01 +00:00
// center view around bounding box center
2018-06-01 13:54:41 +00:00
m_camera . target = bbox . center ( ) ;
2018-05-15 07:50:01 +00:00
m_on_viewport_changed_callback . call ( ) ;
2018-05-28 13:23:01 +00:00
_refresh_if_shown_on_screen ( ) ;
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())
_camera_tranform ( ) ;
2018-05-15 07:50:01 +00:00
// get the view matrix back from opengl
GLfloat matrix [ 16 ] ;
: : glGetFloatv ( GL_MODELVIEW_MATRIX , matrix ) ;
// camera axes
Pointf3 right ( ( coordf_t ) matrix [ 0 ] , ( coordf_t ) matrix [ 4 ] , ( coordf_t ) matrix [ 8 ] ) ;
Pointf3 up ( ( coordf_t ) matrix [ 1 ] , ( coordf_t ) matrix [ 5 ] , ( coordf_t ) matrix [ 9 ] ) ;
Pointf3 forward ( ( coordf_t ) matrix [ 2 ] , ( coordf_t ) matrix [ 6 ] , ( coordf_t ) matrix [ 10 ] ) ;
Pointf3 bb_min = bbox . min ;
Pointf3 bb_max = bbox . max ;
Pointf3 bb_center = bbox . center ( ) ;
// bbox vertices in world space
std : : vector < Pointf3 > vertices ;
vertices . reserve ( 8 ) ;
vertices . push_back ( bb_min ) ;
vertices . emplace_back ( bb_max . x , bb_min . y , bb_min . z ) ;
vertices . emplace_back ( bb_max . x , bb_max . y , bb_min . z ) ;
vertices . emplace_back ( bb_min . x , bb_max . y , bb_min . z ) ;
vertices . emplace_back ( bb_min . x , bb_min . y , bb_max . z ) ;
vertices . emplace_back ( bb_max . x , bb_min . y , bb_max . z ) ;
vertices . push_back ( bb_max ) ;
vertices . emplace_back ( bb_min . x , bb_max . y , bb_max . z ) ;
coordf_t max_x = 0.0 ;
coordf_t max_y = 0.0 ;
// margin factor to give some empty space around the bbox
coordf_t margin_factor = 1.25 ;
for ( const Pointf3 v : vertices )
{
// project vertex on the plane perpendicular to camera forward axis
Pointf3 pos ( v . x - bb_center . x , v . y - bb_center . y , v . z - bb_center . z ) ;
Pointf3 proj_on_plane = pos - dot ( pos , forward ) * forward ;
// calculates vertex coordinate along camera xy axes
coordf_t x_on_plane = dot ( proj_on_plane , right ) ;
coordf_t y_on_plane = dot ( proj_on_plane , up ) ;
max_x = std : : max ( max_x , margin_factor * std : : abs ( x_on_plane ) ) ;
max_y = std : : max ( max_y , margin_factor * std : : abs ( y_on_plane ) ) ;
}
if ( ( max_x = = 0.0 ) | | ( max_y = = 0.0 ) )
return - 1.0f ;
max_x * = 2.0 ;
max_y * = 2.0 ;
2018-05-24 11:46:17 +00:00
const Size & cnv_size = get_canvas_size ( ) ;
return ( float ) std : : min ( ( coordf_t ) cnv_size . get_width ( ) / max_x , ( coordf_t ) cnv_size . get_height ( ) / max_y ) ;
2018-05-15 07:50:01 +00:00
}
void GLCanvas3D : : _deregister_callbacks ( )
{
m_on_viewport_changed_callback . deregister_callback ( ) ;
2018-05-31 11:51:50 +00:00
m_on_double_click_callback . deregister_callback ( ) ;
m_on_right_click_callback . deregister_callback ( ) ;
2018-06-08 09:37:07 +00:00
m_on_select_object_callback . deregister_callback ( ) ;
2018-05-31 14:04:59 +00:00
m_on_model_update_callback . deregister_callback ( ) ;
2018-06-07 07:22:19 +00:00
m_on_remove_object_callback . deregister_callback ( ) ;
m_on_arrange_callback . deregister_callback ( ) ;
m_on_rotate_object_left_callback . deregister_callback ( ) ;
m_on_rotate_object_right_callback . deregister_callback ( ) ;
m_on_scale_object_uniformly_callback . deregister_callback ( ) ;
m_on_increase_objects_callback . deregister_callback ( ) ;
m_on_decrease_objects_callback . deregister_callback ( ) ;
2018-06-07 09:18:28 +00:00
m_on_instance_moved_callback . deregister_callback ( ) ;
m_on_wipe_tower_moved_callback . deregister_callback ( ) ;
m_on_enable_action_buttons_callback . deregister_callback ( ) ;
2018-05-29 12:34:45 +00:00
}
void GLCanvas3D : : _mark_volumes_for_layer_height ( ) const
{
2018-06-11 13:13:13 +00:00
if ( m_print = = nullptr )
2018-05-29 12:34:45 +00:00
return ;
2018-06-11 13:13:13 +00:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-29 12:34:45 +00:00
{
int object_id = int ( vol - > select_group_id / 1000000 ) ;
2018-06-01 13:54:41 +00:00
int shader_id = m_layers_editing . get_shader_program_id ( ) ;
2018-05-29 12:34:45 +00:00
2018-06-01 13:54:41 +00:00
if ( is_layers_editing_enabled ( ) & & ( shader_id ! = - 1 ) & & vol - > selected & &
2018-05-29 12:34:45 +00:00
vol - > has_layer_height_texture ( ) & & ( object_id < ( int ) m_print - > objects . size ( ) ) )
{
2018-06-01 13:54:41 +00:00
vol - > set_layer_height_texture_data ( m_layers_editing . get_z_texture_id ( ) , shader_id ,
m_print - > get_object ( object_id ) , _get_layers_editing_cursor_z_relative ( ) , m_layers_editing . band_width ) ;
2018-05-29 12:34:45 +00:00
}
else
vol - > reset_layer_height_texture_data ( ) ;
}
2018-05-15 07:50:01 +00:00
}
2018-05-28 13:23:01 +00:00
void GLCanvas3D : : _refresh_if_shown_on_screen ( )
{
if ( is_shown_on_screen ( ) )
{
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-05-28 13:23:01 +00:00
if ( m_canvas ! = nullptr )
m_canvas - > Refresh ( ) ;
}
}
2018-05-31 06:44:39 +00:00
void GLCanvas3D : : _camera_tranform ( ) const
{
: : glMatrixMode ( GL_MODELVIEW ) ;
: : glLoadIdentity ( ) ;
2018-06-01 13:54:41 +00:00
: : glRotatef ( - m_camera . get_theta ( ) , 1.0f , 0.0f , 0.0f ) ; //<2F> pitch
: : glRotatef ( m_camera . phi , 0.0f , 0.0f , 1.0f ) ; // yaw
2018-05-31 06:44:39 +00:00
2018-06-01 13:54:41 +00:00
Pointf3 neg_target = m_camera . target . negative ( ) ;
2018-05-31 06:44:39 +00:00
: : glTranslatef ( ( GLfloat ) neg_target . x , ( GLfloat ) neg_target . y , ( GLfloat ) neg_target . z ) ;
}
2018-05-29 11:54:34 +00:00
void GLCanvas3D : : _picking_pass ( ) const
{
2018-06-01 13:54:41 +00:00
const Pointf & pos = m_mouse . position ;
2018-06-11 13:13:13 +00:00
if ( m_picking_enabled & & ! m_mouse . dragging & & ( pos ! = Pointf ( DBL_MAX , DBL_MAX ) ) )
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 )
2018-05-29 11:54:34 +00:00
: : glDisable ( GL_MULTISAMPLE ) ;
: : glDisable ( GL_LIGHTING ) ;
: : glDisable ( GL_BLEND ) ;
: : glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
: : glPushAttrib ( GL_ENABLE_BIT ) ;
2018-06-01 13:54:41 +00:00
_render_volumes ( true ) ;
2018-05-29 11:54:34 +00:00
: : glPopAttrib ( ) ;
2018-06-01 13:54:41 +00:00
if ( m_multisample_allowed )
2018-05-29 11:54:34 +00:00
: : glEnable ( GL_MULTISAMPLE ) ;
const Size & cnv_size = get_canvas_size ( ) ;
GLubyte color [ 4 ] ;
: : glReadPixels ( pos . x , cnv_size . get_height ( ) - pos . y , 1 , 1 , GL_RGBA , GL_UNSIGNED_BYTE , ( void * ) color ) ;
int volume_id = color [ 0 ] + color [ 1 ] * 256 + color [ 2 ] * 256 * 256 ;
m_hover_volume_id = - 1 ;
2018-06-11 13:13:13 +00:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-29 11:54:34 +00:00
{
vol - > hover = false ;
}
2018-06-11 13:13:13 +00:00
if ( volume_id < m_volumes . volumes . size ( ) )
2018-05-29 11:54:34 +00:00
{
m_hover_volume_id = volume_id ;
2018-06-11 13:13:13 +00:00
m_volumes . volumes [ volume_id ] - > hover = true ;
int group_id = m_volumes . volumes [ volume_id ] - > select_group_id ;
2018-05-29 11:54:34 +00:00
if ( group_id ! = - 1 )
{
2018-06-11 13:13:13 +00:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-29 11:54:34 +00:00
{
if ( vol - > select_group_id = = group_id )
vol - > hover = true ;
}
}
}
}
}
void GLCanvas3D : : _render_background ( ) const
{
: : glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
static const float COLOR [ 3 ] = { 10.0f / 255.0f , 98.0f / 255.0f , 144.0f / 255.0f } ;
: : glDisable ( GL_LIGHTING ) ;
: : glPushMatrix ( ) ;
: : glLoadIdentity ( ) ;
: : glMatrixMode ( GL_PROJECTION ) ;
: : glPushMatrix ( ) ;
: : glLoadIdentity ( ) ;
// Draws a bluish bottom to top gradient over the complete screen.
: : glDisable ( GL_DEPTH_TEST ) ;
: : glBegin ( GL_QUADS ) ;
: : glColor3f ( 0.0f , 0.0f , 0.0f ) ;
: : glVertex3f ( - 1.0f , - 1.0f , 1.0f ) ;
: : glVertex3f ( 1.0f , - 1.0f , 1.0f ) ;
: : glColor3f ( COLOR [ 0 ] , COLOR [ 1 ] , COLOR [ 2 ] ) ;
: : glVertex3f ( 1.0f , 1.0f , 1.0f ) ;
: : glVertex3f ( - 1.0f , 1.0f , 1.0f ) ;
: : glEnd ( ) ;
: : glEnable ( GL_DEPTH_TEST ) ;
: : glPopMatrix ( ) ;
: : glMatrixMode ( GL_MODELVIEW ) ;
: : glPopMatrix ( ) ;
}
2018-06-11 08:46:32 +00:00
void GLCanvas3D : : _render_bed ( float theta ) const
2018-05-29 11:54:34 +00:00
{
2018-06-11 08:46:32 +00:00
m_bed . render ( theta ) ;
2018-05-29 11:54:34 +00:00
}
void GLCanvas3D : : _render_axes ( ) const
{
m_axes . render ( ) ;
}
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 ;
: : glEnable ( GL_LIGHTING ) ;
if ( ! m_shader_enabled )
2018-06-01 13:54:41 +00:00
_render_volumes ( false ) ;
2018-06-04 10:26:39 +00:00
else 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
{
2018-05-29 12:34:45 +00:00
_mark_volumes_for_layer_height ( ) ;
2018-05-29 11:54:34 +00:00
if ( m_config ! = nullptr )
{
2018-06-01 13:54:41 +00:00
const BoundingBoxf3 & bed_bb = m_bed . get_bounding_box ( ) ;
2018-06-11 13:13:13 +00:00
m_volumes . set_print_box ( ( float ) bed_bb . min . x , ( float ) bed_bb . min . y , 0.0f , ( float ) bed_bb . max . x , ( float ) bed_bb . max . y , ( float ) m_config - > opt_float ( " max_print_height " ) ) ;
m_volumes . check_outside_state ( m_config ) ;
2018-05-29 11:54:34 +00:00
}
// do not cull backfaces to show broken geometry, if any
: : glDisable ( GL_CULL_FACE ) ;
}
2018-06-01 13:54:41 +00:00
m_shader . start_using ( ) ;
2018-06-11 13:13:13 +00:00
m_volumes . render_VBOs ( ) ;
2018-06-01 13:54:41 +00:00
m_shader . stop_using ( ) ;
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
: : glEnable ( GL_CULL_FACE ) ;
}
else
{
// do not cull backfaces to show broken geometry, if any
2018-06-01 13:54:41 +00:00
if ( m_picking_enabled )
2018-05-29 11:54:34 +00:00
: : glDisable ( GL_CULL_FACE ) ;
2018-06-11 13:13:13 +00:00
m_volumes . render_legacy ( ) ;
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
: : glEnable ( GL_CULL_FACE ) ;
}
}
void GLCanvas3D : : _render_cutting_plane ( ) const
{
m_cutting_plane . render ( volumes_bounding_box ( ) ) ;
}
void GLCanvas3D : : _render_warning_texture ( ) const
{
if ( ! m_warning_texture_enabled )
return ;
// If the warning texture has not been loaded into the GPU, do it now.
unsigned int tex_id = _3DScene : : finalize_warning_texture ( ) ;
if ( tex_id > 0 )
{
unsigned int w = _3DScene : : get_warning_texture_width ( ) ;
unsigned int h = _3DScene : : get_warning_texture_height ( ) ;
if ( ( w > 0 ) & & ( h > 0 ) )
{
: : glDisable ( GL_DEPTH_TEST ) ;
: : glPushMatrix ( ) ;
: : glLoadIdentity ( ) ;
const Size & cnv_size = get_canvas_size ( ) ;
float zoom = get_camera_zoom ( ) ;
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
float l = ( - 0.5f * ( float ) w ) * inv_zoom ;
float t = ( - 0.5f * ( float ) cnv_size . get_height ( ) + ( float ) h ) * inv_zoom ;
float r = l + ( float ) w * inv_zoom ;
float b = t - ( float ) h * inv_zoom ;
render_texture ( tex_id , l , r , b , t ) ;
: : glPopMatrix ( ) ;
: : glEnable ( GL_DEPTH_TEST ) ;
}
}
}
void GLCanvas3D : : _render_legend_texture ( ) const
{
if ( ! m_legend_texture_enabled )
return ;
// If the legend texture has not been loaded into the GPU, do it now.
unsigned int tex_id = _3DScene : : finalize_legend_texture ( ) ;
if ( tex_id > 0 )
{
unsigned int w = _3DScene : : get_legend_texture_width ( ) ;
unsigned int h = _3DScene : : get_legend_texture_height ( ) ;
if ( ( w > 0 ) & & ( h > 0 ) )
{
: : glDisable ( GL_DEPTH_TEST ) ;
: : glPushMatrix ( ) ;
: : glLoadIdentity ( ) ;
const Size & cnv_size = get_canvas_size ( ) ;
float zoom = get_camera_zoom ( ) ;
float inv_zoom = ( zoom ! = 0.0f ) ? 1.0f / zoom : 0.0f ;
float l = ( - 0.5f * ( float ) cnv_size . get_width ( ) ) * inv_zoom ;
float t = ( 0.5f * ( float ) cnv_size . get_height ( ) ) * inv_zoom ;
float r = l + ( float ) w * inv_zoom ;
float b = t - ( float ) h * inv_zoom ;
render_texture ( tex_id , l , r , b , t ) ;
: : glPopMatrix ( ) ;
: : glEnable ( GL_DEPTH_TEST ) ;
}
}
}
void GLCanvas3D : : _render_layer_editing_overlay ( ) const
{
2018-06-11 13:13:13 +00:00
if ( m_print = = nullptr )
2018-05-29 11:54:34 +00:00
return ;
GLVolume * volume = nullptr ;
2018-06-11 13:13:13 +00:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-29 11:54:34 +00:00
{
if ( ( vol ! = nullptr ) & & vol - > selected & & vol - > has_layer_height_texture ( ) )
{
volume = vol ;
break ;
}
}
if ( volume = = nullptr )
return ;
// If the active object was not allocated at the Print, go away.This should only be a momentary case between an object addition / deletion
// and an update by Platter::async_apply_config.
int object_idx = int ( volume - > select_group_id / 1000000 ) ;
if ( ( int ) m_print - > objects . size ( ) < object_idx )
return ;
const PrintObject * print_object = m_print - > get_object ( object_idx ) ;
if ( print_object = = nullptr )
return ;
m_layers_editing . render ( * this , * print_object , * volume ) ;
}
2018-06-01 13:54:41 +00:00
void GLCanvas3D : : _render_volumes ( bool fake_colors ) const
{
static const float INV_255 = 1.0f / 255.0f ;
if ( fake_colors )
: : glDisable ( GL_LIGHTING ) ;
else
: : glEnable ( GL_LIGHTING ) ;
// do not cull backfaces to show broken geometry, if any
: : glDisable ( GL_CULL_FACE ) ;
: : glEnable ( GL_BLEND ) ;
: : glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
: : glEnableClientState ( GL_VERTEX_ARRAY ) ;
: : glEnableClientState ( GL_NORMAL_ARRAY ) ;
unsigned int volume_id = 0 ;
2018-06-11 13:13:13 +00:00
for ( GLVolume * vol : m_volumes . volumes )
2018-06-01 13:54:41 +00:00
{
if ( fake_colors )
{
// Object picking mode. Render the object with a color encoding the object index.
unsigned int r = ( volume_id & 0x000000FF ) > > 0 ;
unsigned int g = ( volume_id & 0x0000FF00 ) > > 8 ;
unsigned int b = ( volume_id & 0x00FF0000 ) > > 16 ;
: : glColor4f ( ( float ) r * INV_255 , ( float ) g * INV_255 , ( float ) b * INV_255 , 1.0f ) ;
}
else
{
vol - > set_render_color ( ) ;
: : glColor4f ( vol - > render_color [ 0 ] , vol - > render_color [ 1 ] , vol - > render_color [ 2 ] , vol - > render_color [ 3 ] ) ;
}
vol - > render ( ) ;
+ + volume_id ;
}
: : glDisableClientState ( GL_NORMAL_ARRAY ) ;
: : glDisableClientState ( GL_VERTEX_ARRAY ) ;
: : glDisable ( GL_BLEND ) ;
: : glEnable ( GL_CULL_FACE ) ;
}
float GLCanvas3D : : _get_layers_editing_cursor_z_relative ( ) const
{
return m_layers_editing . get_cursor_z_relative ( * this ) ;
}
int GLCanvas3D : : _get_layers_editing_first_selected_object_id ( unsigned int objects_count ) const
{
2018-06-11 13:13:13 +00:00
return m_layers_editing . get_first_selected_object_id ( m_volumes , objects_count ) ;
2018-06-01 13:54:41 +00:00
}
2018-05-30 13:18:45 +00:00
void GLCanvas3D : : _perform_layer_editing_action ( wxMouseEvent * evt )
{
2018-06-01 13:54:41 +00:00
int object_idx_selected = m_layers_editing . last_object_id ;
2018-05-30 13:18:45 +00:00
if ( object_idx_selected = = - 1 )
return ;
2018-06-11 13:13:13 +00:00
if ( m_print = = nullptr )
2018-05-30 13:18:45 +00:00
return ;
PrintObject * selected_obj = m_print - > get_object ( object_idx_selected ) ;
if ( selected_obj = = nullptr )
return ;
// A volume is selected. Test, whether hovering over a layer thickness bar.
if ( evt ! = nullptr )
{
const Rect & rect = LayersEditing : : get_bar_rect_screen ( * this ) ;
float b = rect . get_bottom ( ) ;
2018-06-01 13:54:41 +00:00
m_layers_editing . last_z = unscale ( selected_obj - > size . z ) * ( b - evt - > GetY ( ) - 1.0f ) / ( b - rect . get_top ( ) ) ;
m_layers_editing . last_action = evt - > ShiftDown ( ) ? ( evt - > RightIsDown ( ) ? 3 : 2 ) : ( evt - > RightIsDown ( ) ? 0 : 1 ) ;
2018-05-30 13:18:45 +00:00
}
// Mark the volume as modified, so Print will pick its layer height profile ? Where to mark it ?
// Start a timer to refresh the print ? schedule_background_process() ?
// The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself.
2018-06-01 13:54:41 +00:00
selected_obj - > adjust_layer_height_profile ( m_layers_editing . last_z , m_layers_editing . strength , m_layers_editing . band_width , m_layers_editing . last_action ) ;
2018-05-30 13:18:45 +00:00
// searches the id of the first volume of the selected object
int volume_idx = 0 ;
for ( int i = 0 ; i < object_idx_selected ; + + i )
{
PrintObject * obj = m_print - > get_object ( i ) ;
if ( obj ! = nullptr )
{
for ( int j = 0 ; j < ( int ) obj - > region_volumes . size ( ) ; + + j )
{
volume_idx + = ( int ) obj - > region_volumes [ j ] . size ( ) ;
}
}
}
2018-06-11 13:13:13 +00:00
m_volumes . volumes [ volume_idx ] - > generate_layer_height_texture ( selected_obj , 1 ) ;
2018-05-30 13:18:45 +00:00
_refresh_if_shown_on_screen ( ) ;
// Automatic action on mouse down with the same coordinate.
2018-06-01 13:54:41 +00:00
_start_timer ( ) ;
}
bool GLCanvas3D : : _bar_rect_contains ( float x , float y ) const
{
return m_layers_editing . bar_rect_contains ( * this , x , y ) ;
}
bool GLCanvas3D : : _reset_rect_contains ( float x , float y ) const
{
return m_layers_editing . reset_rect_contains ( * this , x , y ) ;
2018-05-30 13:18:45 +00:00
}
2018-05-31 11:51:50 +00:00
Pointf3 GLCanvas3D : : _mouse_to_3d ( const Point & mouse_pos , float * z )
{
if ( ! set_current ( ) )
return Pointf3 ( DBL_MAX , DBL_MAX , DBL_MAX ) ;
GLint viewport [ 4 ] ;
: : glGetIntegerv ( GL_VIEWPORT , viewport ) ;
GLdouble modelview_matrix [ 16 ] ;
: : glGetDoublev ( GL_MODELVIEW_MATRIX , modelview_matrix ) ;
GLdouble projection_matrix [ 16 ] ;
: : glGetDoublev ( GL_PROJECTION_MATRIX , projection_matrix ) ;
GLint y = viewport [ 3 ] - ( GLint ) mouse_pos . y ;
GLfloat mouse_z ;
if ( z = = nullptr )
2018-05-31 14:04:59 +00:00
: : glReadPixels ( ( GLint ) mouse_pos . x , y , 1 , 1 , GL_DEPTH_COMPONENT , GL_FLOAT , ( void * ) & mouse_z ) ;
2018-05-31 11:51:50 +00:00
else
mouse_z = * z ;
GLdouble out_x , out_y , out_z ;
2018-05-31 14:04:59 +00:00
: : gluUnProject ( ( GLdouble ) mouse_pos . x , ( GLdouble ) y , mouse_z , modelview_matrix , projection_matrix , viewport , & out_x , & out_y , & out_z ) ;
2018-05-31 11:51:50 +00:00
return Pointf3 ( ( coordf_t ) out_x , ( coordf_t ) out_y , ( coordf_t ) out_z ) ;
}
2018-06-01 13:54:41 +00:00
void GLCanvas3D : : _start_timer ( )
{
if ( m_timer ! = nullptr )
m_timer - > Start ( 100 , wxTIMER_CONTINUOUS ) ;
}
void GLCanvas3D : : _stop_timer ( )
{
if ( m_timer ! = nullptr )
m_timer - > Stop ( ) ;
}
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 ;
}
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 ;
}
}
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-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-06-11 13:13:13 +00:00
std : : vector < GLVolume * > : : iterator begin = m_volumes . volumes . begin ( ) + initial_volumes_count ;
std : : vector < GLVolume * > : : iterator end = m_volumes . volumes . end ( ) ;
2018-06-05 08:56:55 +00:00
for ( std : : vector < GLVolume * > : : iterator it = begin ; it < end ; + + it )
{
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-06-11 13:13:13 +00:00
std : : vector < GLVolume * > : : iterator begin = m_volumes . volumes . begin ( ) + initial_volumes_count ;
std : : vector < GLVolume * > : : iterator end = m_volumes . volumes . end ( ) ;
2018-06-05 08:56:55 +00:00
for ( std : : vector < GLVolume * > : : iterator it = begin ; it < end ; + + it )
{
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 ( ) )
{
type - > volume - > print_zs . push_back ( unscale ( polyline . polyline . bounding_box ( ) . min . z ) ) ;
type - > volume - > offsets . push_back ( type - > volume - > indexed_vertex_array . quad_indices . size ( ) ) ;
type - > volume - > offsets . push_back ( type - > volume - > indexed_vertex_array . triangle_indices . size ( ) ) ;
_3DScene : : polyline3_to_verts ( polyline . polyline , preview_data . travel . width , preview_data . travel . height , * type - > volume ) ;
}
}
return true ;
}
bool GLCanvas3D : : _travel_paths_by_feedrate ( const GCodePreviewData & preview_data )
{
// Helper structure for feedrate
struct Feedrate
{
float value ;
GLVolume * volume ;
explicit Feedrate ( float value )
: value ( value )
, volume ( nullptr )
{
}
bool operator = = ( const Feedrate & other ) const
{
return value = = other . value ;
}
} ;
typedef std : : vector < 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 ( ) )
{
feedrate - > volume - > print_zs . push_back ( unscale ( polyline . polyline . bounding_box ( ) . min . z ) ) ;
feedrate - > volume - > offsets . push_back ( feedrate - > volume - > indexed_vertex_array . quad_indices . size ( ) ) ;
feedrate - > volume - > offsets . push_back ( feedrate - > volume - > indexed_vertex_array . triangle_indices . size ( ) ) ;
_3DScene : : polyline3_to_verts ( polyline . polyline , preview_data . travel . width , preview_data . travel . height , * feedrate - > volume ) ;
}
}
return true ;
}
bool GLCanvas3D : : _travel_paths_by_tool ( const GCodePreviewData & preview_data , const std : : vector < 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 )
{
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 ) ) ;
if ( tool ! = tools . end ( ) )
{
tool - > volume - > print_zs . push_back ( unscale ( polyline . polyline . bounding_box ( ) . min . z ) ) ;
tool - > volume - > offsets . push_back ( tool - > volume - > indexed_vertex_array . quad_indices . size ( ) ) ;
tool - > volume - > offsets . push_back ( tool - > volume - > indexed_vertex_array . triangle_indices . size ( ) ) ;
_3DScene : : polyline3_to_verts ( polyline . polyline , preview_data . travel . width , preview_data . travel . height , * tool - > volume ) ;
}
}
return true ;
}
void GLCanvas3D : : _load_gcode_retractions ( const GCodePreviewData & preview_data )
{
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 ) ;
std : : sort ( copy . begin ( ) , copy . end ( ) , [ ] ( const GCodePreviewData : : Retraction : : Position & p1 , const GCodePreviewData : : Retraction : : Position & p2 ) { return p1 . position . z < p2 . position . z ; } ) ;
for ( const GCodePreviewData : : Retraction : : Position & position : copy )
{
volume - > print_zs . push_back ( unscale ( position . position . z ) ) ;
volume - > offsets . push_back ( volume - > indexed_vertex_array . quad_indices . size ( ) ) ;
volume - > offsets . push_back ( volume - > indexed_vertex_array . triangle_indices . size ( ) ) ;
_3DScene : : point3_to_verts ( position . position , position . width , position . height , * volume ) ;
}
// finalize volumes and sends geometry to gpu
volume - > bounding_box = volume - > indexed_vertex_array . bounding_box ( ) ;
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 ) ;
std : : sort ( copy . begin ( ) , copy . end ( ) , [ ] ( const GCodePreviewData : : Retraction : : Position & p1 , const GCodePreviewData : : Retraction : : Position & p2 ) { return p1 . position . z < p2 . position . z ; } ) ;
for ( const GCodePreviewData : : Retraction : : Position & position : copy )
{
volume - > print_zs . push_back ( unscale ( position . position . z ) ) ;
volume - > offsets . push_back ( volume - > indexed_vertex_array . quad_indices . size ( ) ) ;
volume - > offsets . push_back ( volume - > indexed_vertex_array . triangle_indices . size ( ) ) ;
_3DScene : : point3_to_verts ( position . position , position . width , position . height , * volume ) ;
}
// finalize volumes and sends geometry to gpu
volume - > bounding_box = volume - > indexed_vertex_array . bounding_box ( ) ;
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_shells ( )
{
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 ) ;
if ( m_print - > objects . empty ( ) )
// nothing to render, return
return ;
// adds objects' volumes
unsigned int object_id = 0 ;
for ( PrintObject * obj : m_print - > objects )
{
ModelObject * model_obj = obj - > model_object ( ) ;
std : : vector < int > instance_ids ( model_obj - > instances . size ( ) ) ;
for ( int i = 0 ; i < model_obj - > instances . size ( ) ; + + i )
{
instance_ids [ i ] = i ;
}
for ( ModelInstance * instance : model_obj - > instances )
{
2018-06-11 13:13:13 +00:00
m_volumes . load_object ( model_obj , object_id , instance_ids , " object " , " object " , " object " , m_use_VBOs & & m_initialized ) ;
2018-06-05 08:56:55 +00:00
}
+ + object_id ;
}
// adds wipe tower's volume
coordf_t max_z = m_print - > objects [ 0 ] - > model_object ( ) - > get_model ( ) - > bounding_box ( ) . max . z ;
const PrintConfig & config = m_print - > config ;
unsigned int extruders_count = config . nozzle_diameter . size ( ) ;
if ( ( extruders_count > 1 ) & & config . single_extruder_multi_material & & config . wipe_tower & & ! config . complete_objects ) {
2018-06-05 10:24:26 +00:00
const float width_per_extruder = 15.0f ; // a simple workaround after wipe_tower_per_color_wipe got obsolete
2018-06-11 13:13:13 +00:00
m_volumes . load_wipe_tower_preview ( 1000 , config . wipe_tower_x , config . wipe_tower_y , config . wipe_tower_width , width_per_extruder * ( extruders_count - 1 ) , max_z , config . wipe_tower_rotation_angle , m_use_VBOs & & m_initialized ) ;
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-06-11 13:13:13 +00:00
std : : vector < GLVolume * > : : iterator begin = m_volumes . volumes . begin ( ) + m_gcode_preview_volume_index . first_volumes [ i ] . id ;
std : : vector < GLVolume * > : : iterator end = ( i + 1 < size ) ? m_volumes . volumes . begin ( ) + m_gcode_preview_volume_index . first_volumes [ i + 1 ] . id : m_volumes . volumes . end ( ) ;
2018-06-05 08:56:55 +00:00
for ( std : : vector < GLVolume * > : : iterator it = begin ; it ! = end ; + + it )
{
GLVolume * volume = * it ;
volume - > outside_printer_detection_enabled = false ;
switch ( m_gcode_preview_volume_index . first_volumes [ i ] . type )
{
case GCodePreviewVolumeIndex : : Extrusion :
{
if ( ( ExtrusionRole ) m_gcode_preview_volume_index . first_volumes [ i ] . flag = = erCustom )
volume - > zoom_to_volumes = false ;
volume - > is_active = preview_data . extrusion . is_role_flag_set ( ( ExtrusionRole ) m_gcode_preview_volume_index . first_volumes [ i ] . flag ) ;
break ;
}
case GCodePreviewVolumeIndex : : Travel :
{
volume - > is_active = preview_data . travel . is_visible ;
volume - > zoom_to_volumes = false ;
break ;
}
case GCodePreviewVolumeIndex : : Retraction :
{
volume - > is_active = preview_data . retraction . is_visible ;
volume - > zoom_to_volumes = false ;
break ;
}
case GCodePreviewVolumeIndex : : Unretraction :
{
volume - > is_active = preview_data . unretraction . is_visible ;
volume - > zoom_to_volumes = false ;
break ;
}
case GCodePreviewVolumeIndex : : Shell :
{
volume - > is_active = preview_data . shell . is_visible ;
volume - > color [ 3 ] = 0.25f ;
volume - > zoom_to_volumes = false ;
break ;
}
default :
{
volume - > is_active = false ;
volume - > zoom_to_volumes = false ;
break ;
}
}
}
}
}
2018-06-07 09:18:28 +00:00
void GLCanvas3D : : _on_move ( const std : : vector < int > & volume_idxs )
{
2018-06-11 13:13:13 +00:00
if ( m_model = = nullptr )
2018-06-07 09:18:28 +00:00
return ;
std : : set < std : : string > done ; //<2F> prevent moving instances twice
bool object_moved = false ;
Pointf3 wipe_tower_origin ( 0.0 , 0.0 , 0.0 ) ;
for ( int volume_idx : volume_idxs )
{
2018-06-11 13:13:13 +00:00
GLVolume * volume = m_volumes . volumes [ volume_idx ] ;
2018-06-07 09:18:28 +00:00
int obj_idx = volume - > object_idx ( ) ;
int instance_idx = volume - > instance_idx ( ) ;
//<2F> prevent moving instances twice
char done_id [ 64 ] ;
: : sprintf ( done_id , " %d_%d " , obj_idx , instance_idx ) ;
if ( done . find ( done_id ) ! = done . end ( ) )
continue ;
done . insert ( done_id ) ;
if ( obj_idx < 1000 )
{
// Move a regular object.
ModelObject * model_object = m_model - > objects [ obj_idx ] ;
model_object - > instances [ instance_idx ] - > offset . translate ( volume - > origin . x , volume - > origin . y ) ;
model_object - > invalidate_bounding_box ( ) ;
object_moved = true ;
}
else if ( obj_idx = = 1000 )
// Move a wipe tower proxy.
wipe_tower_origin = volume - > origin ;
}
if ( object_moved )
m_on_instance_moved_callback . call ( ) ;
if ( wipe_tower_origin ! = Pointf3 ( 0.0 , 0.0 , 0.0 ) )
m_on_wipe_tower_moved_callback . call ( wipe_tower_origin . x , wipe_tower_origin . y ) ;
}
2018-06-08 09:37:07 +00:00
void GLCanvas3D : : _on_select ( int volume_idx )
{
2018-06-11 09:40:11 +00:00
int id = - 1 ;
2018-06-11 13:13:13 +00:00
if ( volume_idx < ( int ) m_volumes . volumes . size ( ) )
2018-06-11 09:40:11 +00:00
{
if ( volume_idx ! = - 1 )
{
if ( m_select_by = = " volume " )
2018-06-11 13:13:13 +00:00
id = m_volumes . volumes [ volume_idx ] - > volume_idx ( ) ;
2018-06-11 09:40:11 +00:00
else if ( m_select_by = = " object " )
2018-06-11 13:13:13 +00:00
id = m_volumes . volumes [ volume_idx ] - > object_idx ( ) ;
2018-06-11 09:40:11 +00:00
}
}
m_on_select_object_callback . call ( id ) ;
2018-06-08 09:37:07 +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-05-09 08:47:04 +00:00
} // namespace GUI
} // namespace Slic3r