2018-11-26 10:49:25 +01:00
#include "slic3r/GUI/GLGizmo.hpp"
2018-05-09 10:47:04 +02:00
#include "GLCanvas3D.hpp"
2018-11-13 17:45:44 +01:00
#include "admesh/stl.h"
#include "libslic3r/libslic3r.h"
2018-11-26 14:41:58 +01:00
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/GCode/PreviewData.hpp"
#include "libslic3r/Geometry.hpp"
2018-11-27 16:55:54 +01:00
#include "libslic3r/Utils.hpp"
2018-11-13 17:45:44 +01:00
#include "slic3r/GUI/3DScene.hpp"
2018-11-22 15:29:59 +01:00
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
2018-11-13 17:45:44 +01:00
#include "slic3r/GUI/GLShader.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/PresetBundle.hpp"
2018-11-26 14:41:58 +01:00
//#include "slic3r/GUI/GLGizmo.hpp"
2018-10-01 15:09:31 +02:00
#include "GUI_App.hpp"
2018-10-05 23:29:15 +02:00
#include "GUI_ObjectList.hpp"
2018-10-04 16:43:10 +02:00
#include "GUI_ObjectManipulation.hpp"
2018-11-26 14:41:58 +01:00
#include "I18N.hpp"
2018-05-14 14:14:19 +02:00
2018-05-23 15:35:11 +02:00
#include <GL/glew.h>
2018-05-24 13:46:17 +02:00
2018-05-09 10:47:04 +02:00
#include <wx/glcanvas.h>
2018-07-19 13:18:19 +02:00
#include <wx/bitmap.h>
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <wx/settings.h>
2018-10-25 10:36:47 +02:00
#include <wx/tooltip.h>
2018-11-27 16:55:54 +01:00
#include <wx/debug.h>
2018-05-09 10:47:04 +02:00
2018-09-12 13:17:47 +02:00
// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx.
2018-11-13 17:45:44 +01:00
#include "libslic3r/Print.hpp"
#include "libslic3r/SLAPrint.hpp"
2018-09-12 11:59:02 +02:00
2018-11-14 08:53:56 +01:00
#include "wxExtensions.hpp"
2018-06-05 14:09:36 +02:00
#include <tbb/parallel_for.h>
#include <tbb/spin_mutex.h>
#include <boost/log/trivial.hpp>
2018-06-11 10:46:32 +02:00
#include <boost/algorithm/string/predicate.hpp>
2018-06-05 14:09:36 +02:00
2018-05-09 10:47:04 +02:00
#include <iostream>
2018-05-24 15:22:53 +02:00
#include <float.h>
2018-06-13 09:12:16 +02:00
#include <algorithm>
2018-05-09 10:47:04 +02:00
2018-06-01 09:00:30 +02:00
static const float TRACKBALLSIZE = 0.8f ;
2018-05-14 12:08:23 +02:00
static const float GIMBALL_LOCK_THETA_MAX = 180.0f ;
2018-05-15 15:38:25 +02:00
static const float GROUND_Z = - 0.02f ;
2018-05-14 12:08:23 +02:00
2018-05-15 11:30:11 +02: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 13:46:17 +02:00
static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f ;
static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f ;
2018-09-19 14:59:57 +02:00
static const float GIZMO_RESET_BUTTON_HEIGHT = 22.0f ;
static const float GIZMO_RESET_BUTTON_WIDTH = 70.f ;
2018-05-24 13:46:17 +02:00
2018-06-21 08:37:04 +02:00
static const float UNIT_MATRIX [] = { 1.0f , 0.0f , 0.0f , 0.0f ,
0.0f , 1.0f , 0.0f , 0.0f ,
0.0f , 0.0f , 1.0f , 0.0f ,
0.0f , 0.0f , 0.0f , 1.0f };
2018-12-12 10:38:07 +01:00
static const float DEFAULT_BG_DARK_COLOR [ 3 ] = { 0.478f , 0.478f , 0.478f };
static const float DEFAULT_BG_LIGHT_COLOR [ 3 ] = { 0.753f , 0.753f , 0.753f };
static const float ERROR_BG_DARK_COLOR [ 3 ] = { 0.478f , 0.192f , 0.039f };
static const float ERROR_BG_LIGHT_COLOR [ 3 ] = { 0.753f , 0.192f , 0.039f };
2018-07-27 09:38:39 +02:00
2018-12-20 11:14:53 +01:00
#if ENABLE_SIDEBAR_VISUAL_HINTS
static const float UNIFORM_SCALE_COLOR [ 3 ] = { 1.0f , 0.38f , 0.0f };
static const float AXES_COLOR [ 3 ][ 3 ] = { { 1.0f , 0.0f , 0.0f }, { 0.0f , 1.0f , 0.0f }, { 0.0f , 0.0f , 1.0f } };
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
2018-05-09 10:47:04 +02:00
namespace Slic3r {
namespace GUI {
2018-06-11 10:46:32 +02:00
bool GeometryBuffer :: set_from_triangles ( const Polygons & triangles , float z , bool generate_tex_coords )
2018-05-15 15:38:25 +02:00
{
2018-06-11 10:46:32 +02:00
m_vertices . clear ();
m_tex_coords . clear ();
2018-05-15 15:38:25 +02:00
2018-06-11 10:46:32 +02: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 15:38:25 +02:00
return false ;
2018-06-11 10:46:32 +02: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 15:38:25 +02:00
2018-08-21 17:43:05 +02:00
float min_x = unscale < float > ( triangles [ 0 ]. points [ 0 ]( 0 ));
float min_y = unscale < float > ( triangles [ 0 ]. points [ 0 ]( 1 ));
2018-06-11 10:46:32 +02:00
float max_x = min_x ;
float max_y = min_y ;
unsigned int v_coord = 0 ;
unsigned int t_coord = 0 ;
2018-05-15 15:38:25 +02:00
for ( const Polygon & t : triangles )
{
for ( unsigned int v = 0 ; v < 3 ; ++ v )
{
const Point & p = t . points [ v ];
2018-08-21 17:43:05 +02:00
float x = unscale < float > ( p ( 0 ));
float y = unscale < float > ( p ( 1 ));
2018-06-11 10:46:32 +02:00
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 15:38:25 +02:00
}
}
return true ;
}
bool GeometryBuffer :: set_from_lines ( const Lines & lines , float z )
{
2018-06-11 10:46:32 +02:00
m_vertices . clear ();
m_tex_coords . clear ();
2018-05-15 15:38:25 +02:00
unsigned int size = 6 * ( unsigned int ) lines . size ();
if ( size == 0 )
return false ;
2018-06-11 10:46:32 +02:00
m_vertices = std :: vector < float > ( size , 0.0f );
2018-05-15 15:38:25 +02:00
unsigned int coord = 0 ;
for ( const Line & l : lines )
{
2018-08-21 17:43:05 +02:00
m_vertices [ coord ++ ] = unscale < float > ( l . a ( 0 ));
m_vertices [ coord ++ ] = unscale < float > ( l . a ( 1 ));
2018-06-11 10:46:32 +02:00
m_vertices [ coord ++ ] = z ;
2018-08-21 17:43:05 +02:00
m_vertices [ coord ++ ] = unscale < float > ( l . b ( 0 ));
m_vertices [ coord ++ ] = unscale < float > ( l . b ( 1 ));
2018-06-11 10:46:32 +02:00
m_vertices [ coord ++ ] = z ;
2018-05-15 15:38:25 +02:00
}
return true ;
}
2018-06-11 10:46:32 +02:00
const float * GeometryBuffer :: get_vertices () const
2018-05-15 15:38:25 +02:00
{
2018-06-11 10:46:32 +02:00
return m_vertices . data ();
2018-05-15 15:38:25 +02:00
}
2018-06-11 10:46:32 +02:00
const float * GeometryBuffer :: get_tex_coords () const
2018-05-15 15:38:25 +02:00
{
2018-06-11 10:46:32 +02:00
return m_tex_coords . data ();
}
unsigned int GeometryBuffer :: get_vertices_count () const
{
return ( unsigned int ) m_vertices . size () / 3 ;
2018-05-15 15:38:25 +02:00
}
2018-05-24 13:46:17 +02: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-05-09 10:47:04 +02:00
GLCanvas3D :: Camera :: Camera ()
2018-06-01 15:54:41 +02:00
: type ( Ortho )
, zoom ( 1.0f )
, phi ( 45.0f )
2018-06-04 12:26:39 +02:00
// , distance(0.0f)
2018-05-14 12:08:23 +02:00
, m_theta ( 45.0f )
2018-12-07 16:23:04 +01:00
, m_target ( Vec3d :: Zero ())
2018-05-09 10:47:04 +02:00
{
}
2018-05-14 11:31:58 +02:00
std :: string GLCanvas3D :: Camera :: get_type_as_string () const
{
2018-06-01 15:54:41 +02:00
switch ( type )
2018-05-14 11:31:58 +02:00
{
default :
2018-06-01 15:54:41 +02:00
case Unknown :
2018-05-14 11:31:58 +02:00
return "unknown" ;
2018-06-04 12:26:39 +02:00
// case Perspective:
// return "perspective";
2018-06-01 15:54:41 +02:00
case Ortho :
2018-05-14 11:31:58 +02:00
return "ortho" ;
};
}
2018-05-14 12:08:23 +02:00
void GLCanvas3D :: Camera :: set_theta ( float theta )
{
2018-06-01 15:54:41 +02:00
m_theta = clamp ( 0.0f , GIMBALL_LOCK_THETA_MAX , theta );
2018-05-14 12:08:23 +02:00
}
2018-12-07 16:23:04 +01:00
void GLCanvas3D :: Camera :: set_target ( const Vec3d & target , GLCanvas3D & canvas )
{
m_target = target ;
m_target ( 0 ) = clamp ( m_scene_box . min ( 0 ), m_scene_box . max ( 0 ), m_target ( 0 ));
m_target ( 1 ) = clamp ( m_scene_box . min ( 1 ), m_scene_box . max ( 1 ), m_target ( 1 ));
m_target ( 2 ) = clamp ( m_scene_box . min ( 2 ), m_scene_box . max ( 2 ), m_target ( 2 ));
if ( ! m_target . isApprox ( target ))
canvas . viewport_changed ();
}
void GLCanvas3D :: Camera :: set_scene_box ( const BoundingBoxf3 & box , GLCanvas3D & canvas )
{
if ( m_scene_box != box )
{
m_scene_box = box ;
canvas . viewport_changed ();
}
}
2018-06-11 10:46:32 +02:00
GLCanvas3D :: Bed :: Bed ()
: m_type ( Custom )
{
}
2018-06-11 11:40:11 +02:00
bool GLCanvas3D :: Bed :: is_prusa () const
{
2018-11-30 15:31:47 +01:00
return ( m_type == MK2 ) || ( m_type == MK3 ) || ( m_type == SL1 );
2018-06-11 11:40:11 +02:00
}
bool GLCanvas3D :: Bed :: is_custom () const
{
return m_type == Custom ;
}
2018-05-14 14:14:19 +02:00
const Pointfs & GLCanvas3D :: Bed :: get_shape () const
{
return m_shape ;
}
2018-08-05 22:52:38 +02:00
bool GLCanvas3D :: Bed :: set_shape ( const Pointfs & shape )
2018-05-14 14:14:19 +02:00
{
2018-08-05 22:52:38 +02:00
EType new_type = _detect_type ();
if ( m_shape == shape && m_type == new_type )
// No change, no need to update the UI.
return false ;
2018-10-03 16:18:23 +02:00
2018-05-14 14:14:19 +02:00
m_shape = shape ;
2018-08-05 22:52:38 +02:00
m_type = new_type ;
2018-05-15 15:38:25 +02:00
2018-05-14 14:14:19 +02:00
_calc_bounding_box ();
2018-05-15 15:38:25 +02:00
ExPolygon poly ;
2018-08-21 21:05:24 +02:00
for ( const Vec2d & p : m_shape )
2018-05-15 15:38:25 +02:00
{
2018-08-17 15:53:43 +02:00
poly . contour . append ( Point ( scale_ ( p ( 0 )), scale_ ( p ( 1 ))));
2018-05-15 15:38:25 +02:00
}
_calc_triangles ( poly );
const BoundingBox & bed_bbox = poly . contour . bounding_box ();
_calc_gridlines ( poly , bed_bbox );
2018-06-11 10:46:32 +02:00
m_polygon = offset_ex ( poly . contour , ( float ) bed_bbox . radius () * 1.7f , jtRound , scale_ ( 0.5 ))[ 0 ]. contour ;
2018-08-05 22:52:38 +02:00
// Let the calee to update the UI.
return true ;
2018-05-14 14:14:19 +02:00
}
const BoundingBoxf3 & GLCanvas3D :: Bed :: get_bounding_box () const
{
return m_bounding_box ;
}
2018-05-31 16:04:59 +02: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 );
}
2019-01-04 12:56:48 +01:00
#if ENABLE_PRINT_BED_MODELS
void GLCanvas3D :: Bed :: render ( float theta , bool useVBOs ) const
{
switch ( m_type )
{
case MK2 :
{
_render_prusa ( "mk2" , theta , useVBOs );
break ;
}
case MK3 :
{
_render_prusa ( "mk3" , theta , useVBOs );
break ;
}
case SL1 :
{
_render_prusa ( "sl1" , theta , useVBOs );
break ;
}
default :
case Custom :
{
_render_custom ();
break ;
}
}
}
#else
2018-06-11 10:46:32 +02:00
void GLCanvas3D :: Bed :: render ( float theta ) const
2018-05-15 15:38:25 +02:00
{
2018-06-11 10:46:32 +02:00
switch ( m_type )
2018-05-15 15:38:25 +02:00
{
2018-06-11 10:46:32 +02:00
case MK2 :
{
2018-11-30 15:31:47 +01:00
_render_prusa ( "mk2" , theta );
2018-06-11 10:46:32 +02:00
break ;
}
case MK3 :
{
2018-11-30 15:31:47 +01:00
_render_prusa ( "mk3" , theta );
break ;
}
case SL1 :
{
_render_prusa ( "sl1" , theta );
2018-06-11 10:46:32 +02:00
break ;
}
default :
case Custom :
{
_render_custom ();
break ;
}
2018-05-15 15:38:25 +02:00
}
}
2019-01-04 12:56:48 +01:00
#endif // ENABLE_PRINT_BED_MODELS
2018-05-15 15:38:25 +02:00
2018-05-14 14:14:19 +02:00
void GLCanvas3D :: Bed :: _calc_bounding_box ()
{
m_bounding_box = BoundingBoxf3 ();
2018-08-21 21:05:24 +02:00
for ( const Vec2d & p : m_shape )
2018-05-14 14:14:19 +02:00
{
2018-08-21 17:43:05 +02:00
m_bounding_box . merge ( Vec3d ( p ( 0 ), p ( 1 ), 0.0 ));
2018-05-14 14:14:19 +02:00
}
}
2018-05-15 15:38:25 +02:00
void GLCanvas3D :: Bed :: _calc_triangles ( const ExPolygon & poly )
{
Polygons triangles ;
poly . triangulate ( & triangles );
2018-06-11 10:46:32 +02:00
if ( ! m_triangles . set_from_triangles ( triangles , GROUND_Z , m_type != Custom ))
2018-05-15 15:38:25 +02:00
printf ( "Unable to create bed triangles \n " );
}
void GLCanvas3D :: Bed :: _calc_gridlines ( const ExPolygon & poly , const BoundingBox & bed_bbox )
{
Polylines axes_lines ;
2018-08-17 15:53:43 +02:00
for ( coord_t x = bed_bbox . min ( 0 ); x <= bed_bbox . max ( 0 ); x += scale_ ( 10.0 ))
2018-05-15 15:38:25 +02:00
{
Polyline line ;
2018-08-17 15:53:43 +02:00
line . append ( Point ( x , bed_bbox . min ( 1 )));
line . append ( Point ( x , bed_bbox . max ( 1 )));
2018-05-15 15:38:25 +02:00
axes_lines . push_back ( line );
}
2018-08-17 15:53:43 +02:00
for ( coord_t y = bed_bbox . min ( 1 ); y <= bed_bbox . max ( 1 ); y += scale_ ( 10.0 ))
2018-05-15 15:38:25 +02:00
{
Polyline line ;
2018-08-17 15:53:43 +02:00
line . append ( Point ( bed_bbox . min ( 0 ), y ));
line . append ( Point ( bed_bbox . max ( 0 ), y ));
2018-05-15 15:38:25 +02:00
axes_lines . push_back ( line );
}
// clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped
2018-10-08 15:17:36 +02:00
Lines gridlines = to_lines ( intersection_pl ( axes_lines , offset ( poly , ( float ) SCALED_EPSILON )));
2018-05-15 15:38:25 +02:00
// 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 10:46:32 +02:00
GLCanvas3D :: Bed :: EType GLCanvas3D :: Bed :: _detect_type () const
{
EType type = Custom ;
2018-10-01 15:09:31 +02:00
auto bundle = wxGetApp (). preset_bundle ;
2018-06-11 10:46:32 +02:00
if ( bundle != nullptr )
{
2018-06-21 10:24:57 +02:00
const Preset * curr = & bundle -> printers . get_selected_preset ();
while ( curr != nullptr )
2018-06-11 10:46:32 +02:00
{
2018-11-30 15:31:47 +01:00
if ( curr -> config . has ( "bed_shape" ))
{
if ( boost :: contains ( curr -> name , "SL1" ))
{
//FIXME add a condition on the size of the print bed?
type = SL1 ;
break ;
}
else if ( _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 ;
break ;
}
else if ( boost :: contains ( curr -> name , "MK3" ))
{
type = MK3 ;
break ;
}
}
}
}
2018-06-21 10:24:57 +02:00
curr = bundle -> printers . get_preset_parent ( * curr );
2018-06-11 10:46:32 +02:00
}
}
return type ;
}
2019-01-04 12:56:48 +01:00
#if ENABLE_PRINT_BED_MODELS
void GLCanvas3D :: Bed :: _render_prusa ( const std :: string & key , float theta , bool useVBOs ) const
#else
2018-11-30 15:31:47 +01:00
void GLCanvas3D :: Bed :: _render_prusa ( const std :: string & key , float theta ) const
2019-01-04 12:56:48 +01:00
#endif // ENABLE_PRINT_BED_MODELS
2018-06-11 10:46:32 +02:00
{
2019-01-04 12:56:48 +01:00
std :: string tex_path = resources_dir () + "/icons/bed/" + key ;
#if ENABLE_PRINT_BED_MODELS
std :: string model_path = resources_dir () + "/models/" + key ;
#endif // ENABLE_PRINT_BED_MODELS
std :: string filename = tex_path + "_top.png" ;
2018-06-11 10:46:32 +02:00
if (( m_top_texture . get_id () == 0 ) || ( m_top_texture . get_source () != filename ))
{
2018-06-14 10:37:28 +02:00
if ( ! m_top_texture . load_from_file ( filename , true ))
2018-06-11 10:46:32 +02:00
{
_render_custom ();
return ;
}
}
2019-01-04 12:56:48 +01:00
filename = tex_path + "_bottom.png" ;
2018-06-11 10:46:32 +02:00
if (( m_bottom_texture . get_id () == 0 ) || ( m_bottom_texture . get_source () != filename ))
{
2018-06-14 10:37:28 +02:00
if ( ! m_bottom_texture . load_from_file ( filename , true ))
2018-06-11 10:46:32 +02:00
{
_render_custom ();
return ;
}
}
2019-01-04 12:56:48 +01:00
#if ENABLE_PRINT_BED_MODELS
if ( theta <= 90.0f )
{
filename = model_path + "_bed.stl" ;
if (( m_model . get_filename () != filename ) && m_model . init_from_file ( filename , useVBOs ))
m_model . center_around ( m_bounding_box . center () - Vec3d ( 0.0 , 0.0 , 1.0 + 0.5 * m_model . get_bounding_box (). size ()( 2 )));
if ( ! m_model . get_filename (). empty ())
{
:: glEnable ( GL_LIGHTING );
m_model . render ();
:: glDisable ( GL_LIGHTING );
}
}
#endif // ENABLE_PRINT_BED_MODELS
2018-06-11 10:46:32 +02:00
unsigned int triangles_vcount = m_triangles . get_vertices_count ();
if ( triangles_vcount > 0 )
{
:: glEnable ( GL_DEPTH_TEST );
2018-08-24 12:06:05 +02:00
:: glDepthMask ( GL_FALSE );
2018-06-11 10:46:32 +02:00
:: glEnable ( GL_BLEND );
:: glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
2018-06-21 13:03:53 +02:00
:: glEnable ( GL_TEXTURE_2D );
2018-06-22 11:19:38 +02:00
:: glTexEnvi ( GL_TEXTURE_ENV , GL_TEXTURE_ENV_MODE , GL_REPLACE );
2018-06-11 10:46:32 +02:00
:: glEnableClientState ( GL_VERTEX_ARRAY );
:: glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
if ( theta > 90.0f )
:: glFrontFace ( GL_CW );
:: 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 );
2018-06-21 13:03:53 +02:00
:: glDisable ( GL_TEXTURE_2D );
2018-06-11 10:46:32 +02:00
:: glDisable ( GL_BLEND );
2018-08-24 12:06:05 +02:00
:: glDepthMask ( GL_TRUE );
2018-06-11 10:46:32 +02:00
}
}
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 11:40:11 +02:00
:: glDisable ( GL_DEPTH_TEST );
2018-06-11 10:46:32 +02:00
:: glEnable ( GL_BLEND );
:: glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
:: glEnableClientState ( GL_VERTEX_ARRAY );
2018-12-12 11:44:17 +01:00
:: glColor4f ( 0.35f , 0.35f , 0.35f , 0.4f );
2018-06-11 10:46:32 +02:00
:: 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 11:40:11 +02:00
// we need depth test for grid, otherwise it would disappear when looking the object from below
:: glEnable ( GL_DEPTH_TEST );
2018-06-11 10:46:32 +02: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 );
2018-06-22 12:21:43 +02:00
:: glDisable ( GL_LIGHTING );
2018-06-11 10:46:32 +02:00
}
}
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-12-17 14:09:35 +01:00
const double GLCanvas3D :: Axes :: Radius = 0.5 ;
const double GLCanvas3D :: Axes :: ArrowBaseRadius = 2.5 * GLCanvas3D :: Axes :: Radius ;
const double GLCanvas3D :: Axes :: ArrowLength = 5.0 ;
2018-05-18 13:02:47 +02:00
GLCanvas3D :: Axes :: Axes ()
2018-10-11 10:52:50 +02:00
: origin ( Vec3d :: Zero ())
2018-12-17 14:09:35 +01:00
, length ( Vec3d :: Zero ())
2018-05-18 13:02:47 +02:00
{
2018-12-17 14:09:35 +01:00
m_quadric = :: gluNewQuadric ();
if ( m_quadric != nullptr )
:: gluQuadricDrawStyle ( m_quadric , GLU_FILL );
2018-05-18 13:02:47 +02:00
}
2018-12-17 14:09:35 +01:00
GLCanvas3D :: Axes ::~ Axes ()
2018-05-18 13:02:47 +02:00
{
2018-12-17 14:09:35 +01:00
if ( m_quadric != nullptr )
:: gluDeleteQuadric ( m_quadric );
}
2018-06-13 13:14:17 +02:00
2018-12-17 14:09:35 +01:00
void GLCanvas3D :: Axes :: render () const
{
if ( m_quadric == nullptr )
return ;
:: glEnable ( GL_DEPTH_TEST );
:: glEnable ( GL_LIGHTING );
// x axis
2018-05-18 13:02:47 +02:00
:: glColor3f ( 1.0f , 0.0f , 0.0f );
2018-12-17 14:09:35 +01:00
:: glPushMatrix ();
:: glTranslated ( origin ( 0 ), origin ( 1 ), origin ( 2 ));
:: glRotated ( 90.0 , 0.0 , 1.0 , 0.0 );
render_axis ( length ( 0 ));
:: glPopMatrix ();
2018-06-13 13:14:17 +02:00
2018-12-17 14:09:35 +01:00
// y axis
:: glColor3f ( 0.0f , 1.0f , 0.0f );
:: glPushMatrix ();
:: glTranslated ( origin ( 0 ), origin ( 1 ), origin ( 2 ));
:: glRotated ( - 90.0 , 1.0 , 0.0 , 0.0 );
render_axis ( length ( 1 ));
:: glPopMatrix ();
// z axis
2018-05-18 13:02:47 +02:00
:: glColor3f ( 0.0f , 0.0f , 1.0f );
2018-12-17 14:09:35 +01:00
:: glPushMatrix ();
:: glTranslated ( origin ( 0 ), origin ( 1 ), origin ( 2 ));
render_axis ( length ( 2 ));
:: glPopMatrix ();
:: glDisable ( GL_LIGHTING );
2018-05-18 13:02:47 +02:00
}
2018-12-17 14:09:35 +01:00
void GLCanvas3D :: Axes :: render_axis ( double length ) const
{
:: gluQuadricOrientation ( m_quadric , GLU_OUTSIDE );
:: gluCylinder ( m_quadric , Radius , Radius , length , 32 , 1 );
:: gluQuadricOrientation ( m_quadric , GLU_INSIDE );
:: gluDisk ( m_quadric , 0.0 , Radius , 32 , 1 );
:: glTranslated ( 0.0 , 0.0 , length );
:: gluQuadricOrientation ( m_quadric , GLU_OUTSIDE );
:: gluCylinder ( m_quadric , ArrowBaseRadius , 0.0 , ArrowLength , 32 , 1 );
:: gluQuadricOrientation ( m_quadric , GLU_INSIDE );
:: gluDisk ( m_quadric , 0.0 , ArrowBaseRadius , 32 , 1 );
}
2018-05-18 11:05:48 +02:00
2018-05-25 14:05:08 +02: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 15:56:14 +02:00
void GLCanvas3D :: Shader :: set_uniform ( const std :: string & name , float value ) const
{
if ( m_shader != nullptr )
m_shader -> set_uniform ( name . c_str (), value );
}
2018-06-21 08:37:04 +02:00
void GLCanvas3D :: Shader :: set_uniform ( const std :: string & name , const float * matrix ) const
{
if ( m_shader != nullptr )
m_shader -> set_uniform ( name . c_str (), matrix );
}
2018-05-29 14:34:45 +02:00
const GLShader * GLCanvas3D :: Shader :: get_shader () const
2018-05-25 14:05:08 +02:00
{
return m_shader ;
}
void GLCanvas3D :: Shader :: _reset ()
{
if ( m_shader != nullptr )
{
m_shader -> release ();
delete m_shader ;
m_shader = nullptr ;
}
}
2018-05-18 14:08:59 +02:00
GLCanvas3D :: LayersEditing :: LayersEditing ()
2018-06-01 15:54:41 +02:00
: m_use_legacy_opengl ( false )
2018-05-25 14:05:08 +02:00
, m_enabled ( false )
, m_z_texture_id ( 0 )
2018-06-01 15:54:41 +02:00
, state ( Unknown )
, band_width ( 2.0f )
, strength ( 0.005f )
, last_object_id ( - 1 )
, last_z ( 0.0f )
, last_action ( 0 )
2018-05-18 14:08:59 +02:00
{
}
2018-05-24 13:46:17 +02:00
GLCanvas3D :: LayersEditing ::~ LayersEditing ()
{
2018-05-25 14:05:08 +02: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 13:15:28 +02:00
return ! m_use_legacy_opengl && m_shader . is_initialized ();
2018-05-25 14:05:08 +02:00
}
2018-05-25 16:28:24 +02:00
void GLCanvas3D :: LayersEditing :: set_use_legacy_opengl ( bool use_legacy_opengl )
2018-05-25 14:05:08 +02:00
{
2018-05-25 16:28:24 +02:00
m_use_legacy_opengl = use_legacy_opengl ;
2018-05-24 13:46:17 +02:00
}
2018-05-18 14:08:59 +02:00
bool GLCanvas3D :: LayersEditing :: is_enabled () const
{
return m_enabled ;
}
2018-05-25 14:05:08 +02:00
void GLCanvas3D :: LayersEditing :: set_enabled ( bool enabled )
{
2018-05-25 16:28:24 +02:00
m_enabled = is_allowed () && enabled ;
2018-05-25 14:05:08 +02:00
}
unsigned int GLCanvas3D :: LayersEditing :: get_z_texture_id () const
{
return m_z_texture_id ;
}
2018-05-25 15:56:14 +02:00
void GLCanvas3D :: LayersEditing :: render ( const GLCanvas3D & canvas , const PrintObject & print_object , const GLVolume & volume ) const
{
if ( ! m_enabled )
return ;
2018-05-30 15:18:45 +02:00
const Rect & bar_rect = get_bar_rect_viewport ( canvas );
const Rect & reset_rect = get_reset_rect_viewport ( canvas );
2018-05-25 15:56:14 +02: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 13:46:17 +02:00
_render_tooltip_texture ( canvas , bar_rect , reset_rect );
2018-06-13 09:12:16 +02:00
_render_reset_texture ( reset_rect );
2018-05-25 15:56:14 +02:00
_render_active_object_annotations ( canvas , volume , print_object , bar_rect );
2018-05-24 15:17:01 +02:00
_render_profile ( print_object , bar_rect );
2018-05-25 15:56:14 +02:00
// Revert the matrices.
2018-06-11 10:46:32 +02:00
:: glPopMatrix ();
2018-05-25 15:56:14 +02:00
2018-06-11 10:46:32 +02:00
:: glEnable ( GL_DEPTH_TEST );
2018-05-24 13:46:17 +02:00
}
2018-06-01 15:54:41 +02:00
int GLCanvas3D :: LayersEditing :: get_shader_program_id () const
2018-05-25 14:05:08 +02:00
{
2018-06-01 15:54:41 +02:00
const GLShader * shader = m_shader . get_shader ();
return ( shader != nullptr ) ? shader -> shader_program_id : - 1 ;
2018-05-25 14:05:08 +02:00
}
2018-05-28 14:10:02 +02:00
float GLCanvas3D :: LayersEditing :: get_cursor_z_relative ( const GLCanvas3D & canvas )
2018-05-28 13:43:29 +02:00
{
const Point & mouse_pos = canvas . get_local_mouse_position ();
2018-05-30 15:18:45 +02:00
const Rect & rect = get_bar_rect_screen ( canvas );
2018-08-17 15:53:43 +02:00
float x = ( float ) mouse_pos ( 0 );
float y = ( float ) mouse_pos ( 1 );
2018-05-30 15:18:45 +02:00
float t = rect . get_top ();
float b = rect . get_bottom ();
2018-05-28 13:43:29 +02:00
2018-05-30 15:18:45 +02:00
return (( rect . get_left () <= x ) && ( x <= rect . get_right ()) && ( t <= y ) && ( y <= b )) ?
2018-05-28 13:43:29 +02:00
// Inside the bar.
( b - y - 1.0f ) / ( b - t - 1.0f ) :
// Outside the bar.
- 1000.0f ;
}
2018-05-28 14:39:59 +02:00
bool GLCanvas3D :: LayersEditing :: bar_rect_contains ( const GLCanvas3D & canvas , float x , float y )
{
2018-05-30 15:18:45 +02:00
const Rect & rect = get_bar_rect_screen ( canvas );
2018-05-28 14:39:59 +02:00
return ( rect . get_left () <= x ) && ( x <= rect . get_right ()) && ( rect . get_top () <= y ) && ( y <= rect . get_bottom ());
}
bool GLCanvas3D :: LayersEditing :: reset_rect_contains ( const GLCanvas3D & canvas , float x , float y )
{
2018-05-30 15:18:45 +02:00
const Rect & rect = get_reset_rect_screen ( canvas );
2018-05-28 14:39:59 +02:00
return ( rect . get_left () <= x ) && ( x <= rect . get_right ()) && ( rect . get_top () <= y ) && ( y <= rect . get_bottom ());
}
2018-05-30 15:18:45 +02: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-25 14:05:08 +02:00
bool GLCanvas3D :: LayersEditing :: _is_initialized () const
{
return m_shader . is_initialized ();
}
2018-05-24 13:46:17 +02:00
void GLCanvas3D :: LayersEditing :: _render_tooltip_texture ( const GLCanvas3D & canvas , const Rect & bar_rect , const Rect & reset_rect ) const
{
2018-06-11 10:46:32 +02:00
if ( m_tooltip_texture . get_id () == 0 )
2018-05-24 13:46:17 +02:00
{
2018-06-11 10:46:32 +02:00
std :: string filename = resources_dir () + "/icons/variable_layer_height_tooltip.png" ;
2018-06-14 10:37:28 +02:00
if ( ! m_tooltip_texture . load_from_file ( filename , false ))
2018-05-24 13:46:17 +02: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 10:46:32 +02:00
float l = bar_left - ( float ) m_tooltip_texture . get_width () * inv_zoom - gap ;
2018-05-24 13:46:17 +02:00
float r = bar_left - gap ;
2018-06-11 10:46:32 +02:00
float t = reset_bottom + ( float ) m_tooltip_texture . get_height () * inv_zoom + gap ;
2018-05-24 13:46:17 +02:00
float b = reset_bottom + gap ;
2018-06-13 09:12:16 +02:00
GLTexture :: render_texture ( m_tooltip_texture . get_id (), l , r , b , t );
2018-05-24 13:46:17 +02:00
}
2018-06-13 09:12:16 +02:00
void GLCanvas3D :: LayersEditing :: _render_reset_texture ( const Rect & reset_rect ) const
2018-05-24 13:46:17 +02:00
{
2018-06-11 10:46:32 +02:00
if ( m_reset_texture . get_id () == 0 )
2018-05-24 13:46:17 +02:00
{
2018-06-11 10:46:32 +02:00
std :: string filename = resources_dir () + "/icons/variable_layer_height_reset.png" ;
2018-06-14 10:37:28 +02:00
if ( ! m_reset_texture . load_from_file ( filename , false ))
2018-05-24 13:46:17 +02:00
return ;
}
2018-06-13 09:12:16 +02:00
GLTexture :: render_texture ( m_reset_texture . get_id (), reset_rect . get_left (), reset_rect . get_right (), reset_rect . get_bottom (), reset_rect . get_top ());
2018-05-24 13:46:17 +02:00
}
2018-05-25 15:56:14 +02:00
void GLCanvas3D :: LayersEditing :: _render_active_object_annotations ( const GLCanvas3D & canvas , const GLVolume & volume , const PrintObject & print_object , const Rect & bar_rect ) const
{
2018-08-17 15:53:43 +02:00
float max_z = print_object . model_object () -> bounding_box (). max ( 2 );
2018-05-25 15:56:14 +02:00
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 13:43:29 +02:00
m_shader . set_uniform ( "z_cursor" , max_z * get_cursor_z_relative ( canvas ));
2018-06-01 15:54:41 +02:00
m_shader . set_uniform ( "z_cursor_band_width" , band_width );
2018-06-21 08:37:04 +02:00
// The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
m_shader . set_uniform ( "volume_world_matrix" , UNIT_MATRIX );
2018-05-25 15:56:14 +02: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 ;
2018-06-22 11:19:38 +02:00
:: glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 );
2018-05-25 15:56:14 +02:00
:: glBindTexture ( GL_TEXTURE_2D , m_z_texture_id );
2018-06-22 11:19:38 +02:00
:: glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , w , h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 );
:: glTexImage2D ( GL_TEXTURE_2D , 1 , GL_RGBA , half_w , half_h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 );
2018-05-25 15:56:14 +02:00
:: 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 15:17:01 +02: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 ;
2018-09-12 11:59:02 +02:00
const PrintConfig & print_config = print_object . print () -> config ();
2018-05-24 15:17:01 +02:00
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 ;
2018-08-24 10:20:00 +02:00
double max_z = unscale < double > ( print_object . size ( 2 ));
2018-09-12 11:59:02 +02:00
double layer_height = dynamic_cast < const ConfigOptionFloat *> ( print_object . config (). option ( "layer_height" )) -> value ;
2018-05-24 15:17:01 +02:00
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 )
{
2018-08-24 10:20:00 +02:00
const std :: vector < double >& profile = model_object -> layer_height_profile ;
2018-05-24 15:17:01 +02:00
:: 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 15:54:41 +02:00
const Point GLCanvas3D :: Mouse :: Drag :: Invalid_2D_Point ( INT_MAX , INT_MAX );
2018-08-21 17:43:05 +02:00
const Vec3d GLCanvas3D :: Mouse :: Drag :: Invalid_3D_Point ( DBL_MAX , DBL_MAX , DBL_MAX );
2018-06-01 15:54:41 +02:00
GLCanvas3D :: Mouse :: Drag :: Drag ()
: start_position_2D ( Invalid_2D_Point )
, start_position_3D ( Invalid_3D_Point )
2018-06-21 08:37:04 +02:00
, move_volume_idx ( - 1 )
2018-06-01 15:54:41 +02:00
{
}
2018-05-23 13:56:54 +02:00
GLCanvas3D :: Mouse :: Mouse ()
2018-06-01 15:54:41 +02:00
: dragging ( false )
2018-11-30 12:49:31 +01:00
, left_down ( false )
2018-06-01 15:54:41 +02:00
, position ( DBL_MAX , DBL_MAX )
2018-11-15 11:38:40 +01:00
, scene_position ( DBL_MAX , DBL_MAX , DBL_MAX )
2018-12-06 14:26:13 +01:00
, ignore_up_event ( false )
2018-05-23 13:56:54 +02:00
{
}
2018-06-01 15:54:41 +02:00
void GLCanvas3D :: Mouse :: set_start_position_2D_as_invalid ()
2018-05-23 13:56:54 +02:00
{
2018-06-01 15:54:41 +02:00
drag . start_position_2D = Drag :: Invalid_2D_Point ;
2018-05-23 13:56:54 +02:00
}
2018-06-01 15:54:41 +02:00
void GLCanvas3D :: Mouse :: set_start_position_3D_as_invalid ()
2018-05-23 13:56:54 +02:00
{
2018-06-01 15:54:41 +02:00
drag . start_position_3D = Drag :: Invalid_3D_Point ;
2018-05-23 13:56:54 +02:00
}
2018-06-01 15:54:41 +02:00
bool GLCanvas3D :: Mouse :: is_start_position_2D_defined () const
2018-05-23 13:56:54 +02:00
{
2018-06-01 15:54:41 +02:00
return ( drag . start_position_2D != Drag :: Invalid_2D_Point );
2018-05-23 13:56:54 +02:00
}
2018-06-01 15:54:41 +02:00
bool GLCanvas3D :: Mouse :: is_start_position_3D_defined () const
2018-05-23 13:56:54 +02:00
{
2018-06-01 15:54:41 +02:00
return ( drag . start_position_3D != Drag :: Invalid_3D_Point );
2018-05-31 13:51:50 +02:00
}
2018-11-02 12:11:28 +01:00
GLCanvas3D :: Selection :: VolumeCache :: TransformCache :: TransformCache ()
: position ( Vec3d :: Zero ())
, rotation ( Vec3d :: Zero ())
, scaling_factor ( Vec3d :: Ones ())
2018-12-10 09:46:01 +01:00
, mirror ( Vec3d :: Ones ())
2018-11-02 12:11:28 +01:00
, rotation_matrix ( Transform3d :: Identity ())
, scale_matrix ( Transform3d :: Identity ())
2018-12-10 09:46:01 +01:00
, mirror_matrix ( Transform3d :: Identity ())
2018-11-02 12:11:28 +01:00
{
}
GLCanvas3D :: Selection :: VolumeCache :: TransformCache :: TransformCache ( const Geometry :: Transformation & transform )
: position ( transform . get_offset ())
, rotation ( transform . get_rotation ())
, scaling_factor ( transform . get_scaling_factor ())
2018-12-10 09:46:01 +01:00
, mirror ( transform . get_mirror ())
2018-11-02 12:11:28 +01:00
{
rotation_matrix = Geometry :: assemble_transform ( Vec3d :: Zero (), rotation );
scale_matrix = Geometry :: assemble_transform ( Vec3d :: Zero (), Vec3d :: Zero (), scaling_factor );
2018-12-10 09:46:01 +01:00
mirror_matrix = Geometry :: assemble_transform ( Vec3d :: Zero (), Vec3d :: Zero (), Vec3d :: Ones (), mirror );
2018-11-02 12:11:28 +01:00
}
GLCanvas3D :: Selection :: VolumeCache :: VolumeCache ( const Geometry :: Transformation & volume_transform , const Geometry :: Transformation & instance_transform )
: m_volume ( volume_transform )
, m_instance ( instance_transform )
{
}
2018-10-08 14:02:12 +02:00
GLCanvas3D :: Selection :: Selection ()
: m_volumes ( nullptr )
, m_model ( nullptr )
, m_mode ( Instance )
2018-10-12 10:09:16 +02:00
, m_type ( Empty )
2018-10-08 14:02:12 +02:00
, m_valid ( false )
, m_bounding_box_dirty ( true )
2018-12-20 11:14:53 +01:00
, m_curved_arrow ( 16 )
2018-10-08 14:02:12 +02:00
{
2018-12-18 12:35:49 +01:00
#if ENABLE_RENDER_SELECTION_CENTER
m_quadric = :: gluNewQuadric ();
if ( m_quadric != nullptr )
:: gluQuadricDrawStyle ( m_quadric , GLU_FILL );
#endif // ENABLE_RENDER_SELECTION_CENTER
2018-10-08 14:02:12 +02:00
}
2018-12-18 12:35:49 +01:00
#if ENABLE_RENDER_SELECTION_CENTER
GLCanvas3D :: Selection ::~ Selection ()
{
if ( m_quadric != nullptr )
:: gluDeleteQuadric ( m_quadric );
}
#endif // ENABLE_RENDER_SELECTION_CENTER
2018-10-08 14:02:12 +02:00
void GLCanvas3D :: Selection :: set_volumes ( GLVolumePtrs * volumes )
{
m_volumes = volumes ;
2018-10-11 08:26:12 +02:00
_update_valid ();
2018-10-08 14:02:12 +02:00
}
2018-12-19 14:44:37 +01:00
#if ENABLE_SIDEBAR_VISUAL_HINTS
bool GLCanvas3D :: Selection :: init ( bool useVBOs )
{
2018-12-20 11:14:53 +01:00
if ( ! m_arrow . init ( useVBOs ))
return false ;
2018-12-19 14:44:37 +01:00
2018-12-20 11:14:53 +01:00
m_arrow . set_scale ( 5.0 * Vec3d :: Ones ());
if ( ! m_curved_arrow . init ( useVBOs ))
return false ;
m_curved_arrow . set_scale ( 5.0 * Vec3d :: Ones ());
return true ;
2018-12-19 14:44:37 +01:00
}
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
2018-10-08 14:02:12 +02:00
void GLCanvas3D :: Selection :: set_model ( Model * model )
{
m_model = model ;
2018-10-11 08:26:12 +02:00
_update_valid ();
2018-10-08 14:02:12 +02:00
}
void GLCanvas3D :: Selection :: add ( unsigned int volume_idx , bool as_single_selection )
{
if ( ! m_valid || (( unsigned int ) m_volumes -> size () <= volume_idx ))
return ;
const GLVolume * volume = ( * m_volumes )[ volume_idx ];
2018-11-06 15:51:33 +01:00
// wipe tower is already selected
if ( is_wipe_tower () && volume -> is_wipe_tower )
return ;
// resets the current list if needed
bool needs_reset = as_single_selection ;
needs_reset |= volume -> is_wipe_tower ;
needs_reset |= is_wipe_tower () && ! volume -> is_wipe_tower ;
needs_reset |= ! is_modifier () && volume -> is_modifier ;
needs_reset |= is_modifier () && ! volume -> is_modifier ;
if ( needs_reset )
2018-10-08 14:02:12 +02:00
clear ();
2018-11-07 12:11:34 +01:00
if ( volume -> is_modifier )
m_mode = Volume ;
2018-11-06 15:51:33 +01:00
2018-10-08 14:02:12 +02:00
switch ( m_mode )
{
case Volume :
{
2018-11-13 18:44:30 +01:00
if ( volume -> volume_idx () >= 0 && ( is_empty () || ( volume -> instance_idx () == get_instance_idx ())))
2018-11-06 15:51:33 +01:00
_add_volume ( volume_idx );
2018-10-08 14:02:12 +02:00
break ;
}
case Instance :
{
2018-10-11 08:26:12 +02:00
_add_instance ( volume -> object_idx (), volume -> instance_idx ());
2018-10-08 14:02:12 +02:00
break ;
}
}
2018-10-11 08:26:12 +02:00
_update_type ();
2018-10-08 14:02:12 +02:00
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: remove ( unsigned int volume_idx )
{
if ( ! m_valid || (( unsigned int ) m_volumes -> size () <= volume_idx ))
return ;
2018-10-11 08:26:12 +02:00
GLVolume * volume = ( * m_volumes )[ volume_idx ];
2018-10-08 14:02:12 +02:00
switch ( m_mode )
{
case Volume :
{
2018-10-11 08:26:12 +02:00
_remove_volume ( volume_idx );
2018-10-08 14:02:12 +02:00
break ;
}
case Instance :
{
2018-10-11 08:26:12 +02:00
_remove_instance ( volume -> object_idx (), volume -> instance_idx ());
2018-10-08 14:02:12 +02:00
break ;
}
}
2018-10-11 08:26:12 +02:00
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: add_object ( unsigned int object_idx , bool as_single_selection )
{
if ( ! m_valid )
return ;
// resets the current list if needed
if ( as_single_selection )
clear ();
2018-11-07 12:11:34 +01:00
m_mode = Instance ;
2018-10-11 08:26:12 +02:00
_add_object ( object_idx );
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: remove_object ( unsigned int object_idx )
{
if ( ! m_valid )
return ;
_remove_object ( object_idx );
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: add_instance ( unsigned int object_idx , unsigned int instance_idx , bool as_single_selection )
{
if ( ! m_valid )
return ;
// resets the current list if needed
if ( as_single_selection )
clear ();
2018-11-07 12:11:34 +01:00
m_mode = Instance ;
2018-10-11 08:26:12 +02:00
_add_instance ( object_idx , instance_idx );
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: remove_instance ( unsigned int object_idx , unsigned int instance_idx )
{
if ( ! m_valid )
return ;
_remove_instance ( object_idx , instance_idx );
_update_type ();
m_bounding_box_dirty = true ;
}
2018-11-07 10:34:44 +01:00
void GLCanvas3D :: Selection :: add_volume ( unsigned int object_idx , unsigned int volume_idx , int instance_idx , bool as_single_selection )
2018-10-11 08:26:12 +02:00
{
if ( ! m_valid )
return ;
// resets the current list if needed
if ( as_single_selection )
clear ();
2018-11-07 12:11:34 +01:00
m_mode = Volume ;
2018-10-11 08:26:12 +02:00
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if (( v -> object_idx () == object_idx ) && ( v -> volume_idx () == volume_idx ))
2018-11-07 10:34:44 +01:00
{
if (( instance_idx != - 1 ) && ( v -> instance_idx () == instance_idx ))
2018-11-07 12:11:34 +01:00
_add_volume ( i );
2018-11-07 10:34:44 +01:00
}
2018-10-11 08:26:12 +02:00
}
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: remove_volume ( unsigned int object_idx , unsigned int volume_idx )
{
if ( ! m_valid )
return ;
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if (( v -> object_idx () == object_idx ) && ( v -> volume_idx () == volume_idx ))
_remove_volume ( i );
}
_update_type ();
2018-10-08 14:02:12 +02:00
m_bounding_box_dirty = true ;
}
2018-11-21 15:28:35 +01:00
void GLCanvas3D :: Selection :: add_all ()
{
if ( ! m_valid )
return ;
m_mode = Instance ;
2018-11-23 11:47:18 +01:00
clear ();
2018-11-21 15:28:35 +01:00
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
2018-11-23 11:47:18 +01:00
if ( ! ( * m_volumes )[ i ] -> is_wipe_tower )
_add_volume ( i );
2018-11-21 15:28:35 +01:00
}
_update_type ();
m_bounding_box_dirty = true ;
}
2018-10-08 14:02:12 +02:00
void GLCanvas3D :: Selection :: clear ()
{
if ( ! m_valid )
return ;
for ( unsigned int i : m_list )
{
( * m_volumes )[ i ] -> selected = false ;
}
m_list . clear ();
2018-11-07 12:11:34 +01:00
2018-10-11 08:26:12 +02:00
_update_type ();
2018-10-08 14:02:12 +02:00
m_bounding_box_dirty = true ;
}
2018-11-16 18:28:50 +01:00
// Update the selection based on the map from old indices to new indices after m_volumes changed.
// If the current selection is by instance, this call may select newly added volumes, if they belong to already selected instances.
void GLCanvas3D :: Selection :: volumes_changed ( const std :: vector < size_t > & map_volume_old_to_new )
{
assert ( m_valid );
// 1) Update the selection set.
IndicesList list_new ;
std :: vector < std :: pair < unsigned int , unsigned int >> model_instances ;
for ( unsigned int idx : m_list ) {
if ( map_volume_old_to_new [ idx ] != size_t ( - 1 )) {
unsigned int new_idx = ( unsigned int ) map_volume_old_to_new [ idx ];
list_new . insert ( new_idx );
if ( m_mode == Instance ) {
// Save the object_idx / instance_idx pair of selected old volumes,
// so we may add the newly added volumes of the same object_idx / instance_idx pair
// to the selection.
const GLVolume * volume = ( * m_volumes )[ new_idx ];
model_instances . emplace_back ( volume -> object_idx (), volume -> instance_idx ());
}
}
}
m_list = std :: move ( list_new );
if ( ! model_instances . empty ()) {
// Instance selection mode. Add the newly added volumes of the same object_idx / instance_idx pair
// to the selection.
assert ( m_mode == Instance );
sort_remove_duplicates ( model_instances );
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i ) {
const GLVolume * volume = ( * m_volumes )[ i ];
for ( const std :: pair < int , int > & model_instance : model_instances )
if ( volume -> object_idx () == model_instance . first && volume -> instance_idx () == model_instance . second )
this -> _add_volume ( i );
}
}
_update_type ();
m_bounding_box_dirty = true ;
}
2018-10-09 15:56:34 +02:00
bool GLCanvas3D :: Selection :: is_single_full_instance () const
2018-10-08 14:02:12 +02:00
{
2018-10-09 15:56:34 +02:00
if ( m_type == SingleFullInstance )
return true ;
2018-12-11 09:43:10 +01:00
if ( m_type == SingleFullObject )
2018-12-11 10:35:12 +01:00
return get_instance_idx () != - 1 ;
2018-12-11 09:43:10 +01:00
2018-12-05 10:26:08 +01:00
if ( m_list . empty () || m_volumes -> empty ())
return false ;
2018-10-08 14:02:12 +02:00
2018-12-05 10:26:08 +01:00
int object_idx = m_valid ? get_object_idx () : - 1 ;
2018-12-07 09:01:58 +01:00
if (( object_idx < 0 ) || (( int ) m_model -> objects . size () <= object_idx ))
2018-12-05 10:26:08 +01:00
return false ;
int instance_idx = ( * m_volumes )[ * m_list . begin ()] -> instance_idx ();
std :: set < int > volumes_idxs ;
2018-12-05 13:55:38 +01:00
for ( unsigned int i : m_list )
2018-12-05 10:26:08 +01:00
{
2018-12-05 13:55:38 +01:00
const GLVolume * v = ( * m_volumes )[ i ];
2018-12-05 10:26:08 +01:00
int volume_idx = v -> volume_idx ();
if (( v -> object_idx () == object_idx ) && ( v -> instance_idx () == instance_idx ) && ( volume_idx >= 0 ))
volumes_idxs . insert ( volume_idx );
}
return m_model -> objects [ object_idx ] -> volumes . size () == volumes_idxs . size ();
2018-10-08 14:02:12 +02:00
}
2018-11-23 11:47:18 +01:00
bool GLCanvas3D :: Selection :: is_from_single_object () const
{
int idx = get_object_idx ();
return ( 0 <= idx ) && ( idx < 1000 );
}
2018-12-18 14:08:46 +01:00
bool GLCanvas3D :: Selection :: requires_uniform_scale () const
{
if ( is_single_full_instance () || is_single_modifier () || is_single_volume ())
return false ;
return true ;
}
2018-10-09 15:56:34 +02:00
int GLCanvas3D :: Selection :: get_object_idx () const
2018-10-08 14:02:12 +02:00
{
2018-10-09 15:56:34 +02:00
return ( m_cache . content . size () == 1 ) ? m_cache . content . begin () -> first : - 1 ;
2018-10-08 14:02:12 +02:00
}
2018-10-09 15:56:34 +02:00
int GLCanvas3D :: Selection :: get_instance_idx () const
2018-10-08 14:02:12 +02:00
{
2018-10-09 15:56:34 +02:00
if ( m_cache . content . size () == 1 )
2018-10-08 14:02:12 +02:00
{
2018-10-09 15:56:34 +02:00
const InstanceIdxsList & idxs = m_cache . content . begin () -> second ;
if ( idxs . size () == 1 )
return * idxs . begin ();
2018-10-08 14:02:12 +02:00
}
2018-10-09 15:56:34 +02:00
return - 1 ;
2018-10-08 14:02:12 +02:00
}
2018-11-02 15:20:26 +01:00
const GLCanvas3D :: Selection :: InstanceIdxsList & GLCanvas3D :: Selection :: get_instance_idxs () const
{
2018-11-03 08:46:51 +01:00
assert ( m_cache . content . size () == 1 );
2018-11-02 15:20:26 +01:00
return m_cache . content . begin () -> second ;
}
2018-10-08 14:02:12 +02:00
const GLVolume * GLCanvas3D :: Selection :: get_volume ( unsigned int volume_idx ) const
{
2018-10-09 15:56:34 +02:00
return ( m_valid && ( volume_idx < ( unsigned int ) m_volumes -> size ())) ? ( * m_volumes )[ volume_idx ] : nullptr ;
2018-10-08 14:02:12 +02:00
}
const BoundingBoxf3 & GLCanvas3D :: Selection :: get_bounding_box () const
{
if ( m_bounding_box_dirty )
2018-10-11 08:26:12 +02:00
_calc_bounding_box ();
2018-10-08 14:02:12 +02:00
return m_bounding_box ;
}
void GLCanvas3D :: Selection :: start_dragging ()
{
if ( ! m_valid )
return ;
2018-10-11 08:26:12 +02:00
_set_caches ();
2018-10-08 14:02:12 +02:00
}
2018-12-19 15:03:49 +01:00
void GLCanvas3D :: Selection :: translate ( const Vec3d & displacement , bool local )
2018-10-08 14:02:12 +02:00
{
if ( ! m_valid )
return ;
for ( unsigned int i : m_list )
{
2018-12-07 11:24:29 +01:00
if (( m_mode == Volume ) || ( * m_volumes )[ i ] -> is_wipe_tower )
{
2018-12-19 15:03:49 +01:00
if ( local )
2018-12-18 13:07:50 +01:00
( * m_volumes )[ i ] -> set_volume_offset ( m_cache . volumes_data [ i ]. get_volume_position () + displacement );
else
{
Vec3d local_displacement = ( m_cache . volumes_data [ i ]. get_instance_rotation_matrix () * m_cache . volumes_data [ i ]. get_instance_scale_matrix () * m_cache . volumes_data [ i ]. get_instance_mirror_matrix ()). inverse () * displacement ;
( * m_volumes )[ i ] -> set_volume_offset ( m_cache . volumes_data [ i ]. get_volume_position () + local_displacement );
}
2018-12-07 11:24:29 +01:00
}
else if ( m_mode == Instance )
( * m_volumes )[ i ] -> set_instance_offset ( m_cache . volumes_data [ i ]. get_instance_position () + displacement );
2018-10-09 15:56:34 +02:00
}
2018-11-06 15:51:33 +01:00
#if !DISABLE_INSTANCES_SYNCH
2018-11-14 12:33:48 +01:00
if ( m_mode == Instance )
_synchronize_unselected_instances ();
else if ( m_mode == Volume )
2018-11-06 15:51:33 +01:00
_synchronize_unselected_volumes ();
#endif // !DISABLE_INSTANCES_SYNCH
2018-10-09 15:56:34 +02:00
m_bounding_box_dirty = true ;
}
2018-11-20 11:57:01 +01:00
void GLCanvas3D :: Selection :: rotate ( const Vec3d & rotation , bool local )
2018-10-09 15:56:34 +02:00
{
if ( ! m_valid )
return ;
for ( unsigned int i : m_list )
{
2018-10-12 15:43:29 +02:00
if ( is_single_full_instance ())
2018-11-21 09:28:27 +01:00
#if ENABLE_WORLD_ROTATIONS
{
2018-12-18 11:11:06 +01:00
if ( local )
( * m_volumes )[ i ] -> set_instance_rotation ( rotation );
else
{
Transform3d m = Geometry :: assemble_transform ( Vec3d :: Zero (), rotation );
Vec3d new_rotation = Geometry :: extract_euler_angles ( m * m_cache . volumes_data [ i ]. get_instance_rotation_matrix ());
( * m_volumes )[ i ] -> set_instance_rotation ( new_rotation );
}
2018-11-21 09:28:27 +01:00
}
#else
2018-11-02 12:11:28 +01:00
( * m_volumes )[ i ] -> set_instance_rotation ( rotation );
2018-11-21 09:28:27 +01:00
#endif // ENABLE_WORLD_ROTATIONS
2018-11-12 08:54:22 +01:00
else if ( is_single_volume () || is_single_modifier ())
2018-11-21 09:28:27 +01:00
#if ENABLE_WORLD_ROTATIONS
{
2018-12-19 15:03:49 +01:00
if ( requires_local_axes ())
2018-12-18 13:07:50 +01:00
( * m_volumes )[ i ] -> set_volume_rotation ( rotation );
else
{
Transform3d m = Geometry :: assemble_transform ( Vec3d :: Zero (), rotation );
const Transform3d & inst_m = m_cache . volumes_data [ i ]. get_instance_rotation_matrix ();
Vec3d new_rotation = Geometry :: extract_euler_angles ( inst_m . inverse () * m * inst_m * m_cache . volumes_data [ i ]. get_volume_rotation_matrix ());
( * m_volumes )[ i ] -> set_volume_rotation ( new_rotation );
}
}
2018-11-21 09:28:27 +01:00
#else
2018-11-12 08:54:22 +01:00
( * m_volumes )[ i ] -> set_volume_rotation ( rotation );
2018-11-21 09:28:27 +01:00
#endif // ENABLE_WORLD_ROTATIONS
2018-10-09 15:56:34 +02:00
else
{
2018-10-18 15:09:41 +02:00
Transform3d m = Geometry :: assemble_transform ( Vec3d :: Zero (), rotation );
2018-11-02 12:11:28 +01:00
if ( m_mode == Instance )
{
// extracts rotations from the composed transformation
Vec3d new_rotation = Geometry :: extract_euler_angles ( m * m_cache . volumes_data [ i ]. get_instance_rotation_matrix ());
2018-11-20 11:57:01 +01:00
if ( ! local )
( * m_volumes )[ i ] -> set_instance_offset ( m_cache . dragging_center + m * ( m_cache . volumes_data [ i ]. get_instance_position () - m_cache . dragging_center ));
2018-11-02 12:11:28 +01:00
( * m_volumes )[ i ] -> set_instance_rotation ( new_rotation );
}
else if ( m_mode == Volume )
{
// extracts rotations from the composed transformation
Vec3d new_rotation = Geometry :: extract_euler_angles ( m * m_cache . volumes_data [ i ]. get_volume_rotation_matrix ());
2018-11-20 11:57:01 +01:00
if ( ! local )
{
Vec3d offset = m * ( m_cache . volumes_data [ i ]. get_volume_position () + m_cache . volumes_data [ i ]. get_instance_position () - m_cache . dragging_center );
( * m_volumes )[ i ] -> set_volume_offset ( m_cache . dragging_center - m_cache . volumes_data [ i ]. get_instance_position () + offset );
}
2018-11-02 12:11:28 +01:00
( * m_volumes )[ i ] -> set_volume_rotation ( new_rotation );
}
2018-10-12 15:43:29 +02:00
}
}
2018-10-31 09:39:43 +01:00
#if !DISABLE_INSTANCES_SYNCH
2018-10-12 15:43:29 +02:00
if ( m_mode == Instance )
_synchronize_unselected_instances ();
2018-11-06 15:51:33 +01:00
else if ( m_mode == Volume )
_synchronize_unselected_volumes ();
2018-10-31 09:39:43 +01:00
#endif // !DISABLE_INSTANCES_SYNCH
2018-10-12 15:43:29 +02:00
m_bounding_box_dirty = true ;
}
2018-11-02 15:20:26 +01:00
void GLCanvas3D :: Selection :: flattening_rotate ( const Vec3d & normal )
{
// We get the normal in untransformed coordinates. We must transform it using the instance matrix, find out
// how to rotate the instance so it faces downwards and do the rotation. All that for all selected instances.
// The function assumes that is_from_single_object() holds.
if ( ! m_valid )
return ;
for ( unsigned int i : m_list )
{
2018-11-05 09:34:04 +01:00
Transform3d wst = m_cache . volumes_data [ i ]. get_instance_scale_matrix () * m_cache . volumes_data [ i ]. get_volume_scale_matrix ();
Vec3d scaling_factor = Vec3d ( 1. / wst ( 0 , 0 ), 1. / wst ( 1 , 1 ), 1. / wst ( 2 , 2 ));
2018-11-02 15:20:26 +01:00
2018-11-05 09:34:04 +01:00
Vec3d rotation = Geometry :: extract_euler_angles ( m_cache . volumes_data [ i ]. get_instance_rotation_matrix () * m_cache . volumes_data [ i ]. get_volume_rotation_matrix ());
Vec3d transformed_normal = Geometry :: assemble_transform ( Vec3d :: Zero (), rotation , scaling_factor ) * normal ;
2018-11-02 15:20:26 +01:00
transformed_normal . normalize ();
Vec3d axis = transformed_normal ( 2 ) > 0.999f ? Vec3d ( 1. , 0. , 0. ) : Vec3d ( transformed_normal . cross ( Vec3d ( 0. , 0. , - 1. )));
axis . normalize ();
Transform3d extra_rotation = Transform3d :: Identity ();
extra_rotation . rotate ( Eigen :: AngleAxisd ( acos ( - transformed_normal ( 2 )), axis ));
2018-11-05 09:34:04 +01:00
Vec3d new_rotation = Geometry :: extract_euler_angles ( extra_rotation * m_cache . volumes_data [ i ]. get_instance_rotation_matrix () );
( * m_volumes )[ i ] -> set_instance_rotation ( new_rotation );
2018-11-02 15:20:26 +01:00
}
2018-11-06 08:47:21 +01:00
#if !DISABLE_INSTANCES_SYNCH
2019-01-04 21:34:25 +01:00
// we want to synchronize z-rotation as well, otherwise the flattening behaves funny
// when applied on one of several identical instances
2018-11-06 08:47:21 +01:00
if ( m_mode == Instance )
2019-01-04 21:34:25 +01:00
_synchronize_unselected_instances ( true );
2018-11-06 08:47:21 +01:00
#endif // !DISABLE_INSTANCES_SYNCH
2018-11-02 15:20:26 +01:00
m_bounding_box_dirty = true ;
}
2018-11-20 15:39:36 +01:00
void GLCanvas3D :: Selection :: scale ( const Vec3d & scale , bool local )
2018-10-12 15:43:29 +02:00
{
if ( ! m_valid )
return ;
for ( unsigned int i : m_list )
{
if ( is_single_full_instance ())
2018-11-02 12:11:28 +01:00
( * m_volumes )[ i ] -> set_instance_scaling_factor ( scale );
2018-11-12 08:54:22 +01:00
else if ( is_single_volume () || is_single_modifier ())
( * m_volumes )[ i ] -> set_volume_scaling_factor ( scale );
2018-10-12 15:43:29 +02:00
else
{
2018-10-18 15:09:41 +02:00
Transform3d m = Geometry :: assemble_transform ( Vec3d :: Zero (), Vec3d :: Zero (), scale );
2018-11-02 12:11:28 +01:00
if ( m_mode == Instance )
{
Eigen :: Matrix < double , 3 , 3 , Eigen :: DontAlign > new_matrix = ( m * m_cache . volumes_data [ i ]. get_instance_scale_matrix ()). matrix (). block ( 0 , 0 , 3 , 3 );
// extracts scaling factors from the composed transformation
Vec3d new_scale ( new_matrix . col ( 0 ). norm (), new_matrix . col ( 1 ). norm (), new_matrix . col ( 2 ). norm ());
2018-11-20 15:39:36 +01:00
if ( ! local )
( * m_volumes )[ i ] -> set_instance_offset ( m_cache . dragging_center + m * ( m_cache . volumes_data [ i ]. get_instance_position () - m_cache . dragging_center ));
2018-11-02 12:11:28 +01:00
( * m_volumes )[ i ] -> set_instance_scaling_factor ( new_scale );
}
else if ( m_mode == Volume )
{
Eigen :: Matrix < double , 3 , 3 , Eigen :: DontAlign > new_matrix = ( m * m_cache . volumes_data [ i ]. get_volume_scale_matrix ()). matrix (). block ( 0 , 0 , 3 , 3 );
// extracts scaling factors from the composed transformation
Vec3d new_scale ( new_matrix . col ( 0 ). norm (), new_matrix . col ( 1 ). norm (), new_matrix . col ( 2 ). norm ());
2018-11-20 15:39:36 +01:00
if ( ! local )
{
Vec3d offset = m * ( m_cache . volumes_data [ i ]. get_volume_position () + m_cache . volumes_data [ i ]. get_instance_position () - m_cache . dragging_center );
( * m_volumes )[ i ] -> set_volume_offset ( m_cache . dragging_center - m_cache . volumes_data [ i ]. get_instance_position () + offset );
}
2018-11-02 12:11:28 +01:00
( * m_volumes )[ i ] -> set_volume_scaling_factor ( new_scale );
}
2018-10-09 15:56:34 +02:00
}
2018-10-08 14:02:12 +02:00
}
2018-10-31 09:39:43 +01:00
#if !DISABLE_INSTANCES_SYNCH
2018-10-10 11:04:04 +02:00
if ( m_mode == Instance )
2018-10-11 08:26:12 +02:00
_synchronize_unselected_instances ();
2018-11-06 15:51:33 +01:00
else if ( m_mode == Volume )
_synchronize_unselected_volumes ();
2018-10-31 09:39:43 +01:00
#endif // !DISABLE_INSTANCES_SYNCH
2018-10-10 11:04:04 +02:00
2018-11-16 11:30:36 +01:00
#if ENABLE_ENSURE_ON_BED_WHILE_SCALING
_ensure_on_bed ();
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
2018-10-08 14:02:12 +02:00
m_bounding_box_dirty = true ;
}
2018-10-18 15:50:51 +02:00
void GLCanvas3D :: Selection :: mirror ( Axis axis )
{
if ( ! m_valid )
return ;
2018-11-07 14:51:11 +01:00
bool single_full_instance = is_single_full_instance ();
2018-10-18 15:50:51 +02:00
for ( unsigned int i : m_list )
{
2018-11-07 14:51:11 +01:00
if ( single_full_instance )
2018-11-02 12:11:28 +01:00
( * m_volumes )[ i ] -> set_instance_mirror ( axis , - ( * m_volumes )[ i ] -> get_instance_mirror ( axis ));
2018-11-07 14:51:11 +01:00
else if ( m_mode == Volume )
( * m_volumes )[ i ] -> set_volume_mirror ( axis , - ( * m_volumes )[ i ] -> get_volume_mirror ( axis ));
2018-10-18 15:50:51 +02:00
}
2018-10-31 09:39:43 +01:00
#if !DISABLE_INSTANCES_SYNCH
2018-10-18 15:50:51 +02:00
if ( m_mode == Instance )
_synchronize_unselected_instances ();
2018-11-06 15:51:33 +01:00
else if ( m_mode == Volume )
_synchronize_unselected_volumes ();
2018-10-31 09:39:43 +01:00
#endif // !DISABLE_INSTANCES_SYNCH
2018-10-18 15:50:51 +02:00
m_bounding_box_dirty = true ;
}
2018-10-30 16:03:03 +01:00
void GLCanvas3D :: Selection :: translate ( unsigned int object_idx , const Vec3d & displacement )
{
if ( ! m_valid )
return ;
for ( unsigned int i : m_list )
{
GLVolume * v = ( * m_volumes )[ i ];
if ( v -> object_idx () == object_idx )
2018-11-02 12:11:28 +01:00
v -> set_instance_offset ( v -> get_instance_offset () + displacement );
2018-10-30 16:03:03 +01:00
}
std :: set < unsigned int > done ; // prevent processing volumes twice
done . insert ( m_list . begin (), m_list . end ());
for ( unsigned int i : m_list )
{
if ( done . size () == m_volumes -> size ())
break ;
2018-10-31 09:39:43 +01:00
int object_idx = ( * m_volumes )[ i ] -> object_idx ();
2018-10-30 16:03:03 +01:00
if ( object_idx >= 1000 )
continue ;
// Process unselected volumes of the object.
for ( unsigned int j = 0 ; j < ( unsigned int ) m_volumes -> size (); ++ j )
{
if ( done . size () == m_volumes -> size ())
break ;
if ( done . find ( j ) != done . end ())
continue ;
GLVolume * v = ( * m_volumes )[ j ];
if ( v -> object_idx () != object_idx )
continue ;
2018-11-02 12:11:28 +01:00
v -> set_instance_offset ( v -> get_instance_offset () + displacement );
2018-10-30 16:03:03 +01:00
done . insert ( j );
}
}
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: translate ( unsigned int object_idx , unsigned int instance_idx , const Vec3d & displacement )
{
if ( ! m_valid )
return ;
for ( unsigned int i : m_list )
{
GLVolume * v = ( * m_volumes )[ i ];
if (( v -> object_idx () == object_idx ) && ( v -> instance_idx () == instance_idx ))
2018-11-02 12:11:28 +01:00
v -> set_instance_offset ( v -> get_instance_offset () + displacement );
2018-10-30 16:03:03 +01:00
}
std :: set < unsigned int > done ; // prevent processing volumes twice
done . insert ( m_list . begin (), m_list . end ());
for ( unsigned int i : m_list )
{
if ( done . size () == m_volumes -> size ())
break ;
2018-10-31 09:39:43 +01:00
int object_idx = ( * m_volumes )[ i ] -> object_idx ();
2018-10-30 16:03:03 +01:00
if ( object_idx >= 1000 )
continue ;
// Process unselected volumes of the object.
for ( unsigned int j = 0 ; j < ( unsigned int ) m_volumes -> size (); ++ j )
{
if ( done . size () == m_volumes -> size ())
break ;
if ( done . find ( j ) != done . end ())
continue ;
GLVolume * v = ( * m_volumes )[ j ];
if (( v -> object_idx () != object_idx ) || ( v -> instance_idx () != instance_idx ))
continue ;
2018-11-02 12:11:28 +01:00
v -> set_instance_offset ( v -> get_instance_offset () + displacement );
2018-10-30 16:03:03 +01:00
done . insert ( j );
}
}
m_bounding_box_dirty = true ;
}
2018-11-14 08:53:56 +01:00
void GLCanvas3D :: Selection :: erase ()
{
if ( ! m_valid )
return ;
if ( is_single_full_object ())
wxGetApp (). obj_list () -> delete_from_model_and_list ( ItemType :: itObject , get_object_idx (), 0 );
else if ( is_multiple_full_object ())
{
std :: vector < ItemForDelete > items ;
items . reserve ( m_cache . content . size ());
for ( ObjectIdxsToInstanceIdxsMap :: iterator it = m_cache . content . begin (); it != m_cache . content . end (); ++ it )
{
items . emplace_back ( ItemType :: itObject , it -> first , 0 );
}
wxGetApp (). obj_list () -> delete_from_model_and_list ( items );
}
else if ( is_multiple_full_instance ())
{
std :: set < std :: pair < int , int >> instances_idxs ;
for ( ObjectIdxsToInstanceIdxsMap :: iterator obj_it = m_cache . content . begin (); obj_it != m_cache . content . end (); ++ obj_it )
{
for ( InstanceIdxsList :: reverse_iterator inst_it = obj_it -> second . rbegin (); inst_it != obj_it -> second . rend (); ++ inst_it )
{
instances_idxs . insert ( std :: make_pair ( obj_it -> first , * inst_it ));
}
}
std :: vector < ItemForDelete > items ;
items . reserve ( instances_idxs . size ());
for ( const std :: pair < int , int >& i : instances_idxs )
{
items . emplace_back ( ItemType :: itInstance , i . first , i . second );
}
wxGetApp (). obj_list () -> delete_from_model_and_list ( items );
}
2019-01-04 21:36:58 +01:00
else if ( is_single_full_instance ())
wxGetApp (). obj_list () -> delete_from_model_and_list ( ItemType :: itInstance , get_object_idx (), get_instance_idx ());
2018-12-03 12:13:18 +01:00
else if ( is_mixed ())
{
std :: set < ItemForDelete > items_set ;
std :: map < int , int > volumes_in_obj ;
for ( auto i : m_list ) {
const auto gl_vol = ( * m_volumes )[ i ];
const auto glv_obj_idx = gl_vol -> object_idx ();
const auto model_object = m_model -> objects [ glv_obj_idx ];
if ( model_object -> instances . size () == 1 ) {
if ( model_object -> volumes . size () == 1 )
items_set . insert ( ItemForDelete ( ItemType :: itObject , glv_obj_idx , - 1 ));
else {
items_set . insert ( ItemForDelete ( ItemType :: itVolume , glv_obj_idx , gl_vol -> volume_idx ()));
int idx = ( volumes_in_obj . find ( glv_obj_idx ) == volumes_in_obj . end ()) ? 0 : volumes_in_obj . at ( glv_obj_idx );
volumes_in_obj [ glv_obj_idx ] = ++ idx ;
}
continue ;
}
const auto glv_ins_idx = gl_vol -> instance_idx ();
for ( auto obj_ins : m_cache . content ) {
if ( obj_ins . first == glv_obj_idx ) {
if ( obj_ins . second . find ( glv_ins_idx ) != obj_ins . second . end ()) {
if ( obj_ins . second . size () == model_object -> instances . size ())
items_set . insert ( ItemForDelete ( ItemType :: itVolume , glv_obj_idx , gl_vol -> volume_idx ()));
else
items_set . insert ( ItemForDelete ( ItemType :: itInstance , glv_obj_idx , glv_ins_idx ));
break ;
}
}
}
}
std :: vector < ItemForDelete > items ;
items . reserve ( items_set . size ());
for ( const ItemForDelete & i : items_set ) {
if ( i . type == ItemType :: itVolume ) {
const int vol_in_obj_cnt = volumes_in_obj . find ( i . obj_idx ) == volumes_in_obj . end () ? 0 : volumes_in_obj . at ( i . obj_idx );
if ( vol_in_obj_cnt == m_model -> objects [ i . obj_idx ] -> volumes . size ()) {
if ( i . sub_obj_idx == vol_in_obj_cnt - 1 )
items . emplace_back ( ItemType :: itObject , i . obj_idx , 0 );
continue ;
}
}
items . emplace_back ( i . type , i . obj_idx , i . sub_obj_idx );
}
wxGetApp (). obj_list () -> delete_from_model_and_list ( items );
}
2018-11-14 08:53:56 +01:00
else
{
std :: set < std :: pair < int , int >> volumes_idxs ;
for ( unsigned int i : m_list )
{
const GLVolume * v = ( * m_volumes )[ i ];
2018-11-17 17:23:56 +01:00
// Only remove volumes associated with ModelVolumes from the object list.
// Temporary meshes (SLA supports or pads) are not managed by the object list.
if ( v -> volume_idx () >= 0 )
volumes_idxs . insert ( std :: make_pair ( v -> object_idx (), v -> volume_idx ()));
2018-11-14 08:53:56 +01:00
}
std :: vector < ItemForDelete > items ;
items . reserve ( volumes_idxs . size ());
for ( const std :: pair < int , int >& v : volumes_idxs )
{
items . emplace_back ( ItemType :: itVolume , v . first , v . second );
}
wxGetApp (). obj_list () -> delete_from_model_and_list ( items );
}
}
2018-11-06 10:31:19 +01:00
void GLCanvas3D :: Selection :: render () const
2018-10-08 14:02:12 +02:00
{
2018-12-18 12:35:49 +01:00
if ( ! m_valid || is_empty ())
2018-10-08 14:02:12 +02:00
return ;
2018-10-10 11:04:04 +02:00
// render cumulative bounding box of selected volumes
2018-10-11 08:26:12 +02:00
_render_selected_volumes ();
2018-11-07 12:11:34 +01:00
_render_synchronized_volumes ();
2018-10-08 14:02:12 +02:00
}
2018-12-18 12:35:49 +01:00
#if ENABLE_RENDER_SELECTION_CENTER
void GLCanvas3D :: Selection :: render_center () const
{
if ( ! m_valid || is_empty () || ( m_quadric == nullptr ))
return ;
const Vec3d & center = get_bounding_box (). center ();
:: glDisable ( GL_DEPTH_TEST );
:: glEnable ( GL_LIGHTING );
:: glColor3f ( 1.0f , 1.0f , 1.0f );
:: glPushMatrix ();
:: glTranslated ( center ( 0 ), center ( 1 ), center ( 2 ));
:: gluSphere ( m_quadric , 0.75 , 32 , 32 );
:: glPopMatrix ();
:: glDisable ( GL_LIGHTING );
}
#endif // ENABLE_RENDER_SELECTION_CENTER
2018-12-19 14:44:37 +01:00
#if ENABLE_SIDEBAR_VISUAL_HINTS
void GLCanvas3D :: Selection :: render_sidebar_hints ( const std :: string & sidebar_field ) const
{
if ( sidebar_field . empty ())
return ;
:: glClear ( GL_DEPTH_BUFFER_BIT );
:: glEnable ( GL_DEPTH_TEST );
:: glEnable ( GL_LIGHTING );
:: glPushMatrix ();
const Vec3d & center = get_bounding_box (). center ();
if ( is_single_full_instance ())
2019-01-09 10:12:04 +01:00
{
2018-12-19 14:44:37 +01:00
:: glTranslated ( center ( 0 ), center ( 1 ), center ( 2 ));
2019-01-09 10:12:04 +01:00
if ( boost :: starts_with ( sidebar_field , "scale" ))
{
Transform3d orient_matrix = ( * m_volumes )[ * m_list . begin ()] -> get_instance_transformation (). get_matrix ( true , false , true , true );
:: glMultMatrixd ( orient_matrix . data ());
}
}
2018-12-19 14:44:37 +01:00
else if ( is_single_volume () || is_single_modifier ())
{
2019-01-09 10:12:04 +01:00
Transform3d orient_matrix = ( * m_volumes )[ * m_list . begin ()] -> get_instance_transformation (). get_matrix ( true , false , true , true );
2018-12-19 14:44:37 +01:00
const Vec3d & offset = get_bounding_box (). center ();
:: glTranslated ( offset ( 0 ), offset ( 1 ), offset ( 2 ));
:: glMultMatrixd ( orient_matrix . data ());
}
else
2018-12-20 11:14:53 +01:00
{
2018-12-19 14:44:37 +01:00
:: glTranslated ( center ( 0 ), center ( 1 ), center ( 2 ));
2018-12-20 11:14:53 +01:00
if ( requires_local_axes ())
{
2019-01-09 10:12:04 +01:00
Transform3d orient_matrix = ( * m_volumes )[ * m_list . begin ()] -> get_instance_transformation (). get_matrix ( true , false , true , true );
2018-12-20 11:14:53 +01:00
:: glMultMatrixd ( orient_matrix . data ());
}
}
2018-12-19 14:44:37 +01:00
if ( boost :: starts_with ( sidebar_field , "position" ))
_render_sidebar_position_hints ( sidebar_field );
else if ( boost :: starts_with ( sidebar_field , "rotation" ))
_render_sidebar_rotation_hints ( sidebar_field );
else if ( boost :: starts_with ( sidebar_field , "scale" ))
_render_sidebar_scale_hints ( sidebar_field );
else if ( boost :: starts_with ( sidebar_field , "size" ))
_render_sidebar_size_hints ( sidebar_field );
:: glPopMatrix ();
:: glDisable ( GL_LIGHTING );
}
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
2018-12-19 15:03:49 +01:00
bool GLCanvas3D :: Selection :: requires_local_axes () const
{
return ( m_mode == Volume ) && is_from_single_instance ();
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _update_valid ()
2018-10-08 14:02:12 +02:00
{
m_valid = ( m_volumes != nullptr ) && ( m_model != nullptr );
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _update_type ()
2018-10-09 15:56:34 +02:00
{
m_cache . content . clear ();
m_type = Mixed ;
for ( unsigned int i : m_list )
{
const GLVolume * volume = ( * m_volumes )[ i ];
int obj_idx = volume -> object_idx ();
int inst_idx = volume -> instance_idx ();
ObjectIdxsToInstanceIdxsMap :: iterator obj_it = m_cache . content . find ( obj_idx );
if ( obj_it == m_cache . content . end ())
obj_it = m_cache . content . insert ( ObjectIdxsToInstanceIdxsMap :: value_type ( obj_idx , InstanceIdxsList ())). first ;
obj_it -> second . insert ( inst_idx );
}
2018-11-07 12:11:34 +01:00
bool requires_disable = false ;
2018-10-09 15:56:34 +02:00
if ( ! m_valid )
m_type = Invalid ;
else
{
if ( m_list . empty ())
m_type = Empty ;
else if ( m_list . size () == 1 )
{
const GLVolume * first = ( * m_volumes )[ * m_list . begin ()];
if ( first -> is_wipe_tower )
m_type = WipeTower ;
else if ( first -> is_modifier )
2018-11-07 12:11:34 +01:00
{
2018-11-06 15:51:33 +01:00
m_type = SingleModifier ;
2018-11-07 12:11:34 +01:00
requires_disable = true ;
}
2018-10-09 15:56:34 +02:00
else
{
const ModelObject * model_object = m_model -> objects [ first -> object_idx ()];
unsigned int volumes_count = ( unsigned int ) model_object -> volumes . size ();
unsigned int instances_count = ( unsigned int ) model_object -> instances . size ();
if ( volumes_count * instances_count == 1 )
2018-11-23 10:50:25 +01:00
{
2018-10-09 15:56:34 +02:00
m_type = SingleFullObject ;
2018-11-23 10:50:25 +01:00
// ensures the correct mode is selected
m_mode = Instance ;
}
2018-10-09 15:56:34 +02:00
else if ( volumes_count == 1 ) // instances_count > 1
2018-11-23 10:50:25 +01:00
{
2018-10-09 15:56:34 +02:00
m_type = SingleFullInstance ;
2018-11-23 10:50:25 +01:00
// ensures the correct mode is selected
m_mode = Instance ;
}
2018-11-07 10:34:44 +01:00
else
2018-11-07 12:11:34 +01:00
{
2018-11-07 10:34:44 +01:00
m_type = SingleVolume ;
2018-11-07 12:11:34 +01:00
requires_disable = true ;
}
2018-10-09 15:56:34 +02:00
}
}
else
{
if ( m_cache . content . size () == 1 ) // single object
{
const ModelObject * model_object = m_model -> objects [ m_cache . content . begin () -> first ];
2018-11-22 12:07:26 +01:00
unsigned int model_volumes_count = ( unsigned int ) model_object -> volumes . size ();
unsigned int sla_volumes_count = 0 ;
for ( unsigned int i : m_list )
{
if (( * m_volumes )[ i ] -> volume_idx () < 0 )
++ sla_volumes_count ;
}
unsigned int volumes_count = model_volumes_count + sla_volumes_count ;
2018-10-09 15:56:34 +02:00
unsigned int instances_count = ( unsigned int ) model_object -> instances . size ();
2018-11-07 10:34:44 +01:00
unsigned int selected_instances_count = ( unsigned int ) m_cache . content . begin () -> second . size ();
2018-10-09 15:56:34 +02:00
if ( volumes_count * instances_count == ( unsigned int ) m_list . size ())
2018-11-23 10:50:25 +01:00
{
2018-10-09 15:56:34 +02:00
m_type = SingleFullObject ;
2018-11-23 10:50:25 +01:00
// ensures the correct mode is selected
m_mode = Instance ;
}
2018-11-07 10:34:44 +01:00
else if ( selected_instances_count == 1 )
{
if ( volumes_count == ( unsigned int ) m_list . size ())
2018-11-23 10:50:25 +01:00
{
2018-11-07 10:34:44 +01:00
m_type = SingleFullInstance ;
2018-11-23 10:50:25 +01:00
// ensures the correct mode is selected
m_mode = Instance ;
}
2018-11-07 10:34:44 +01:00
else
{
unsigned int modifiers_count = 0 ;
for ( unsigned int i : m_list )
{
if (( * m_volumes )[ i ] -> is_modifier )
++ modifiers_count ;
}
if ( modifiers_count == 0 )
2018-11-07 12:11:34 +01:00
{
2018-11-07 10:34:44 +01:00
m_type = MultipleVolume ;
2018-11-07 12:11:34 +01:00
requires_disable = true ;
}
2018-11-07 10:34:44 +01:00
else if ( modifiers_count == ( unsigned int ) m_list . size ())
2018-11-07 12:11:34 +01:00
{
2018-11-07 10:34:44 +01:00
m_type = MultipleModifier ;
2018-11-07 12:11:34 +01:00
requires_disable = true ;
}
2018-11-07 10:34:44 +01:00
}
}
else if (( selected_instances_count > 1 ) && ( selected_instances_count * volumes_count == ( unsigned int ) m_list . size ()))
2018-11-23 10:50:25 +01:00
{
2018-11-07 10:34:44 +01:00
m_type = MultipleFullInstance ;
2018-11-23 10:50:25 +01:00
// ensures the correct mode is selected
m_mode = Instance ;
}
2018-10-09 15:56:34 +02:00
}
2018-11-08 15:45:55 +01:00
else
{
int sels_cntr = 0 ;
for ( ObjectIdxsToInstanceIdxsMap :: iterator it = m_cache . content . begin (); it != m_cache . content . end (); ++ it )
{
const ModelObject * model_object = m_model -> objects [ it -> first ];
unsigned int volumes_count = ( unsigned int ) model_object -> volumes . size ();
unsigned int instances_count = ( unsigned int ) model_object -> instances . size ();
sels_cntr += volumes_count * instances_count ;
}
if ( sels_cntr == ( unsigned int ) m_list . size ())
2018-11-23 10:50:25 +01:00
{
2018-11-08 15:45:55 +01:00
m_type = MultipleFullObject ;
2018-11-23 10:50:25 +01:00
// ensures the correct mode is selected
m_mode = Instance ;
}
2018-11-08 15:45:55 +01:00
}
2018-10-09 15:56:34 +02:00
}
}
2018-11-07 12:11:34 +01:00
int object_idx = get_object_idx ();
int instance_idx = get_instance_idx ();
for ( GLVolume * v : * m_volumes )
{
v -> disabled = requires_disable ? ( v -> object_idx () != object_idx ) || ( v -> instance_idx () != instance_idx ) : false ;
}
2018-12-13 08:55:19 +01:00
#if ENABLE_SELECTION_DEBUG_OUTPUT
2018-11-23 10:50:25 +01:00
std :: cout << "Selection: " ;
std :: cout << "mode: " ;
switch ( m_mode )
{
case Volume :
{
std :: cout << "Volume" ;
break ;
}
case Instance :
{
std :: cout << "Instance" ;
break ;
}
}
std :: cout << " - type: " ;
2018-10-09 15:56:34 +02:00
switch ( m_type )
{
case Invalid :
{
2018-11-23 10:50:25 +01:00
std :: cout << "Invalid" << std :: endl ;
2018-10-09 15:56:34 +02:00
break ;
}
case Empty :
{
2018-11-23 10:50:25 +01:00
std :: cout << "Empty" << std :: endl ;
2018-10-09 15:56:34 +02:00
break ;
}
case WipeTower :
{
2018-11-23 10:50:25 +01:00
std :: cout << "WipeTower" << std :: endl ;
2018-10-09 15:56:34 +02:00
break ;
}
2018-11-06 15:51:33 +01:00
case SingleModifier :
2018-10-09 15:56:34 +02:00
{
2018-11-23 10:50:25 +01:00
std :: cout << "SingleModifier" << std :: endl ;
2018-11-06 15:51:33 +01:00
break ;
}
case MultipleModifier :
{
2018-11-23 10:50:25 +01:00
std :: cout << "MultipleModifier" << std :: endl ;
2018-10-09 15:56:34 +02:00
break ;
}
2018-11-07 10:34:44 +01:00
case SingleVolume :
{
2018-11-23 10:50:25 +01:00
std :: cout << "SingleVolume" << std :: endl ;
2018-11-07 10:34:44 +01:00
break ;
}
case MultipleVolume :
{
2018-11-23 10:50:25 +01:00
std :: cout << "MultipleVolume" << std :: endl ;
2018-11-07 10:34:44 +01:00
break ;
}
2018-10-09 15:56:34 +02:00
case SingleFullObject :
{
2018-11-23 10:50:25 +01:00
std :: cout << "SingleFullObject" << std :: endl ;
2018-10-09 15:56:34 +02:00
break ;
}
2018-11-08 15:45:55 +01:00
case MultipleFullObject :
{
2018-11-23 10:50:25 +01:00
std :: cout << "MultipleFullObject" << std :: endl ;
2018-11-08 15:45:55 +01:00
break ;
}
2018-10-09 15:56:34 +02:00
case SingleFullInstance :
{
2018-11-23 10:50:25 +01:00
std :: cout << "SingleFullInstance" << std :: endl ;
2018-10-09 15:56:34 +02:00
break ;
}
2018-11-07 10:34:44 +01:00
case MultipleFullInstance :
{
2018-11-23 10:50:25 +01:00
std :: cout << "MultipleFullInstance" << std :: endl ;
2018-11-07 10:34:44 +01:00
break ;
}
2018-10-09 15:56:34 +02:00
case Mixed :
{
2018-11-23 10:50:25 +01:00
std :: cout << "Mixed" << std :: endl ;
2018-10-09 15:56:34 +02:00
break ;
}
}
2018-12-13 08:55:19 +01:00
#endif // ENABLE_SELECTION_DEBUG_OUTPUT
2018-10-09 15:56:34 +02:00
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _set_caches ()
2018-10-08 14:02:12 +02:00
{
m_cache . volumes_data . clear ();
for ( unsigned int i : m_list )
{
const GLVolume * v = ( * m_volumes )[ i ];
2018-11-02 12:11:28 +01:00
m_cache . volumes_data . emplace ( i , VolumeCache ( v -> get_volume_transformation (), v -> get_instance_transformation ()));
2018-10-08 14:02:12 +02:00
}
m_cache . dragging_center = get_bounding_box (). center ();
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _add_volume ( unsigned int volume_idx )
2018-10-08 14:02:12 +02:00
{
m_list . insert ( volume_idx );
( * m_volumes )[ volume_idx ] -> selected = true ;
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _add_instance ( unsigned int object_idx , unsigned int instance_idx )
2018-10-08 14:02:12 +02:00
{
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if (( v -> object_idx () == object_idx ) && ( v -> instance_idx () == instance_idx ))
2018-10-11 08:26:12 +02:00
_add_volume ( i );
2018-10-08 14:02:12 +02:00
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _add_object ( unsigned int object_idx )
2018-10-08 14:02:12 +02:00
{
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if ( v -> object_idx () == object_idx )
2018-10-11 08:26:12 +02:00
_add_volume ( i );
2018-10-08 14:02:12 +02:00
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _remove_volume ( unsigned int volume_idx )
2018-10-08 14:02:12 +02:00
{
IndicesList :: iterator v_it = m_list . find ( volume_idx );
if ( v_it == m_list . end ())
return ;
m_list . erase ( v_it );
( * m_volumes )[ volume_idx ] -> selected = false ;
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _remove_instance ( unsigned int object_idx , unsigned int instance_idx )
2018-10-08 14:02:12 +02:00
{
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if (( v -> object_idx () == object_idx ) && ( v -> instance_idx () == instance_idx ))
2018-10-11 08:26:12 +02:00
_remove_volume ( i );
2018-10-08 14:02:12 +02:00
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _remove_object ( unsigned int object_idx )
2018-10-08 14:02:12 +02:00
{
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if ( v -> object_idx () == object_idx )
2018-10-11 08:26:12 +02:00
_remove_volume ( i );
2018-10-08 14:02:12 +02:00
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _calc_bounding_box () const
2018-10-08 14:02:12 +02:00
{
m_bounding_box = BoundingBoxf3 ();
if ( m_valid )
{
for ( unsigned int i : m_list )
{
2018-10-16 13:49:40 +02:00
m_bounding_box . merge (( * m_volumes )[ i ] -> transformed_convex_hull_bounding_box ());
2018-10-08 14:02:12 +02:00
}
}
m_bounding_box_dirty = false ;
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _render_selected_volumes () const
2018-10-10 11:04:04 +02:00
{
float color [ 3 ] = { 1.0f , 1.0f , 1.0f };
2018-10-11 08:26:12 +02:00
_render_bounding_box ( get_bounding_box (), color );
2018-10-10 11:04:04 +02:00
}
2018-11-07 12:11:34 +01:00
void GLCanvas3D :: Selection :: _render_synchronized_volumes () const
{
if ( m_mode == Instance )
return ;
float color [ 3 ] = { 1.0f , 1.0f , 0.0f };
for ( unsigned int i : m_list )
{
const GLVolume * volume = ( * m_volumes )[ i ];
int object_idx = volume -> object_idx ();
int instance_idx = volume -> instance_idx ();
int volume_idx = volume -> volume_idx ();
for ( unsigned int j = 0 ; j < ( unsigned int ) m_volumes -> size (); ++ j )
{
if ( i == j )
continue ;
const GLVolume * v = ( * m_volumes )[ j ];
if (( v -> object_idx () != object_idx ) || ( v -> volume_idx () != volume_idx ))
continue ;
_render_bounding_box ( v -> transformed_convex_hull_bounding_box (), color );
}
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _render_bounding_box ( const BoundingBoxf3 & box , float * color ) const
2018-10-08 14:02:12 +02:00
{
if ( color == nullptr )
return ;
Vec3f b_min = box . min . cast < float > ();
Vec3f b_max = box . max . cast < float > ();
2018-11-07 10:34:44 +01:00
Vec3f size = 0.2f * box . size (). cast < float > ();
2018-10-08 14:02:12 +02:00
:: glEnable ( GL_DEPTH_TEST );
:: glColor3fv ( color );
2018-11-07 10:34:44 +01:00
:: glLineWidth ( 2.0f );
2018-10-08 14:02:12 +02:00
:: glBegin ( GL_LINES );
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ) + size ( 0 ), b_min ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ), b_min ( 1 ) + size ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_min ( 2 ) + size ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ) - size ( 0 ), b_min ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ), b_min ( 1 ) + size ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_min ( 2 ) + size ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ) - size ( 0 ), b_max ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ), b_max ( 1 ) - size ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_min ( 2 ) + size ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ) + size ( 0 ), b_max ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ), b_max ( 1 ) - size ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_min ( 2 ) + size ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ) + size ( 0 ), b_min ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ), b_min ( 1 ) + size ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_max ( 2 ) - size ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ) - size ( 0 ), b_min ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ), b_min ( 1 ) + size ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_max ( 2 ) - size ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ) - size ( 0 ), b_max ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ), b_max ( 1 ) - size ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_max ( 2 ) - size ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ) + size ( 0 ), b_max ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ), b_max ( 1 ) - size ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_max ( 2 ) - size ( 2 ));
:: glEnd ();
}
2018-10-10 11:04:04 +02:00
2018-12-19 14:44:37 +01:00
#if ENABLE_SIDEBAR_VISUAL_HINTS
void GLCanvas3D :: Selection :: _render_sidebar_position_hints ( const std :: string & sidebar_field ) const
{
if ( boost :: ends_with ( sidebar_field , "x" ))
{
:: glRotated ( - 90.0 , 0.0 , 0.0 , 1.0 );
_render_sidebar_position_hint ( X );
}
else if ( boost :: ends_with ( sidebar_field , "y" ))
_render_sidebar_position_hint ( Y );
else if ( boost :: ends_with ( sidebar_field , "z" ))
{
:: glRotated ( 90.0 , 1.0 , 0.0 , 0.0 );
_render_sidebar_position_hint ( Z );
}
}
void GLCanvas3D :: Selection :: _render_sidebar_rotation_hints ( const std :: string & sidebar_field ) const
{
2018-12-20 11:14:53 +01:00
if ( boost :: ends_with ( sidebar_field , "x" ))
{
:: glRotated ( 90.0 , 0.0 , 1.0 , 0.0 );
_render_sidebar_rotation_hint ( X );
}
else if ( boost :: ends_with ( sidebar_field , "y" ))
{
:: glRotated ( - 90.0 , 1.0 , 0.0 , 0.0 );
_render_sidebar_rotation_hint ( Y );
}
else if ( boost :: ends_with ( sidebar_field , "z" ))
_render_sidebar_rotation_hint ( Z );
2018-12-19 14:44:37 +01:00
}
void GLCanvas3D :: Selection :: _render_sidebar_scale_hints ( const std :: string & sidebar_field ) const
{
2019-01-08 15:16:40 +01:00
bool uniform_scale = requires_uniform_scale () || wxGetApp (). obj_manipul () -> get_uniform_scaling ();
if ( boost :: ends_with ( sidebar_field , "x" ) || uniform_scale )
2018-12-19 14:44:37 +01:00
{
:: glPushMatrix ();
:: glRotated ( - 90.0 , 0.0 , 0.0 , 1.0 );
_render_sidebar_scale_hint ( X );
:: glPopMatrix ();
}
2019-01-08 15:16:40 +01:00
if ( boost :: ends_with ( sidebar_field , "y" ) || uniform_scale )
2018-12-19 14:44:37 +01:00
{
:: glPushMatrix ();
_render_sidebar_scale_hint ( Y );
:: glPopMatrix ();
}
2019-01-08 15:16:40 +01:00
if ( boost :: ends_with ( sidebar_field , "z" ) || uniform_scale )
2018-12-19 14:44:37 +01:00
{
:: glPushMatrix ();
:: glRotated ( 90.0 , 1.0 , 0.0 , 0.0 );
_render_sidebar_scale_hint ( Z );
:: glPopMatrix ();
}
}
void GLCanvas3D :: Selection :: _render_sidebar_size_hints ( const std :: string & sidebar_field ) const
{
_render_sidebar_scale_hints ( sidebar_field );
}
void GLCanvas3D :: Selection :: _render_sidebar_position_hint ( Axis axis ) const
{
2018-12-20 11:14:53 +01:00
m_arrow . set_color ( AXES_COLOR [ axis ], 3 );
2018-12-19 14:44:37 +01:00
m_arrow . render ();
}
2018-12-20 11:14:53 +01:00
void GLCanvas3D :: Selection :: _render_sidebar_rotation_hint ( Axis axis ) const
2018-12-19 14:44:37 +01:00
{
2018-12-20 11:14:53 +01:00
m_curved_arrow . set_color ( AXES_COLOR [ axis ], 3 );
m_curved_arrow . render ();
:: glRotated ( 180.0 , 0.0 , 0.0 , 1.0 );
m_curved_arrow . render ();
2018-12-19 14:44:37 +01:00
}
void GLCanvas3D :: Selection :: _render_sidebar_scale_hint ( Axis axis ) const
{
2019-01-08 15:16:40 +01:00
m_arrow . set_color ((( requires_uniform_scale () || wxGetApp (). obj_manipul () -> get_uniform_scaling ()) ? UNIFORM_SCALE_COLOR : AXES_COLOR [ axis ]), 3 );
2018-12-19 14:44:37 +01:00
:: glTranslated ( 0.0 , 5.0 , 0.0 );
m_arrow . render ();
:: glTranslated ( 0.0 , - 10.0 , 0.0 );
:: glRotated ( 180.0 , 0.0 , 0.0 , 1.0 );
m_arrow . render ();
}
void GLCanvas3D :: Selection :: _render_sidebar_size_hint ( Axis axis , double length ) const
{
}
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
2019-01-04 21:34:25 +01:00
void GLCanvas3D :: Selection :: _synchronize_unselected_instances ( bool including_z )
2018-10-10 11:04:04 +02:00
{
std :: set < unsigned int > done ; // prevent processing volumes twice
done . insert ( m_list . begin (), m_list . end ());
for ( unsigned int i : m_list )
{
if ( done . size () == m_volumes -> size ())
break ;
const GLVolume * volume = ( * m_volumes )[ i ];
int object_idx = volume -> object_idx ();
if ( object_idx >= 1000 )
continue ;
int instance_idx = volume -> instance_idx ();
2018-11-02 12:11:28 +01:00
const Vec3d & rotation = volume -> get_instance_rotation ();
const Vec3d & scaling_factor = volume -> get_instance_scaling_factor ();
const Vec3d & mirror = volume -> get_instance_mirror ();
2018-10-10 11:04:04 +02:00
// Process unselected instances.
for ( unsigned int j = 0 ; j < ( unsigned int ) m_volumes -> size (); ++ j )
{
if ( done . size () == m_volumes -> size ())
break ;
if ( done . find ( j ) != done . end ())
continue ;
GLVolume * v = ( * m_volumes )[ j ];
if (( v -> object_idx () != object_idx ) || ( v -> instance_idx () == instance_idx ))
continue ;
2019-01-04 21:34:25 +01:00
v -> set_instance_rotation ( Vec3d ( rotation ( 0 ), rotation ( 1 ), including_z ? rotation ( 2 ) : v -> get_instance_rotation ()( 2 )));
2018-11-02 12:11:28 +01:00
v -> set_instance_scaling_factor ( scaling_factor );
v -> set_instance_mirror ( mirror );
2018-10-10 11:04:04 +02:00
done . insert ( j );
}
}
}
2018-10-08 14:02:12 +02:00
2018-11-06 15:51:33 +01:00
void GLCanvas3D :: Selection :: _synchronize_unselected_volumes ()
{
for ( unsigned int i : m_list )
{
const GLVolume * volume = ( * m_volumes )[ i ];
int object_idx = volume -> object_idx ();
if ( object_idx >= 1000 )
continue ;
int volume_idx = volume -> volume_idx ();
const Vec3d & offset = volume -> get_volume_offset ();
const Vec3d & rotation = volume -> get_volume_rotation ();
const Vec3d & scaling_factor = volume -> get_volume_scaling_factor ();
const Vec3d & mirror = volume -> get_volume_mirror ();
// Process unselected volumes.
for ( unsigned int j = 0 ; j < ( unsigned int ) m_volumes -> size (); ++ j )
{
if ( j == i )
continue ;
GLVolume * v = ( * m_volumes )[ j ];
if (( v -> object_idx () != object_idx ) || ( v -> volume_idx () != volume_idx ))
continue ;
v -> set_volume_offset ( offset );
v -> set_volume_rotation ( rotation );
v -> set_volume_scaling_factor ( scaling_factor );
v -> set_volume_mirror ( mirror );
}
}
}
2018-11-16 11:30:36 +01:00
#if ENABLE_ENSURE_ON_BED_WHILE_SCALING
void GLCanvas3D :: Selection :: _ensure_on_bed ()
{
2018-11-20 15:39:36 +01:00
typedef std :: map < std :: pair < int , int > , double > InstancesToZMap ;
InstancesToZMap instances_min_z ;
2018-11-23 13:20:04 +01:00
for ( GLVolume * volume : * m_volumes )
2018-11-16 11:30:36 +01:00
{
2018-11-23 13:20:04 +01:00
if ( ! volume -> is_wipe_tower && ! volume -> is_modifier )
2018-11-20 10:15:31 +01:00
{
double min_z = volume -> transformed_convex_hull_bounding_box (). min ( 2 );
2018-11-20 15:39:36 +01:00
std :: pair < int , int > instance = std :: make_pair ( volume -> object_idx (), volume -> instance_idx ());
InstancesToZMap :: iterator it = instances_min_z . find ( instance );
if ( it == instances_min_z . end ())
it = instances_min_z . insert ( InstancesToZMap :: value_type ( instance , DBL_MAX )). first ;
it -> second = std :: min ( it -> second , min_z );
2018-11-20 10:15:31 +01:00
}
2018-11-16 11:30:36 +01:00
}
2018-11-20 15:39:36 +01:00
for ( GLVolume * volume : * m_volumes )
{
std :: pair < int , int > instance = std :: make_pair ( volume -> object_idx (), volume -> instance_idx ());
InstancesToZMap :: iterator it = instances_min_z . find ( instance );
if ( it != instances_min_z . end ())
volume -> set_instance_offset ( Z , volume -> get_instance_offset ( Z ) - it -> second );
}
2018-11-16 11:30:36 +01:00
}
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
2018-12-17 12:49:47 +01:00
const float GLCanvas3D :: Gizmos :: OverlayIconsScale = 1.0f ;
const float GLCanvas3D :: Gizmos :: OverlayBorder = 5.0f ;
const float GLCanvas3D :: Gizmos :: OverlayGapY = 5.0f * OverlayIconsScale ;
2018-06-13 09:12:16 +02:00
GLCanvas3D :: Gizmos :: Gizmos ()
: m_enabled ( false )
2018-06-13 09:26:58 +02:00
, m_current ( Undefined )
2018-06-13 09:12:16 +02:00
{
}
GLCanvas3D :: Gizmos ::~ Gizmos ()
{
_reset ();
}
2018-08-24 14:11:41 +02:00
bool GLCanvas3D :: Gizmos :: init ( GLCanvas3D & parent )
2018-06-13 09:12:16 +02:00
{
2018-09-11 09:00:28 +02:00
GLGizmoBase * gizmo = new GLGizmoMove3D ( parent );
2018-06-13 09:12:16 +02:00
if ( gizmo == nullptr )
return false ;
if ( ! gizmo -> init ())
return false ;
2018-09-11 09:00:28 +02:00
m_gizmos . insert ( GizmosMap :: value_type ( Move , gizmo ));
gizmo = new GLGizmoScale3D ( parent );
2018-06-13 09:12:16 +02:00
if ( gizmo == nullptr )
return false ;
if ( ! gizmo -> init ())
return false ;
m_gizmos . insert ( GizmosMap :: value_type ( Scale , gizmo ));
2018-08-24 14:11:41 +02:00
gizmo = new GLGizmoRotate3D ( parent );
2018-06-13 09:12:16 +02:00
if ( gizmo == nullptr )
{
_reset ();
return false ;
}
if ( ! gizmo -> init ())
{
_reset ();
return false ;
}
m_gizmos . insert ( GizmosMap :: value_type ( Rotate , gizmo ));
2018-08-27 14:00:53 +02:00
gizmo = new GLGizmoFlatten ( parent );
2018-08-09 16:55:43 +02:00
if ( gizmo == nullptr )
return false ;
if ( ! gizmo -> init ()) {
_reset ();
return false ;
}
m_gizmos . insert ( GizmosMap :: value_type ( Flatten , gizmo ));
2018-10-18 15:13:38 +02:00
gizmo = new GLGizmoCut ( parent );
2018-11-19 14:46:37 +01:00
if ( gizmo == nullptr )
return false ;
if ( ! gizmo -> init ()) {
_reset ();
2018-10-18 15:13:38 +02:00
return false ;
}
2018-11-19 14:46:37 +01:00
m_gizmos . insert ( GizmosMap :: value_type ( Cut , gizmo ));
2018-10-18 15:13:38 +02:00
2018-09-12 12:14:20 +02:00
gizmo = new GLGizmoSlaSupports ( parent );
if ( gizmo == nullptr )
return false ;
if ( ! gizmo -> init ()) {
_reset ();
return false ;
}
m_gizmos . insert ( GizmosMap :: value_type ( SlaSupports , gizmo ));
2018-12-17 13:20:57 +01:00
m_background_texture . metadata . filename = "toolbar_background.png" ;
m_background_texture . metadata . left = 16 ;
m_background_texture . metadata . top = 16 ;
m_background_texture . metadata . right = 16 ;
m_background_texture . metadata . bottom = 16 ;
if ( ! m_background_texture . metadata . filename . empty ())
{
if ( ! m_background_texture . texture . load_from_file ( resources_dir () + "/icons/" + m_background_texture . metadata . filename , false ))
{
_reset ();
return false ;
}
}
2018-06-13 09:12:16 +02:00
return true ;
}
bool GLCanvas3D :: Gizmos :: is_enabled () const
{
return m_enabled ;
}
void GLCanvas3D :: Gizmos :: set_enabled ( bool enable )
{
m_enabled = enable ;
}
2018-10-25 09:35:08 +02:00
std :: string GLCanvas3D :: Gizmos :: update_hover_state ( const GLCanvas3D & canvas , const Vec2d & mouse_pos , const GLCanvas3D :: Selection & selection )
2018-06-13 10:49:59 +02:00
{
2018-10-25 09:35:08 +02:00
std :: string name = "" ;
2018-06-13 10:49:59 +02:00
if ( ! m_enabled )
2018-10-25 09:35:08 +02:00
return name ;
2018-06-13 10:49:59 +02:00
float cnv_h = ( float ) canvas . get_canvas_size (). get_height ();
float height = _get_total_overlay_height ();
2018-12-17 12:49:47 +01:00
float top_y = 0.5f * ( cnv_h - height ) + OverlayBorder ;
2018-10-16 14:56:35 +02:00
for ( GizmosMap :: iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
2018-06-13 10:49:59 +02:00
{
2018-11-19 14:46:37 +01:00
if (( it -> second == nullptr ) || ! it -> second -> is_selectable ())
2018-06-13 10:49:59 +02:00
continue ;
2018-12-17 13:20:57 +01:00
float icon_size = ( float ) it -> second -> get_textures_size () * OverlayIconsScale ;
2018-06-13 10:49:59 +02:00
2018-10-16 14:56:35 +02:00
if ( it -> second -> is_activable ( selection ) && ( it -> second -> get_state () != GLGizmoBase :: On ))
2018-06-13 13:14:17 +02:00
{
2018-12-17 13:20:57 +01:00
bool inside = ( OverlayBorder <= ( float ) mouse_pos ( 0 )) && (( float ) mouse_pos ( 0 ) <= OverlayBorder + icon_size ) && ( top_y <= ( float ) mouse_pos ( 1 )) && (( float ) mouse_pos ( 1 ) <= top_y + icon_size );
2018-06-13 13:14:17 +02:00
it -> second -> set_state ( inside ? GLGizmoBase :: Hover : GLGizmoBase :: Off );
2018-10-25 09:35:08 +02:00
if ( inside )
name = it -> second -> get_name ();
2018-06-13 13:14:17 +02:00
}
2018-12-17 13:20:57 +01:00
top_y += ( icon_size + OverlayGapY );
2018-06-13 10:49:59 +02:00
}
2018-10-25 09:35:08 +02:00
return name ;
2018-06-13 10:49:59 +02:00
}
2018-10-08 14:02:12 +02:00
void GLCanvas3D :: Gizmos :: update_on_off_state ( const GLCanvas3D & canvas , const Vec2d & mouse_pos , const GLCanvas3D :: Selection & selection )
2018-06-13 13:14:17 +02:00
{
if ( ! m_enabled )
return ;
float cnv_h = ( float ) canvas . get_canvas_size (). get_height ();
float height = _get_total_overlay_height ();
2018-12-17 12:49:47 +01:00
float top_y = 0.5f * ( cnv_h - height ) + OverlayBorder ;
2018-10-16 14:56:35 +02:00
for ( GizmosMap :: iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
2018-06-13 13:14:17 +02:00
{
2018-11-19 14:46:37 +01:00
if (( it -> second == nullptr ) || ! it -> second -> is_selectable ())
2018-06-13 13:14:17 +02:00
continue ;
2018-12-17 13:20:57 +01:00
float icon_size = ( float ) it -> second -> get_textures_size () * OverlayIconsScale ;
2018-06-13 13:14:17 +02:00
2018-12-17 13:20:57 +01:00
bool inside = ( OverlayBorder <= ( float ) mouse_pos ( 0 )) && (( float ) mouse_pos ( 0 ) <= OverlayBorder + icon_size ) && ( top_y <= ( float ) mouse_pos ( 1 )) && (( float ) mouse_pos ( 1 ) <= top_y + icon_size );
2018-12-17 12:49:47 +01:00
if ( it -> second -> is_activable ( selection ) && inside )
2018-06-13 15:44:04 +02:00
{
if (( it -> second -> get_state () == GLGizmoBase :: On ))
{
2018-10-16 14:56:35 +02:00
it -> second -> set_state ( GLGizmoBase :: Hover );
2018-06-13 15:44:04 +02:00
m_current = Undefined ;
}
2018-10-16 14:56:35 +02:00
else if (( it -> second -> get_state () == GLGizmoBase :: Hover ))
2018-06-13 15:44:04 +02:00
{
it -> second -> set_state ( GLGizmoBase :: On );
m_current = it -> first ;
}
}
2018-06-13 13:14:17 +02:00
else
it -> second -> set_state ( GLGizmoBase :: Off );
2018-12-17 13:20:57 +01:00
top_y += ( icon_size + OverlayGapY );
2018-06-13 13:14:17 +02:00
}
2018-10-16 14:56:35 +02:00
GizmosMap :: iterator it = m_gizmos . find ( m_current );
if (( it != m_gizmos . end ()) && ( it -> second != nullptr ) && ( it -> second -> get_state () != GLGizmoBase :: On ))
it -> second -> set_state ( GLGizmoBase :: On );
2018-06-13 13:14:17 +02:00
}
2018-10-16 14:56:35 +02:00
void GLCanvas3D :: Gizmos :: update_on_off_state ( const Selection & selection )
{
GizmosMap :: iterator it = m_gizmos . find ( m_current );
if (( it != m_gizmos . end ()) && ( it -> second != nullptr ))
{
if ( ! it -> second -> is_activable ( selection ))
{
it -> second -> set_state ( GLGizmoBase :: Off );
m_current = Undefined ;
}
}
}
2018-06-13 13:14:17 +02:00
void GLCanvas3D :: Gizmos :: reset_all_states ()
{
2018-06-13 15:44:04 +02:00
if ( ! m_enabled )
return ;
2018-06-13 13:14:17 +02:00
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
if ( it -> second != nullptr )
2018-06-15 14:10:28 +02:00
{
2018-06-13 13:14:17 +02:00
it -> second -> set_state ( GLGizmoBase :: Off );
2018-06-15 14:10:28 +02:00
it -> second -> set_hover_id ( - 1 );
}
2018-06-13 13:14:17 +02:00
}
2018-06-13 15:44:04 +02:00
m_current = Undefined ;
2018-06-13 13:14:17 +02:00
}
2018-06-14 15:32:26 +02:00
void GLCanvas3D :: Gizmos :: set_hover_id ( int id )
{
if ( ! m_enabled )
return ;
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
if (( it -> second != nullptr ) && ( it -> second -> get_state () == GLGizmoBase :: On ))
it -> second -> set_hover_id ( id );
}
}
2018-10-09 15:56:34 +02:00
void GLCanvas3D :: Gizmos :: enable_grabber ( EType type , unsigned int id , bool enable )
2018-10-08 14:02:12 +02:00
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( type );
if ( it != m_gizmos . end ())
2018-10-09 15:56:34 +02:00
{
if ( enable )
it -> second -> enable_grabber ( id );
else
it -> second -> disable_grabber ( id );
}
2018-10-08 14:02:12 +02:00
}
2018-08-21 21:05:24 +02:00
bool GLCanvas3D :: Gizmos :: overlay_contains_mouse ( const GLCanvas3D & canvas , const Vec2d & mouse_pos ) const
2018-06-13 13:14:17 +02:00
{
2018-06-13 15:44:04 +02:00
if ( ! m_enabled )
return false ;
2018-06-14 12:34:19 +02:00
float cnv_h = ( float ) canvas . get_canvas_size (). get_height ();
float height = _get_total_overlay_height ();
2018-12-17 12:49:47 +01:00
float top_y = 0.5f * ( cnv_h - height ) + OverlayBorder ;
2018-06-13 13:14:17 +02:00
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
2018-11-19 14:46:37 +01:00
if (( it -> second == nullptr ) || ! it -> second -> is_selectable ())
2018-06-14 12:34:19 +02:00
continue ;
2018-12-17 13:20:57 +01:00
float icon_size = ( float ) it -> second -> get_textures_size () * OverlayIconsScale ;
2018-06-14 12:34:19 +02:00
2018-12-17 13:20:57 +01:00
if (( OverlayBorder <= ( float ) mouse_pos ( 0 )) && (( float ) mouse_pos ( 0 ) <= OverlayBorder + icon_size ) && ( top_y <= ( float ) mouse_pos ( 1 )) && (( float ) mouse_pos ( 1 ) <= top_y + icon_size ))
2018-06-13 13:14:17 +02:00
return true ;
2018-06-14 12:34:19 +02:00
2018-12-17 13:20:57 +01:00
top_y += ( icon_size + OverlayGapY );
2018-06-13 13:14:17 +02:00
}
return false ;
}
2018-06-15 14:10:28 +02:00
bool GLCanvas3D :: Gizmos :: grabber_contains_mouse () const
{
if ( ! m_enabled )
return false ;
GLGizmoBase * curr = _get_current ();
return ( curr != nullptr ) ? ( curr -> get_hover_id () != - 1 ) : false ;
}
2018-11-14 12:57:12 +01:00
void GLCanvas3D :: Gizmos :: update ( const Linef3 & mouse_ray , bool shift_down , const Point * mouse_pos )
2018-06-15 14:10:28 +02:00
{
if ( ! m_enabled )
return ;
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
2018-11-14 12:57:12 +01:00
curr -> update ( GLGizmoBase :: UpdateData ( mouse_ray , mouse_pos , shift_down ));
2018-06-15 14:10:28 +02:00
}
2018-06-19 09:46:26 +02:00
GLCanvas3D :: Gizmos :: EType GLCanvas3D :: Gizmos :: get_current_type () const
2018-06-18 15:07:17 +02:00
{
2018-06-19 09:46:26 +02:00
return m_current ;
2018-06-18 15:07:17 +02:00
}
bool GLCanvas3D :: Gizmos :: is_running () const
{
if ( ! m_enabled )
return false ;
GLGizmoBase * curr = _get_current ();
return ( curr != nullptr ) ? ( curr -> get_state () == GLGizmoBase :: On ) : false ;
}
2018-11-22 10:14:31 +01:00
bool GLCanvas3D :: Gizmos :: handle_shortcut ( int key , const Selection & selection )
{
if ( ! m_enabled )
return false ;
bool handled = false ;
for ( GizmosMap :: iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
if (( it -> second == nullptr ) || ! it -> second -> is_selectable ())
continue ;
int it_key = it -> second -> get_shortcut_key ();
if ( it -> second -> is_activable ( selection ) && (( it_key == key - 64 ) || ( it_key == key - 96 )))
{
if (( it -> second -> get_state () == GLGizmoBase :: On ))
{
it -> second -> set_state ( GLGizmoBase :: Off );
m_current = Undefined ;
handled = true ;
}
else if (( it -> second -> get_state () == GLGizmoBase :: Off ))
{
it -> second -> set_state ( GLGizmoBase :: On );
m_current = it -> first ;
handled = true ;
}
}
else
it -> second -> set_state ( GLGizmoBase :: Off );
}
return handled ;
}
2018-06-15 14:10:28 +02:00
bool GLCanvas3D :: Gizmos :: is_dragging () const
{
2018-10-16 13:49:40 +02:00
if ( ! m_enabled )
return false ;
2018-09-11 09:00:28 +02:00
GLGizmoBase * curr = _get_current ();
return ( curr != nullptr ) ? curr -> is_dragging () : false ;
2018-06-15 14:10:28 +02:00
}
2018-10-15 11:30:50 +02:00
void GLCanvas3D :: Gizmos :: start_dragging ( const GLCanvas3D :: Selection & selection )
{
2018-10-16 13:49:40 +02:00
if ( ! m_enabled )
return ;
2018-10-15 11:30:50 +02:00
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> start_dragging ( selection );
}
2018-06-15 14:10:28 +02:00
void GLCanvas3D :: Gizmos :: stop_dragging ()
{
2018-10-16 13:49:40 +02:00
if ( ! m_enabled )
return ;
2018-07-12 11:26:13 +02:00
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> stop_dragging ();
2018-06-15 14:10:28 +02:00
}
2018-10-08 14:02:12 +02:00
Vec3d GLCanvas3D :: Gizmos :: get_displacement () const
{
if ( ! m_enabled )
return Vec3d :: Zero ();
GizmosMap :: const_iterator it = m_gizmos . find ( Move );
return ( it != m_gizmos . end ()) ? reinterpret_cast < GLGizmoMove3D *> ( it -> second ) -> get_displacement () : Vec3d :: Zero ();
}
2018-09-11 09:00:28 +02:00
2018-09-24 15:54:09 +02:00
Vec3d GLCanvas3D :: Gizmos :: get_scale () const
2018-06-18 15:07:17 +02:00
{
if ( ! m_enabled )
2018-09-24 15:54:09 +02:00
return Vec3d :: Ones ();
2018-06-18 15:07:17 +02:00
GizmosMap :: const_iterator it = m_gizmos . find ( Scale );
2018-09-24 15:54:09 +02:00
return ( it != m_gizmos . end ()) ? reinterpret_cast < GLGizmoScale3D *> ( it -> second ) -> get_scale () : Vec3d :: Ones ();
2018-06-18 15:07:17 +02:00
}
2018-09-24 15:54:09 +02:00
void GLCanvas3D :: Gizmos :: set_scale ( const Vec3d & scale )
2018-06-19 09:46:26 +02:00
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( Scale );
if ( it != m_gizmos . end ())
2018-08-21 08:50:35 +02:00
reinterpret_cast < GLGizmoScale3D *> ( it -> second ) -> set_scale ( scale );
2018-06-19 09:46:26 +02:00
}
2018-09-20 15:00:40 +02:00
Vec3d GLCanvas3D :: Gizmos :: get_rotation () const
{
if ( ! m_enabled )
return Vec3d :: Zero ();
GizmosMap :: const_iterator it = m_gizmos . find ( Rotate );
return ( it != m_gizmos . end ()) ? reinterpret_cast < GLGizmoRotate3D *> ( it -> second ) -> get_rotation () : Vec3d :: Zero ();
}
void GLCanvas3D :: Gizmos :: set_rotation ( const Vec3d & rotation )
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( Rotate );
if ( it != m_gizmos . end ())
reinterpret_cast < GLGizmoRotate3D *> ( it -> second ) -> set_rotation ( rotation );
}
2018-11-02 15:20:26 +01:00
Vec3d GLCanvas3D :: Gizmos :: get_flattening_normal () const
2018-09-20 15:00:40 +02:00
{
if ( ! m_enabled )
return Vec3d :: Zero ();
GizmosMap :: const_iterator it = m_gizmos . find ( Flatten );
2018-11-02 15:20:26 +01:00
return ( it != m_gizmos . end ()) ? reinterpret_cast < GLGizmoFlatten *> ( it -> second ) -> get_flattening_normal () : Vec3d :: Zero ();
2018-09-20 15:00:40 +02:00
}
2018-08-09 16:55:43 +02:00
2018-08-17 15:40:47 +02:00
void GLCanvas3D :: Gizmos :: set_flattening_data ( const ModelObject * model_object )
2018-08-09 16:55:43 +02:00
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( Flatten );
if ( it != m_gizmos . end ())
2018-08-17 15:40:47 +02:00
reinterpret_cast < GLGizmoFlatten *> ( it -> second ) -> set_flattening_data ( model_object );
2018-08-09 16:55:43 +02:00
}
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
void GLCanvas3D :: Gizmos :: set_sla_support_data ( ModelObject * model_object , const GLCanvas3D :: Selection & selection )
#else
2018-09-24 13:39:44 +02:00
void GLCanvas3D :: Gizmos :: set_model_object_ptr ( ModelObject * model_object )
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-12 12:14:20 +02:00
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( SlaSupports );
if ( it != m_gizmos . end ())
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
reinterpret_cast < GLGizmoSlaSupports *> ( it -> second ) -> set_sla_support_data ( model_object , selection );
#else
2018-09-12 12:14:20 +02:00
reinterpret_cast < GLGizmoSlaSupports *> ( it -> second ) -> set_model_object_ptr ( model_object );
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-12 12:14:20 +02:00
}
2018-09-19 14:59:57 +02:00
void GLCanvas3D :: Gizmos :: clicked_on_object ( const Vec2d & mouse_position )
2018-09-12 12:14:20 +02:00
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( SlaSupports );
if ( it != m_gizmos . end ())
2018-09-19 14:59:57 +02:00
reinterpret_cast < GLGizmoSlaSupports *> ( it -> second ) -> clicked_on_object ( mouse_position );
}
void GLCanvas3D :: Gizmos :: delete_current_grabber ( bool delete_all )
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( SlaSupports );
if ( it != m_gizmos . end ())
reinterpret_cast < GLGizmoSlaSupports *> ( it -> second ) -> delete_current_grabber ( delete_all );
2018-09-12 12:14:20 +02:00
}
2018-10-15 11:30:50 +02:00
void GLCanvas3D :: Gizmos :: render_current_gizmo ( const GLCanvas3D :: Selection & selection ) const
2018-06-13 09:12:16 +02:00
{
if ( ! m_enabled )
return ;
2018-10-15 11:30:50 +02:00
_render_current_gizmo ( selection );
2018-06-13 09:12:16 +02:00
}
2018-10-15 11:30:50 +02:00
void GLCanvas3D :: Gizmos :: render_current_gizmo_for_picking_pass ( const GLCanvas3D :: Selection & selection ) const
{
if ( ! m_enabled )
return ;
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> render_for_picking ( selection );
}
2018-06-14 15:32:26 +02:00
2018-11-26 10:56:07 +01:00
void GLCanvas3D :: Gizmos :: render_overlay ( const GLCanvas3D & canvas , const GLCanvas3D :: Selection & selection ) const
2018-08-21 14:27:36 +02:00
{
if ( ! m_enabled )
return ;
:: glDisable ( GL_DEPTH_TEST );
:: glPushMatrix ();
:: glLoadIdentity ();
2018-11-26 10:56:07 +01:00
_render_overlay ( canvas , selection );
2018-08-21 14:27:36 +02:00
:: glPopMatrix ();
}
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
void GLCanvas3D :: Gizmos :: create_external_gizmo_widgets ( wxWindow * parent )
{
for ( auto & entry : m_gizmos ) {
entry . second -> create_external_gizmo_widgets ( parent );
}
}
2018-11-26 10:56:07 +01:00
#endif // not ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
2018-06-13 09:12:16 +02:00
void GLCanvas3D :: Gizmos :: _reset ()
{
for ( GizmosMap :: value_type & gizmo : m_gizmos )
{
delete gizmo . second ;
gizmo . second = nullptr ;
}
m_gizmos . clear ();
}
2018-11-26 10:56:07 +01:00
void GLCanvas3D :: Gizmos :: _render_overlay ( const GLCanvas3D & canvas , const GLCanvas3D :: Selection & selection ) const
2018-06-13 09:12:16 +02:00
{
if ( m_gizmos . empty ())
return ;
2018-06-13 10:49:59 +02:00
float cnv_w = ( float ) canvas . get_canvas_size (). get_width ();
2018-10-31 10:19:44 +01:00
#if ENABLE_IMGUI
float cnv_h = ( float ) canvas . get_canvas_size (). get_height ();
#endif // ENABLE_IMGUI
2018-06-13 09:12:16 +02:00
float zoom = canvas . get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
2018-06-13 10:49:59 +02:00
float height = _get_total_overlay_height ();
2018-12-17 13:20:57 +01:00
float scaled_border = OverlayBorder * inv_zoom ;
float top_x = ( - 0.5f * cnv_w ) * inv_zoom ;
float top_y = ( 0.5f * height ) * inv_zoom ;
float left = top_x ;
float top = top_y ;
float right = left + _get_total_overlay_width () * inv_zoom ;
float bottom = top - height * inv_zoom ;
// renders background
unsigned int bg_tex_id = m_background_texture . texture . get_id ();
float bg_tex_width = ( float ) m_background_texture . texture . get_width ();
float bg_tex_height = ( float ) m_background_texture . texture . get_height ();
if (( bg_tex_id != 0 ) && ( bg_tex_width > 0 ) && ( bg_tex_height > 0 ))
{
float inv_bg_tex_width = ( bg_tex_width != 0.0f ) ? 1.0f / bg_tex_width : 0.0f ;
float inv_bg_tex_height = ( bg_tex_height != 0.0f ) ? 1.0f / bg_tex_height : 0.0f ;
float bg_uv_left = 0.0f ;
float bg_uv_right = 1.0f ;
float bg_uv_top = 1.0f ;
float bg_uv_bottom = 0.0f ;
float bg_left = left ;
float bg_right = right ;
float bg_top = top ;
float bg_bottom = bottom ;
float bg_width = right - left ;
float bg_height = top - bottom ;
float bg_min_size = std :: min ( bg_width , bg_height );
float bg_uv_i_left = ( float ) m_background_texture . metadata . left * inv_bg_tex_width ;
float bg_uv_i_right = 1.0f - ( float ) m_background_texture . metadata . right * inv_bg_tex_width ;
float bg_uv_i_top = 1.0f - ( float ) m_background_texture . metadata . top * inv_bg_tex_height ;
float bg_uv_i_bottom = ( float ) m_background_texture . metadata . bottom * inv_bg_tex_height ;
float bg_i_left = bg_left + scaled_border ;
float bg_i_right = bg_right - scaled_border ;
float bg_i_top = bg_top - scaled_border ;
float bg_i_bottom = bg_bottom + scaled_border ;
bg_uv_left = bg_uv_i_left ;
bg_i_left = bg_left ;
if (( OverlayBorder > 0 ) && ( bg_uv_top != bg_uv_i_top ))
{
if ( bg_uv_left != bg_uv_i_left )
GLTexture :: render_sub_texture ( bg_tex_id , bg_left , bg_i_left , bg_i_top , bg_top , { { bg_uv_left , bg_uv_i_top }, { bg_uv_i_left , bg_uv_i_top }, { bg_uv_i_left , bg_uv_top }, { bg_uv_left , bg_uv_top } });
GLTexture :: render_sub_texture ( bg_tex_id , bg_i_left , bg_i_right , bg_i_top , bg_top , { { bg_uv_i_left , bg_uv_i_top }, { bg_uv_i_right , bg_uv_i_top }, { bg_uv_i_right , bg_uv_top }, { bg_uv_i_left , bg_uv_top } });
if ( bg_uv_right != bg_uv_i_right )
GLTexture :: render_sub_texture ( bg_tex_id , bg_i_right , bg_right , bg_i_top , bg_top , { { bg_uv_i_right , bg_uv_i_top }, { bg_uv_right , bg_uv_i_top }, { bg_uv_right , bg_uv_top }, { bg_uv_i_right , bg_uv_top } });
}
if (( OverlayBorder > 0 ) && ( bg_uv_left != bg_uv_i_left ))
GLTexture :: render_sub_texture ( bg_tex_id , bg_left , bg_i_left , bg_i_bottom , bg_i_top , { { bg_uv_left , bg_uv_i_bottom }, { bg_uv_i_left , bg_uv_i_bottom }, { bg_uv_i_left , bg_uv_i_top }, { bg_uv_left , bg_uv_i_top } });
GLTexture :: render_sub_texture ( bg_tex_id , bg_i_left , bg_i_right , bg_i_bottom , bg_i_top , { { bg_uv_i_left , bg_uv_i_bottom }, { bg_uv_i_right , bg_uv_i_bottom }, { bg_uv_i_right , bg_uv_i_top }, { bg_uv_i_left , bg_uv_i_top } });
if (( OverlayBorder > 0 ) && ( bg_uv_right != bg_uv_i_right ))
GLTexture :: render_sub_texture ( bg_tex_id , bg_i_right , bg_right , bg_i_bottom , bg_i_top , { { bg_uv_i_right , bg_uv_i_bottom }, { bg_uv_right , bg_uv_i_bottom }, { bg_uv_right , bg_uv_i_top }, { bg_uv_i_right , bg_uv_i_top } });
if (( OverlayBorder > 0 ) && ( bg_uv_bottom != bg_uv_i_bottom ))
{
if ( bg_uv_left != bg_uv_i_left )
GLTexture :: render_sub_texture ( bg_tex_id , bg_left , bg_i_left , bg_bottom , bg_i_bottom , { { bg_uv_left , bg_uv_bottom }, { bg_uv_i_left , bg_uv_bottom }, { bg_uv_i_left , bg_uv_i_bottom }, { bg_uv_left , bg_uv_i_bottom } });
GLTexture :: render_sub_texture ( bg_tex_id , bg_i_left , bg_i_right , bg_bottom , bg_i_bottom , { { bg_uv_i_left , bg_uv_bottom }, { bg_uv_i_right , bg_uv_bottom }, { bg_uv_i_right , bg_uv_i_bottom }, { bg_uv_i_left , bg_uv_i_bottom } });
if ( bg_uv_right != bg_uv_i_right )
GLTexture :: render_sub_texture ( bg_tex_id , bg_i_right , bg_right , bg_bottom , bg_i_bottom , { { bg_uv_i_right , bg_uv_bottom }, { bg_uv_right , bg_uv_bottom }, { bg_uv_right , bg_uv_i_bottom }, { bg_uv_i_right , bg_uv_i_bottom } });
}
}
top_x += OverlayBorder * inv_zoom ;
top_y -= OverlayBorder * inv_zoom ;
2018-06-13 09:12:16 +02:00
float scaled_gap_y = OverlayGapY * inv_zoom ;
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
2018-11-19 14:46:37 +01:00
if (( it -> second == nullptr ) || ! it -> second -> is_selectable ())
2018-11-02 10:31:57 +01:00
continue ;
2018-11-19 14:46:37 +01:00
2018-12-17 13:20:57 +01:00
float icon_size = ( float ) it -> second -> get_textures_size () * OverlayIconsScale * inv_zoom ;
GLTexture :: render_texture ( it -> second -> get_texture_id (), top_x , top_x + icon_size , top_y - icon_size , top_y );
2018-10-31 10:19:44 +01:00
#if ENABLE_IMGUI
if ( it -> second -> get_state () == GLGizmoBase :: On )
2018-12-17 13:20:57 +01:00
it -> second -> render_input_window ( 2.0f * OverlayBorder + icon_size * zoom , 0.5f * cnv_h - top_y * zoom , selection );
2018-10-31 10:19:44 +01:00
#endif // ENABLE_IMGUI
2018-12-17 13:20:57 +01:00
top_y -= ( icon_size + scaled_gap_y );
2018-06-13 09:12:16 +02:00
}
}
2018-10-15 11:30:50 +02:00
void GLCanvas3D :: Gizmos :: _render_current_gizmo ( const GLCanvas3D :: Selection & selection ) const
{
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> render ( selection );
}
2018-06-13 10:49:59 +02:00
float GLCanvas3D :: Gizmos :: _get_total_overlay_height () const
{
2018-12-17 12:49:47 +01:00
float height = 2.0f * OverlayBorder ;
2018-06-15 14:10:28 +02:00
2018-06-13 10:49:59 +02:00
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
2018-12-17 12:49:47 +01:00
if (( it -> second == nullptr ) || ! it -> second -> is_selectable ())
2018-11-02 15:20:26 +01:00
continue ;
2018-11-30 13:27:54 +01:00
2018-12-17 12:49:47 +01:00
height += ( float ) it -> second -> get_textures_size () * OverlayIconsScale + OverlayGapY ;
2018-06-13 10:49:59 +02:00
}
2018-11-02 15:20:26 +01:00
return height - OverlayGapY ;
2018-06-13 10:49:59 +02:00
}
2018-06-13 09:12:16 +02:00
2018-12-17 13:20:57 +01:00
float GLCanvas3D :: Gizmos :: _get_total_overlay_width () const
{
float max_icon_width = 0.0f ;
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
if (( it -> second == nullptr ) || ! it -> second -> is_selectable ())
continue ;
max_icon_width = std :: max ( max_icon_width , ( float ) it -> second -> get_textures_size () * OverlayIconsScale );
}
return max_icon_width + 2.0f * OverlayBorder ;
}
2018-10-08 14:02:12 +02:00
GLGizmoBase * GLCanvas3D :: Gizmos :: _get_current () const
{
GizmosMap :: const_iterator it = m_gizmos . find ( m_current );
return ( it != m_gizmos . end ()) ? it -> second : nullptr ;
}
2018-07-19 13:18:19 +02:00
const unsigned char GLCanvas3D :: WarningTexture :: Background_Color [ 3 ] = { 9 , 91 , 134 };
const unsigned char GLCanvas3D :: WarningTexture :: Opacity = 255 ;
2018-07-31 14:20:16 +02:00
GLCanvas3D :: WarningTexture :: WarningTexture ()
: GUI :: GLTexture ()
, m_original_width ( 0 )
, m_original_height ( 0 )
{
}
2018-07-19 13:18:19 +02:00
bool GLCanvas3D :: WarningTexture :: generate ( const std :: string & msg )
{
reset ();
if ( msg . empty ())
return false ;
wxMemoryDC memDC ;
// select default font
2018-07-31 14:20:16 +02:00
wxFont font = wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT );
font . MakeLarger ();
2018-07-31 15:32:16 +02:00
font . MakeBold ();
2018-07-31 14:20:16 +02:00
memDC . SetFont ( font );
2018-07-19 13:18:19 +02:00
// calculates texture size
wxCoord w , h ;
memDC . GetTextExtent ( msg , & w , & h );
2018-07-31 14:20:16 +02:00
2018-09-12 11:59:02 +02:00
int pow_of_two_size = next_highest_power_of_2 ( std :: max < unsigned int > ( w , h ));
2018-07-31 14:20:16 +02:00
m_original_width = ( int ) w ;
m_original_height = ( int ) h ;
m_width = pow_of_two_size ;
m_height = pow_of_two_size ;
2018-07-19 13:18:19 +02:00
// generates bitmap
wxBitmap bitmap ( m_width , m_height );
memDC . SelectObject ( bitmap );
memDC . SetBackground ( wxBrush ( wxColour ( Background_Color [ 0 ], Background_Color [ 1 ], Background_Color [ 2 ])));
memDC . Clear ();
// draw message
2018-10-25 15:32:35 +02:00
memDC . SetTextForeground ( * wxWHITE );
2018-07-19 13:18:19 +02:00
memDC . DrawText ( msg , 0 , 0 );
memDC . SelectObject ( wxNullBitmap );
// Convert the bitmap into a linear data ready to be loaded into the GPU.
wxImage image = bitmap . ConvertToImage ();
image . SetMaskColour ( Background_Color [ 0 ], Background_Color [ 1 ], Background_Color [ 2 ]);
// prepare buffer
std :: vector < unsigned char > data ( 4 * m_width * m_height , 0 );
for ( int h = 0 ; h < m_height ; ++ h )
{
int hh = h * m_width ;
unsigned char * px_ptr = data . data () + 4 * hh ;
for ( int w = 0 ; w < m_width ; ++ w )
{
* px_ptr ++ = image . GetRed ( w , h );
* px_ptr ++ = image . GetGreen ( w , h );
* px_ptr ++ = image . GetBlue ( w , h );
* px_ptr ++ = image . IsTransparent ( w , h ) ? 0 : Opacity ;
}
}
// sends buffer to gpu
:: glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 );
:: glGenTextures ( 1 , & m_id );
:: glBindTexture ( GL_TEXTURE_2D , ( GLuint ) m_id );
:: glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ());
:: 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 );
:: glBindTexture ( GL_TEXTURE_2D , 0 );
return true ;
}
2018-07-31 14:20:16 +02:00
void GLCanvas3D :: WarningTexture :: render ( const GLCanvas3D & canvas ) const
{
2018-07-31 14:32:59 +02:00
if (( m_id > 0 ) && ( m_original_width > 0 ) && ( m_original_height > 0 ) && ( m_width > 0 ) && ( m_height > 0 ))
2018-07-31 14:20:16 +02:00
{
:: glDisable ( GL_DEPTH_TEST );
:: glPushMatrix ();
:: glLoadIdentity ();
const Size & cnv_size = canvas . get_canvas_size ();
float zoom = canvas . get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
float left = ( - 0.5f * ( float ) m_original_width ) * inv_zoom ;
float top = ( - 0.5f * ( float ) cnv_size . get_height () + ( float ) m_original_height + 2.0f ) * inv_zoom ;
float right = left + ( float ) m_original_width * inv_zoom ;
float bottom = top - ( float ) m_original_height * inv_zoom ;
float uv_left = 0.0f ;
float uv_top = 0.0f ;
float uv_right = ( float ) m_original_width / ( float ) m_width ;
float uv_bottom = ( float ) m_original_height / ( float ) m_height ;
GLTexture :: Quad_UVs uvs ;
uvs . left_top = { uv_left , uv_top };
uvs . left_bottom = { uv_left , uv_bottom };
uvs . right_bottom = { uv_right , uv_bottom };
uvs . right_top = { uv_right , uv_top };
GLTexture :: render_sub_texture ( m_id , left , right , bottom , top , uvs );
:: glPopMatrix ();
:: glEnable ( GL_DEPTH_TEST );
}
}
2018-07-19 13:18:19 +02:00
const unsigned char GLCanvas3D :: LegendTexture :: Squares_Border_Color [ 3 ] = { 64 , 64 , 64 };
2018-12-13 11:13:58 +01:00
const unsigned char GLCanvas3D :: LegendTexture :: Default_Background_Color [ 3 ] = { ( unsigned char )( DEFAULT_BG_LIGHT_COLOR [ 0 ] * 255.0f ), ( unsigned char )( DEFAULT_BG_LIGHT_COLOR [ 1 ] * 255.0f ), ( unsigned char )( DEFAULT_BG_LIGHT_COLOR [ 2 ] * 255.0f ) };
const unsigned char GLCanvas3D :: LegendTexture :: Error_Background_Color [ 3 ] = { ( unsigned char )( ERROR_BG_LIGHT_COLOR [ 0 ] * 255.0f ), ( unsigned char )( ERROR_BG_LIGHT_COLOR [ 1 ] * 255.0f ), ( unsigned char )( ERROR_BG_LIGHT_COLOR [ 2 ] * 255.0f ) };
2018-07-19 13:18:19 +02:00
const unsigned char GLCanvas3D :: LegendTexture :: Opacity = 255 ;
2018-07-31 14:32:59 +02:00
GLCanvas3D :: LegendTexture :: LegendTexture ()
: GUI :: GLTexture ()
, m_original_width ( 0 )
, m_original_height ( 0 )
{
}
2018-12-13 11:13:58 +01:00
bool GLCanvas3D :: LegendTexture :: generate ( const GCodePreviewData & preview_data , const std :: vector < float >& tool_colors , const GLCanvas3D & canvas , bool use_error_colors )
2018-07-19 13:18:19 +02:00
{
reset ();
// collects items to render
auto title = _ ( preview_data . get_legend_title ());
2018-11-26 16:07:09 +01:00
2018-11-28 12:32:43 +01:00
std :: vector < std :: pair < double , double >> cp_legend_values ;
if ( preview_data . extrusion . view_type == GCodePreviewData :: Extrusion :: ColorPrint )
{
const auto & config = wxGetApp (). preset_bundle -> full_config ();
const std :: vector < double >& color_print_values = config . option < ConfigOptionFloats > ( "colorprint_heights" ) -> values ;
const int values_cnt = color_print_values . size ();
if ( values_cnt > 0 ) {
auto print_zs = canvas . get_current_print_zs ( true );
auto z = 0 ;
for ( auto i = 0 ; i < values_cnt ; ++ i )
{
double prev_z = - 1.0 ;
for ( z ; z < print_zs . size (); ++ z )
if ( fabs ( color_print_values [ i ] - print_zs [ z ]) < EPSILON ) {
prev_z = print_zs [ z - 1 ];
break ;
}
if ( prev_z < 0 )
continue ;
cp_legend_values . push_back ( std :: pair < double , double > ( prev_z , color_print_values [ i ]));
}
}
}
const GCodePreviewData :: LegendItemsList & items = preview_data . get_legend_items ( tool_colors , /*color_print_values*/ cp_legend_values );
2018-07-19 13:18:19 +02:00
unsigned int items_count = ( unsigned int ) items . size ();
if ( items_count == 0 )
// nothing to render, return
return false ;
wxMemoryDC memDC ;
2018-12-13 11:13:58 +01:00
wxMemoryDC mask_memDC ;
2018-07-19 13:18:19 +02:00
// select default font
memDC . SetFont ( wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT ));
2018-12-13 11:13:58 +01:00
mask_memDC . SetFont ( wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT ));
2018-07-19 13:18:19 +02:00
// calculates texture size
wxCoord w , h ;
memDC . GetTextExtent ( title , & w , & h );
int title_width = ( int ) w ;
int title_height = ( int ) h ;
int max_text_width = 0 ;
int max_text_height = 0 ;
for ( const GCodePreviewData :: LegendItem & item : items )
{
memDC . GetTextExtent ( GUI :: from_u8 ( item . text ), & w , & h );
max_text_width = std :: max ( max_text_width , ( int ) w );
max_text_height = std :: max ( max_text_height , ( int ) h );
}
2018-07-31 14:32:59 +02:00
m_original_width = std :: max ( 2 * Px_Border + title_width , 2 * ( Px_Border + Px_Square_Contour ) + Px_Square + Px_Text_Offset + max_text_width );
m_original_height = 2 * ( Px_Border + Px_Square_Contour ) + title_height + Px_Title_Offset + items_count * Px_Square ;
2018-07-19 13:18:19 +02:00
if ( items_count > 1 )
2018-07-31 14:32:59 +02:00
m_original_height += ( items_count - 1 ) * Px_Square_Contour ;
2018-09-25 11:53:05 +02:00
int pow_of_two_size = ( int ) next_highest_power_of_2 ( std :: max < uint32_t > ( m_original_width , m_original_height ));
2018-07-31 14:32:59 +02:00
m_width = pow_of_two_size ;
m_height = pow_of_two_size ;
2018-07-19 13:18:19 +02:00
// generates bitmap
wxBitmap bitmap ( m_width , m_height );
2018-12-13 11:13:58 +01:00
wxBitmap mask ( m_width , m_height );
2018-07-19 13:18:19 +02:00
memDC . SelectObject ( bitmap );
2018-12-13 11:13:58 +01:00
mask_memDC . SelectObject ( mask );
memDC . SetBackground ( wxBrush ( use_error_colors ? * wxWHITE : * wxBLACK ));
mask_memDC . SetBackground ( wxBrush ( * wxBLACK ));
2018-07-19 13:18:19 +02:00
memDC . Clear ();
2018-12-13 11:13:58 +01:00
mask_memDC . Clear ();
2018-07-19 13:18:19 +02:00
// draw title
2018-12-13 11:13:58 +01:00
memDC . SetTextForeground ( use_error_colors ? * wxWHITE : * wxBLACK );
mask_memDC . SetTextForeground ( * wxWHITE );
2018-07-19 13:18:19 +02:00
int title_x = Px_Border ;
int title_y = Px_Border ;
memDC . DrawText ( title , title_x , title_y );
2018-12-13 11:13:58 +01:00
mask_memDC . DrawText ( title , title_x , title_y );
mask_memDC . SetPen ( wxPen ( * wxWHITE ));
mask_memDC . SetBrush ( wxBrush ( * wxWHITE ));
2018-07-19 13:18:19 +02:00
// draw icons contours as background
int squares_contour_x = Px_Border ;
int squares_contour_y = Px_Border + title_height + Px_Title_Offset ;
int squares_contour_width = Px_Square + 2 * Px_Square_Contour ;
int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour ;
if ( items_count > 1 )
squares_contour_height += ( items_count - 1 ) * Px_Square_Contour ;
wxColour color ( Squares_Border_Color [ 0 ], Squares_Border_Color [ 1 ], Squares_Border_Color [ 2 ]);
wxPen pen ( color );
wxBrush brush ( color );
memDC . SetPen ( pen );
memDC . SetBrush ( brush );
memDC . DrawRectangle ( wxRect ( squares_contour_x , squares_contour_y , squares_contour_width , squares_contour_height ));
2018-12-13 11:13:58 +01:00
mask_memDC . DrawRectangle ( wxRect ( squares_contour_x , squares_contour_y , squares_contour_width , squares_contour_height ));
2018-07-19 13:18:19 +02:00
// draw items (colored icon + text)
int icon_x = squares_contour_x + Px_Square_Contour ;
int icon_x_inner = icon_x + 1 ;
int icon_y = squares_contour_y + Px_Square_Contour ;
int icon_y_step = Px_Square + Px_Square_Contour ;
int text_x = icon_x + Px_Square + Px_Text_Offset ;
int text_y_offset = ( Px_Square - max_text_height ) / 2 ;
int px_inner_square = Px_Square - 2 ;
for ( const GCodePreviewData :: LegendItem & item : items )
{
// draw darker icon perimeter
const std :: vector < unsigned char >& item_color_bytes = item . color . as_bytes ();
wxImage :: HSVValue dark_hsv = wxImage :: RGBtoHSV ( wxImage :: RGBValue ( item_color_bytes [ 0 ], item_color_bytes [ 1 ], item_color_bytes [ 2 ]));
dark_hsv . value *= 0.75 ;
wxImage :: RGBValue dark_rgb = wxImage :: HSVtoRGB ( dark_hsv );
color . Set ( dark_rgb . red , dark_rgb . green , dark_rgb . blue , item_color_bytes [ 3 ]);
pen . SetColour ( color );
brush . SetColour ( color );
memDC . SetPen ( pen );
memDC . SetBrush ( brush );
memDC . DrawRectangle ( wxRect ( icon_x , icon_y , Px_Square , Px_Square ));
// draw icon interior
color . Set ( item_color_bytes [ 0 ], item_color_bytes [ 1 ], item_color_bytes [ 2 ], item_color_bytes [ 3 ]);
pen . SetColour ( color );
brush . SetColour ( color );
memDC . SetPen ( pen );
memDC . SetBrush ( brush );
memDC . DrawRectangle ( wxRect ( icon_x_inner , icon_y + 1 , px_inner_square , px_inner_square ));
// draw text
memDC . DrawText ( GUI :: from_u8 ( item . text ), text_x , icon_y + text_y_offset );
2018-12-13 11:13:58 +01:00
mask_memDC . DrawText ( GUI :: from_u8 ( item . text ), text_x , icon_y + text_y_offset );
2018-07-19 13:18:19 +02:00
// update y
icon_y += icon_y_step ;
}
memDC . SelectObject ( wxNullBitmap );
2018-12-13 11:13:58 +01:00
mask_memDC . SelectObject ( wxNullBitmap );
2018-07-19 13:18:19 +02:00
// Convert the bitmap into a linear data ready to be loaded into the GPU.
wxImage image = bitmap . ConvertToImage ();
2018-12-13 11:13:58 +01:00
wxImage mask_image = mask . ConvertToImage ();
2018-07-19 13:18:19 +02:00
// prepare buffer
std :: vector < unsigned char > data ( 4 * m_width * m_height , 0 );
for ( int h = 0 ; h < m_height ; ++ h )
{
int hh = h * m_width ;
unsigned char * px_ptr = data . data () + 4 * hh ;
for ( int w = 0 ; w < m_width ; ++ w )
{
* px_ptr ++ = image . GetRed ( w , h );
* px_ptr ++ = image . GetGreen ( w , h );
* px_ptr ++ = image . GetBlue ( w , h );
2018-12-13 11:13:58 +01:00
* px_ptr ++ = ( mask_image . GetRed ( w , h ) + mask_image . GetGreen ( w , h ) + mask_image . GetBlue ( w , h )) / 3 ;
2018-07-19 13:18:19 +02:00
}
}
// sends buffer to gpu
:: glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 );
:: glGenTextures ( 1 , & m_id );
:: glBindTexture ( GL_TEXTURE_2D , ( GLuint ) m_id );
:: glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ());
:: 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 );
:: glBindTexture ( GL_TEXTURE_2D , 0 );
return true ;
}
2018-07-31 14:32:59 +02:00
void GLCanvas3D :: LegendTexture :: render ( const GLCanvas3D & canvas ) const
{
if (( m_id > 0 ) && ( m_original_width > 0 ) && ( m_original_height > 0 ) && ( m_width > 0 ) && ( m_height > 0 ))
{
:: glDisable ( GL_DEPTH_TEST );
:: glPushMatrix ();
:: glLoadIdentity ();
const Size & cnv_size = canvas . get_canvas_size ();
float zoom = canvas . get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
float left = ( - 0.5f * ( float ) cnv_size . get_width ()) * inv_zoom ;
float top = ( 0.5f * ( float ) cnv_size . get_height ()) * inv_zoom ;
float right = left + ( float ) m_original_width * inv_zoom ;
float bottom = top - ( float ) m_original_height * inv_zoom ;
float uv_left = 0.0f ;
float uv_top = 0.0f ;
float uv_right = ( float ) m_original_width / ( float ) m_width ;
float uv_bottom = ( float ) m_original_height / ( float ) m_height ;
GLTexture :: Quad_UVs uvs ;
uvs . left_top = { uv_left , uv_top };
uvs . left_bottom = { uv_left , uv_bottom };
uvs . right_bottom = { uv_right , uv_bottom };
uvs . right_top = { uv_right , uv_top };
GLTexture :: render_sub_texture ( m_id , left , right , bottom , top , uvs );
:: glPopMatrix ();
:: glEnable ( GL_DEPTH_TEST );
}
}
2018-12-06 10:38:19 +01:00
wxDEFINE_EVENT ( EVT_GLCANVAS_INIT , SimpleEvent );
2018-10-25 12:10:35 +02:00
wxDEFINE_EVENT ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS , SimpleEvent );
2018-10-16 16:04:19 +02:00
wxDEFINE_EVENT ( EVT_GLCANVAS_OBJECT_SELECT , SimpleEvent );
2018-10-03 11:34:39 +02:00
wxDEFINE_EVENT ( EVT_GLCANVAS_VIEWPORT_CHANGED , SimpleEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_RIGHT_CLICK , Vec2dEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_MODEL_UPDATE , SimpleEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_REMOVE_OBJECT , SimpleEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_ARRANGE , SimpleEvent );
2018-12-19 14:01:13 +01:00
wxDEFINE_EVENT ( EVT_GLCANVAS_QUESTION_MARK , SimpleEvent );
2018-10-18 15:09:41 +02:00
wxDEFINE_EVENT ( EVT_GLCANVAS_INCREASE_INSTANCES , Event < int > );
2018-10-09 17:14:59 +02:00
wxDEFINE_EVENT ( EVT_GLCANVAS_INSTANCE_MOVED , SimpleEvent );
2019-01-03 11:24:03 +01:00
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
wxDEFINE_EVENT ( EVT_GLCANVAS_INSTANCE_ROTATED , SimpleEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_INSTANCE_SCALED , SimpleEvent );
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
2018-10-03 11:34:39 +02:00
wxDEFINE_EVENT ( EVT_GLCANVAS_WIPETOWER_MOVED , Vec3dEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , Event < bool > );
wxDEFINE_EVENT ( EVT_GLCANVAS_UPDATE_GEOMETRY , Vec3dsEvent < 2 > );
2018-11-23 12:47:32 +01:00
wxDEFINE_EVENT ( EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED , SimpleEvent );
2018-10-03 11:34:39 +02:00
2018-06-27 11:31:11 +02:00
GLCanvas3D :: GLCanvas3D ( wxGLCanvas * canvas )
2018-05-09 10:47:04 +02:00
: m_canvas ( canvas )
2018-06-27 11:31:11 +02:00
, m_context ( nullptr )
2018-11-27 16:55:54 +01:00
, m_in_render ( false )
2018-12-17 10:55:14 +01:00
, m_toolbar ( GLToolbar :: Normal )
2018-12-06 10:38:19 +01:00
, m_view_toolbar ( nullptr )
2018-11-27 14:50:57 +01:00
, m_use_clipping_planes ( false )
2018-12-18 10:40:53 +01:00
, m_sidebar_field ( "" )
2018-05-23 11:14:49 +02:00
, m_config ( nullptr )
2018-11-22 15:29:59 +01:00
, m_process ( nullptr )
2018-06-07 11:18:28 +02:00
, m_model ( nullptr )
2018-05-09 10:47:04 +02:00
, m_dirty ( true )
2018-06-05 12:24:26 +02:00
, m_initialized ( false )
2018-06-04 12:26:39 +02:00
, m_use_VBOs ( false )
2018-06-05 10:56:55 +02:00
, m_force_zoom_to_bed_enabled ( false )
2018-05-14 14:14:19 +02:00
, m_apply_zoom_to_volumes_filter ( false )
2018-05-23 15:35:11 +02:00
, m_hover_volume_id ( - 1 )
2018-07-27 12:08:33 +02:00
, m_toolbar_action_running ( false )
2018-05-21 14:40:09 +02:00
, m_warning_texture_enabled ( false )
2018-05-21 14:57:43 +02:00
, m_legend_texture_enabled ( false )
2018-05-22 09:02:42 +02:00
, m_picking_enabled ( false )
2018-05-31 13:51:50 +02:00
, m_moving_enabled ( false )
2018-07-27 09:38:39 +02:00
, m_dynamic_background_enabled ( false )
2018-05-23 15:35:11 +02:00
, m_multisample_allowed ( false )
2018-10-08 14:02:12 +02:00
, m_regenerate_volumes ( true )
2018-11-26 10:41:16 +01:00
, m_moving ( false )
2018-06-06 10:16:58 +02:00
, m_color_by ( "volume" )
2018-06-08 09:40:00 +02:00
, m_reload_delayed ( false )
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
, m_external_gizmo_widgets_parent ( nullptr )
2018-11-26 10:56:07 +01:00
#endif // not ENABLE_IMGUI
2018-05-09 10:47:04 +02:00
{
2018-05-30 15:18:45 +02:00
if ( m_canvas != nullptr )
2018-06-27 11:31:11 +02:00
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-27 11:31:11 +02:00
m_context = new wxGLContext ( m_canvas );
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-10-25 09:35:08 +02:00
m_timer . SetOwner ( m_canvas );
2018-06-27 11:31:11 +02:00
}
2018-10-08 14:02:12 +02:00
m_selection . set_volumes ( & m_volumes . volumes );
2018-05-09 10:47:04 +02:00
}
2018-05-15 09:50:01 +02:00
GLCanvas3D ::~ GLCanvas3D ()
{
2018-06-11 15:13:13 +02:00
reset_volumes ();
2018-06-06 14:19:28 +02:00
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-27 11:31:11 +02:00
if ( m_context != nullptr )
{
delete m_context ;
m_context = nullptr ;
}
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-10-03 11:34:39 +02:00
}
2018-06-27 11:31:11 +02:00
2018-10-03 14:07:10 +02:00
void GLCanvas3D :: post_event ( wxEvent && event )
2018-10-03 11:34:39 +02:00
{
2018-10-03 14:07:10 +02:00
event . SetEventObject ( m_canvas );
2018-10-03 11:34:39 +02:00
wxPostEvent ( m_canvas , event );
}
void GLCanvas3D :: viewport_changed ()
{
post_event ( SimpleEvent ( EVT_GLCANVAS_VIEWPORT_CHANGED ));
2018-05-23 09:57:44 +02:00
}
2018-05-25 14:05:08 +02:00
bool GLCanvas3D :: init ( bool useVBOs , bool use_legacy_opengl )
2018-05-23 09:57:44 +02:00
{
2018-06-05 12:24:26 +02:00
if ( m_initialized )
return true ;
2018-06-27 11:31:11 +02:00
if (( m_canvas == nullptr ) || ( m_context == nullptr ))
return false ;
2018-05-31 08:44:39 +02:00
:: glClearColor ( 1.0f , 1.0f , 1.0f , 1.0f );
2018-05-23 15:35:11 +02:00
:: glClearDepth ( 1.0f );
2018-06-01 15:54:41 +02:00
2018-05-31 08:44:39 +02:00
:: glDepthFunc ( GL_LESS );
:: glEnable ( GL_DEPTH_TEST );
2018-05-23 15:35:11 +02: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 10:46:32 +02: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 15:35:11 +02:00
2018-05-31 08:44:39 +02:00
// light from above
2018-06-11 10:46:32 +02: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 08:44:39 +02:00
2018-05-23 15:35:11 +02: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 15:54:41 +02:00
if ( m_multisample_allowed )
2018-05-23 15:35:11 +02:00
:: glEnable ( GL_MULTISAMPLE );
2018-05-23 09:57:44 +02:00
if ( useVBOs && ! m_shader . init ( "gouraud.vs" , "gouraud.fs" ))
return false ;
2018-05-25 14:05:08 +02:00
if ( useVBOs && ! m_layers_editing . init ( "variable_layer_height.vs" , "variable_layer_height.fs" ))
return false ;
2018-06-04 12:26:39 +02:00
m_use_VBOs = useVBOs ;
2018-06-04 13:15:28 +02:00
m_layers_editing . set_use_legacy_opengl ( use_legacy_opengl );
2018-05-25 14:05:08 +02:00
2018-06-05 12:24:26 +02: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 15:13:13 +02:00
if ( ! m_volumes . empty ())
m_volumes . finalize_geometry ( m_use_VBOs );
2018-06-05 12:24:26 +02:00
2018-10-18 15:13:38 +02:00
if ( m_gizmos . is_enabled ()) {
if ( ! m_gizmos . init ( * this )) {
2018-11-19 14:03:28 +01:00
std :: cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std :: endl ;
return false ;
2018-10-18 15:13:38 +02:00
}
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
if ( m_external_gizmo_widgets_parent != nullptr ) {
m_gizmos . create_external_gizmo_widgets ( m_external_gizmo_widgets_parent );
m_canvas -> GetParent () -> Layout ();
}
2018-11-26 10:56:07 +01:00
#endif // not ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
}
2018-06-13 09:12:16 +02:00
2018-07-23 13:49:48 +02:00
if ( ! _init_toolbar ())
return false ;
2018-12-19 14:44:37 +01:00
#if ENABLE_SIDEBAR_VISUAL_HINTS
if ( ! m_selection . init ( m_use_VBOs ))
return false ;
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
2018-12-06 10:38:19 +01:00
post_event ( SimpleEvent ( EVT_GLCANVAS_INIT ));
2018-06-05 12:24:26 +02:00
m_initialized = true ;
2018-05-23 09:57:44 +02:00
return true ;
2018-05-15 09:50:01 +02:00
}
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-27 11:31:11 +02:00
bool GLCanvas3D :: set_current ()
2018-05-09 10:47:04 +02:00
{
2018-06-27 11:31:11 +02:00
if (( m_canvas != nullptr ) && ( m_context != nullptr ))
2018-06-25 15:17:13 +02:00
return m_canvas -> SetCurrent ( * m_context );
2018-05-18 14:08:59 +02:00
return false ;
2018-05-09 10:47:04 +02:00
}
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-05-09 10:47:04 +02:00
2018-06-22 16:06:37 +02:00
void GLCanvas3D :: set_as_dirty ()
{
m_dirty = true ;
}
2018-06-11 13:48:02 +02:00
unsigned int GLCanvas3D :: get_volumes_count () const
2018-05-14 14:14:19 +02:00
{
2018-06-11 15:13:13 +02:00
return ( unsigned int ) m_volumes . volumes . size ();
2018-05-14 14:14:19 +02:00
}
2018-05-18 14:08:59 +02:00
void GLCanvas3D :: reset_volumes ()
{
2018-06-25 15:17:13 +02:00
if ( ! m_volumes . empty ())
2018-05-18 14:08:59 +02:00
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-25 15:17:13 +02:00
// ensures this canvas is current
2018-06-27 11:31:11 +02:00
if ( ! set_current ())
2018-06-25 15:17:13 +02:00
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-25 15:17:13 +02:00
2018-10-08 14:02:12 +02:00
m_selection . clear ();
2018-06-11 15:13:13 +02:00
m_volumes . release_geometry ();
m_volumes . clear ();
2018-06-01 15:54:41 +02:00
m_dirty = true ;
2018-05-18 14:08:59 +02:00
}
2018-07-27 08:49:58 +02:00
enable_warning_texture ( false );
_reset_warning_texture ();
2018-05-18 14:08:59 +02:00
}
2018-12-04 13:55:25 +01:00
int GLCanvas3D :: check_volumes_outside_state () const
{
ModelInstance :: EPrintVolumeState state ;
m_volumes . check_outside_state ( m_config , & state );
return ( int ) state ;
}
2018-06-11 13:48:02 +02:00
2018-05-23 11:14:49 +02:00
void GLCanvas3D :: set_config ( DynamicPrintConfig * config )
{
m_config = config ;
}
2018-11-22 15:29:59 +01:00
void GLCanvas3D :: set_process ( BackgroundSlicingProcess * process )
2018-05-28 15:23:01 +02:00
{
2018-11-22 15:29:59 +01:00
m_process = process ;
2018-11-13 17:45:44 +01:00
}
2018-06-07 11:18:28 +02:00
void GLCanvas3D :: set_model ( Model * model )
{
m_model = model ;
2018-10-08 14:02:12 +02:00
m_selection . set_model ( m_model );
2018-06-07 11:18:28 +02:00
}
2018-05-14 14:14:19 +02:00
void GLCanvas3D :: set_bed_shape ( const Pointfs & shape )
{
2018-08-05 22:52:38 +02:00
bool new_shape = m_bed . set_shape ( shape );
2018-05-18 13:02:47 +02:00
// Set the origin and size for painting of the coordinate system axes.
2018-08-24 10:20:00 +02:00
m_axes . origin = Vec3d ( 0.0 , 0.0 , ( double ) GROUND_Z );
2018-12-17 14:09:35 +01:00
set_bed_axes_length ( 0.1 * m_bed . get_bounding_box (). max_size ());
2018-06-27 12:05:23 +02:00
2018-07-30 13:57:05 +02:00
if ( new_shape )
2018-11-20 10:36:26 +01:00
zoom_to_bed ();
2018-07-30 10:35:08 +02:00
2018-06-27 12:05:23 +02:00
m_dirty = true ;
2018-05-14 14:14:19 +02:00
}
2018-12-17 14:09:35 +01:00
void GLCanvas3D :: set_bed_axes_length ( double length )
2018-05-18 13:02:47 +02:00
{
2018-12-17 14:09:35 +01:00
m_axes . length = length * Vec3d :: Ones ();
2018-05-15 11:07:32 +02:00
}
2018-06-06 12:36:52 +02:00
void GLCanvas3D :: set_color_by ( const std :: string & value )
{
m_color_by = value ;
}
2018-05-09 10:47:04 +02:00
float GLCanvas3D :: get_camera_zoom () const
{
2018-06-01 15:54:41 +02:00
return m_camera . zoom ;
2018-05-14 14:14:19 +02:00
}
BoundingBoxf3 GLCanvas3D :: volumes_bounding_box () const
{
BoundingBoxf3 bb ;
2018-06-11 15:13:13 +02:00
for ( const GLVolume * volume : m_volumes . volumes )
2018-05-14 14:14:19 +02:00
{
2018-06-11 15:13:13 +02:00
if ( ! m_apply_zoom_to_volumes_filter || (( volume != nullptr ) && volume -> zoom_to_volumes ))
bb . merge ( volume -> transformed_bounding_box ());
2018-05-14 14:14:19 +02:00
}
return bb ;
}
2018-12-07 16:23:04 +01:00
BoundingBoxf3 GLCanvas3D :: scene_bounding_box () const
{
BoundingBoxf3 bb = volumes_bounding_box ();
bb . merge ( m_bed . get_bounding_box ());
2019-01-08 09:10:57 +01:00
if ( m_config != nullptr )
{
double h = m_config -> opt_float ( "max_print_height" );
bb . min ( 2 ) = std :: min ( bb . min ( 2 ), - h );
bb . max ( 2 ) = std :: max ( bb . max ( 2 ), h );
}
2018-12-07 16:23:04 +01:00
return bb ;
}
2018-05-18 14:08:59 +02:00
bool GLCanvas3D :: is_layers_editing_enabled () const
{
return m_layers_editing . is_enabled ();
}
2018-05-25 16:28:24 +02:00
bool GLCanvas3D :: is_layers_editing_allowed () const
{
return m_layers_editing . is_allowed ();
}
2018-06-08 09:40:00 +02:00
bool GLCanvas3D :: is_reload_delayed () const
{
return m_reload_delayed ;
}
2018-05-25 14:05:08 +02:00
void GLCanvas3D :: enable_layers_editing ( bool enable )
{
m_layers_editing . set_enabled ( enable );
}
2018-05-21 14:40:09 +02:00
void GLCanvas3D :: enable_warning_texture ( bool enable )
{
m_warning_texture_enabled = enable ;
}
2018-05-21 14:57:43 +02:00
void GLCanvas3D :: enable_legend_texture ( bool enable )
{
m_legend_texture_enabled = enable ;
}
2018-05-22 09:02:42 +02:00
void GLCanvas3D :: enable_picking ( bool enable )
{
m_picking_enabled = enable ;
2018-10-08 14:02:12 +02:00
m_selection . set_mode ( Selection :: Instance );
2018-05-22 09:02:42 +02:00
}
2018-05-31 13:51:50 +02:00
void GLCanvas3D :: enable_moving ( bool enable )
{
m_moving_enabled = enable ;
}
2018-06-13 09:12:16 +02:00
void GLCanvas3D :: enable_gizmos ( bool enable )
{
m_gizmos . set_enabled ( enable );
}
2018-07-23 13:49:48 +02:00
void GLCanvas3D :: enable_toolbar ( bool enable )
{
m_toolbar . set_enabled ( enable );
}
2018-06-05 10:56:55 +02:00
void GLCanvas3D :: enable_force_zoom_to_bed ( bool enable )
{
m_force_zoom_to_bed_enabled = enable ;
}
2018-07-27 09:38:39 +02:00
void GLCanvas3D :: enable_dynamic_background ( bool enable )
{
m_dynamic_background_enabled = enable ;
}
2018-05-23 15:35:11 +02:00
void GLCanvas3D :: allow_multisample ( bool allow )
{
m_multisample_allowed = allow ;
}
2018-07-23 13:49:48 +02:00
void GLCanvas3D :: enable_toolbar_item ( const std :: string & name , bool enable )
{
if ( enable )
m_toolbar . enable_item ( name );
else
m_toolbar . disable_item ( name );
}
2018-07-27 12:08:33 +02:00
bool GLCanvas3D :: is_toolbar_item_pressed ( const std :: string & name ) const
{
return m_toolbar . is_item_pressed ( name );
}
2018-07-23 13:49:48 +02:00
2018-05-15 10:32:38 +02:00
void GLCanvas3D :: zoom_to_bed ()
{
2018-06-01 15:54:41 +02:00
_zoom_to_bounding_box ( m_bed . get_bounding_box ());
2018-05-15 10:32:38 +02:00
}
void GLCanvas3D :: zoom_to_volumes ()
{
m_apply_zoom_to_volumes_filter = true ;
_zoom_to_bounding_box ( volumes_bounding_box ());
m_apply_zoom_to_volumes_filter = false ;
}
2018-10-26 09:50:28 +02:00
void GLCanvas3D :: zoom_to_selection ()
{
if ( ! m_selection . is_empty ())
_zoom_to_bounding_box ( m_selection . get_bounding_box ());
}
2018-05-15 11:30:11 +02: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 ;
2018-10-17 12:59:58 +02:00
if ( dir_vec != nullptr )
2018-05-15 11:30:11 +02:00
{
2018-06-01 15:54:41 +02:00
m_camera . phi = dir_vec [ 0 ];
2018-05-15 11:30:11 +02:00
m_camera . set_theta ( dir_vec [ 1 ]);
2018-10-03 11:34:39 +02:00
viewport_changed ();
2018-05-15 11:30:11 +02:00
if ( m_canvas != nullptr )
m_canvas -> Refresh ();
}
}
2018-05-29 15:36:09 +02:00
void GLCanvas3D :: set_viewport_from_scene ( const GLCanvas3D & other )
{
2018-06-01 15:54:41 +02:00
m_camera . phi = other . m_camera . phi ;
m_camera . set_theta ( other . m_camera . get_theta ());
2018-12-07 16:23:04 +01:00
m_camera . set_scene_box ( other . m_camera . get_scene_box (), * this );
m_camera . set_target ( other . m_camera . get_target (), * this );
2018-06-01 15:54:41 +02:00
m_camera . zoom = other . m_camera . zoom ;
m_dirty = true ;
2018-05-29 15:36:09 +02:00
}
2018-05-29 15:07:06 +02:00
void GLCanvas3D :: update_volumes_colors_by_extruder ()
{
2018-06-11 15:13:13 +02:00
if ( m_config != nullptr )
m_volumes . update_colors_by_extruder ( m_config );
2018-05-29 15:07:06 +02:00
}
2018-09-19 14:59:57 +02:00
// Returns a Rect object denoting size and position of the Reset button used by a gizmo.
// Returns in either screen or viewport coords.
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-09-19 14:59:57 +02:00
Rect GLCanvas3D :: get_gizmo_reset_rect ( const GLCanvas3D & canvas , bool viewport ) const
{
const Size & cnv_size = canvas . get_canvas_size ();
float w = ( viewport ? - 0.5f : 0.f ) * ( float ) cnv_size . get_width ();
float h = ( viewport ? 0.5f : 1.f ) * ( float ) cnv_size . get_height ();
float zoom = canvas . get_camera_zoom ();
float inv_zoom = viewport ? (( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ) : 1.f ;
const float gap = 30.f ;
return Rect (( w + gap + 80.f ) * inv_zoom , ( viewport ? - 1.f : 1.f ) * ( h - GIZMO_RESET_BUTTON_HEIGHT ) * inv_zoom ,
( w + gap + 80.f + GIZMO_RESET_BUTTON_WIDTH ) * inv_zoom , ( viewport ? - 1.f : 1.f ) * ( h * inv_zoom ));
}
bool GLCanvas3D :: gizmo_reset_rect_contains ( const GLCanvas3D & canvas , float x , float y ) const
{
const Rect & rect = get_gizmo_reset_rect ( canvas , false );
return ( rect . get_left () <= x ) && ( x <= rect . get_right ()) && ( rect . get_top () <= y ) && ( y <= rect . get_bottom ());
}
2018-11-26 15:54:12 +01:00
#endif // not ENABLE_IMGUI
2018-09-19 14:59:57 +02:00
2018-06-04 12:26:39 +02:00
void GLCanvas3D :: render ()
2018-05-23 15:35:11 +02:00
{
2018-11-27 16:55:54 +01:00
wxCHECK_RET ( ! m_in_render , "GLCanvas3D::render() called recursively" );
m_in_render = true ;
Slic3r :: ScopeGuard in_render_guard ([ this ]() { m_in_render = false ; });
( void ) in_render_guard ;
2018-05-29 14:34:45 +02:00
if ( m_canvas == nullptr )
return ;
2018-06-12 09:18:25 +02:00
if ( ! _is_shown_on_screen ())
2018-06-04 12:26:39 +02:00
return ;
2018-06-25 15:17:13 +02:00
// ensures this canvas is current and initialized
2018-10-04 10:41:11 +02:00
#if ENABLE_USE_UNIQUE_GLCONTEXT
if ( ! _set_current () || ! _3DScene :: init ( m_canvas ))
#else
2018-06-27 11:31:11 +02:00
if ( ! set_current () || ! _3DScene :: init ( m_canvas ))
2018-10-04 10:41:11 +02:00
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-04 12:26:39 +02:00
return ;
2018-06-05 10:56:55 +02:00
if ( m_force_zoom_to_bed_enabled )
_force_zoom_to_bed ();
2018-06-04 12:26:39 +02:00
2018-05-31 08:44:39 +02:00
_camera_tranform ();
2018-06-04 12:26:39 +02:00
2018-06-11 10:46:32 +02: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 12:26:39 +02:00
2018-06-11 11:40:11 +02:00
float theta = m_camera . get_theta ();
bool is_custom_bed = m_bed . is_custom ();
2018-10-31 10:19:44 +01:00
#if ENABLE_IMGUI
2018-11-26 10:56:07 +01:00
wxGetApp (). imgui () -> new_frame ();
2018-10-31 10:19:44 +01:00
#endif // ENABLE_IMGUI
2018-08-24 11:17:53 +02:00
// picking pass
2018-05-29 13:54:34 +02:00
_picking_pass ();
2018-08-21 14:27:36 +02:00
2018-08-24 11:17:53 +02:00
// draw scene
2018-11-14 10:43:52 +01:00
:: glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
2018-08-24 11:17:53 +02:00
_render_background ();
2018-08-21 14:27:36 +02:00
2018-12-21 09:56:11 +01:00
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
// textured bed needs to be rendered after objects if the texture is transparent
bool early_bed_render = is_custom_bed || ( theta <= 90.0f );
if ( early_bed_render )
#else
2018-08-24 11:17:53 +02:00
if ( is_custom_bed ) // untextured bed needs to be rendered before objects
2018-12-21 09:56:11 +01:00
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-06-11 11:40:11 +02:00
_render_bed ( theta );
2018-09-10 12:08:57 +02:00
2018-11-14 10:43:52 +01:00
_render_objects ();
2018-11-28 15:13:25 +01:00
_render_sla_slices ();
2018-10-08 14:02:12 +02:00
_render_selection ();
2018-10-26 15:45:52 +02:00
2018-12-17 14:09:35 +01:00
_render_axes ();
2018-12-21 09:56:11 +01:00
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
if ( ! early_bed_render )
#else
2018-08-24 11:17:53 +02:00
if ( ! is_custom_bed ) // textured bed needs to be rendered after objects
2018-12-21 09:56:11 +01:00
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-06-11 11:40:11 +02:00
_render_bed ( theta );
2018-08-24 11:17:53 +02:00
2018-12-18 12:35:49 +01:00
#if ENABLE_RENDER_SELECTION_CENTER
_render_selection_center ();
#endif // ENABLE_RENDER_SELECTION_CENTER
2018-11-15 11:38:40 +01:00
// we need to set the mouse's scene position here because the depth buffer
// could be invalidated by the following gizmo render methods
// this position is used later into on_mouse() to drag the objects
m_mouse . scene_position = _mouse_to_3d ( m_mouse . position . cast < int > ());
2018-12-19 14:44:37 +01:00
#if ENABLE_SIDEBAR_VISUAL_HINTS
_render_selection_sidebar_hints ();
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
2018-08-24 12:06:05 +02:00
_render_current_gizmo ();
2018-10-26 09:50:28 +02:00
#if ENABLE_SHOW_CAMERA_TARGET
_render_camera_target ();
#endif // ENABLE_SHOW_CAMERA_TARGET
2018-08-24 11:17:53 +02:00
// draw overlays
2018-08-21 14:27:36 +02:00
_render_gizmos_overlay ();
2018-05-29 13:54:34 +02:00
_render_warning_texture ();
_render_legend_texture ();
2018-12-06 10:38:19 +01:00
_resize_toolbars ();
2018-07-23 13:49:48 +02:00
_render_toolbar ();
2018-12-06 10:38:19 +01:00
_render_view_toolbar ();
2018-06-14 12:34:19 +02:00
_render_layer_editing_overlay ();
2018-05-29 14:34:45 +02:00
2018-10-31 10:19:44 +01:00
#if ENABLE_IMGUI
2018-11-26 10:56:07 +01:00
wxGetApp (). imgui () -> render ();
2018-10-31 10:19:44 +01:00
#endif // ENABLE_IMGUI
2018-05-29 14:34:45 +02:00
m_canvas -> SwapBuffers ();
2018-05-21 15:24:52 +02:00
}
2018-11-21 15:28:35 +01:00
void GLCanvas3D :: select_all ()
{
m_selection . add_all ();
2018-12-04 08:38:31 +01:00
m_dirty = true ;
2018-11-21 15:28:35 +01:00
}
2018-11-14 08:53:56 +01:00
void GLCanvas3D :: delete_selected ()
{
m_selection . erase ();
}
2018-11-21 12:27:20 +01:00
void GLCanvas3D :: ensure_on_bed ( unsigned int object_idx )
{
typedef std :: map < std :: pair < int , int > , double > InstancesToZMap ;
InstancesToZMap instances_min_z ;
for ( GLVolume * volume : m_volumes . volumes )
{
if (( volume -> object_idx () == object_idx ) && ! volume -> is_modifier )
{
double min_z = volume -> transformed_convex_hull_bounding_box (). min ( 2 );
std :: pair < int , int > instance = std :: make_pair ( volume -> object_idx (), volume -> instance_idx ());
InstancesToZMap :: iterator it = instances_min_z . find ( instance );
if ( it == instances_min_z . end ())
it = instances_min_z . insert ( InstancesToZMap :: value_type ( instance , DBL_MAX )). first ;
it -> second = std :: min ( it -> second , min_z );
}
}
for ( GLVolume * volume : m_volumes . volumes )
{
std :: pair < int , int > instance = std :: make_pair ( volume -> object_idx (), volume -> instance_idx ());
InstancesToZMap :: iterator it = instances_min_z . find ( instance );
if ( it != instances_min_z . end ())
volume -> set_instance_offset ( Z , volume -> get_instance_offset ( Z ) - it -> second );
}
}
2018-06-04 15:42:34 +02:00
std :: vector < double > GLCanvas3D :: get_current_print_zs ( bool active_only ) const
{
2018-06-11 15:13:13 +02:00
return m_volumes . get_current_print_zs ( active_only );
2018-06-04 15:42:34 +02:00
}
void GLCanvas3D :: set_toolpaths_range ( double low , double high )
{
2018-06-11 15:13:13 +02:00
m_volumes . set_range ( low , high );
2018-06-04 15:42:34 +02:00
}
2018-06-06 10:16:58 +02:00
std :: vector < int > GLCanvas3D :: load_object ( const ModelObject & model_object , int obj_idx , std :: vector < int > instance_idxs )
{
if ( instance_idxs . empty ())
{
for ( unsigned int i = 0 ; i < model_object . instances . size (); ++ i )
{
instance_idxs . push_back ( i );
}
}
2018-10-08 16:05:55 +02:00
return m_volumes . load_object ( & model_object , obj_idx , instance_idxs , m_color_by , m_use_VBOs && m_initialized );
2018-06-06 10:16:58 +02:00
}
2018-06-07 09:22:19 +02:00
std :: vector < int > GLCanvas3D :: load_object ( const Model & model , int obj_idx )
2018-06-06 10:16:58 +02: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 09:22:19 +02:00
return load_object ( * model_object , obj_idx , std :: vector < int > ());
2018-06-06 10:16:58 +02:00
}
return std :: vector < int > ();
}
2018-10-18 15:50:51 +02:00
void GLCanvas3D :: mirror_selection ( Axis axis )
{
m_selection . mirror ( axis );
2018-11-21 10:36:09 +01:00
do_mirror ();
2018-10-18 15:50:51 +02:00
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
}
2018-11-16 18:28:50 +01:00
// Reload the 3D scene of
// 1) Model / ModelObjects / ModelInstances / ModelVolumes
// 2) Print bed
// 3) SLA support meshes for their respective ModelObjects / ModelInstances
// 4) Wipe tower preview
// 5) Out of bed collision status & message overlay (texture)
2018-11-21 13:52:46 +01:00
void GLCanvas3D :: reload_scene ( bool refresh_immediately , bool force_full_scene_refresh )
2018-06-08 09:40:00 +02:00
{
2018-06-11 15:13:13 +02:00
if (( m_canvas == nullptr ) || ( m_config == nullptr ) || ( m_model == nullptr ))
2018-06-08 09:40:00 +02:00
return ;
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-25 15:17:13 +02:00
// ensures this canvas is current
2018-06-27 11:31:11 +02:00
if ( ! set_current ())
2018-06-25 15:17:13 +02:00
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-25 15:17:13 +02:00
2018-11-16 18:28:50 +01:00
struct ModelVolumeState {
ModelVolumeState ( const GLVolume * volume ) :
2018-11-22 13:20:13 +01:00
model_volume ( nullptr ), geometry_id ( volume -> geometry_id ), volume_idx ( - 1 ) {}
ModelVolumeState ( const ModelVolume * model_volume , const ModelID & instance_id , const GLVolume :: CompositeID & composite_id ) :
model_volume ( model_volume ), geometry_id ( std :: make_pair ( model_volume -> id (). id , instance_id . id )), composite_id ( composite_id ), volume_idx ( - 1 ) {}
2018-11-16 18:28:50 +01:00
ModelVolumeState ( const ModelID & volume_id , const ModelID & instance_id ) :
2018-11-22 13:20:13 +01:00
model_volume ( nullptr ), geometry_id ( std :: make_pair ( volume_id . id , instance_id . id )), volume_idx ( - 1 ) {}
2018-11-16 18:28:50 +01:00
bool new_geometry () const { return this -> volume_idx == size_t ( - 1 ); }
2018-11-22 13:20:13 +01:00
const ModelVolume * model_volume ;
2018-11-16 18:28:50 +01:00
// ModelID of ModelVolume + ModelID of ModelInstance
// or timestamp of an SLAPrintObjectStep + ModelID of ModelInstance
std :: pair < size_t , size_t > geometry_id ;
GLVolume :: CompositeID composite_id ;
// Volume index in the new GLVolume vector.
size_t volume_idx ;
};
std :: vector < ModelVolumeState > model_volume_state ;
std :: vector < ModelVolumeState > aux_volume_state ;
2018-10-22 11:45:03 +02:00
2018-11-16 18:28:50 +01:00
// SLA steps to pull the preview meshes for.
typedef std :: array < SLAPrintObjectStep , 2 > SLASteps ;
SLASteps sla_steps = { slaposSupportTree , slaposBasePool };
struct SLASupportState {
std :: array < PrintStateBase :: StateWithTimeStamp , std :: tuple_size < SLASteps >:: value > step ;
};
// State of the sla_steps for all SLAPrintObjects.
std :: vector < SLASupportState > sla_support_state ;
2018-06-25 15:17:13 +02:00
2018-11-16 18:28:50 +01:00
std :: vector < size_t > map_glvolume_old_to_new ( m_volumes . volumes . size (), size_t ( - 1 ));
std :: vector < GLVolume *> glvolumes_new ;
glvolumes_new . reserve ( m_volumes . volumes . size ());
auto model_volume_state_lower = []( const ModelVolumeState & m1 , const ModelVolumeState & m2 ) { return m1 . geometry_id < m2 . geometry_id ; };
2018-06-08 09:40:00 +02:00
2018-11-21 13:52:46 +01:00
m_reload_delayed = ! m_canvas -> IsShown () && ! refresh_immediately && ! force_full_scene_refresh ;
2018-06-08 09:40:00 +02:00
2018-12-12 14:40:56 +01:00
PrinterTechnology printer_technology = m_process -> current_printer_technology ();
int volume_idx_wipe_tower_old = - 1 ;
2018-11-16 18:28:50 +01:00
2018-10-08 14:02:12 +02:00
if ( m_regenerate_volumes )
{
2018-11-16 18:28:50 +01:00
// Release invalidated volumes to conserve GPU memory in case of delayed refresh (see m_reload_delayed).
// First initialize model_volumes_new_sorted & model_instances_new_sorted.
for ( int object_idx = 0 ; object_idx < ( int ) m_model -> objects . size (); ++ object_idx ) {
const ModelObject * model_object = m_model -> objects [ object_idx ];
for ( int instance_idx = 0 ; instance_idx < ( int ) model_object -> instances . size (); ++ instance_idx ) {
const ModelInstance * model_instance = model_object -> instances [ instance_idx ];
for ( int volume_idx = 0 ; volume_idx < ( int ) model_object -> volumes . size (); ++ volume_idx ) {
const ModelVolume * model_volume = model_object -> volumes [ volume_idx ];
2018-11-22 13:20:13 +01:00
model_volume_state . emplace_back ( model_volume , model_instance -> id (), GLVolume :: CompositeID ( object_idx , volume_idx , instance_idx ));
2018-11-16 18:28:50 +01:00
}
}
}
if ( printer_technology == ptSLA ) {
2018-11-22 15:29:59 +01:00
const SLAPrint * sla_print = this -> sla_print ();
2018-11-16 18:28:50 +01:00
#ifdef _DEBUG
// Verify that the SLAPrint object is synchronized with m_model.
2018-11-22 15:29:59 +01:00
check_model_ids_equal ( * m_model , sla_print -> model ());
2018-11-16 18:28:50 +01:00
#endif /* _DEBUG */
2018-11-22 15:29:59 +01:00
sla_support_state . reserve ( sla_print -> objects (). size ());
for ( const SLAPrintObject * print_object : sla_print -> objects ()) {
2018-11-16 18:28:50 +01:00
SLASupportState state ;
for ( size_t istep = 0 ; istep < sla_steps . size (); ++ istep ) {
state . step [ istep ] = print_object -> step_state_with_timestamp ( sla_steps [ istep ]);
2018-11-17 17:23:56 +01:00
if ( state . step [ istep ]. state == PrintStateBase :: DONE ) {
if ( ! print_object -> has_mesh ( sla_steps [ istep ]))
// Consider the DONE step without a valid mesh as invalid for the purpose
// of mesh visualization.
state . step [ istep ]. state = PrintStateBase :: INVALID ;
else
for ( const ModelInstance * model_instance : print_object -> model_object () -> instances )
aux_volume_state . emplace_back ( state . step [ istep ]. timestamp , model_instance -> id ());
}
2018-11-16 18:28:50 +01:00
}
sla_support_state . emplace_back ( state );
}
}
std :: sort ( model_volume_state . begin (), model_volume_state . end (), model_volume_state_lower );
std :: sort ( aux_volume_state . begin (), aux_volume_state . end (), model_volume_state_lower );
// Release all ModelVolume based GLVolumes not found in the current Model.
for ( size_t volume_id = 0 ; volume_id < m_volumes . volumes . size (); ++ volume_id ) {
GLVolume * volume = m_volumes . volumes [ volume_id ];
ModelVolumeState key ( volume );
ModelVolumeState * mvs = nullptr ;
if ( volume -> volume_idx () < 0 ) {
auto it = std :: lower_bound ( aux_volume_state . begin (), aux_volume_state . end (), key , model_volume_state_lower );
if ( it != aux_volume_state . end () && it -> geometry_id == key . geometry_id )
mvs = & ( * it );
} else {
auto it = std :: lower_bound ( model_volume_state . begin (), model_volume_state . end (), key , model_volume_state_lower );
if ( it != model_volume_state . end () && it -> geometry_id == key . geometry_id )
mvs = & ( * it );
}
2018-11-21 13:52:46 +01:00
if ( mvs == nullptr || force_full_scene_refresh ) {
2018-11-16 18:28:50 +01:00
// This GLVolume will be released.
2018-12-12 14:40:56 +01:00
if ( volume -> is_wipe_tower ) {
// There is only one wipe tower.
assert ( volume_idx_wipe_tower_old == - 1 );
volume_idx_wipe_tower_old = ( int ) volume_id ;
}
2018-11-16 18:28:50 +01:00
volume -> release_geometry ();
if ( ! m_reload_delayed )
delete volume ;
} else {
// This GLVolume will be reused.
2018-11-22 13:33:20 +01:00
volume -> set_sla_shift_z ( 0.0 );
2018-11-16 18:28:50 +01:00
map_glvolume_old_to_new [ volume_id ] = glvolumes_new . size ();
mvs -> volume_idx = glvolumes_new . size ();
glvolumes_new . emplace_back ( volume );
2018-11-22 13:20:13 +01:00
// Update color of the volume based on the current extruder.
2018-11-22 13:33:21 +01:00
if ( mvs -> model_volume != nullptr ) {
int extruder_id = mvs -> model_volume -> extruder_id ();
if ( extruder_id != - 1 )
volume -> extruder_id = extruder_id ;
2018-11-23 10:50:25 +01:00
2018-12-05 12:10:09 +01:00
volume -> is_modifier = ! mvs -> model_volume -> is_model_part ();
volume -> set_color_from_model_volume ( mvs -> model_volume );
2018-11-23 10:50:25 +01:00
// updates volumes transformations
2018-12-04 11:35:04 +01:00
volume -> set_instance_transformation ( mvs -> model_volume -> get_object () -> instances [ mvs -> composite_id . instance_id ] -> get_transformation ());
2018-11-23 10:50:25 +01:00
volume -> set_volume_transformation ( mvs -> model_volume -> get_transformation ());
}
2018-11-16 18:28:50 +01:00
}
2018-10-08 14:02:12 +02:00
}
}
2018-11-16 18:28:50 +01:00
if ( m_reload_delayed )
return ;
set_bed_shape ( dynamic_cast < const ConfigOptionPoints *> ( m_config -> option ( "bed_shape" )) -> values );
2018-06-08 09:40:00 +02:00
2018-10-08 14:02:12 +02:00
if ( m_regenerate_volumes )
2018-06-08 09:40:00 +02:00
{
2018-11-16 18:28:50 +01:00
m_volumes . volumes = std :: move ( glvolumes_new );
for ( unsigned int obj_idx = 0 ; obj_idx < ( unsigned int ) m_model -> objects . size (); ++ obj_idx ) {
const ModelObject & model_object = * m_model -> objects [ obj_idx ];
// Object will share a single common layer height texture between all printable volumes.
std :: shared_ptr < LayersTexture > layer_height_texture ;
for ( int volume_idx = 0 ; volume_idx < ( int ) model_object . volumes . size (); ++ volume_idx ) {
const ModelVolume & model_volume = * model_object . volumes [ volume_idx ];
for ( int instance_idx = 0 ; instance_idx < ( int ) model_object . instances . size (); ++ instance_idx ) {
const ModelInstance & model_instance = * model_object . instances [ instance_idx ];
ModelVolumeState key ( model_volume . id (), model_instance . id ());
auto it = std :: lower_bound ( model_volume_state . begin (), model_volume_state . end (), key , model_volume_state_lower );
assert ( it != model_volume_state . end () && it -> geometry_id == key . geometry_id );
if ( it -> new_geometry ()) {
// New volume.
if ( model_volume . is_model_part () && ! layer_height_texture ) {
// New object part needs to have the layer height texture assigned, which is shared with the other volumes of the same part.
// Search for the layer height texture in the other volumes.
for ( int iv = volume_idx ; iv < ( int ) model_object . volumes . size (); ++ iv ) {
const ModelVolume & mv = * model_object . volumes [ iv ];
if ( mv . is_model_part ())
for ( int ii = instance_idx ; ii < ( int ) model_object . instances . size (); ++ ii ) {
const ModelInstance & mi = * model_object . instances [ ii ];
ModelVolumeState key ( mv . id (), mi . id ());
auto it = std :: lower_bound ( model_volume_state . begin (), model_volume_state . end (), key , model_volume_state_lower );
assert ( it != model_volume_state . end () && it -> geometry_id == key . geometry_id );
if ( ! it -> new_geometry ()) {
// Found an old printable GLVolume (existing before this function was called).
assert ( m_volumes . volumes [ it -> volume_idx ] -> geometry_id == key . geometry_id );
// Reuse the layer height texture.
const GLVolume * volume = m_volumes . volumes [ it -> volume_idx ];
assert ( volume -> layer_height_texture );
layer_height_texture = volume -> layer_height_texture ;
goto iv_end ;
}
}
}
iv_end :
if ( ! layer_height_texture )
layer_height_texture = std :: make_shared < LayersTexture > ();
}
m_volumes . load_object_volume ( & model_object , layer_height_texture , obj_idx , volume_idx , instance_idx , m_color_by , m_use_VBOs && m_initialized );
m_volumes . volumes . back () -> geometry_id = key . geometry_id ;
} else {
// Recycling an old GLVolume.
GLVolume & existing_volume = * m_volumes . volumes [ it -> volume_idx ];
assert ( existing_volume . geometry_id == key . geometry_id );
// Update the Object/Volume/Instance indices into the current Model.
existing_volume . composite_id = it -> composite_id ;
if ( model_volume . is_model_part () && ! layer_height_texture ) {
assert ( existing_volume . layer_height_texture );
// cache its layer height texture
layer_height_texture = existing_volume . layer_height_texture ;
}
}
}
}
}
if ( printer_technology == ptSLA ) {
size_t idx = 0 ;
2018-11-22 15:29:59 +01:00
const SLAPrint * sla_print = this -> sla_print ();
2018-11-26 15:19:42 +01:00
std :: vector < double > shift_zs ( m_model -> objects . size (), 0 );
for ( const SLAPrintObject * print_object : sla_print -> objects ()) {
2018-11-16 18:28:50 +01:00
SLASupportState & state = sla_support_state [ idx ++ ];
const ModelObject * model_object = print_object -> model_object ();
// Find an index of the ModelObject
int object_idx ;
2018-11-17 17:23:56 +01:00
if ( std :: all_of ( state . step . begin (), state . step . end (), []( const PrintStateBase :: StateWithTimeStamp & state ){ return state . state != PrintStateBase :: DONE ; }))
2018-11-16 18:28:50 +01:00
continue ;
// There may be new SLA volumes added to the scene for this print_object.
// Find the object index of this print_object in the Model::objects list.
2018-11-22 15:29:59 +01:00
auto it = std :: find ( sla_print -> model (). objects . begin (), sla_print -> model (). objects . end (), model_object );
assert ( it != sla_print -> model (). objects . end ());
object_idx = it - sla_print -> model (). objects . begin ();
2018-11-26 15:19:42 +01:00
// Cache the Z offset to be applied to all volumes with this object_idx.
shift_zs [ object_idx ] = print_object -> get_current_elevation ();
2018-11-16 18:28:50 +01:00
// Collect indices of this print_object's instances, for which the SLA support meshes are to be added to the scene.
// pairs of <instance_idx, print_instance_idx>
std :: vector < std :: pair < size_t , size_t >> instances [ std :: tuple_size < SLASteps >:: value ];
for ( size_t print_instance_idx = 0 ; print_instance_idx < print_object -> instances (). size (); ++ print_instance_idx ) {
const SLAPrintObject :: Instance & instance = print_object -> instances ()[ print_instance_idx ];
// Find index of ModelInstance corresponding to this SLAPrintObject::Instance.
auto it = std :: find_if ( model_object -> instances . begin (), model_object -> instances . end (),
[ & instance ]( const ModelInstance * mi ) { return mi -> id () == instance . instance_id ; });
assert ( it != model_object -> instances . end ());
int instance_idx = it - model_object -> instances . begin ();
for ( size_t istep = 0 ; istep < sla_steps . size (); ++ istep )
if ( state . step [ istep ]. state == PrintStateBase :: DONE ) {
ModelVolumeState key ( state . step [ istep ]. timestamp , instance . instance_id . id );
auto it = std :: lower_bound ( aux_volume_state . begin (), aux_volume_state . end (), key , model_volume_state_lower );
assert ( it != aux_volume_state . end () && it -> geometry_id == key . geometry_id );
if ( it -> new_geometry ())
instances [ istep ]. emplace_back ( std :: pair < size_t , size_t > ( instance_idx , print_instance_idx ));
else
// Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
2018-11-22 13:33:20 +01:00
m_volumes . volumes [ it -> volume_idx ] -> composite_id = GLVolume :: CompositeID ( object_idx , m_volumes . volumes [ it -> volume_idx ] -> volume_idx (), instance_idx );
2018-11-16 18:28:50 +01:00
}
}
2018-11-19 13:04:19 +01:00
// stores the current volumes count
size_t volumes_count = m_volumes . volumes . size ();
2018-11-19 12:20:44 +01:00
for ( size_t istep = 0 ; istep < sla_steps . size (); ++ istep )
if ( ! instances [ istep ]. empty ())
m_volumes . load_object_auxiliary ( print_object , object_idx , instances [ istep ], sla_steps [ istep ], state . step [ istep ]. timestamp , m_use_VBOs && m_initialized );
2018-11-16 18:28:50 +01:00
}
2018-11-26 15:19:42 +01:00
// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
for ( GLVolume * volume : m_volumes . volumes )
volume -> set_sla_shift_z ( shift_zs [ volume -> object_idx ()]);
2018-11-16 18:28:50 +01:00
}
2018-11-13 17:45:44 +01:00
if ( printer_technology == ptFFF && m_config -> has ( "nozzle_diameter" ))
2018-06-08 09:40:00 +02:00
{
2018-10-08 14:02:12 +02:00
// Should the wipe tower be visualized ?
unsigned int extruders_count = ( unsigned int ) dynamic_cast < const ConfigOptionFloats *> ( m_config -> option ( "nozzle_diameter" )) -> values . size ();
2018-06-08 09:40:00 +02:00
2018-10-08 14:02:12 +02:00
bool semm = dynamic_cast < const ConfigOptionBool *> ( m_config -> option ( "single_extruder_multi_material" )) -> value ;
bool wt = dynamic_cast < const ConfigOptionBool *> ( m_config -> option ( "wipe_tower" )) -> value ;
bool co = dynamic_cast < const ConfigOptionBool *> ( m_config -> option ( "complete_objects" )) -> value ;
2018-06-08 09:40:00 +02:00
2018-10-08 14:02:12 +02:00
if (( extruders_count > 1 ) && semm && wt && ! co )
{
// Height of a print (Show at least a slab)
double height = std :: max ( m_model -> bounding_box (). max ( 2 ), 10.0 );
2018-07-27 15:56:27 +02:00
2018-10-08 14:02:12 +02:00
float x = dynamic_cast < const ConfigOptionFloat *> ( m_config -> option ( "wipe_tower_x" )) -> value ;
float y = dynamic_cast < const ConfigOptionFloat *> ( m_config -> option ( "wipe_tower_y" )) -> value ;
float w = dynamic_cast < const ConfigOptionFloat *> ( m_config -> option ( "wipe_tower_width" )) -> value ;
float a = dynamic_cast < const ConfigOptionFloat *> ( m_config -> option ( "wipe_tower_rotation_angle" )) -> value ;
2018-11-22 15:29:59 +01:00
const Print * print = m_process -> fff_print ();
float depth = print -> get_wipe_tower_depth ();
if ( ! print -> is_step_done ( psWipeTower ))
2018-10-08 14:02:12 +02:00
depth = ( 900.f / w ) * ( float )( extruders_count - 1 ) ;
2018-12-12 14:40:56 +01:00
int volume_idx_wipe_tower_new = m_volumes . load_wipe_tower_preview (
1000 , x , y , w , depth , ( float ) height , a , m_use_VBOs && m_initialized , ! print -> is_step_done ( psWipeTower ),
print -> config (). nozzle_diameter . values [ 0 ] * 1.25f * 4.5f );
if ( volume_idx_wipe_tower_old != - 1 )
map_glvolume_old_to_new [ volume_idx_wipe_tower_old ] = volume_idx_wipe_tower_new ;
2018-10-08 14:02:12 +02:00
}
2018-06-08 09:40:00 +02:00
}
2018-10-08 14:02:12 +02:00
update_volumes_colors_by_extruder ();
2018-11-16 18:28:50 +01:00
// Update selection indices based on the old/new GLVolumeCollection.
m_selection . volumes_changed ( map_glvolume_old_to_new );
}
_update_gizmos_data ();
// Update the toolbar
post_event ( SimpleEvent ( EVT_GLCANVAS_OBJECT_SELECT ));
2018-06-08 09:40:00 +02:00
// checks for geometry outside the print volume to render it accordingly
2018-06-11 15:13:13 +02:00
if ( ! m_volumes . empty ())
2018-06-08 09:40:00 +02:00
{
2018-07-18 14:26:42 +02:00
ModelInstance :: EPrintVolumeState state ;
bool contained = m_volumes . check_outside_state ( m_config , & state );
2018-06-08 09:40:00 +02:00
if ( ! contained )
{
enable_warning_texture ( true );
2018-07-19 13:18:19 +02:00
_generate_warning_texture ( L ( "Detected object outside print volume" ));
2018-10-03 11:34:39 +02:00
post_event ( Event < bool > ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , state == ModelInstance :: PVS_Fully_Outside ));
2018-06-08 09:40:00 +02:00
}
else
{
enable_warning_texture ( false );
2018-06-11 15:13:13 +02:00
m_volumes . reset_outside_state ();
2018-07-19 13:18:19 +02:00
_reset_warning_texture ();
2018-10-03 11:34:39 +02:00
post_event ( Event < bool > ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , ! m_model -> objects . empty ()));
2018-06-08 09:40:00 +02:00
}
}
else
{
enable_warning_texture ( false );
2018-07-19 13:18:19 +02:00
_reset_warning_texture ();
2018-10-03 11:34:39 +02:00
post_event ( Event < bool > ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , false ));
2018-06-08 09:40:00 +02:00
}
2018-10-08 14:02:12 +02:00
// restore to default value
m_regenerate_volumes = true ;
2018-12-07 16:23:04 +01:00
m_camera . set_scene_box ( scene_bounding_box (), * this );
m_camera . set_target ( m_camera . get_target (), * this );
2018-11-16 18:28:50 +01:00
// and force this canvas to be redrawn.
m_dirty = true ;
2018-06-08 09:40:00 +02:00
}
2018-06-05 10:56:55 +02:00
void GLCanvas3D :: load_gcode_preview ( const GCodePreviewData & preview_data , const std :: vector < std :: string >& str_tool_colors )
{
2018-11-22 15:29:59 +01:00
const Print * print = this -> fff_print ();
if (( m_canvas != nullptr ) && ( print != nullptr ))
2018-06-05 10:56:55 +02:00
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-25 15:17:13 +02:00
// ensures that this canvas is current
2018-06-27 11:31:11 +02:00
if ( ! set_current ())
2018-06-05 10:56:55 +02:00
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-05 10:56:55 +02:00
2018-12-13 11:13:58 +01:00
std :: vector < float > tool_colors = _parse_colors ( str_tool_colors );
2018-06-11 15:13:13 +02:00
if ( m_volumes . empty ())
2018-06-05 10:56:55 +02:00
{
m_gcode_preview_volume_index . reset ();
_load_gcode_extrusion_paths ( preview_data , tool_colors );
_load_gcode_travel_paths ( preview_data , tool_colors );
_load_gcode_retractions ( preview_data );
_load_gcode_unretractions ( preview_data );
2018-12-13 11:13:58 +01:00
if ( ! m_volumes . empty ())
2018-06-05 10:56:55 +02:00
{
// removes empty volumes
2018-06-11 15:13:13 +02: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-11-26 15:16:35 +01:00
_load_shells_fff ();
2018-06-05 10:56:55 +02:00
}
2018-07-24 13:39:17 +02:00
_update_toolpath_volumes_outside_state ();
2018-06-05 10:56:55 +02:00
}
_update_gcode_volumes_visibility ( preview_data );
2018-07-24 13:39:17 +02:00
_show_warning_texture_if_needed ();
2018-12-13 11:13:58 +01:00
if ( m_volumes . empty ())
reset_legend_texture ();
else
_generate_legend_texture ( preview_data , tool_colors );
2018-06-05 10:56:55 +02:00
}
}
2018-11-26 15:16:35 +01:00
void GLCanvas3D :: load_sla_preview ()
{
const SLAPrint * print = this -> sla_print ();
if (( m_canvas != nullptr ) && ( print != nullptr ))
{
_load_shells_sla ();
}
}
2018-07-24 13:39:17 +02:00
void GLCanvas3D :: load_preview ( const std :: vector < std :: string >& str_tool_colors )
{
2018-11-22 15:29:59 +01:00
const Print * print = this -> fff_print ();
if ( print == nullptr )
2018-07-24 13:39:17 +02:00
return ;
_load_print_toolpaths ();
_load_wipe_tower_toolpaths ( str_tool_colors );
2018-11-22 15:29:59 +01:00
for ( const PrintObject * object : print -> objects ())
2018-07-24 13:39:17 +02:00
{
if ( object != nullptr )
_load_print_object_toolpaths ( * object , str_tool_colors );
}
2018-07-27 08:49:58 +02:00
for ( GLVolume * volume : m_volumes . volumes )
{
volume -> is_extrusion_path = true ;
}
2018-07-24 13:39:17 +02:00
_update_toolpath_volumes_outside_state ();
_show_warning_texture_if_needed ();
reset_legend_texture ();
}
2018-06-06 14:19:28 +02: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 09:22:19 +02:00
m_canvas -> Bind ( wxEVT_KEY_DOWN , & GLCanvas3D :: on_key_down , this );
2018-06-06 14:19:28 +02: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 09:22:19 +02:00
m_canvas -> Unbind ( wxEVT_KEY_DOWN , & GLCanvas3D :: on_key_down , this );
2018-06-06 14:19:28 +02:00
}
}
2018-05-14 14:47:13 +02:00
void GLCanvas3D :: on_size ( wxSizeEvent & evt )
{
2018-06-01 15:54:41 +02:00
m_dirty = true ;
2018-05-14 14:47:13 +02:00
}
void GLCanvas3D :: on_idle ( wxIdleEvent & evt )
{
2018-06-01 15:54:41 +02:00
if ( ! m_dirty )
2018-05-14 14:47:13 +02:00
return ;
2018-05-28 15:23:01 +02:00
_refresh_if_shown_on_screen ();
2018-05-14 14:47:13 +02:00
}
2018-05-15 16:09:04 +02: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-11-29 15:33:52 +01:00
// key ESC
case 27 : { m_gizmos . reset_all_states (); m_dirty = true ; break ; }
2018-06-07 09:22:19 +02:00
// key +
2018-10-18 15:09:41 +02:00
case 43 : { post_event ( Event < int > ( EVT_GLCANVAS_INCREASE_INSTANCES , + 1 )); break ; }
2018-06-07 09:22:19 +02:00
// key -
2018-10-18 15:09:41 +02:00
case 45 : { post_event ( Event < int > ( EVT_GLCANVAS_INCREASE_INSTANCES , - 1 )); break ; }
2018-12-19 14:01:13 +01:00
// key ?
case 63 : { post_event ( SimpleEvent ( EVT_GLCANVAS_QUESTION_MARK )); break ; }
2018-06-07 09:22:19 +02:00
// key A/a
case 65 :
2018-10-03 11:34:39 +02:00
case 97 : { post_event ( SimpleEvent ( EVT_GLCANVAS_ARRANGE )); break ; }
2018-05-15 16:09:04 +02:00
// key B/b
case 66 :
2018-05-31 13:51:50 +02:00
case 98 : { zoom_to_bed (); break ; }
2018-11-29 09:03:38 +01:00
// key I/i
case 73 :
case 105 : { set_camera_zoom ( 1.0f ); break ; }
// key O/o
case 79 :
case 111 : { set_camera_zoom ( - 1.0f ); break ; }
2018-10-26 09:50:28 +02:00
// key Z/z
case 90 :
case 122 :
{
if ( m_selection . is_empty ())
zoom_to_volumes ();
else
zoom_to_selection ();
break ;
}
2018-06-07 09:22:19 +02:00
default :
{
2018-11-22 11:13:00 +01:00
if ( m_gizmos . handle_shortcut ( keyCode , m_selection ))
2018-11-22 10:14:31 +01:00
{
_update_gizmos_data ();
2018-11-29 15:33:52 +01:00
m_dirty = true ;
2018-11-22 10:14:31 +01:00
}
else
evt . Skip ();
2018-06-07 09:22:19 +02:00
break ;
}
2018-05-15 16:09:04 +02:00
}
}
}
}
}
2018-05-28 15:23:01 +02: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
2018-06-13 13:14:17 +02:00
if ( is_layers_editing_enabled ())
2018-05-28 15:23:01 +02:00
{
2018-10-09 15:56:34 +02:00
int object_idx_selected = m_selection . get_object_idx ();
2018-05-28 15:23:01 +02:00
if ( object_idx_selected != - 1 )
{
// A volume is selected. Test, whether hovering over a layer thickness bar.
2018-06-13 13:14:17 +02:00
if ( m_layers_editing . bar_rect_contains ( * this , ( float ) evt . GetX (), ( float ) evt . GetY ()))
2018-05-28 15:23:01 +02:00
{
// Adjust the width of the selection.
2018-06-01 15:54:41 +02: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 15:23:01 +02: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 ();
2018-11-29 09:03:38 +01:00
set_camera_zoom ( zoom );
2018-05-28 15:23:01 +02:00
}
2018-05-30 15:18:45 +02:00
void GLCanvas3D :: on_timer ( wxTimerEvent & evt )
{
2018-06-01 15:54:41 +02:00
if ( m_layers_editing . state != LayersEditing :: Editing )
2018-05-30 15:18:45 +02:00
return ;
_perform_layer_editing_action ();
}
2018-05-31 13:51:50 +02:00
void GLCanvas3D :: on_mouse ( wxMouseEvent & evt )
{
2018-10-31 10:19:44 +01:00
#if ENABLE_IMGUI
2018-11-26 10:56:07 +01:00
auto imgui = wxGetApp (). imgui ();
if ( imgui -> update_mouse_data ( evt )) {
render ();
if ( imgui -> want_any_input ()) {
return ;
}
}
2018-10-31 10:19:44 +01:00
#endif // ENABLE_IMGUI
2018-05-31 13:51:50 +02:00
Point pos ( evt . GetX (), evt . GetY ());
2018-10-09 15:56:34 +02:00
int selected_object_idx = m_selection . get_object_idx ();
2018-10-08 14:02:12 +02:00
int layer_editing_object_idx = is_layers_editing_enabled () ? selected_object_idx : - 1 ;
2018-06-13 13:14:17 +02:00
m_layers_editing . last_object_id = layer_editing_object_idx ;
2018-06-14 12:34:19 +02:00
bool gizmos_overlay_contains_mouse = m_gizmos . overlay_contains_mouse ( * this , m_mouse . position );
2018-12-17 10:55:14 +01:00
int toolbar_contains_mouse = m_toolbar . contains_mouse ( m_mouse . position , * this );
2018-12-06 10:38:19 +01:00
int view_toolbar_contains_mouse = ( m_view_toolbar != nullptr ) ? m_view_toolbar -> contains_mouse ( m_mouse . position , * this ) : - 1 ;
2018-05-31 13:51:50 +02:00
if ( evt . Entering ())
{
2019-01-08 10:06:10 +01:00
//#if defined(__WXMSW__) || defined(__linux__)
// // On Windows and Linux needs focus in order to catch key events
// Set focus in order to remove it from sidebar fields
2019-01-02 17:49:23 +01:00
if ( m_canvas != nullptr ) {
// Only set focus, if the top level window of this canvas is active.
auto p = dynamic_cast < wxWindow *> ( evt . GetEventObject ());
while ( p -> GetParent ())
p = p -> GetParent ();
auto * top_level_wnd = dynamic_cast < wxTopLevelWindow *> ( p );
if ( top_level_wnd && top_level_wnd -> IsActive ())
m_canvas -> SetFocus ();
}
2018-06-01 15:54:41 +02:00
m_mouse . set_start_position_2D_as_invalid ();
2019-01-08 10:06:10 +01:00
//#endif
2018-07-26 12:10:45 +02:00
}
2018-07-26 12:51:31 +02:00
else if ( evt . Leaving ())
{
2018-07-30 09:09:14 +02:00
// to remove hover on objects when the mouse goes out of this canvas
2018-08-21 21:05:24 +02:00
m_mouse . position = Vec2d ( - 1.0 , - 1.0 );
2018-07-30 09:09:14 +02:00
m_dirty = true ;
2018-07-26 12:51:31 +02:00
}
2018-07-27 12:08:33 +02:00
else if ( evt . LeftDClick () && ( toolbar_contains_mouse != - 1 ))
{
m_toolbar_action_running = true ;
2019-01-07 09:15:31 +01:00
m_mouse . set_start_position_3D_as_invalid ();
2018-12-17 10:55:14 +01:00
m_toolbar . do_action (( unsigned int ) toolbar_contains_mouse , * this );
2018-07-27 12:08:33 +02:00
}
2018-12-06 14:26:13 +01:00
else if ( evt . LeftDClick () && ( m_gizmos . get_current_type () != Gizmos :: Undefined ))
{
m_mouse . ignore_up_event = true ;
}
2018-05-31 13:51:50 +02:00
else if ( evt . LeftDown () || evt . RightDown ())
{
2018-11-30 12:49:31 +01:00
m_mouse . left_down = evt . LeftDown ();
2018-05-31 13:51:50 +02:00
// If user pressed left or right button we first check whether this happened
// on a volume or not.
2018-06-01 15:54:41 +02:00
m_layers_editing . state = LayersEditing :: Unknown ;
2018-08-17 15:53:43 +02:00
if (( layer_editing_object_idx != - 1 ) && m_layers_editing . bar_rect_contains ( * this , pos ( 0 ), pos ( 1 )))
2018-05-31 13:51:50 +02:00
{
// A volume is selected and the mouse is inside the layer thickness bar.
// Start editing the layer height.
2018-06-01 15:54:41 +02:00
m_layers_editing . state = LayersEditing :: Editing ;
_perform_layer_editing_action ( & evt );
2018-05-31 13:51:50 +02:00
}
2018-08-17 15:53:43 +02:00
else if (( layer_editing_object_idx != - 1 ) && m_layers_editing . reset_rect_contains ( * this , pos ( 0 ), pos ( 1 )))
2018-05-31 13:51:50 +02:00
{
if ( evt . LeftDown ())
{
// A volume is selected and the mouse is inside the reset button.
2018-09-12 11:59:02 +02:00
// The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself,
// therefore it is safe to call it while the background processing is running.
2018-11-22 15:29:59 +01:00
const_cast < PrintObject *> ( this -> fff_print () -> get_object ( layer_editing_object_idx )) -> reset_layer_height_profile ();
2018-05-31 13:51:50 +02:00
// Index 2 means no editing, just wait for mouse up event.
2018-06-01 15:54:41 +02:00
m_layers_editing . state = LayersEditing :: Completed ;
m_dirty = true ;
2018-05-31 13:51:50 +02:00
}
}
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-09-19 14:59:57 +02:00
else if (( m_gizmos . get_current_type () == Gizmos :: SlaSupports ) && gizmo_reset_rect_contains ( * this , pos ( 0 ), pos ( 1 )))
{
if ( evt . LeftDown ())
{
m_gizmos . delete_current_grabber ( true );
m_dirty = true ;
}
}
2018-11-26 15:54:12 +01:00
#endif // not ENABLE_IMGUI
2018-10-08 14:02:12 +02:00
else if ( ! m_selection . is_empty () && gizmos_overlay_contains_mouse )
{
m_gizmos . update_on_off_state ( * this , m_mouse . position , m_selection );
2018-11-01 15:08:39 +01:00
_update_gizmos_data ();
2018-10-08 14:02:12 +02:00
m_dirty = true ;
}
2018-10-19 13:26:42 +02:00
else if ( evt . LeftDown () && ! m_selection . is_empty () && m_gizmos . grabber_contains_mouse ())
2018-06-19 09:46:26 +02:00
{
2018-11-01 15:08:39 +01:00
_update_gizmos_data ();
2018-10-08 14:02:12 +02:00
m_selection . start_dragging ();
2018-10-15 11:30:50 +02:00
m_gizmos . start_dragging ( m_selection );
2018-08-09 16:55:43 +02:00
if ( m_gizmos . get_current_type () == Gizmos :: Flatten ) {
2018-09-20 15:00:40 +02:00
// Rotate the object so the normal points downward:
2018-11-02 15:20:26 +01:00
m_selection . flattening_rotate ( m_gizmos . get_flattening_normal ());
2018-11-21 10:36:09 +01:00
do_flatten ();
2018-10-16 13:49:40 +02:00
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
2018-08-09 16:55:43 +02:00
}
2018-08-27 14:00:53 +02:00
2018-06-18 15:07:17 +02:00
m_dirty = true ;
2018-06-15 14:10:28 +02:00
}
2018-09-19 14:59:57 +02:00
else if (( selected_object_idx != - 1 ) && m_gizmos . grabber_contains_mouse () && evt . RightDown ()) {
if ( m_gizmos . get_current_type () == Gizmos :: SlaSupports )
m_gizmos . delete_current_grabber ();
}
2018-12-06 10:38:19 +01:00
else if ( view_toolbar_contains_mouse != - 1 )
{
if ( m_view_toolbar != nullptr )
m_view_toolbar -> do_action (( unsigned int ) view_toolbar_contains_mouse , * this );
}
2018-07-27 12:08:33 +02:00
else if ( toolbar_contains_mouse != - 1 )
{
m_toolbar_action_running = true ;
2019-01-07 09:15:31 +01:00
m_mouse . set_start_position_3D_as_invalid ();
2018-12-17 10:55:14 +01:00
m_toolbar . do_action (( unsigned int ) toolbar_contains_mouse , * this );
2018-11-30 12:49:31 +01:00
m_mouse . left_down = false ;
2018-07-27 12:08:33 +02:00
}
2018-05-31 13:51:50 +02: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-10-08 14:02:12 +02:00
if ( m_picking_enabled && (( m_hover_volume_id != - 1 ) || ! is_layers_editing_enabled ()))
2018-05-31 13:51:50 +02:00
{
2018-10-18 08:51:06 +02:00
if ( evt . LeftDown () && ( m_hover_volume_id != - 1 ))
2018-10-08 14:02:12 +02:00
{
2018-10-30 11:18:15 +01:00
bool already_selected = m_selection . contains_volume ( m_hover_volume_id );
bool shift_down = evt . ShiftDown ();
2019-01-03 11:24:03 +01:00
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
Selection :: IndicesList curr_idxs = m_selection . get_volume_idxs ();
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
2018-10-30 11:18:15 +01:00
if ( already_selected && shift_down )
2018-10-22 08:54:04 +02:00
m_selection . remove ( m_hover_volume_id );
2018-10-30 11:18:15 +01:00
else
{
2018-11-30 12:49:31 +01:00
bool add_as_single = ! already_selected && ! shift_down ;
2018-10-30 11:18:15 +01:00
m_selection . add ( m_hover_volume_id , add_as_single );
}
2018-10-08 14:02:12 +02:00
2019-01-03 11:24:03 +01:00
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
if ( curr_idxs != m_selection . get_volume_idxs ())
{
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
m_gizmos . update_on_off_state ( m_selection );
_update_gizmos_data ();
#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
post_event ( SimpleEvent ( EVT_GLCANVAS_OBJECT_SELECT ));
m_dirty = true ;
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
}
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
2018-10-08 14:02:12 +02:00
}
2018-05-31 13:51:50 +02:00
}
// propagate event through callback
2018-10-08 14:02:12 +02:00
if ( m_hover_volume_id != - 1 )
2018-05-31 13:51:50 +02:00
{
2018-10-08 14:02:12 +02:00
if ( evt . LeftDown () && m_moving_enabled && ( m_mouse . drag . move_volume_idx == - 1 ))
2018-05-31 13:51:50 +02:00
{
// Only accept the initial position, if it is inside the volume bounding box.
2018-10-08 14:02:12 +02:00
BoundingBoxf3 volume_bbox = m_volumes . volumes [ m_hover_volume_id ] -> transformed_bounding_box ();
2018-05-31 13:51:50 +02:00
volume_bbox . offset ( 1.0 );
2018-11-15 11:38:40 +01:00
if ( volume_bbox . contains ( m_mouse . scene_position ))
2018-05-31 13:51:50 +02:00
{
// The dragging operation is initiated.
2018-10-08 14:02:12 +02:00
m_mouse . drag . move_volume_idx = m_hover_volume_id ;
m_selection . start_dragging ();
2018-11-15 11:38:40 +01:00
m_mouse . drag . start_position_3D = m_mouse . scene_position ;
2018-11-26 10:41:16 +01:00
m_moving = true ;
2018-05-31 13:51:50 +02:00
}
}
else if ( evt . RightDown ())
{
2018-07-27 14:38:19 +02:00
// forces a frame render to ensure that m_hover_volume_id is updated even when the user right clicks while
// the context menu is already shown, ensuring it to disappear if the mouse is outside any volume
2018-08-23 15:37:38 +02:00
m_mouse . position = Vec2d (( double ) pos ( 0 ), ( double ) pos ( 1 ));
2018-07-27 14:38:19 +02:00
render ();
if ( m_hover_volume_id != - 1 )
{
2018-10-18 08:51:06 +02:00
// if right clicking on volume, propagate event through callback (shows context menu)
if ( m_volumes . volumes [ m_hover_volume_id ] -> hover && ! m_volumes . volumes [ m_hover_volume_id ] -> is_wipe_tower )
{
// forces the selection of the volume
m_selection . add ( m_hover_volume_id );
m_gizmos . update_on_off_state ( m_selection );
2018-10-25 12:36:10 +02:00
post_event ( SimpleEvent ( EVT_GLCANVAS_OBJECT_SELECT ));
2018-11-01 15:08:39 +01:00
_update_gizmos_data ();
2018-10-18 08:51:06 +02:00
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
// forces a frame render to update the view before the context menu is shown
render ();
2018-10-03 11:34:39 +02:00
post_event ( Vec2dEvent ( EVT_GLCANVAS_RIGHT_CLICK , pos . cast < double > ()));
2018-10-18 08:51:06 +02:00
}
2018-07-27 14:38:19 +02:00
}
2018-05-31 13:51:50 +02:00
}
}
}
}
2018-06-21 08:37:04 +02:00
else if ( evt . Dragging () && evt . LeftIsDown () && ! gizmos_overlay_contains_mouse && ( m_layers_editing . state == LayersEditing :: Unknown ) && ( m_mouse . drag . move_volume_idx != - 1 ))
2018-05-31 16:04:59 +02:00
{
2018-06-01 15:54:41 +02:00
m_mouse . dragging = true ;
2018-05-31 16:04:59 +02:00
// Get new position at the same Z of the initial click point.
float z0 = 0.0f ;
float z1 = 1.0f ;
2018-10-30 11:18:15 +01:00
// we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag
Vec3d cur_pos = m_selection . contains_volume ( m_hover_volume_id ) ? Linef3 ( _mouse_to_3d ( pos , & z0 ), _mouse_to_3d ( pos , & z1 )). intersect_plane ( m_mouse . drag . start_position_3D ( 2 )) : m_mouse . drag . start_position_3D ;
2018-05-31 16:04:59 +02:00
2018-12-21 12:42:06 +01:00
m_regenerate_volumes = false ;
2018-11-21 11:33:31 +01:00
m_selection . translate ( cur_pos - m_mouse . drag . start_position_3D );
2018-10-08 14:02:12 +02:00
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
2018-06-01 15:54:41 +02:00
m_dirty = true ;
2018-05-31 16:04:59 +02:00
}
2018-06-15 14:10:28 +02:00
else if ( evt . Dragging () && m_gizmos . is_dragging ())
{
2018-08-28 09:31:23 +02:00
if ( ! m_canvas -> HasCapture ())
m_canvas -> CaptureMouse ();
2018-08-24 15:49:57 +02:00
2018-06-15 14:10:28 +02:00
m_mouse . dragging = true ;
2018-11-14 12:57:12 +01:00
m_gizmos . update ( mouse_ray ( pos ), evt . ShiftDown (), & pos );
2018-06-18 15:07:17 +02:00
2018-06-19 09:46:26 +02:00
switch ( m_gizmos . get_current_type ())
{
2018-09-11 09:00:28 +02:00
case Gizmos :: Move :
{
// Apply new temporary offset
2018-10-08 14:02:12 +02:00
m_selection . translate ( m_gizmos . get_displacement ());
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
2018-09-11 09:00:28 +02:00
break ;
}
2018-06-19 09:46:26 +02:00
case Gizmos :: Scale :
{
2018-09-24 15:54:09 +02:00
// Apply new temporary scale factors
2018-11-20 15:39:36 +01:00
m_selection . scale ( m_gizmos . get_scale (), evt . AltDown ());
2018-10-12 15:43:29 +02:00
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
2018-06-19 09:46:26 +02:00
break ;
}
case Gizmos :: Rotate :
{
2018-10-12 15:43:29 +02:00
// Apply new temporary rotations
2018-11-20 11:57:01 +01:00
m_selection . rotate ( m_gizmos . get_rotation (), evt . AltDown ());
2018-10-09 15:56:34 +02:00
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
2018-06-19 09:46:26 +02:00
break ;
}
default :
break ;
}
2018-06-22 11:19:38 +02:00
2018-06-15 14:10:28 +02:00
m_dirty = true ;
}
2018-06-14 12:34:19 +02:00
else if ( evt . Dragging () && ! gizmos_overlay_contains_mouse )
2018-05-31 16:04:59 +02:00
{
2018-06-01 15:54:41 +02:00
m_mouse . dragging = true ;
2018-06-13 13:14:17 +02:00
if (( m_layers_editing . state != LayersEditing :: Unknown ) && ( layer_editing_object_idx != - 1 ))
2018-05-31 16:04:59 +02:00
{
2018-06-01 15:54:41 +02:00
if ( m_layers_editing . state == LayersEditing :: Editing )
_perform_layer_editing_action ( & evt );
2018-05-31 16:04:59 +02:00
}
2018-06-01 09:00:30 +02:00
else if ( evt . LeftIsDown ())
{
// if dragging over blank area with left button, rotate
2018-06-01 15:54:41 +02:00
if ( m_mouse . is_start_position_3D_defined ())
2018-06-01 09:00:30 +02:00
{
2018-08-21 17:43:05 +02:00
const Vec3d & orig = m_mouse . drag . start_position_3D ;
2018-08-17 15:53:43 +02:00
m_camera . phi += ((( float ) pos ( 0 ) - ( float ) orig ( 0 )) * TRACKBALLSIZE );
m_camera . set_theta ( m_camera . get_theta () - (( float ) pos ( 1 ) - ( float ) orig ( 1 )) * TRACKBALLSIZE );
2018-06-01 09:00:30 +02:00
2018-10-03 11:34:39 +02:00
viewport_changed ();
2018-06-01 09:00:30 +02:00
2018-06-01 15:54:41 +02:00
m_dirty = true ;
2018-06-01 09:00:30 +02:00
}
2018-08-24 10:20:00 +02:00
m_mouse . drag . start_position_3D = Vec3d (( double ) pos ( 0 ), ( double ) pos ( 1 ), 0.0 );
2018-06-01 09:00:30 +02:00
}
else if ( evt . MiddleIsDown () || evt . RightIsDown ())
{
// If dragging over blank area with right button, pan.
2018-06-01 15:54:41 +02:00
if ( m_mouse . is_start_position_2D_defined ())
2018-06-01 09:00:30 +02:00
{
// get point in model space at Z = 0
float z = 0.0f ;
2018-08-21 17:43:05 +02:00
const Vec3d & cur_pos = _mouse_to_3d ( pos , & z );
Vec3d orig = _mouse_to_3d ( m_mouse . drag . start_position_2D , & z );
2018-12-07 16:23:04 +01:00
m_camera . set_target ( m_camera . get_target () + orig - cur_pos , * this );
2018-06-01 09:00:30 +02:00
2018-10-03 11:34:39 +02:00
viewport_changed ();
2018-06-01 09:00:30 +02:00
2018-06-01 15:54:41 +02:00
m_dirty = true ;
2018-06-01 09:00:30 +02:00
}
2018-06-01 15:54:41 +02:00
m_mouse . drag . start_position_2D = pos ;
2018-06-01 09:00:30 +02:00
}
2018-05-31 16:04:59 +02:00
}
else if ( evt . LeftUp () || evt . MiddleUp () || evt . RightUp ())
{
2018-06-01 15:54:41 +02:00
if ( m_layers_editing . state != LayersEditing :: Unknown )
2018-05-31 16:04:59 +02:00
{
2018-06-01 15:54:41 +02:00
m_layers_editing . state = LayersEditing :: Unknown ;
_stop_timer ();
2018-05-31 16:04:59 +02:00
2018-06-13 13:14:17 +02:00
if ( layer_editing_object_idx != - 1 )
2018-10-03 11:34:39 +02:00
post_event ( SimpleEvent ( EVT_GLCANVAS_MODEL_UPDATE ));
2018-05-31 16:04:59 +02:00
}
2018-06-21 08:37:04 +02:00
else if (( m_mouse . drag . move_volume_idx != - 1 ) && m_mouse . dragging )
2018-05-31 16:04:59 +02:00
{
2018-10-08 14:02:12 +02:00
m_regenerate_volumes = false ;
2018-11-21 10:36:09 +01:00
do_move ();
2018-10-08 14:02:12 +02:00
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
2018-11-26 08:36:31 +01:00
// Let the platter know that the dragging finished, so a delayed refresh
// of the scene with the background processing data should be performed.
post_event ( SimpleEvent ( EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED ));
2018-05-31 16:04:59 +02:00
}
2018-11-26 15:54:12 +01:00
else if ( evt . LeftUp () && m_gizmos . get_current_type () == Gizmos :: SlaSupports && m_hover_volume_id != - 1 )
2018-09-12 12:14:20 +02:00
{
2018-10-26 15:45:52 +02:00
int id = m_selection . get_object_idx ();
2018-09-12 12:14:20 +02:00
if (( id != - 1 ) && ( m_model != nullptr )) {
2018-09-19 14:59:57 +02:00
m_gizmos . clicked_on_object ( Vec2d ( pos ( 0 ), pos ( 1 )));
2018-09-12 12:14:20 +02:00
}
}
2018-07-27 14:38:19 +02:00
else if ( evt . LeftUp () && ! m_mouse . dragging && ( m_hover_volume_id == - 1 ) && ! gizmos_overlay_contains_mouse && ! m_gizmos . is_dragging () && ! is_layers_editing_enabled ())
2018-06-14 12:34:19 +02:00
{
// deselect and propagate event through callback
2018-12-19 11:59:59 +01:00
if ( ! evt . ShiftDown () && m_picking_enabled && ! m_toolbar_action_running && ! m_mouse . ignore_up_event )
2018-06-14 12:34:19 +02:00
{
2018-10-08 14:02:12 +02:00
m_selection . clear ();
2018-11-07 12:11:34 +01:00
m_selection . set_mode ( Selection :: Instance );
2018-10-08 14:02:12 +02:00
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
2018-10-19 16:14:55 +02:00
post_event ( SimpleEvent ( EVT_GLCANVAS_OBJECT_SELECT ));
2018-11-01 15:08:39 +01:00
_update_gizmos_data ();
2018-06-14 12:34:19 +02:00
}
2018-12-06 14:26:13 +01:00
m_mouse . ignore_up_event = false ;
2018-06-14 12:34:19 +02:00
}
2018-06-15 14:10:28 +02:00
else if ( evt . LeftUp () && m_gizmos . is_dragging ())
{
2018-06-21 08:37:04 +02:00
switch ( m_gizmos . get_current_type ())
{
2018-09-11 09:00:28 +02:00
case Gizmos :: Move :
{
2018-10-08 14:02:12 +02:00
m_regenerate_volumes = false ;
2018-11-21 10:36:09 +01:00
do_move ();
2018-09-11 09:00:28 +02:00
break ;
}
2018-06-21 08:37:04 +02:00
case Gizmos :: Scale :
{
2018-11-21 10:36:09 +01:00
do_scale ();
2018-06-21 08:37:04 +02:00
break ;
}
case Gizmos :: Rotate :
{
2018-11-21 10:36:09 +01:00
do_rotate ();
2018-06-21 08:37:04 +02:00
break ;
}
2018-11-26 19:22:16 +01:00
case Gizmos :: SlaSupports :
// End of mouse dragging, update the SLAPrint/SLAPrintObjects with the new support points.
post_event ( SimpleEvent ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS ));
break ;
2018-06-21 08:37:04 +02:00
default :
break ;
}
2018-06-15 14:10:28 +02:00
m_gizmos . stop_dragging ();
2018-11-21 09:28:27 +01:00
#if ENABLE_WORLD_ROTATIONS
_update_gizmos_data ();
#endif // ENABLE_WORLD_ROTATIONS
2018-12-07 16:23:04 +01:00
2018-10-08 14:02:12 +02:00
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
2018-11-23 12:47:32 +01:00
// Let the platter know that the dragging finished, so a delayed refresh
// of the scene with the background processing data should be performed.
post_event ( SimpleEvent ( EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED ));
2018-12-07 16:23:04 +01:00
m_camera . set_scene_box ( scene_bounding_box (), * this );
2018-12-10 10:40:57 +01:00
set_camera_zoom ( 0.0f );
2018-06-15 14:10:28 +02:00
}
2018-06-14 12:34:19 +02:00
2018-12-21 14:41:47 +01:00
m_moving = false ;
2018-06-21 08:37:04 +02:00
m_mouse . drag . move_volume_idx = - 1 ;
2018-06-01 15:54:41 +02:00
m_mouse . set_start_position_3D_as_invalid ();
m_mouse . set_start_position_2D_as_invalid ();
m_mouse . dragging = false ;
2018-11-30 12:49:31 +01:00
m_mouse . left_down = false ;
2018-07-27 12:08:33 +02:00
m_toolbar_action_running = false ;
2018-07-18 14:26:42 +02:00
m_dirty = true ;
2018-08-24 15:49:57 +02:00
if ( m_canvas -> HasCapture ())
m_canvas -> ReleaseMouse ();
2018-05-31 16:04:59 +02:00
}
2018-05-31 13:51:50 +02:00
else if ( evt . Moving ())
{
2018-12-06 10:38:19 +01:00
m_mouse . position = pos . cast < double > ();
std :: string tooltip = "" ;
// updates gizmos overlay
if ( ! m_selection . is_empty ())
tooltip = m_gizmos . update_hover_state ( * this , m_mouse . position , m_selection );
else
m_gizmos . reset_all_states ();
// updates toolbar overlay
if ( tooltip . empty ())
2018-12-17 10:55:14 +01:00
tooltip = m_toolbar . update_hover_state ( m_mouse . position , * this );
2018-12-06 10:38:19 +01:00
// updates view toolbar overlay
if ( tooltip . empty () && ( m_view_toolbar != nullptr ))
{
2018-12-06 12:37:39 +01:00
tooltip = m_view_toolbar -> update_hover_state ( m_mouse . position , * this );
2018-12-06 10:38:19 +01:00
if ( ! tooltip . empty ())
m_dirty = true ;
}
set_tooltip ( tooltip );
2018-05-31 13:51:50 +02:00
// Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over.
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled )
m_dirty = true ;
2018-05-31 13:51:50 +02:00
}
else
evt . Skip ();
}
2018-06-04 12:26:39 +02:00
void GLCanvas3D :: on_paint ( wxPaintEvent & evt )
{
render ();
}
2018-06-07 09:22:19 +02:00
void GLCanvas3D :: on_key_down ( wxKeyEvent & evt )
{
if ( evt . HasModifiers ())
evt . Skip ();
else
{
int key = evt . GetKeyCode ();
2018-11-22 10:14:31 +01:00
#ifdef __WXOSX__
if ( key == WXK_BACK )
#else
2018-06-07 09:22:19 +02:00
if ( key == WXK_DELETE )
2018-11-22 10:14:31 +01:00
#endif // __WXOSX__
2018-10-03 11:34:39 +02:00
post_event ( SimpleEvent ( EVT_GLCANVAS_REMOVE_OBJECT ));
2018-06-07 09:22:19 +02:00
else
2018-11-22 10:14:31 +01:00
evt . Skip ();
2018-06-07 09:22:19 +02:00
}
}
2018-05-24 13:46:17 +02: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 15:56:14 +02:00
Point GLCanvas3D :: get_local_mouse_position () const
{
if ( m_canvas == nullptr )
return Point ();
wxPoint mouse_pos = m_canvas -> ScreenToClient ( wxGetMousePosition ());
2018-05-28 13:43:29 +02:00
return Point ( mouse_pos . x , mouse_pos . y );
2018-05-25 15:56:14 +02:00
}
2018-07-19 13:18:19 +02:00
void GLCanvas3D :: reset_legend_texture ()
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
if ( ! set_current ())
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
m_legend_texture . reset ();
}
2018-10-25 09:35:08 +02:00
void GLCanvas3D :: set_tooltip ( const std :: string & tooltip ) const
2018-07-25 10:01:17 +02:00
{
if ( m_canvas != nullptr )
2018-10-25 09:35:08 +02:00
{
wxToolTip * t = m_canvas -> GetToolTip ();
if ( t != nullptr )
{
if ( t -> GetTip () != tooltip )
t -> SetTip ( tooltip );
}
else
m_canvas -> SetToolTip ( tooltip );
}
2018-07-25 10:01:17 +02:00
}
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
void GLCanvas3D :: set_external_gizmo_widgets_parent ( wxWindow * parent )
{
m_external_gizmo_widgets_parent = parent ;
}
2018-11-26 10:56:07 +01:00
#endif // not ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
2018-11-21 10:36:09 +01:00
void GLCanvas3D :: do_move ()
{
if ( m_model == nullptr )
return ;
std :: set < std :: pair < int , int >> done ; // keeps track of modified instances
bool object_moved = false ;
Vec3d wipe_tower_origin = Vec3d :: Zero ();
Selection :: EMode selection_mode = m_selection . get_mode ();
for ( const GLVolume * v : m_volumes . volumes )
{
int object_idx = v -> object_idx ();
int instance_idx = v -> instance_idx ();
int volume_idx = v -> volume_idx ();
std :: pair < int , int > done_id ( object_idx , instance_idx );
if (( 0 <= object_idx ) && ( object_idx < ( int ) m_model -> objects . size ()))
{
done . insert ( done_id );
// Move instances/volumes
ModelObject * model_object = m_model -> objects [ object_idx ];
if ( model_object != nullptr )
{
2019-01-03 11:24:03 +01:00
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
if ( selection_mode == Selection :: Instance )
model_object -> instances [ instance_idx ] -> set_offset ( v -> get_instance_offset ());
else if ( selection_mode == Selection :: Volume )
model_object -> volumes [ volume_idx ] -> set_offset ( v -> get_volume_offset ());
object_moved = true ;
model_object -> invalidate_bounding_box ();
#else
2018-11-21 10:36:09 +01:00
if ( selection_mode == Selection :: Instance )
{
model_object -> instances [ instance_idx ] -> set_offset ( v -> get_instance_offset ());
object_moved = true ;
}
else if ( selection_mode == Selection :: Volume )
{
model_object -> volumes [ volume_idx ] -> set_offset ( v -> get_volume_offset ());
object_moved = true ;
}
if ( object_moved )
2019-01-03 11:24:03 +01:00
model_object -> invalidate_bounding_box ();
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
2018-11-21 10:36:09 +01:00
}
}
else if ( object_idx == 1000 )
// Move a wipe tower proxy.
wipe_tower_origin = v -> get_volume_offset ();
}
// Fixes sinking/flying instances
for ( const std :: pair < int , int >& i : done )
{
ModelObject * m = m_model -> objects [ i . first ];
Vec3d shift ( 0.0 , 0.0 , - m -> get_instance_min_z ( i . second ));
m_selection . translate ( i . first , i . second , shift );
m -> translate_instance ( i . second , shift );
}
if ( object_moved )
post_event ( SimpleEvent ( EVT_GLCANVAS_INSTANCE_MOVED ));
if ( wipe_tower_origin != Vec3d :: Zero ())
post_event ( Vec3dEvent ( EVT_GLCANVAS_WIPETOWER_MOVED , std :: move ( wipe_tower_origin )));
}
void GLCanvas3D :: do_rotate ()
{
if ( m_model == nullptr )
return ;
std :: set < std :: pair < int , int >> done ; // keeps track of modified instances
Selection :: EMode selection_mode = m_selection . get_mode ();
for ( const GLVolume * v : m_volumes . volumes )
{
int object_idx = v -> object_idx ();
if (( object_idx < 0 ) || (( int ) m_model -> objects . size () <= object_idx ))
continue ;
int instance_idx = v -> instance_idx ();
int volume_idx = v -> volume_idx ();
done . insert ( std :: pair < int , int > ( object_idx , instance_idx ));
// Rotate instances/volumes.
ModelObject * model_object = m_model -> objects [ object_idx ];
if ( model_object != nullptr )
{
if ( selection_mode == Selection :: Instance )
{
model_object -> instances [ instance_idx ] -> set_rotation ( v -> get_instance_rotation ());
model_object -> instances [ instance_idx ] -> set_offset ( v -> get_instance_offset ());
}
else if ( selection_mode == Selection :: Volume )
{
model_object -> volumes [ volume_idx ] -> set_rotation ( v -> get_volume_rotation ());
model_object -> volumes [ volume_idx ] -> set_offset ( v -> get_volume_offset ());
}
model_object -> invalidate_bounding_box ();
}
}
// Fixes sinking/flying instances
for ( const std :: pair < int , int >& i : done )
{
ModelObject * m = m_model -> objects [ i . first ];
Vec3d shift ( 0.0 , 0.0 , - m -> get_instance_min_z ( i . second ));
m_selection . translate ( i . first , i . second , shift );
m -> translate_instance ( i . second , shift );
}
2019-01-03 11:24:03 +01:00
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
if ( ! done . empty ())
post_event ( SimpleEvent ( EVT_GLCANVAS_INSTANCE_ROTATED ));
#else
2018-11-21 10:36:09 +01:00
post_event ( SimpleEvent ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS ));
2019-01-03 11:24:03 +01:00
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
2018-11-21 10:36:09 +01:00
}
void GLCanvas3D :: do_scale ()
{
if ( m_model == nullptr )
return ;
std :: set < std :: pair < int , int >> done ; // keeps track of modified instances
Selection :: EMode selection_mode = m_selection . get_mode ();
for ( const GLVolume * v : m_volumes . volumes )
{
int object_idx = v -> object_idx ();
if (( object_idx < 0 ) || (( int ) m_model -> objects . size () <= object_idx ))
continue ;
int instance_idx = v -> instance_idx ();
int volume_idx = v -> volume_idx ();
done . insert ( std :: pair < int , int > ( object_idx , instance_idx ));
// Rotate instances/volumes
ModelObject * model_object = m_model -> objects [ object_idx ];
if ( model_object != nullptr )
{
if ( selection_mode == Selection :: Instance )
{
model_object -> instances [ instance_idx ] -> set_scaling_factor ( v -> get_instance_scaling_factor ());
model_object -> instances [ instance_idx ] -> set_offset ( v -> get_instance_offset ());
}
else if ( selection_mode == Selection :: Volume )
{
model_object -> instances [ instance_idx ] -> set_offset ( v -> get_instance_offset ());
model_object -> volumes [ volume_idx ] -> set_scaling_factor ( v -> get_volume_scaling_factor ());
model_object -> volumes [ volume_idx ] -> set_offset ( v -> get_volume_offset ());
}
model_object -> invalidate_bounding_box ();
}
}
// Fixes sinking/flying instances
for ( const std :: pair < int , int >& i : done )
{
ModelObject * m = m_model -> objects [ i . first ];
Vec3d shift ( 0.0 , 0.0 , - m -> get_instance_min_z ( i . second ));
m_selection . translate ( i . first , i . second , shift );
m -> translate_instance ( i . second , shift );
}
2019-01-03 11:24:03 +01:00
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
if ( ! done . empty ())
post_event ( SimpleEvent ( EVT_GLCANVAS_INSTANCE_ROTATED ));
#else
2018-11-21 10:36:09 +01:00
post_event ( SimpleEvent ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS ));
2019-01-03 11:24:03 +01:00
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
2018-11-21 10:36:09 +01:00
}
void GLCanvas3D :: do_flatten ()
{
do_rotate ();
}
void GLCanvas3D :: do_mirror ()
{
if ( m_model == nullptr )
return ;
std :: set < std :: pair < int , int >> done ; // keeps track of modified instances
Selection :: EMode selection_mode = m_selection . get_mode ();
for ( const GLVolume * v : m_volumes . volumes )
{
int object_idx = v -> object_idx ();
if (( object_idx < 0 ) || (( int ) m_model -> objects . size () <= object_idx ))
continue ;
int instance_idx = v -> instance_idx ();
int volume_idx = v -> volume_idx ();
done . insert ( std :: pair < int , int > ( object_idx , instance_idx ));
// Mirror instances/volumes
ModelObject * model_object = m_model -> objects [ object_idx ];
if ( model_object != nullptr )
{
if ( selection_mode == Selection :: Instance )
model_object -> instances [ instance_idx ] -> set_mirror ( v -> get_instance_mirror ());
else if ( selection_mode == Selection :: Volume )
model_object -> volumes [ volume_idx ] -> set_mirror ( v -> get_volume_mirror ());
2019-01-02 10:49:13 +01:00
2018-11-21 10:36:09 +01:00
model_object -> invalidate_bounding_box ();
}
}
2018-12-10 09:46:01 +01:00
// Fixes sinking/flying instances
for ( const std :: pair < int , int >& i : done )
{
ModelObject * m = m_model -> objects [ i . first ];
Vec3d shift ( 0.0 , 0.0 , - m -> get_instance_min_z ( i . second ));
m_selection . translate ( i . first , i . second , shift );
m -> translate_instance ( i . second , shift );
}
2018-11-21 10:36:09 +01:00
post_event ( SimpleEvent ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS ));
}
2018-11-29 09:03:38 +01:00
void GLCanvas3D :: set_camera_zoom ( float zoom )
{
zoom = std :: max ( std :: min ( zoom , 4.0f ), - 4.0f ) / 10.0f ;
zoom = get_camera_zoom () / ( 1.0f - zoom );
// Don't allow to zoom too far outside the scene.
float zoom_min = _get_zoom_to_bounding_box_factor ( _max_bounding_box ());
if ( zoom_min > 0.0f )
zoom = std :: max ( zoom , zoom_min * 0.8f );
m_camera . zoom = zoom ;
viewport_changed ();
_refresh_if_shown_on_screen ();
}
2018-12-03 13:29:07 +01:00
void GLCanvas3D :: update_gizmos_on_off_state ()
{
set_as_dirty ();
m_gizmos . update_on_off_state ( get_selection ());
}
2018-12-18 10:40:53 +01:00
void GLCanvas3D :: handle_sidebar_focus_event ( const std :: string & opt_key , bool focus_on )
{
m_sidebar_field = focus_on ? opt_key : "" ;
if ( ! m_sidebar_field . empty ())
{
m_gizmos . reset_all_states ();
m_dirty = true ;
}
}
2018-06-12 09:18:25 +02:00
bool GLCanvas3D :: _is_shown_on_screen () const
{
2018-06-27 12:05:23 +02:00
return ( m_canvas != nullptr ) ? m_canvas -> IsShownOnScreen () : false ;
2018-06-12 09:18:25 +02:00
}
2018-06-05 10:56:55 +02:00
void GLCanvas3D :: _force_zoom_to_bed ()
2018-06-04 12:26:39 +02:00
{
zoom_to_bed ();
2018-06-05 10:56:55 +02:00
m_force_zoom_to_bed_enabled = false ;
2018-06-04 12:26:39 +02:00
}
2018-07-23 13:49:48 +02:00
bool GLCanvas3D :: _init_toolbar ()
{
if ( ! m_toolbar . is_enabled ())
return true ;
2018-12-17 10:55:14 +01:00
ItemsIconsTexture :: Metadata icons_data ;
icons_data . filename = "toolbar.png" ;
icons_data . icon_size = 36 ;
icons_data . icon_border_size = 1 ;
icons_data . icon_gap_size = 1 ;
2018-12-19 14:44:37 +01:00
// icons_data.filename = "toolbar141.png";
// icons_data.icon_size = 52;
// icons_data.icon_border_size = 0;
// icons_data.icon_gap_size = 0;
2018-12-17 10:55:14 +01:00
BackgroundTexture :: Metadata background_data ;
background_data . filename = "toolbar_background.png" ;
background_data . left = 16 ;
background_data . top = 16 ;
background_data . right = 16 ;
background_data . bottom = 16 ;
if ( ! m_toolbar . init ( icons_data , background_data ))
2018-07-31 12:25:00 +02:00
{
// unable to init the toolbar texture, disable it
m_toolbar . set_enabled ( false );
return true ;
}
// m_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
m_toolbar . set_layout_type ( GLToolbar :: Layout :: Horizontal );
2018-12-17 10:55:14 +01:00
m_toolbar . set_layout_orientation ( GLToolbar :: Layout :: Top );
m_toolbar . set_border ( 5.0f );
2018-07-31 12:25:00 +02:00
m_toolbar . set_separator_size ( 5 );
m_toolbar . set_gap_size ( 2 );
GLToolbarItem :: Data item ;
2018-07-23 13:49:48 +02:00
item . name = "add" ;
item . tooltip = GUI :: L_str ( "Add..." );
2018-07-31 12:25:00 +02:00
item . sprite_id = 0 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-09-17 12:15:11 +02:00
item . action_event = EVT_GLTOOLBAR_ADD ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "delete" ;
item . tooltip = GUI :: L_str ( "Delete" );
2018-07-31 12:25:00 +02:00
item . sprite_id = 1 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-09-17 12:15:11 +02:00
item . action_event = EVT_GLTOOLBAR_DELETE ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "deleteall" ;
item . tooltip = GUI :: L_str ( "Delete all" );
2018-07-31 12:25:00 +02:00
item . sprite_id = 2 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_DELETE_ALL ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "arrange" ;
item . tooltip = GUI :: L_str ( "Arrange" );
2018-07-31 12:25:00 +02:00
item . sprite_id = 3 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_ARRANGE ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
if ( ! m_toolbar . add_separator ())
return false ;
item . name = "more" ;
item . tooltip = GUI :: L_str ( "Add instance" );
2018-07-31 12:25:00 +02:00
item . sprite_id = 4 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_MORE ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "fewer" ;
item . tooltip = GUI :: L_str ( "Remove instance" );
2018-07-31 12:25:00 +02:00
item . sprite_id = 5 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_FEWER ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
if ( ! m_toolbar . add_separator ())
return false ;
2018-10-24 12:55:38 +02:00
item . name = "splitobjects" ;
item . tooltip = GUI :: L_str ( "Split to objects" );
2018-08-24 15:08:19 +02:00
item . sprite_id = 6 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-24 12:55:38 +02:00
item . action_event = EVT_GLTOOLBAR_SPLIT_OBJECTS ;
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "splitvolumes" ;
item . tooltip = GUI :: L_str ( "Split to parts" );
2018-11-30 08:48:32 +01:00
item . sprite_id = 8 ;
2018-10-24 12:55:38 +02:00
item . is_toggable = false ;
item . action_event = EVT_GLTOOLBAR_SPLIT_VOLUMES ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
if ( ! m_toolbar . add_separator ())
return false ;
item . name = "layersediting" ;
item . tooltip = GUI :: L_str ( "Layers editing" );
2018-11-30 08:48:32 +01:00
item . sprite_id = 7 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = true ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_LAYERSEDITING ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
enable_toolbar_item ( "add" , true );
return true ;
}
2018-10-04 10:41:11 +02:00
#if ENABLE_USE_UNIQUE_GLCONTEXT
bool GLCanvas3D :: _set_current ()
{
if (( m_canvas != nullptr ) && ( m_context != nullptr ))
return m_canvas -> SetCurrent ( * m_context );
return false ;
}
#endif ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-01 15:54:41 +02:00
void GLCanvas3D :: _resize ( unsigned int w , unsigned int h )
2018-05-30 15:18:45 +02:00
{
2018-06-25 15:17:13 +02:00
if (( m_canvas == nullptr ) && ( m_context == nullptr ))
2018-06-01 15:54:41 +02:00
return ;
2018-10-31 10:19:44 +01:00
#if ENABLE_IMGUI
2018-11-26 10:56:07 +01:00
wxGetApp (). imgui () -> set_display_size (( float ) w , ( float ) h );
2018-10-31 10:19:44 +01:00
#endif // ENABLE_IMGUI
2018-06-25 15:17:13 +02:00
// ensures that this canvas is current
2018-10-04 10:41:11 +02:00
#if ENABLE_USE_UNIQUE_GLCONTEXT
_set_current ();
#else
2018-06-27 11:31:11 +02:00
set_current ();
2018-10-04 10:41:11 +02:00
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-01 15:54:41 +02:00
:: 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 12:26:39 +02: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 15:54:41 +02:00
default :
{
throw std :: runtime_error ( "Invalid camera type." );
break ;
}
}
:: glMatrixMode ( GL_MODELVIEW );
m_dirty = false ;
2018-05-30 15:18:45 +02:00
}
2018-06-01 15:54:41 +02:00
BoundingBoxf3 GLCanvas3D :: _max_bounding_box () const
2018-05-30 15:18:45 +02:00
{
2018-06-01 15:54:41 +02:00
BoundingBoxf3 bb = m_bed . get_bounding_box ();
bb . merge ( volumes_bounding_box ());
return bb ;
2018-05-30 15:18:45 +02:00
}
2018-05-14 14:14:19 +02:00
void GLCanvas3D :: _zoom_to_bounding_box ( const BoundingBoxf3 & bbox )
{
2018-05-15 09:50:01 +02: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 15:54:41 +02:00
m_camera . zoom = zoom ;
2018-05-15 09:50:01 +02:00
// center view around bounding box center
2018-12-07 16:23:04 +01:00
m_camera . set_target ( bbox . center (), * this );
2018-05-15 09:50:01 +02:00
2018-10-03 11:34:39 +02:00
viewport_changed ();
2018-05-15 09:50:01 +02:00
2018-05-28 15:23:01 +02:00
_refresh_if_shown_on_screen ();
2018-05-15 09:50:01 +02:00
}
2018-05-14 14:14:19 +02:00
}
2018-05-15 09:50:01 +02: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 08:44:39 +02:00
// we need the view matrix, we let opengl calculate it (same as done in render())
_camera_tranform ();
2018-05-15 09:50:01 +02:00
// get the view matrix back from opengl
GLfloat matrix [ 16 ];
:: glGetFloatv ( GL_MODELVIEW_MATRIX , matrix );
// camera axes
2018-08-24 10:20:00 +02:00
Vec3d right (( double ) matrix [ 0 ], ( double ) matrix [ 4 ], ( double ) matrix [ 8 ]);
Vec3d up (( double ) matrix [ 1 ], ( double ) matrix [ 5 ], ( double ) matrix [ 9 ]);
Vec3d forward (( double ) matrix [ 2 ], ( double ) matrix [ 6 ], ( double ) matrix [ 10 ]);
2018-05-15 09:50:01 +02:00
2018-08-21 17:43:05 +02:00
Vec3d bb_min = bbox . min ;
Vec3d bb_max = bbox . max ;
Vec3d bb_center = bbox . center ();
2018-05-15 09:50:01 +02:00
// bbox vertices in world space
2018-08-21 17:43:05 +02:00
std :: vector < Vec3d > vertices ;
2018-05-15 09:50:01 +02:00
vertices . reserve ( 8 );
vertices . push_back ( bb_min );
2018-08-17 15:53:43 +02:00
vertices . emplace_back ( bb_max ( 0 ), bb_min ( 1 ), bb_min ( 2 ));
vertices . emplace_back ( bb_max ( 0 ), bb_max ( 1 ), bb_min ( 2 ));
vertices . emplace_back ( bb_min ( 0 ), bb_max ( 1 ), bb_min ( 2 ));
vertices . emplace_back ( bb_min ( 0 ), bb_min ( 1 ), bb_max ( 2 ));
vertices . emplace_back ( bb_max ( 0 ), bb_min ( 1 ), bb_max ( 2 ));
2018-05-15 09:50:01 +02:00
vertices . push_back ( bb_max );
2018-08-17 15:53:43 +02:00
vertices . emplace_back ( bb_min ( 0 ), bb_max ( 1 ), bb_max ( 2 ));
2018-05-15 09:50:01 +02:00
2018-08-24 10:20:00 +02:00
double max_x = 0.0 ;
double max_y = 0.0 ;
2018-05-15 09:50:01 +02:00
// margin factor to give some empty space around the bbox
2018-08-24 10:20:00 +02:00
double margin_factor = 1.25 ;
2018-05-15 09:50:01 +02:00
2018-12-21 09:56:11 +01:00
for ( const Vec3d & v : vertices )
2018-05-15 09:50:01 +02:00
{
// project vertex on the plane perpendicular to camera forward axis
2018-08-21 17:43:05 +02:00
Vec3d pos ( v ( 0 ) - bb_center ( 0 ), v ( 1 ) - bb_center ( 1 ), v ( 2 ) - bb_center ( 2 ));
Vec3d proj_on_plane = pos - pos . dot ( forward ) * forward ;
2018-05-15 09:50:01 +02:00
// calculates vertex coordinate along camera xy axes
2018-08-24 10:20:00 +02:00
double x_on_plane = proj_on_plane . dot ( right );
double y_on_plane = proj_on_plane . dot ( up );
2018-05-15 09:50:01 +02:00
max_x = std :: max ( max_x , margin_factor * std :: abs ( x_on_plane ));
max_y = std :: max ( max_y , margin_factor * std :: abs ( y_on_plane ));
}
if (( max_x == 0.0 ) || ( max_y == 0.0 ))
return - 1.0f ;
max_x *= 2.0 ;
max_y *= 2.0 ;
2018-05-24 13:46:17 +02:00
const Size & cnv_size = get_canvas_size ();
2018-08-24 10:20:00 +02:00
return ( float ) std :: min (( double ) cnv_size . get_width () / max_x , ( double ) cnv_size . get_height () / max_y );
2018-05-15 09:50:01 +02:00
}
2018-05-29 14:34:45 +02:00
void GLCanvas3D :: _mark_volumes_for_layer_height () const
{
2018-11-22 15:29:59 +01:00
const Print * print = ( m_process == nullptr ) ? nullptr : m_process -> fff_print ();
if ( print == nullptr )
2018-05-29 14:34:45 +02:00
return ;
2018-06-11 15:13:13 +02:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-29 14:34:45 +02:00
{
2018-10-08 14:02:12 +02:00
int object_id = vol -> object_idx ();
2018-06-01 15:54:41 +02:00
int shader_id = m_layers_editing . get_shader_program_id ();
2018-05-29 14:34:45 +02:00
2018-06-01 15:54:41 +02:00
if ( is_layers_editing_enabled () && ( shader_id != - 1 ) && vol -> selected &&
2018-11-22 15:29:59 +01:00
vol -> has_layer_height_texture () && ( object_id < ( int ) print -> objects (). size ()))
2018-05-29 14:34:45 +02:00
{
2018-06-01 15:54:41 +02:00
vol -> set_layer_height_texture_data ( m_layers_editing . get_z_texture_id (), shader_id ,
2018-11-22 15:29:59 +01:00
print -> get_object ( object_id ), _get_layers_editing_cursor_z_relative (), m_layers_editing . band_width );
2018-05-29 14:34:45 +02:00
}
else
vol -> reset_layer_height_texture_data ();
}
2018-05-15 09:50:01 +02:00
}
2018-05-28 15:23:01 +02:00
void GLCanvas3D :: _refresh_if_shown_on_screen ()
{
2018-06-12 09:18:25 +02:00
if ( _is_shown_on_screen ())
2018-05-28 15:23:01 +02:00
{
const Size & cnv_size = get_canvas_size ();
2018-06-01 15:54:41 +02:00
_resize (( unsigned int ) cnv_size . get_width (), ( unsigned int ) cnv_size . get_height ());
2018-11-02 10:31:10 +01:00
// Because of performance problems on macOS, where PaintEvents are not delivered
// frequently enough, we call render() here directly when we can.
// We can't do that when m_force_zoom_to_bed_enabled == true, because then render()
// ends up calling back here via _force_zoom_to_bed(), causing a stack overflow.
if ( m_canvas != nullptr ) {
m_force_zoom_to_bed_enabled ? m_canvas -> Refresh () : render ();
}
2018-05-28 15:23:01 +02:00
}
}
2018-05-31 08:44:39 +02:00
void GLCanvas3D :: _camera_tranform () const
{
:: glMatrixMode ( GL_MODELVIEW );
:: glLoadIdentity ();
2018-08-06 16:37:41 +02:00
:: glRotatef ( - m_camera . get_theta (), 1.0f , 0.0f , 0.0f ); // pitch
2018-06-12 09:18:25 +02:00
:: glRotatef ( m_camera . phi , 0.0f , 0.0f , 1.0f ); // yaw
2018-05-31 08:44:39 +02:00
2018-12-07 16:23:04 +01:00
Vec3d target = - m_camera . get_target ();
:: glTranslated ( target ( 0 ), target ( 1 ), target ( 2 ));
2018-05-31 08:44:39 +02:00
}
2018-05-29 13:54:34 +02:00
void GLCanvas3D :: _picking_pass () const
{
2018-08-21 21:05:24 +02:00
const Vec2d & pos = m_mouse . position ;
2018-06-01 15:54:41 +02:00
2018-11-30 12:49:31 +01:00
if ( m_picking_enabled && ! m_mouse . dragging && ! m_mouse . left_down && ( pos != Vec2d ( DBL_MAX , DBL_MAX )))
2018-05-29 13:54:34 +02: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 15:54:41 +02:00
if ( m_multisample_allowed )
2018-05-29 13:54:34 +02:00
:: glDisable ( GL_MULTISAMPLE );
:: glDisable ( GL_BLEND );
2018-07-18 15:09:26 +02:00
:: glEnable ( GL_DEPTH_TEST );
2018-05-29 13:54:34 +02:00
:: glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
2018-08-21 14:27:36 +02:00
_render_volumes ( true );
2018-10-15 11:30:50 +02:00
m_gizmos . render_current_gizmo_for_picking_pass ( m_selection );
2018-05-29 13:54:34 +02:00
2018-06-01 15:54:41 +02:00
if ( m_multisample_allowed )
2018-05-29 13:54:34 +02:00
:: glEnable ( GL_MULTISAMPLE );
2018-07-30 09:09:14 +02:00
int volume_id = - 1 ;
2018-05-29 13:54:34 +02:00
2018-07-30 09:09:14 +02:00
GLubyte color [ 4 ] = { 0 , 0 , 0 , 0 };
const Size & cnv_size = get_canvas_size ();
2018-08-17 18:07:45 +02:00
bool inside = ( 0 <= pos ( 0 )) && ( pos ( 0 ) < cnv_size . get_width ()) && ( 0 <= pos ( 1 )) && ( pos ( 1 ) < cnv_size . get_height ());
2018-07-30 09:09:14 +02:00
if ( inside )
{
2018-08-17 18:07:45 +02:00
:: glReadPixels ( pos ( 0 ), cnv_size . get_height () - pos ( 1 ) - 1 , 1 , 1 , GL_RGBA , GL_UNSIGNED_BYTE , ( void * ) color );
2018-07-30 09:09:14 +02:00
volume_id = color [ 0 ] + color [ 1 ] * 256 + color [ 2 ] * 256 * 256 ;
}
if (( 0 <= volume_id ) && ( volume_id < ( int ) m_volumes . volumes . size ()))
2018-05-29 13:54:34 +02:00
{
m_hover_volume_id = volume_id ;
2018-06-14 15:32:26 +02:00
m_gizmos . set_hover_id ( - 1 );
2018-05-29 13:54:34 +02:00
}
2018-06-14 15:32:26 +02:00
else
2018-07-30 09:09:14 +02:00
{
m_hover_volume_id = - 1 ;
m_gizmos . set_hover_id ( inside ? ( 254 - ( int ) color [ 2 ]) : - 1 );
}
2018-06-13 10:49:59 +02:00
2018-10-08 14:02:12 +02:00
_update_volumes_hover_state ();
2018-05-29 13:54:34 +02:00
}
}
void GLCanvas3D :: _render_background () const
{
:: glPushMatrix ();
:: glLoadIdentity ();
:: glMatrixMode ( GL_PROJECTION );
:: glPushMatrix ();
:: glLoadIdentity ();
2018-12-13 11:13:58 +01:00
// Draws a bottom to top gradient over the complete screen.
2018-05-29 13:54:34 +02:00
:: glDisable ( GL_DEPTH_TEST );
:: glBegin ( GL_QUADS );
2018-12-12 10:38:07 +01:00
if ( m_dynamic_background_enabled && _is_any_volume_outside ())
:: glColor3fv ( ERROR_BG_DARK_COLOR );
else
:: glColor3fv ( DEFAULT_BG_DARK_COLOR );
2018-07-27 09:38:39 +02:00
:: glVertex2f ( - 1.0f , - 1.0f );
:: glVertex2f ( 1.0f , - 1.0f );
if ( m_dynamic_background_enabled && _is_any_volume_outside ())
2018-12-12 10:38:07 +01:00
:: glColor3fv ( ERROR_BG_LIGHT_COLOR );
2018-07-27 09:38:39 +02:00
else
2018-12-12 10:38:07 +01:00
:: glColor3fv ( DEFAULT_BG_LIGHT_COLOR );
2018-07-27 09:38:39 +02:00
:: glVertex2f ( 1.0f , 1.0f );
:: glVertex2f ( - 1.0f , 1.0f );
2018-05-29 13:54:34 +02:00
:: glEnd ();
:: glEnable ( GL_DEPTH_TEST );
:: glPopMatrix ();
:: glMatrixMode ( GL_MODELVIEW );
:: glPopMatrix ();
}
2018-06-11 10:46:32 +02:00
void GLCanvas3D :: _render_bed ( float theta ) const
2018-05-29 13:54:34 +02:00
{
2019-01-04 12:56:48 +01:00
#if ENABLE_PRINT_BED_MODELS
m_bed . render ( theta , m_use_VBOs );
#else
2018-06-11 10:46:32 +02:00
m_bed . render ( theta );
2019-01-04 12:56:48 +01:00
#endif // ENABLE_PRINT_BED_MODELS
2018-05-29 13:54:34 +02:00
}
2018-12-17 14:09:35 +01:00
void GLCanvas3D :: _render_axes () const
2018-05-29 13:54:34 +02:00
{
2018-12-17 14:09:35 +01:00
m_axes . render ();
2018-05-29 13:54:34 +02:00
}
2018-06-04 12:26:39 +02:00
void GLCanvas3D :: _render_objects () const
2018-05-29 13:54:34 +02:00
{
2018-06-11 15:13:13 +02:00
if ( m_volumes . empty ())
2018-05-29 13:54:34 +02:00
return ;
:: glEnable ( GL_LIGHTING );
2018-08-21 14:27:36 +02:00
:: glEnable ( GL_DEPTH_TEST );
2018-05-29 13:54:34 +02:00
2018-12-20 13:20:21 +01:00
if ( m_use_VBOs )
2018-05-29 13:54:34 +02:00
{
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled )
2018-05-29 13:54:34 +02:00
{
2018-05-29 14:34:45 +02:00
_mark_volumes_for_layer_height ();
2018-05-29 13:54:34 +02:00
if ( m_config != nullptr )
{
2018-06-01 15:54:41 +02:00
const BoundingBoxf3 & bed_bb = m_bed . get_bounding_box ();
2018-08-17 15:53:43 +02:00
m_volumes . set_print_box (( float ) bed_bb . min ( 0 ), ( float ) bed_bb . min ( 1 ), 0.0f , ( float ) bed_bb . max ( 0 ), ( float ) bed_bb . max ( 1 ), ( float ) m_config -> opt_float ( "max_print_height" ));
2018-07-18 14:26:42 +02:00
m_volumes . check_outside_state ( m_config , nullptr );
2018-05-29 13:54:34 +02:00
}
2018-12-21 09:56:11 +01:00
#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-05-29 13:54:34 +02:00
// do not cull backfaces to show broken geometry, if any
:: glDisable ( GL_CULL_FACE );
2018-12-21 09:56:11 +01:00
#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-05-29 13:54:34 +02:00
}
2018-11-27 14:50:57 +01:00
if ( m_use_clipping_planes )
2018-11-28 15:13:25 +01:00
m_volumes . set_z_range ( - m_clipping_planes [ 0 ]. get_data ()[ 3 ], m_clipping_planes [ 1 ]. get_data ()[ 3 ]);
2018-11-27 14:50:57 +01:00
else
m_volumes . set_z_range ( - FLT_MAX , FLT_MAX );
2018-06-01 15:54:41 +02:00
m_shader . start_using ();
2018-12-21 09:56:11 +01:00
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
// do not cull backfaces to show broken geometry, if any
m_volumes . render_VBOs ( GLVolumeCollection :: Opaque , m_picking_enabled );
m_volumes . render_VBOs ( GLVolumeCollection :: Transparent , false );
#else
2018-06-11 15:13:13 +02:00
m_volumes . render_VBOs ();
2018-12-21 09:56:11 +01:00
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-06-01 15:54:41 +02:00
m_shader . stop_using ();
2018-05-29 13:54:34 +02:00
2018-12-21 09:56:11 +01:00
#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled )
2018-05-29 13:54:34 +02:00
:: glEnable ( GL_CULL_FACE );
2018-12-21 09:56:11 +01:00
#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-05-29 13:54:34 +02:00
}
else
{
2018-11-27 14:50:57 +01:00
if ( m_use_clipping_planes )
{
2018-11-28 09:28:07 +01:00
:: glClipPlane ( GL_CLIP_PLANE0 , ( GLdouble * ) m_clipping_planes [ 0 ]. get_data ());
2018-11-27 14:50:57 +01:00
:: glEnable ( GL_CLIP_PLANE0 );
2018-11-28 09:28:07 +01:00
:: glClipPlane ( GL_CLIP_PLANE1 , ( GLdouble * ) m_clipping_planes [ 1 ]. get_data ());
2018-11-27 14:50:57 +01:00
:: glEnable ( GL_CLIP_PLANE1 );
}
2018-12-21 09:56:11 +01:00
#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-05-29 13:54:34 +02:00
// do not cull backfaces to show broken geometry, if any
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled )
2018-05-29 13:54:34 +02:00
:: glDisable ( GL_CULL_FACE );
2018-12-21 09:56:11 +01:00
#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-05-29 13:54:34 +02:00
2018-12-21 09:56:11 +01:00
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
// do not cull backfaces to show broken geometry, if any
m_volumes . render_legacy ( GLVolumeCollection :: Opaque , m_picking_enabled );
m_volumes . render_legacy ( GLVolumeCollection :: Transparent , false );
#else
2018-06-11 15:13:13 +02:00
m_volumes . render_legacy ();
2018-12-21 09:56:11 +01:00
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-05-29 13:54:34 +02:00
2018-12-21 09:56:11 +01:00
#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled )
2018-05-29 13:54:34 +02:00
:: glEnable ( GL_CULL_FACE );
2018-12-21 09:56:11 +01:00
#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
2018-11-27 14:50:57 +01:00
if ( m_use_clipping_planes )
{
:: glDisable ( GL_CLIP_PLANE0 );
:: glDisable ( GL_CLIP_PLANE1 );
}
2018-05-29 13:54:34 +02:00
}
2018-06-22 12:21:43 +02:00
:: glDisable ( GL_LIGHTING );
2018-05-29 13:54:34 +02:00
}
2018-10-08 14:02:12 +02:00
void GLCanvas3D :: _render_selection () const
{
2018-11-06 10:31:19 +01:00
if ( ! m_gizmos . is_running ())
m_selection . render ();
2018-10-08 14:02:12 +02:00
}
2018-12-18 12:35:49 +01:00
#if ENABLE_RENDER_SELECTION_CENTER
void GLCanvas3D :: _render_selection_center () const
{
if ( ! m_gizmos . is_running ())
m_selection . render_center ();
}
#endif // ENABLE_RENDER_SELECTION_CENTER
2018-05-29 13:54:34 +02:00
void GLCanvas3D :: _render_warning_texture () const
{
if ( ! m_warning_texture_enabled )
return ;
2018-07-31 14:20:16 +02:00
m_warning_texture . render ( * this );
2018-05-29 13:54:34 +02:00
}
void GLCanvas3D :: _render_legend_texture () const
{
if ( ! m_legend_texture_enabled )
return ;
2018-07-31 14:32:59 +02:00
m_legend_texture . render ( * this );
2018-05-29 13:54:34 +02:00
}
2018-09-10 12:08:57 +02:00
void GLCanvas3D :: _render_layer_editing_overlay () const
{
2018-11-22 15:29:59 +01:00
const Print * print = this -> fff_print ();
if (( print == nullptr ) || print -> objects (). empty ())
2018-09-10 12:08:57 +02:00
return ;
GLVolume * volume = nullptr ;
2018-06-11 15:13:13 +02:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-29 13:54:34 +02: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.
2018-10-08 14:02:12 +02:00
int object_idx = volume -> object_idx ();
2018-11-22 15:29:59 +01:00
if (( int ) print -> objects (). size () <= object_idx )
2018-05-29 13:54:34 +02:00
return ;
2018-11-22 15:29:59 +01:00
const PrintObject * print_object = print -> get_object ( object_idx );
2018-05-29 13:54:34 +02:00
if ( print_object == nullptr )
return ;
m_layers_editing . render ( * this , * print_object , * volume );
}
2018-06-01 15:54:41 +02:00
void GLCanvas3D :: _render_volumes ( bool fake_colors ) const
{
2018-06-14 15:32:26 +02:00
static const GLfloat INV_255 = 1.0f / 255.0f ;
2018-06-01 15:54:41 +02:00
2018-06-22 12:21:43 +02:00
if ( ! fake_colors )
2018-06-01 15:54:41 +02:00
:: 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 15:13:13 +02:00
for ( GLVolume * vol : m_volumes . volumes )
2018-06-01 15:54:41 +02: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 ;
2018-06-14 15:32:26 +02:00
:: glColor3f (( GLfloat ) r * INV_255 , ( GLfloat ) g * INV_255 , ( GLfloat ) b * INV_255 );
2018-06-01 15:54:41 +02:00
}
else
{
vol -> set_render_color ();
2018-10-11 10:52:50 +02:00
:: glColor4fv ( vol -> render_color );
2018-06-01 15:54:41 +02:00
}
2018-11-07 12:11:34 +01:00
if ( ! fake_colors || ! vol -> disabled )
vol -> render ();
2018-06-01 15:54:41 +02:00
++ volume_id ;
}
:: glDisableClientState ( GL_NORMAL_ARRAY );
:: glDisableClientState ( GL_VERTEX_ARRAY );
:: glDisable ( GL_BLEND );
:: glEnable ( GL_CULL_FACE );
2018-06-22 12:21:43 +02:00
if ( ! fake_colors )
:: glDisable ( GL_LIGHTING );
2018-06-01 15:54:41 +02:00
}
2018-08-21 14:27:36 +02:00
void GLCanvas3D :: _render_current_gizmo () const
2018-06-13 09:12:16 +02:00
{
2018-10-15 11:30:50 +02:00
m_gizmos . render_current_gizmo ( m_selection );
2018-08-21 14:27:36 +02:00
}
void GLCanvas3D :: _render_gizmos_overlay () const
{
2018-11-26 10:56:07 +01:00
m_gizmos . render_overlay ( * this , m_selection );
2018-06-13 09:12:16 +02:00
}
2018-07-23 13:49:48 +02:00
void GLCanvas3D :: _render_toolbar () const
{
2018-12-17 10:55:14 +01:00
m_toolbar . render ( * this );
2018-07-23 13:49:48 +02:00
}
2018-12-06 10:38:19 +01:00
void GLCanvas3D :: _render_view_toolbar () const
{
if ( m_view_toolbar != nullptr )
m_view_toolbar -> render ( * this );
}
2018-10-26 09:50:28 +02:00
#if ENABLE_SHOW_CAMERA_TARGET
void GLCanvas3D :: _render_camera_target () const
{
double half_length = 5.0 ;
:: glDisable ( GL_DEPTH_TEST );
:: glLineWidth ( 2.0f );
:: glBegin ( GL_LINES );
2018-12-07 16:23:04 +01:00
const Vec3d & target = m_camera . get_target ();
// draw line for x axis
:: glColor3f ( 1.0f , 0.0f , 0.0f );
:: glVertex3d ( target ( 0 ) - half_length , target ( 1 ), target ( 2 ));
:: glVertex3d ( target ( 0 ) + half_length , target ( 1 ), target ( 2 ));
// draw line for y axis
:: glColor3f ( 0.0f , 1.0f , 0.0f );
:: glVertex3d ( target ( 0 ), target ( 1 ) - half_length , target ( 2 ));
:: glVertex3d ( target ( 0 ), target ( 1 ) + half_length , target ( 2 ));
2018-12-18 13:07:50 +01:00
// draw line for z axis
2018-12-07 16:23:04 +01:00
:: glColor3f ( 0.0f , 0.0f , 1.0f );
:: glVertex3d ( target ( 0 ), target ( 1 ), target ( 2 ) - half_length );
:: glVertex3d ( target ( 0 ), target ( 1 ), target ( 2 ) + half_length );
2018-10-26 09:50:28 +02:00
:: glEnd ();
}
#endif // ENABLE_SHOW_CAMERA_TARGET
2018-11-28 15:13:25 +01:00
void GLCanvas3D :: _render_sla_slices () const
{
if ( ! m_use_clipping_planes || wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology () != ptSLA )
return ;
const SLAPrint * print = this -> sla_print ();
2018-12-10 12:59:49 +01:00
const PrintObjects & print_objects = print -> objects ();
if ( print_objects . empty ())
2018-11-28 15:13:25 +01:00
// nothing to render, return
return ;
double clip_min_z = - m_clipping_planes [ 0 ]. get_data ()[ 3 ];
double clip_max_z = m_clipping_planes [ 1 ]. get_data ()[ 3 ];
2018-12-10 12:59:49 +01:00
for ( unsigned int i = 0 ; i < ( unsigned int ) print_objects . size (); ++ i )
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
const SLAPrintObject * obj = print_objects [ i ];
Pointf3s bottom_obj_triangles ;
Pointf3s bottom_sup_triangles ;
Pointf3s top_obj_triangles ;
Pointf3s top_sup_triangles ;
double shift_z = obj -> get_current_elevation ();
double min_z = clip_min_z - shift_z ;
double max_z = clip_max_z - shift_z ;
if ( m_sla_caps [ 0 ]. matches ( min_z ))
{
SlaCap :: ObjectIdToTrianglesMap :: const_iterator it = m_sla_caps [ 0 ]. triangles . find ( i );
if ( it != m_sla_caps [ 0 ]. triangles . end ())
{
bottom_obj_triangles = it -> second . object ;
bottom_sup_triangles = it -> second . suppports ;
}
}
if ( m_sla_caps [ 1 ]. matches ( max_z ))
{
SlaCap :: ObjectIdToTrianglesMap :: const_iterator it = m_sla_caps [ 1 ]. triangles . find ( i );
if ( it != m_sla_caps [ 1 ]. triangles . end ())
{
top_obj_triangles = it -> second . object ;
top_sup_triangles = it -> second . suppports ;
}
}
const std :: vector < SLAPrintObject :: Instance >& instances = obj -> instances ();
struct InstanceTransform
{
Vec3d offset ;
float rotation ;
};
std :: vector < InstanceTransform > instance_transforms ;
for ( const SLAPrintObject :: Instance & inst : instances )
{
instance_transforms . push_back ({ to_3d ( unscale ( inst . shift ), shift_z ), Geometry :: rad2deg ( inst . rotation ) });
}
if (( bottom_obj_triangles . empty () || bottom_sup_triangles . empty () || top_obj_triangles . empty () || top_sup_triangles . empty ()) && obj -> is_step_done ( slaposIndexSlices ))
2018-11-28 15:13:25 +01:00
{
const std :: vector < ExPolygons >& model_slices = obj -> get_model_slices ();
const std :: vector < ExPolygons >& support_slices = obj -> get_support_slices ();
2018-12-10 12:59:49 +01:00
const SLAPrintObject :: SliceIndex & index = obj -> get_slice_index ();
SLAPrintObject :: SliceIndex :: const_iterator it_min_z = std :: find_if ( index . begin (), index . end (), [ min_z ]( const SLAPrintObject :: SliceIndex :: value_type & id ) -> bool { return std :: abs ( min_z - id . first ) < EPSILON ; });
SLAPrintObject :: SliceIndex :: const_iterator it_max_z = std :: find_if ( index . begin (), index . end (), [ max_z ]( const SLAPrintObject :: SliceIndex :: value_type & id ) -> bool { return std :: abs ( max_z - id . first ) < EPSILON ; });
if ( it_min_z != index . end ())
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
if ( bottom_obj_triangles . empty () && ( it_min_z -> second . model_slices_idx < model_slices . size ()))
2018-11-28 15:13:25 +01:00
{
2018-11-29 11:11:39 +01:00
// calculate model bottom cap
2018-12-10 12:59:49 +01:00
const ExPolygons & polys = model_slices [ it_min_z -> second . model_slices_idx ];
for ( const ExPolygon & poly : polys )
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
Polygons poly_triangles ;
poly . triangulate ( & poly_triangles );
for ( const Polygon & t : poly_triangles )
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
for ( int v = 2 ; v >= 0 ; -- v )
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
bottom_obj_triangles . emplace_back ( to_3d ( unscale ( t . points [ v ]), min_z ));
2018-11-28 15:13:25 +01:00
}
}
}
}
2018-12-10 12:59:49 +01:00
if ( bottom_sup_triangles . empty () && ( it_min_z -> second . support_slices_idx < support_slices . size ()))
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
// calculate support bottom cap
const ExPolygons & polys = support_slices [ it_min_z -> second . support_slices_idx ];
for ( const ExPolygon & poly : polys )
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
Polygons poly_triangles ;
poly . triangulate ( & poly_triangles );
for ( const Polygon & t : poly_triangles )
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
for ( int v = 2 ; v >= 0 ; -- v )
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
bottom_sup_triangles . emplace_back ( to_3d ( unscale ( t . points [ v ]), min_z ));
2018-11-28 15:13:25 +01:00
}
}
}
2018-11-29 11:11:39 +01:00
2018-12-10 12:59:49 +01:00
m_sla_caps [ 0 ]. triangles . insert ( SlaCap :: ObjectIdToTrianglesMap :: value_type ( i , { bottom_obj_triangles , bottom_sup_triangles }));
m_sla_caps [ 0 ]. z = min_z ;
2018-11-28 15:13:25 +01:00
}
}
2018-12-10 12:59:49 +01:00
if ( it_max_z != index . end ())
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
if ( top_obj_triangles . empty () && ( it_max_z -> second . model_slices_idx < model_slices . size ()))
{
// calculate model top cap
const ExPolygons & polys = model_slices [ it_max_z -> second . model_slices_idx ];
for ( const ExPolygon & poly : polys )
{
Polygons poly_triangles ;
poly . triangulate ( & poly_triangles );
for ( const Polygon & t : poly_triangles )
{
for ( int v = 0 ; v < 3 ; ++ v )
{
top_obj_triangles . emplace_back ( to_3d ( unscale ( t . points [ v ]), max_z ));
}
}
}
}
if ( top_sup_triangles . empty () && ( it_max_z -> second . support_slices_idx < support_slices . size ()))
{
// calculate support top cap
const ExPolygons & polys = support_slices [ it_max_z -> second . support_slices_idx ];
for ( const ExPolygon & poly : polys )
{
Polygons poly_triangles ;
poly . triangulate ( & poly_triangles );
for ( const Polygon & t : poly_triangles )
{
for ( int v = 0 ; v < 3 ; ++ v )
{
top_sup_triangles . emplace_back ( to_3d ( unscale ( t . points [ v ]), max_z ));
}
}
}
}
m_sla_caps [ 1 ]. triangles . insert ( SlaCap :: ObjectIdToTrianglesMap :: value_type ( i , { top_obj_triangles , top_sup_triangles }));
m_sla_caps [ 1 ]. z = max_z ;
}
}
if ( ! bottom_obj_triangles . empty () || ! top_obj_triangles . empty () || ! bottom_sup_triangles . empty () || ! top_sup_triangles . empty ())
{
for ( const InstanceTransform & inst : instance_transforms )
{
:: glPushMatrix ();
:: glTranslated ( inst . offset ( 0 ), inst . offset ( 1 ), inst . offset ( 2 ));
:: glRotatef ( inst . rotation , 0.0 , 0.0 , 1.0 );
:: glBegin ( GL_TRIANGLES );
2018-11-29 11:11:39 +01:00
:: glColor3f ( 1.0f , 0.37f , 0.0f );
2018-12-10 12:59:49 +01:00
for ( const Vec3d & v : bottom_obj_triangles )
2018-11-28 15:13:25 +01:00
{
2018-12-10 12:59:49 +01:00
:: glVertex3dv (( GLdouble * ) v . data ());
2018-11-28 15:13:25 +01:00
}
2018-12-10 12:59:49 +01:00
for ( const Vec3d & v : top_obj_triangles )
{
:: glVertex3dv (( GLdouble * ) v . data ());
}
:: glColor3f ( 1.0f , 0.0f , 0.37f );
for ( const Vec3d & v : bottom_sup_triangles )
{
:: glVertex3dv (( GLdouble * ) v . data ());
}
for ( const Vec3d & v : top_sup_triangles )
{
:: glVertex3dv (( GLdouble * ) v . data ());
}
:: glEnd ();
:: glPopMatrix ();
2018-11-28 15:13:25 +01:00
}
}
}
}
2018-12-19 14:44:37 +01:00
#if ENABLE_SIDEBAR_VISUAL_HINTS
void GLCanvas3D :: _render_selection_sidebar_hints () const
{
if ( m_use_VBOs )
m_shader . start_using ();
m_selection . render_sidebar_hints ( m_sidebar_field );
if ( m_use_VBOs )
m_shader . stop_using ();
}
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
2018-10-08 14:02:12 +02:00
void GLCanvas3D :: _update_volumes_hover_state () const
{
for ( GLVolume * v : m_volumes . volumes )
{
v -> hover = false ;
}
if ( m_hover_volume_id == - 1 )
return ;
GLVolume * volume = m_volumes . volumes [ m_hover_volume_id ];
switch ( m_selection . get_mode ())
{
case Selection :: Volume :
{
volume -> hover = true ;
break ;
}
case Selection :: Instance :
{
int object_idx = volume -> object_idx ();
int instance_idx = volume -> instance_idx ();
for ( GLVolume * v : m_volumes . volumes )
{
if (( v -> object_idx () == object_idx ) && ( v -> instance_idx () == instance_idx ))
v -> hover = true ;
}
break ;
}
}
}
2018-11-01 15:08:39 +01:00
void GLCanvas3D :: _update_gizmos_data ()
{
if ( ! m_gizmos . is_enabled ())
return ;
bool enable_move_z = ! m_selection . is_wipe_tower ();
m_gizmos . enable_grabber ( Gizmos :: Move , 2 , enable_move_z );
2018-11-12 09:54:04 +01:00
bool enable_scale_xyz = m_selection . is_single_full_instance () || m_selection . is_single_volume () || m_selection . is_single_modifier ();
2018-11-01 15:08:39 +01:00
for ( int i = 0 ; i < 6 ; ++ i )
{
m_gizmos . enable_grabber ( Gizmos :: Scale , i , enable_scale_xyz );
}
if ( m_selection . is_single_full_instance ())
{
2018-11-02 12:11:28 +01:00
// all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
const GLVolume * volume = m_volumes . volumes [ * m_selection . get_volume_idxs (). begin ()];
m_gizmos . set_scale ( volume -> get_instance_scaling_factor ());
2018-11-21 09:28:27 +01:00
#if ENABLE_WORLD_ROTATIONS
m_gizmos . set_rotation ( Vec3d :: Zero ());
#else
2018-11-02 12:11:28 +01:00
m_gizmos . set_rotation ( volume -> get_instance_rotation ());
2018-11-21 09:28:27 +01:00
#endif // ENABLE_WORLD_ROTATIONS
2018-11-02 12:11:28 +01:00
ModelObject * model_object = m_model -> objects [ m_selection . get_object_idx ()];
m_gizmos . set_flattening_data ( model_object );
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
m_gizmos . set_sla_support_data ( model_object , m_selection );
#else
2018-11-02 12:11:28 +01:00
m_gizmos . set_model_object_ptr ( model_object );
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-11-01 15:08:39 +01:00
}
2018-11-12 08:54:22 +01:00
else if ( m_selection . is_single_volume () || m_selection . is_single_modifier ())
{
const GLVolume * volume = m_volumes . volumes [ * m_selection . get_volume_idxs (). begin ()];
m_gizmos . set_scale ( volume -> get_volume_scaling_factor ());
2018-11-21 09:28:27 +01:00
#if ENABLE_WORLD_ROTATIONS
m_gizmos . set_rotation ( Vec3d :: Zero ());
#else
2018-11-12 08:54:22 +01:00
m_gizmos . set_rotation ( volume -> get_volume_rotation ());
2018-11-21 09:28:27 +01:00
#endif // ENABLE_WORLD_ROTATIONS
2018-11-12 08:54:22 +01:00
m_gizmos . set_flattening_data ( nullptr );
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
m_gizmos . set_sla_support_data ( nullptr , m_selection );
#else
2018-11-12 08:54:22 +01:00
m_gizmos . set_model_object_ptr ( nullptr );
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-11-12 08:54:22 +01:00
}
2018-11-01 15:08:39 +01:00
else
{
m_gizmos . set_scale ( Vec3d :: Ones ());
m_gizmos . set_rotation ( Vec3d :: Zero ());
2018-11-02 15:20:26 +01:00
m_gizmos . set_flattening_data ( m_selection . is_from_single_object () ? m_model -> objects [ m_selection . get_object_idx ()] : nullptr );
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
m_gizmos . set_sla_support_data ( nullptr , m_selection );
#else
2018-11-02 12:11:28 +01:00
m_gizmos . set_model_object_ptr ( nullptr );
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-11-01 15:08:39 +01:00
}
}
2018-06-01 15:54:41 +02:00
float GLCanvas3D :: _get_layers_editing_cursor_z_relative () const
{
return m_layers_editing . get_cursor_z_relative ( * this );
}
2018-05-30 15:18:45 +02:00
void GLCanvas3D :: _perform_layer_editing_action ( wxMouseEvent * evt )
{
2018-06-01 15:54:41 +02:00
int object_idx_selected = m_layers_editing . last_object_id ;
2018-05-30 15:18:45 +02:00
if ( object_idx_selected == - 1 )
return ;
2018-11-22 15:29:59 +01:00
const Print * print = this -> fff_print ();
if ( print == nullptr )
2018-05-30 15:18:45 +02:00
return ;
2018-11-22 15:29:59 +01:00
const PrintObject * selected_obj = print -> get_object ( object_idx_selected );
2018-05-30 15:18:45 +02:00
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-08-21 17:43:05 +02:00
m_layers_editing . last_z = unscale < double > ( selected_obj -> size ( 2 )) * ( b - evt -> GetY () - 1.0f ) / ( b - rect . get_top ());
2018-06-01 15:54:41 +02:00
m_layers_editing . last_action = evt -> ShiftDown () ? ( evt -> RightIsDown () ? 3 : 2 ) : ( evt -> RightIsDown () ? 0 : 1 );
2018-05-30 15:18:45 +02: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() ?
2018-09-12 11:59:02 +02:00
// The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself,
// therefore it is safe to call it while the background processing is running.
const_cast < PrintObject *> ( 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 15:18:45 +02: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 )
{
2018-11-22 15:29:59 +01:00
const PrintObject * obj = print -> get_object ( i );
2018-05-30 15:18:45 +02:00
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 15:13:13 +02:00
m_volumes . volumes [ volume_idx ] -> generate_layer_height_texture ( selected_obj , 1 );
2018-05-30 15:18:45 +02:00
_refresh_if_shown_on_screen ();
// Automatic action on mouse down with the same coordinate.
2018-06-01 15:54:41 +02:00
_start_timer ();
}
2018-08-21 17:43:05 +02:00
Vec3d GLCanvas3D :: _mouse_to_3d ( const Point & mouse_pos , float * z )
2018-05-31 13:51:50 +02:00
{
2018-06-25 15:17:13 +02:00
if ( m_canvas == nullptr )
2018-08-21 17:43:05 +02:00
return Vec3d ( DBL_MAX , DBL_MAX , DBL_MAX );
2018-05-31 13:51:50 +02:00
2018-06-25 15:17:13 +02:00
_camera_tranform ();
2018-05-31 13:51:50 +02:00
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 );
2018-08-17 15:53:43 +02:00
GLint y = viewport [ 3 ] - ( GLint ) mouse_pos ( 1 );
2018-05-31 13:51:50 +02:00
GLfloat mouse_z ;
if ( z == nullptr )
2018-08-17 15:53:43 +02:00
:: glReadPixels (( GLint ) mouse_pos ( 0 ), y , 1 , 1 , GL_DEPTH_COMPONENT , GL_FLOAT , ( void * ) & mouse_z );
2018-05-31 13:51:50 +02:00
else
mouse_z = * z ;
GLdouble out_x , out_y , out_z ;
2018-08-17 15:53:43 +02:00
:: gluUnProject (( GLdouble ) mouse_pos ( 0 ), ( GLdouble ) y , ( GLdouble ) mouse_z , modelview_matrix , projection_matrix , viewport , & out_x , & out_y , & out_z );
2018-08-24 10:20:00 +02:00
return Vec3d (( double ) out_x , ( double ) out_y , ( double ) out_z );
2018-05-31 13:51:50 +02:00
}
2018-08-21 17:43:05 +02:00
Vec3d GLCanvas3D :: _mouse_to_bed_3d ( const Point & mouse_pos )
2018-08-20 10:23:17 +02:00
{
return mouse_ray ( mouse_pos ). intersect_plane ( 0.0 );
}
Linef3 GLCanvas3D :: mouse_ray ( const Point & mouse_pos )
2018-06-15 14:10:28 +02:00
{
2018-06-18 15:07:17 +02:00
float z0 = 0.0f ;
float z1 = 1.0f ;
2018-08-20 10:23:17 +02:00
return Linef3 ( _mouse_to_3d ( mouse_pos , & z0 ), _mouse_to_3d ( mouse_pos , & z1 ));
2018-06-15 14:10:28 +02:00
}
2018-06-01 15:54:41 +02:00
void GLCanvas3D :: _start_timer ()
{
2018-10-25 09:35:08 +02:00
m_timer . Start ( 100 , wxTIMER_CONTINUOUS );
2018-06-01 15:54:41 +02:00
}
void GLCanvas3D :: _stop_timer ()
{
2018-10-25 09:35:08 +02:00
m_timer . Stop ();
2018-06-01 15:54:41 +02:00
}
2018-07-24 13:39:17 +02:00
void GLCanvas3D :: _load_print_toolpaths ()
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-24 13:39:17 +02:00
// ensures this canvas is current
if ( ! set_current ())
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-24 13:39:17 +02:00
2018-11-22 15:29:59 +01:00
const Print * print = this -> fff_print ();
if ( print == nullptr )
2018-07-24 13:39:17 +02:00
return ;
2018-11-22 15:29:59 +01:00
if ( ! print -> is_step_done ( psSkirt ) || ! print -> is_step_done ( psBrim ))
2018-07-24 13:39:17 +02:00
return ;
2018-11-22 15:29:59 +01:00
if ( ! print -> has_skirt () && ( print -> config (). brim_width . value == 0 ))
2018-07-24 13:39:17 +02:00
return ;
const float color [] = { 0.5f , 1.0f , 0.5f , 1.0f }; // greenish
// number of skirt layers
size_t total_layer_count = 0 ;
2018-11-22 15:29:59 +01:00
for ( const PrintObject * print_object : print -> objects ())
2018-07-24 13:39:17 +02:00
{
total_layer_count = std :: max ( total_layer_count , print_object -> total_layer_count ());
}
2018-11-22 15:29:59 +01:00
size_t skirt_height = print -> has_infinite_skirt () ? total_layer_count : std :: min < size_t > ( print -> config (). skirt_height . value , total_layer_count );
if (( skirt_height == 0 ) && ( print -> config (). brim_width . value > 0 ))
2018-07-24 13:39:17 +02:00
skirt_height = 1 ;
// get first skirt_height layers (maybe this should be moved to a PrintObject method?)
2018-11-22 15:29:59 +01:00
const PrintObject * object0 = print -> objects (). front ();
2018-07-24 13:39:17 +02:00
std :: vector < float > print_zs ;
print_zs . reserve ( skirt_height * 2 );
2018-09-12 11:59:02 +02:00
for ( size_t i = 0 ; i < std :: min ( skirt_height , object0 -> layers (). size ()); ++ i )
2018-07-24 13:39:17 +02:00
{
2018-09-12 11:59:02 +02:00
print_zs . push_back ( float ( object0 -> layers ()[ i ] -> print_z ));
2018-07-24 13:39:17 +02:00
}
//FIXME why there are support layers?
2018-09-12 11:59:02 +02:00
for ( size_t i = 0 ; i < std :: min ( skirt_height , object0 -> support_layers (). size ()); ++ i )
2018-07-24 13:39:17 +02:00
{
2018-09-12 11:59:02 +02:00
print_zs . push_back ( float ( object0 -> support_layers ()[ i ] -> print_z ));
2018-07-24 13:39:17 +02:00
}
sort_remove_duplicates ( print_zs );
if ( print_zs . size () > skirt_height )
print_zs . erase ( print_zs . begin () + skirt_height , print_zs . end ());
m_volumes . volumes . emplace_back ( new GLVolume ( color ));
GLVolume & volume = * m_volumes . volumes . back ();
for ( size_t i = 0 ; i < skirt_height ; ++ i ) {
volume . print_zs . push_back ( print_zs [ i ]);
volume . offsets . push_back ( volume . indexed_vertex_array . quad_indices . size ());
volume . offsets . push_back ( volume . indexed_vertex_array . triangle_indices . size ());
if ( i == 0 )
2018-11-22 15:29:59 +01:00
_3DScene :: extrusionentity_to_verts ( print -> brim (), print_zs [ i ], Point ( 0 , 0 ), volume );
2018-07-24 13:39:17 +02:00
2018-11-22 15:29:59 +01:00
_3DScene :: extrusionentity_to_verts ( print -> skirt (), print_zs [ i ], Point ( 0 , 0 ), volume );
2018-07-24 13:39:17 +02:00
}
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 ;
2018-09-12 11:59:02 +02:00
ctxt . shifted_copies = & print_object . copies ();
2018-07-24 13:39:17 +02:00
// order layers by print_z
2018-09-12 11:59:02 +02:00
ctxt . layers . reserve ( print_object . layers (). size () + print_object . support_layers (). size ());
for ( const Layer * layer : print_object . layers ())
2018-07-24 13:39:17 +02:00
ctxt . layers . push_back ( layer );
2018-09-12 11:59:02 +02:00
for ( const Layer * layer : print_object . support_layers ())
2018-07-24 13:39:17 +02:00
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)
2018-09-12 11:59:02 +02:00
ctxt . has_perimeters = print_object . is_step_done ( posPerimeters );
ctxt . has_infill = print_object . is_step_done ( posInfill );
ctxt . has_support = print_object . is_step_done ( posSupportMaterial );
2018-07-24 13:39:17 +02:00
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 ();
m_volumes . volumes . emplace_back ( volume );
new_volume_mutex . unlock ();
return volume ;
};
const size_t volumes_cnt_initial = m_volumes . volumes . size ();
std :: vector < GLVolumeCollection > volumes_per_thread ( ctxt . layers . size ());
tbb :: parallel_for (
tbb :: blocked_range < size_t > ( 0 , ctxt . layers . size (), grain_size ),
[ & ctxt , & new_volume ]( const tbb :: blocked_range < size_t >& range ) {
2018-10-08 14:02:12 +02:00
GLVolumePtrs vols ;
2018-07-24 13:39:17 +02:00
if ( ctxt . color_by_tool ()) {
for ( size_t i = 0 ; i < ctxt . number_tools (); ++ i )
vols . emplace_back ( new_volume ( ctxt . color_tool ( i )));
}
else
vols = { new_volume ( ctxt . color_perimeters ()), new_volume ( ctxt . color_infill ()), new_volume ( ctxt . color_support ()) };
for ( GLVolume * vol : vols )
vol -> indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ());
for ( size_t idx_layer = range . begin (); idx_layer < range . end (); ++ idx_layer ) {
const Layer * layer = ctxt . layers [ idx_layer ];
for ( size_t i = 0 ; i < vols . size (); ++ i ) {
GLVolume & vol = * vols [ i ];
if ( vol . print_zs . empty () || vol . print_zs . back () != layer -> print_z ) {
vol . print_zs . push_back ( layer -> print_z );
vol . offsets . push_back ( vol . indexed_vertex_array . quad_indices . size ());
vol . offsets . push_back ( vol . indexed_vertex_array . triangle_indices . size ());
}
}
for ( const Point & copy : * ctxt . shifted_copies ) {
2018-09-12 11:59:02 +02:00
for ( const LayerRegion * layerm : layer -> regions ()) {
2018-07-24 13:39:17 +02:00
if ( ctxt . has_perimeters )
_3DScene :: extrusionentity_to_verts ( layerm -> perimeters , float ( layer -> print_z ), copy ,
2018-09-12 11:59:02 +02:00
* vols [ ctxt . volume_idx ( layerm -> region () -> config (). perimeter_extruder . value , 0 )]);
2018-07-24 13:39:17 +02:00
if ( ctxt . has_infill ) {
for ( const ExtrusionEntity * ee : layerm -> fills . entities ) {
// fill represents infill extrusions of a single island.
const auto * fill = dynamic_cast < const ExtrusionEntityCollection *> ( ee );
if ( ! fill -> entities . empty ())
_3DScene :: extrusionentity_to_verts ( * fill , float ( layer -> print_z ), copy ,
* vols [ ctxt . volume_idx (
is_solid_infill ( fill -> entities . front () -> role ()) ?
2018-09-12 11:59:02 +02:00
layerm -> region () -> config (). solid_infill_extruder :
layerm -> region () -> config (). infill_extruder ,
2018-07-24 13:39:17 +02:00
1 )]);
}
}
}
if ( ctxt . has_support ) {
const SupportLayer * support_layer = dynamic_cast < const SupportLayer *> ( layer );
if ( support_layer ) {
for ( const ExtrusionEntity * extrusion_entity : support_layer -> support_fills . entities )
_3DScene :: extrusionentity_to_verts ( extrusion_entity , float ( layer -> print_z ), copy ,
* vols [ ctxt . volume_idx (
( extrusion_entity -> role () == erSupportMaterial ) ?
2018-09-12 11:59:02 +02:00
support_layer -> object () -> config (). support_material_extruder :
support_layer -> object () -> config (). support_material_interface_extruder ,
2018-07-24 13:39:17 +02:00
2 )]);
}
}
}
for ( size_t i = 0 ; i < vols . size (); ++ i ) {
GLVolume & vol = * vols [ i ];
if ( vol . indexed_vertex_array . vertices_and_normals_interleaved . size () / 6 > ctxt . alloc_size_max ()) {
// Store the vertex arrays and restart their containers,
vols [ i ] = new_volume ( vol . color );
GLVolume & vol_new = * vols [ i ];
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new . indexed_vertex_array = std :: move ( vol . indexed_vertex_array );
// Copy the content back to the old GLVolume.
vol . indexed_vertex_array = vol_new . indexed_vertex_array ;
// Finalize a bounding box of the old GLVolume.
vol . bounding_box = vol . indexed_vertex_array . bounding_box ();
// Clear the buffers, but keep them pre-allocated.
vol_new . indexed_vertex_array . clear ();
// Just make sure that clear did not clear the reserved memory.
vol_new . indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ());
}
}
}
for ( GLVolume * vol : vols ) {
vol -> bounding_box = vol -> indexed_vertex_array . bounding_box ();
vol -> indexed_vertex_array . shrink_to_fit ();
}
});
BOOST_LOG_TRIVIAL ( debug ) << "Loading print object toolpaths in parallel - finalizing results" ;
// Remove empty volumes from the newly added volumes.
m_volumes . volumes . erase (
std :: remove_if ( m_volumes . volumes . begin () + volumes_cnt_initial , m_volumes . volumes . end (),
[]( const GLVolume * volume ) { return volume -> empty (); }),
m_volumes . volumes . end ());
for ( size_t i = volumes_cnt_initial ; i < m_volumes . volumes . size (); ++ i )
m_volumes . volumes [ i ] -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
BOOST_LOG_TRIVIAL ( debug ) << "Loading print object toolpaths in parallel - end" ;
}
void GLCanvas3D :: _load_wipe_tower_toolpaths ( const std :: vector < std :: string >& str_tool_colors )
{
2018-11-22 15:29:59 +01:00
const Print * print = this -> fff_print ();
if (( print == nullptr ) || print -> wipe_tower_data (). tool_changes . empty ())
2018-07-24 13:39:17 +02:00
return ;
2018-11-22 15:29:59 +01:00
if ( ! print -> is_step_done ( psWipeTower ))
2018-07-24 13:39:17 +02:00
return ;
std :: vector < float > tool_colors = _parse_colors ( str_tool_colors );
struct Ctxt
{
const Print * print ;
const std :: vector < float > * tool_colors ;
2018-08-02 13:29:39 +02:00
WipeTower :: xy wipe_tower_pos ;
float wipe_tower_angle ;
2018-07-24 13:39:17 +02:00
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max () { return 131072 ; } // 3.15MB
static const size_t alloc_size_reserve () { return alloc_size_max () * 2 ; }
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 ) {
2018-09-12 11:59:02 +02:00
const auto & tool_changes = print -> wipe_tower_data (). tool_changes ;
2018-07-24 13:39:17 +02:00
return priming . empty () ?
2018-09-12 11:59:02 +02:00
(( idx == tool_changes . size ()) ? final : tool_changes [ idx ]) :
(( idx == 0 ) ? priming : ( idx == tool_changes . size () + 1 ) ? final : tool_changes [ idx - 1 ]);
2018-07-24 13:39:17 +02:00
}
std :: vector < WipeTower :: ToolChangeResult > priming ;
std :: vector < WipeTower :: ToolChangeResult > final ;
} ctxt ;
2018-11-22 15:29:59 +01:00
ctxt . print = print ;
2018-07-24 13:39:17 +02:00
ctxt . tool_colors = tool_colors . empty () ? nullptr : & tool_colors ;
2018-11-22 15:29:59 +01:00
if ( print -> wipe_tower_data (). priming && print -> config (). single_extruder_multi_material_priming )
ctxt . priming . emplace_back ( * print -> wipe_tower_data (). priming . get ());
if ( print -> wipe_tower_data (). final_purge )
ctxt . final . emplace_back ( * print -> wipe_tower_data (). final_purge . get ());
2018-07-24 13:39:17 +02:00
2018-09-12 11:59:02 +02:00
ctxt . wipe_tower_angle = ctxt . print -> config (). wipe_tower_rotation_angle . value / 180.f * PI ;
ctxt . wipe_tower_pos = WipeTower :: xy ( ctxt . print -> config (). wipe_tower_x . value , ctxt . print -> config (). wipe_tower_y . value );
2018-08-02 13:29:39 +02:00
2018-07-24 13:39:17 +02:00
BOOST_LOG_TRIVIAL ( debug ) << "Loading wipe tower toolpaths in parallel - start" ;
//FIXME Improve the heuristics for a grain size.
2018-11-22 15:29:59 +01:00
size_t n_items = print -> wipe_tower_data (). tool_changes . size () + ( ctxt . priming . empty () ? 0 : 1 );
2018-07-24 13:39:17 +02:00
size_t grain_size = std :: max ( n_items / 128 , size_t ( 1 ));
tbb :: spin_mutex new_volume_mutex ;
auto new_volume = [ this , & new_volume_mutex ]( const float * color ) -> GLVolume * {
auto * volume = new GLVolume ( color );
new_volume_mutex . lock ();
m_volumes . volumes . emplace_back ( volume );
new_volume_mutex . unlock ();
return volume ;
};
const size_t volumes_cnt_initial = m_volumes . volumes . size ();
std :: vector < GLVolumeCollection > volumes_per_thread ( n_items );
tbb :: parallel_for (
tbb :: blocked_range < size_t > ( 0 , n_items , grain_size ),
[ & ctxt , & new_volume ]( const tbb :: blocked_range < size_t >& range ) {
// Bounding box of this slab of a wipe tower.
2018-10-08 14:02:12 +02:00
GLVolumePtrs vols ;
2018-07-24 13:39:17 +02:00
if ( ctxt . color_by_tool ()) {
for ( size_t i = 0 ; i < ctxt . number_tools (); ++ i )
vols . emplace_back ( new_volume ( ctxt . color_tool ( i )));
}
else
vols = { new_volume ( ctxt . color_support ()) };
for ( GLVolume * volume : vols )
volume -> indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ());
for ( size_t idx_layer = range . begin (); idx_layer < range . end (); ++ idx_layer ) {
const std :: vector < WipeTower :: ToolChangeResult > & layer = ctxt . tool_change ( idx_layer );
for ( size_t i = 0 ; i < vols . size (); ++ i ) {
GLVolume & vol = * vols [ i ];
if ( vol . print_zs . empty () || vol . print_zs . back () != layer . front (). print_z ) {
vol . print_zs . push_back ( layer . front (). print_z );
vol . offsets . push_back ( vol . indexed_vertex_array . quad_indices . size ());
vol . offsets . push_back ( vol . indexed_vertex_array . triangle_indices . size ());
}
}
for ( const WipeTower :: ToolChangeResult & extrusions : layer ) {
for ( size_t i = 1 ; i < extrusions . extrusions . size ();) {
const WipeTower :: Extrusion & e = extrusions . extrusions [ i ];
if ( e . width == 0. ) {
++ i ;
continue ;
}
size_t j = i + 1 ;
if ( ctxt . color_by_tool ())
for (; j < extrusions . extrusions . size () && extrusions . extrusions [ j ]. tool == e . tool && extrusions . extrusions [ j ]. width > 0.f ; ++ j );
else
for (; j < extrusions . extrusions . size () && extrusions . extrusions [ j ]. width > 0.f ; ++ j );
size_t n_lines = j - i ;
Lines lines ;
std :: vector < double > widths ;
std :: vector < double > heights ;
lines . reserve ( n_lines );
widths . reserve ( n_lines );
heights . assign ( n_lines , extrusions . layer_height );
2018-08-02 13:29:39 +02:00
WipeTower :: Extrusion e_prev = extrusions . extrusions [ i - 1 ];
if ( ! extrusions . priming ) { // wipe tower extrusions describe the wipe tower at the origin with no rotation
e_prev . pos . rotate ( ctxt . wipe_tower_angle );
e_prev . pos . translate ( ctxt . wipe_tower_pos );
}
2018-07-24 13:39:17 +02:00
for (; i < j ; ++ i ) {
2018-08-02 13:29:39 +02:00
WipeTower :: Extrusion e = extrusions . extrusions [ i ];
2018-07-24 13:39:17 +02:00
assert ( e . width > 0.f );
2018-08-02 13:29:39 +02:00
if ( ! extrusions . priming ) {
e . pos . rotate ( ctxt . wipe_tower_angle );
e . pos . translate ( ctxt . wipe_tower_pos );
}
2018-07-24 13:39:17 +02:00
lines . emplace_back ( Point :: new_scale ( e_prev . pos . x , e_prev . pos . y ), Point :: new_scale ( e . pos . x , e . pos . y ));
widths . emplace_back ( e . width );
2018-08-02 13:29:39 +02:00
e_prev = e ;
2018-07-24 13:39:17 +02:00
}
_3DScene :: thick_lines_to_verts ( lines , widths , heights , lines . front (). a == lines . back (). b , extrusions . print_z ,
* vols [ ctxt . volume_idx ( e . tool , 0 )]);
}
}
}
for ( size_t i = 0 ; i < vols . size (); ++ i ) {
GLVolume & vol = * vols [ i ];
if ( vol . indexed_vertex_array . vertices_and_normals_interleaved . size () / 6 > ctxt . alloc_size_max ()) {
// Store the vertex arrays and restart their containers,
vols [ i ] = new_volume ( vol . color );
GLVolume & vol_new = * vols [ i ];
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new . indexed_vertex_array = std :: move ( vol . indexed_vertex_array );
// Copy the content back to the old GLVolume.
vol . indexed_vertex_array = vol_new . indexed_vertex_array ;
// Finalize a bounding box of the old GLVolume.
vol . bounding_box = vol . indexed_vertex_array . bounding_box ();
// Clear the buffers, but keep them pre-allocated.
vol_new . indexed_vertex_array . clear ();
// Just make sure that clear did not clear the reserved memory.
vol_new . indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ());
}
}
for ( GLVolume * vol : vols ) {
vol -> bounding_box = vol -> indexed_vertex_array . bounding_box ();
vol -> indexed_vertex_array . shrink_to_fit ();
}
});
BOOST_LOG_TRIVIAL ( debug ) << "Loading wipe tower toolpaths in parallel - finalizing results" ;
// Remove empty volumes from the newly added volumes.
m_volumes . volumes . erase (
std :: remove_if ( m_volumes . volumes . begin () + volumes_cnt_initial , m_volumes . volumes . end (),
[]( const GLVolume * volume ) { return volume -> empty (); }),
m_volumes . volumes . end ());
for ( size_t i = volumes_cnt_initial ; i < m_volumes . volumes . size (); ++ i )
m_volumes . volumes [ i ] -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
BOOST_LOG_TRIVIAL ( debug ) << "Loading wipe tower toolpaths in parallel - end" ;
}
2018-06-05 10:56:55 +02:00
static inline int hex_digit_to_int ( const char c )
{
return
( c >= '0' && c <= '9' ) ? int ( c - '0' ) :
( c >= 'A' && c <= 'F' ) ? int ( c - 'A' ) + 10 :
( c >= 'a' && c <= 'f' ) ? int ( c - 'a' ) + 10 : - 1 ;
}
void GLCanvas3D :: _load_gcode_extrusion_paths ( const GCodePreviewData & preview_data , const std :: vector < float >& tool_colors )
{
// helper functions to select data in dependence of the extrusion view type
struct Helper
{
static float path_filter ( GCodePreviewData :: Extrusion :: EViewType type , const ExtrusionPath & path )
{
switch ( type )
{
case GCodePreviewData :: Extrusion :: FeatureType :
return ( float ) path . role ();
case GCodePreviewData :: Extrusion :: Height :
return path . height ;
case GCodePreviewData :: Extrusion :: Width :
return path . width ;
case GCodePreviewData :: Extrusion :: Feedrate :
return path . feedrate ;
case GCodePreviewData :: Extrusion :: VolumetricRate :
return path . feedrate * ( float ) path . mm3_per_mm ;
case GCodePreviewData :: Extrusion :: Tool :
return ( float ) path . extruder_id ;
2018-11-26 16:07:09 +01:00
case GCodePreviewData :: Extrusion :: ColorPrint :
return ( float ) path . cp_color_id ;
2018-06-13 09:26:58 +02:00
default :
return 0.0f ;
2018-06-05 10:56:55 +02:00
}
return 0.0f ;
}
static GCodePreviewData :: Color path_color ( const GCodePreviewData & data , const std :: vector < float >& tool_colors , float value )
{
switch ( data . extrusion . view_type )
{
case GCodePreviewData :: Extrusion :: FeatureType :
return data . get_extrusion_role_color (( ExtrusionRole )( int ) value );
case GCodePreviewData :: Extrusion :: Height :
return data . get_height_color ( value );
case GCodePreviewData :: Extrusion :: Width :
return data . get_width_color ( value );
case GCodePreviewData :: Extrusion :: Feedrate :
return data . get_feedrate_color ( value );
case GCodePreviewData :: Extrusion :: VolumetricRate :
return data . get_volumetric_rate_color ( value );
case GCodePreviewData :: Extrusion :: Tool :
{
GCodePreviewData :: Color color ;
:: memcpy (( void * ) color . rgba , ( const void * )( tool_colors . data () + ( unsigned int ) value * 4 ), 4 * sizeof ( float ));
return color ;
}
2018-11-26 16:07:09 +01:00
case GCodePreviewData :: Extrusion :: ColorPrint :
{
int val = int ( value );
while ( val >= GCodePreviewData :: Range :: Colors_Count )
val -= GCodePreviewData :: Range :: Colors_Count ;
GCodePreviewData :: Color color = GCodePreviewData :: Range :: Default_Colors [ val ];
return color ;
}
2018-06-13 09:26:58 +02:00
default :
return GCodePreviewData :: Color :: Dummy ;
2018-06-05 10:56:55 +02:00
}
return GCodePreviewData :: Color :: Dummy ;
}
};
// Helper structure for filters
struct Filter
{
float value ;
ExtrusionRole role ;
GLVolume * volume ;
Filter ( float value , ExtrusionRole role )
: value ( value )
, role ( role )
, volume ( nullptr )
{
}
bool operator == ( const Filter & other ) const
{
if ( value != other . value )
return false ;
if ( role != other . role )
return false ;
return true ;
}
};
typedef std :: vector < Filter > FiltersList ;
2018-06-11 15:13:13 +02:00
size_t initial_volumes_count = m_volumes . volumes . size ();
2018-06-05 10:56:55 +02: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 15:13:13 +02: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 10:56:55 +02:00
GLVolume * volume = new GLVolume ( Helper :: path_color ( preview_data , tool_colors , filter . value ). rgba );
if ( volume != nullptr )
{
filter . volume = volume ;
2018-07-27 08:49:58 +02:00
volume -> is_extrusion_path = true ;
2018-06-11 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
}
else
{
// an error occourred - restore to previous state and return
m_gcode_preview_volume_index . first_volumes . pop_back ();
2018-06-11 15:13:13 +02:00
if ( initial_volumes_count != m_volumes . volumes . size ())
2018-06-05 10:56:55 +02:00
{
2018-10-08 14:02:12 +02:00
GLVolumePtrs :: iterator begin = m_volumes . volumes . begin () + initial_volumes_count ;
GLVolumePtrs :: iterator end = m_volumes . volumes . end ();
for ( GLVolumePtrs :: iterator it = begin ; it < end ; ++ it )
2018-06-05 10:56:55 +02:00
{
GLVolume * volume = * it ;
delete volume ;
}
2018-06-11 15:13:13 +02:00
m_volumes . volumes . erase ( begin , end );
2018-06-05 10:56:55 +02: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 15:13:13 +02:00
if ( m_volumes . volumes . size () > initial_volumes_count )
2018-06-05 10:56:55 +02:00
{
2018-06-11 15:13:13 +02:00
for ( size_t i = initial_volumes_count ; i < m_volumes . volumes . size (); ++ i )
2018-06-05 10:56:55 +02:00
{
2018-06-11 15:13:13 +02:00
GLVolume * volume = m_volumes . volumes [ i ];
2018-06-05 10:56:55 +02:00
volume -> bounding_box = volume -> indexed_vertex_array . bounding_box ();
2018-06-05 12:24:26 +02:00
volume -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
2018-06-05 10:56:55 +02:00
}
}
}
void GLCanvas3D :: _load_gcode_travel_paths ( const GCodePreviewData & preview_data , const std :: vector < float >& tool_colors )
{
2018-06-11 15:13:13 +02:00
size_t initial_volumes_count = m_volumes . volumes . size ();
2018-06-05 10:56:55 +02: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 15:13:13 +02:00
if ( initial_volumes_count != m_volumes . volumes . size ())
2018-06-05 10:56:55 +02:00
{
2018-10-08 14:02:12 +02:00
GLVolumePtrs :: iterator begin = m_volumes . volumes . begin () + initial_volumes_count ;
GLVolumePtrs :: iterator end = m_volumes . volumes . end ();
for ( GLVolumePtrs :: iterator it = begin ; it < end ; ++ it )
2018-06-05 10:56:55 +02:00
{
GLVolume * volume = * it ;
delete volume ;
}
2018-06-11 15:13:13 +02:00
m_volumes . volumes . erase ( begin , end );
2018-06-05 10:56:55 +02:00
}
return ;
}
// finalize volumes and sends geometry to gpu
2018-06-11 15:13:13 +02:00
if ( m_volumes . volumes . size () > initial_volumes_count )
2018-06-05 10:56:55 +02:00
{
2018-06-11 15:13:13 +02:00
for ( size_t i = initial_volumes_count ; i < m_volumes . volumes . size (); ++ i )
2018-06-05 10:56:55 +02:00
{
2018-06-11 15:13:13 +02:00
GLVolume * volume = m_volumes . volumes [ i ];
2018-06-05 10:56:55 +02:00
volume -> bounding_box = volume -> indexed_vertex_array . bounding_box ();
2018-06-05 12:24:26 +02:00
volume -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
2018-06-05 10:56:55 +02: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 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
}
}
// populates volumes
for ( const GCodePreviewData :: Travel :: Polyline & polyline : preview_data . travel . polylines )
{
TypesList :: iterator type = std :: find ( types . begin (), types . end (), Type ( polyline . type ));
if ( type != types . end ())
{
2018-08-21 17:43:05 +02:00
type -> volume -> print_zs . push_back ( unscale < double > ( polyline . polyline . bounding_box (). min ( 2 )));
2018-06-05 10:56:55 +02:00
type -> volume -> offsets . push_back ( type -> volume -> indexed_vertex_array . quad_indices . size ());
type -> volume -> offsets . push_back ( type -> volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: polyline3_to_verts ( polyline . polyline , preview_data . travel . width , preview_data . travel . height , * type -> volume );
}
}
return true ;
}
bool GLCanvas3D :: _travel_paths_by_feedrate ( const GCodePreviewData & preview_data )
{
// Helper structure for feedrate
struct Feedrate
{
float value ;
GLVolume * volume ;
explicit Feedrate ( float value )
: value ( value )
, volume ( nullptr )
{
}
bool operator == ( const Feedrate & other ) const
{
return value == other . value ;
}
};
typedef std :: vector < Feedrate > FeedratesList ;
// colors travels by feedrate
// detects feedrates
FeedratesList feedrates ;
for ( const GCodePreviewData :: Travel :: Polyline & polyline : preview_data . travel . polylines )
{
if ( std :: find ( feedrates . begin (), feedrates . end (), Feedrate ( polyline . feedrate )) == feedrates . end ())
feedrates . emplace_back ( polyline . feedrate );
}
// nothing to render, return
if ( feedrates . empty ())
return true ;
// creates a new volume for each feedrate
for ( Feedrate & feedrate : feedrates )
{
GLVolume * volume = new GLVolume ( preview_data . get_feedrate_color ( feedrate . value ). rgba );
if ( volume == nullptr )
return false ;
else
{
feedrate . volume = volume ;
2018-06-11 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
}
}
// populates volumes
for ( const GCodePreviewData :: Travel :: Polyline & polyline : preview_data . travel . polylines )
{
FeedratesList :: iterator feedrate = std :: find ( feedrates . begin (), feedrates . end (), Feedrate ( polyline . feedrate ));
if ( feedrate != feedrates . end ())
{
2018-08-21 17:43:05 +02:00
feedrate -> volume -> print_zs . push_back ( unscale < double > ( polyline . polyline . bounding_box (). min ( 2 )));
2018-06-05 10:56:55 +02:00
feedrate -> volume -> offsets . push_back ( feedrate -> volume -> indexed_vertex_array . quad_indices . size ());
feedrate -> volume -> offsets . push_back ( feedrate -> volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: polyline3_to_verts ( polyline . polyline , preview_data . travel . width , preview_data . travel . height , * feedrate -> volume );
}
}
return true ;
}
bool GLCanvas3D :: _travel_paths_by_tool ( const GCodePreviewData & preview_data , const std :: vector < float >& tool_colors )
{
// Helper structure for tool
struct Tool
{
unsigned int value ;
GLVolume * volume ;
explicit Tool ( unsigned int value )
: value ( value )
, volume ( nullptr )
{
}
bool operator == ( const Tool & other ) const
{
return value == other . value ;
}
};
typedef std :: vector < Tool > ToolsList ;
// colors travels by tool
// detects tools
ToolsList tools ;
for ( const GCodePreviewData :: Travel :: Polyline & polyline : preview_data . travel . polylines )
{
if ( std :: find ( tools . begin (), tools . end (), Tool ( polyline . extruder_id )) == tools . end ())
tools . emplace_back ( polyline . extruder_id );
}
// nothing to render, return
if ( tools . empty ())
return true ;
// creates a new volume for each tool
for ( Tool & tool : tools )
{
GLVolume * volume = new GLVolume ( tool_colors . data () + tool . value * 4 );
if ( volume == nullptr )
return false ;
else
{
tool . volume = volume ;
2018-06-11 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02: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 ())
{
2018-08-21 17:43:05 +02:00
tool -> volume -> print_zs . push_back ( unscale < double > ( polyline . polyline . bounding_box (). min ( 2 )));
2018-06-05 10:56:55 +02:00
tool -> volume -> offsets . push_back ( tool -> volume -> indexed_vertex_array . quad_indices . size ());
tool -> volume -> offsets . push_back ( tool -> volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: polyline3_to_verts ( polyline . polyline , preview_data . travel . width , preview_data . travel . height , * tool -> volume );
}
}
return true ;
}
void GLCanvas3D :: _load_gcode_retractions ( const GCodePreviewData & preview_data )
{
2018-06-11 15:13:13 +02:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex :: Retraction , 0 , ( unsigned int ) m_volumes . volumes . size ());
2018-06-05 10:56:55 +02: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 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
GCodePreviewData :: Retraction :: PositionsList copy ( preview_data . retraction . positions );
2018-08-17 15:53:43 +02:00
std :: sort ( copy . begin (), copy . end (), []( const GCodePreviewData :: Retraction :: Position & p1 , const GCodePreviewData :: Retraction :: Position & p2 ){ return p1 . position ( 2 ) < p2 . position ( 2 ); });
2018-06-05 10:56:55 +02:00
for ( const GCodePreviewData :: Retraction :: Position & position : copy )
{
2018-08-21 17:43:05 +02:00
volume -> print_zs . push_back ( unscale < double > ( position . position ( 2 )));
2018-06-05 10:56:55 +02:00
volume -> offsets . push_back ( volume -> indexed_vertex_array . quad_indices . size ());
volume -> offsets . push_back ( volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: point3_to_verts ( position . position , position . width , position . height , * volume );
}
// finalize volumes and sends geometry to gpu
volume -> bounding_box = volume -> indexed_vertex_array . bounding_box ();
2018-06-05 12:24:26 +02:00
volume -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
2018-06-05 10:56:55 +02:00
}
}
void GLCanvas3D :: _load_gcode_unretractions ( const GCodePreviewData & preview_data )
{
2018-06-11 15:13:13 +02:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex :: Unretraction , 0 , ( unsigned int ) m_volumes . volumes . size ());
2018-06-05 10:56:55 +02: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 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
GCodePreviewData :: Retraction :: PositionsList copy ( preview_data . unretraction . positions );
2018-08-17 15:53:43 +02:00
std :: sort ( copy . begin (), copy . end (), []( const GCodePreviewData :: Retraction :: Position & p1 , const GCodePreviewData :: Retraction :: Position & p2 ){ return p1 . position ( 2 ) < p2 . position ( 2 ); });
2018-06-05 10:56:55 +02:00
for ( const GCodePreviewData :: Retraction :: Position & position : copy )
{
2018-08-21 17:43:05 +02:00
volume -> print_zs . push_back ( unscale < double > ( position . position ( 2 )));
2018-06-05 10:56:55 +02:00
volume -> offsets . push_back ( volume -> indexed_vertex_array . quad_indices . size ());
volume -> offsets . push_back ( volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: point3_to_verts ( position . position , position . width , position . height , * volume );
}
// finalize volumes and sends geometry to gpu
volume -> bounding_box = volume -> indexed_vertex_array . bounding_box ();
2018-06-05 12:24:26 +02:00
volume -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
2018-06-05 10:56:55 +02:00
}
}
2018-11-26 15:16:35 +01:00
void GLCanvas3D :: _load_shells_fff ()
2018-06-05 10:56:55 +02:00
{
2018-06-11 15:13:13 +02:00
size_t initial_volumes_count = m_volumes . volumes . size ();
2018-06-05 10:56:55 +02:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex :: Shell , 0 , ( unsigned int ) initial_volumes_count );
2018-11-22 15:29:59 +01:00
const Print * print = this -> fff_print ();
if ( print -> objects (). empty ())
2018-06-05 10:56:55 +02:00
// nothing to render, return
return ;
// adds objects' volumes
2018-11-26 15:16:35 +01:00
int object_id = 0 ;
2018-11-22 15:29:59 +01:00
for ( const PrintObject * obj : print -> objects ())
2018-06-05 10:56:55 +02:00
{
2018-09-14 09:28:00 +02:00
const ModelObject * model_obj = obj -> model_object ();
2018-06-05 10:56:55 +02:00
std :: vector < int > instance_ids ( model_obj -> instances . size ());
2018-06-13 09:26:58 +02:00
for ( int i = 0 ; i < ( int ) model_obj -> instances . size (); ++ i )
2018-06-05 10:56:55 +02:00
{
instance_ids [ i ] = i ;
}
2018-10-08 16:05:55 +02:00
m_volumes . load_object ( model_obj , object_id , instance_ids , "object" , m_use_VBOs && m_initialized );
2018-06-05 10:56:55 +02:00
++ object_id ;
}
2018-11-12 15:36:40 +01:00
if ( wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology () == ptFFF ) {
// adds wipe tower's volume
2018-11-22 15:29:59 +01:00
double max_z = print -> objects ()[ 0 ] -> model_object () -> get_model () -> bounding_box (). max ( 2 );
const PrintConfig & config = print -> config ();
2018-11-12 15:36:40 +01:00
unsigned int extruders_count = config . nozzle_diameter . size ();
if (( extruders_count > 1 ) && config . single_extruder_multi_material && config . wipe_tower && ! config . complete_objects ) {
2018-11-22 15:29:59 +01:00
float depth = print -> get_wipe_tower_depth ();
if ( ! print -> is_step_done ( psWipeTower ))
2018-11-12 15:36:40 +01:00
depth = ( 900.f / config . wipe_tower_width ) * ( float )( extruders_count - 1 ) ;
m_volumes . load_wipe_tower_preview ( 1000 , config . wipe_tower_x , config . wipe_tower_y , config . wipe_tower_width , depth , max_z , config . wipe_tower_rotation_angle ,
2018-11-22 15:29:59 +01:00
m_use_VBOs && m_initialized , ! print -> is_step_done ( psWipeTower ), print -> config (). nozzle_diameter . values [ 0 ] * 1.25f * 4.5f );
2018-11-12 15:36:40 +01:00
}
2018-06-05 10:56:55 +02:00
}
}
2018-11-26 15:16:35 +01:00
void GLCanvas3D :: _load_shells_sla ()
{
const SLAPrint * print = this -> sla_print ();
if ( print -> objects (). empty ())
// nothing to render, return
return ;
// adds objects' volumes
int obj_idx = 0 ;
for ( const SLAPrintObject * obj : print -> objects ())
{
2018-12-10 13:27:00 +01:00
if ( ! obj -> is_step_done ( slaposIndexSlices ))
continue ;
2018-11-26 15:16:35 +01:00
unsigned int initial_volumes_count = ( unsigned int ) m_volumes . volumes . size ();
const ModelObject * model_obj = obj -> model_object ();
std :: vector < int > instance_idxs ( model_obj -> instances . size ());
for ( int i = 0 ; i < ( int ) model_obj -> instances . size (); ++ i )
{
instance_idxs [ i ] = i ;
}
m_volumes . load_object ( model_obj , obj_idx , instance_idxs , "object" , m_use_VBOs && m_initialized );
const std :: vector < SLAPrintObject :: Instance >& instances = obj -> instances ();
for ( const SLAPrintObject :: Instance & instance : instances )
{
Vec3d offset = unscale ( instance . shift ( 0 ), instance . shift ( 1 ), 0 );
Vec3d rotation ( 0.0 , 0.0 , ( double ) instance . rotation );
unsigned int partial_volumes_count = ( unsigned int ) m_volumes . volumes . size ();
// add supports
2018-11-26 16:17:59 +01:00
if ( obj -> is_step_done ( slaposSupportTree ) && obj -> has_mesh ( slaposSupportTree ))
2018-11-26 15:16:35 +01:00
{
const TriangleMesh & mesh = obj -> support_mesh ();
m_volumes . volumes . emplace_back ( new GLVolume ( GLVolume :: SLA_SUPPORT_COLOR ));
GLVolume & v = * m_volumes . volumes . back ();
if ( m_use_VBOs )
v . indexed_vertex_array . load_mesh_full_shading ( mesh );
else
v . indexed_vertex_array . load_mesh_flat_shading ( mesh );
v . shader_outside_printer_detection_enabled = true ;
v . composite_id . volume_id = - 1 ;
v . set_instance_offset ( offset );
v . set_instance_rotation ( rotation );
}
// add pad
2018-11-26 16:17:59 +01:00
if ( obj -> is_step_done ( slaposBasePool ) && obj -> has_mesh ( slaposBasePool ))
2018-11-26 15:16:35 +01:00
{
const TriangleMesh & mesh = obj -> pad_mesh ();
m_volumes . volumes . emplace_back ( new GLVolume ( GLVolume :: SLA_PAD_COLOR ));
GLVolume & v = * m_volumes . volumes . back ();
if ( m_use_VBOs )
v . indexed_vertex_array . load_mesh_full_shading ( mesh );
else
v . indexed_vertex_array . load_mesh_flat_shading ( mesh );
2018-12-10 13:57:43 +01:00
v . shader_outside_printer_detection_enabled = false ;
2018-11-26 15:16:35 +01:00
v . composite_id . volume_id = - 1 ;
v . set_instance_offset ( offset );
v . set_instance_rotation ( rotation );
}
// finalize volumes and sends geometry to gpu
for ( unsigned int i = partial_volumes_count ; i < m_volumes . volumes . size (); ++ i )
{
GLVolume & v = * m_volumes . volumes [ i ];
v . bounding_box = v . indexed_vertex_array . bounding_box ();
v . indexed_vertex_array . finalize_geometry ( m_use_VBOs );
}
++ obj_idx ;
}
// apply shift z
double shift_z = obj -> get_current_elevation ();
for ( unsigned int i = initial_volumes_count ; i < m_volumes . volumes . size (); ++ i )
{
m_volumes . volumes [ i ] -> set_sla_shift_z ( shift_z );
}
}
update_volumes_colors_by_extruder ();
}
2018-06-05 10:56:55 +02:00
void GLCanvas3D :: _update_gcode_volumes_visibility ( const GCodePreviewData & preview_data )
{
unsigned int size = ( unsigned int ) m_gcode_preview_volume_index . first_volumes . size ();
for ( unsigned int i = 0 ; i < size ; ++ i )
{
2018-10-08 14:02:12 +02:00
GLVolumePtrs :: iterator begin = m_volumes . volumes . begin () + m_gcode_preview_volume_index . first_volumes [ i ]. id ;
GLVolumePtrs :: iterator end = ( i + 1 < size ) ? m_volumes . volumes . begin () + m_gcode_preview_volume_index . first_volumes [ i + 1 ]. id : m_volumes . volumes . end ();
for ( GLVolumePtrs :: iterator it = begin ; it != end ; ++ it )
2018-06-05 10:56:55 +02:00
{
GLVolume * volume = * it ;
switch ( m_gcode_preview_volume_index . first_volumes [ i ]. type )
{
case GCodePreviewVolumeIndex :: Extrusion :
{
if (( ExtrusionRole ) m_gcode_preview_volume_index . first_volumes [ i ]. flag == erCustom )
volume -> zoom_to_volumes = false ;
volume -> is_active = preview_data . extrusion . is_role_flag_set (( ExtrusionRole ) m_gcode_preview_volume_index . first_volumes [ i ]. flag );
break ;
}
case GCodePreviewVolumeIndex :: Travel :
{
volume -> is_active = preview_data . travel . is_visible ;
volume -> zoom_to_volumes = false ;
break ;
}
case GCodePreviewVolumeIndex :: Retraction :
{
volume -> is_active = preview_data . retraction . is_visible ;
volume -> zoom_to_volumes = false ;
break ;
}
case GCodePreviewVolumeIndex :: Unretraction :
{
volume -> is_active = preview_data . unretraction . is_visible ;
volume -> zoom_to_volumes = false ;
break ;
}
case GCodePreviewVolumeIndex :: Shell :
{
volume -> is_active = preview_data . shell . is_visible ;
volume -> color [ 3 ] = 0.25f ;
volume -> zoom_to_volumes = false ;
break ;
}
default :
{
volume -> is_active = false ;
volume -> zoom_to_volumes = false ;
break ;
}
}
}
}
}
2018-07-24 13:39:17 +02:00
void GLCanvas3D :: _update_toolpath_volumes_outside_state ()
{
// tolerance to avoid false detection at bed edges
2018-08-24 10:20:00 +02:00
static const double tolerance_x = 0.05 ;
static const double tolerance_y = 0.05 ;
2018-07-24 13:39:17 +02:00
BoundingBoxf3 print_volume ;
if ( m_config != nullptr )
{
const ConfigOptionPoints * opt = dynamic_cast < const ConfigOptionPoints *> ( m_config -> option ( "bed_shape" ));
if ( opt != nullptr )
{
BoundingBox bed_box_2D = get_extents ( Polygon :: new_scale ( opt -> values ));
2018-08-21 17:43:05 +02:00
print_volume = BoundingBoxf3 ( Vec3d ( unscale < double > ( bed_box_2D . min ( 0 )) - tolerance_x , unscale < double > ( bed_box_2D . min ( 1 )) - tolerance_y , 0.0 ), Vec3d ( unscale < double > ( bed_box_2D . max ( 0 )) + tolerance_x , unscale < double > ( bed_box_2D . max ( 1 )) + tolerance_y , m_config -> opt_float ( "max_print_height" )));
2018-07-24 13:39:17 +02:00
// Allow the objects to protrude below the print bed
2018-08-17 18:07:45 +02:00
print_volume . min ( 2 ) = - 1e10 ;
2018-07-24 13:39:17 +02:00
}
}
for ( GLVolume * volume : m_volumes . volumes )
{
2018-07-27 08:49:58 +02:00
volume -> is_outside = (( print_volume . radius () > 0.0 ) && volume -> is_extrusion_path ) ? ! print_volume . contains ( volume -> bounding_box ) : false ;
2018-07-24 13:39:17 +02:00
}
}
void GLCanvas3D :: _show_warning_texture_if_needed ()
{
2018-07-27 09:38:39 +02:00
if ( _is_any_volume_outside ())
2018-07-24 13:39:17 +02:00
{
enable_warning_texture ( true );
_generate_warning_texture ( L ( "Detected toolpath outside print volume" ));
}
else
{
enable_warning_texture ( false );
_reset_warning_texture ();
}
}
2018-06-05 14:09:36 +02:00
std :: vector < float > GLCanvas3D :: _parse_colors ( const std :: vector < std :: string >& colors )
{
2018-06-08 11:37:07 +02:00
static const float INV_255 = 1.0f / 255.0f ;
2018-06-05 14:09:36 +02:00
std :: vector < float > output ( colors . size () * 4 , 1.0f );
2018-06-08 11:37:07 +02:00
for ( size_t i = 0 ; i < colors . size (); ++ i )
{
2018-06-05 14:09:36 +02: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 11:37:07 +02:00
output [ i * 4 + j ] = float ( digit1 * 16 + digit2 ) * INV_255 ;
2018-06-05 14:09:36 +02:00
}
}
}
return output ;
}
2018-07-19 13:18:19 +02:00
void GLCanvas3D :: _generate_legend_texture ( const GCodePreviewData & preview_data , const std :: vector < float >& tool_colors )
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
if ( ! set_current ())
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
2018-12-13 11:13:58 +01:00
m_legend_texture . generate ( preview_data , tool_colors , * this , m_dynamic_background_enabled && _is_any_volume_outside ());
2018-07-19 13:18:19 +02:00
}
void GLCanvas3D :: _generate_warning_texture ( const std :: string & msg )
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
if ( ! set_current ())
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
m_warning_texture . generate ( msg );
}
void GLCanvas3D :: _reset_warning_texture ()
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
if ( ! set_current ())
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
m_warning_texture . reset ();
}
2018-07-27 09:38:39 +02:00
bool GLCanvas3D :: _is_any_volume_outside () const
{
for ( const GLVolume * volume : m_volumes . volumes )
{
if (( volume != nullptr ) && volume -> is_outside )
return true ;
}
return false ;
}
2018-12-06 10:38:19 +01:00
void GLCanvas3D :: _resize_toolbars () const
2018-07-31 12:25:00 +02:00
{
Size cnv_size = get_canvas_size ();
float zoom = get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
2018-12-17 10:55:14 +01:00
GLToolbar :: Layout :: EOrientation orientation = m_toolbar . get_layout_orientation ();
2018-07-31 12:25:00 +02:00
switch ( m_toolbar . get_layout_type ())
{
default :
case GLToolbar :: Layout :: Horizontal :
{
// centers the toolbar on the top edge of the 3d scene
2018-12-17 10:55:14 +01:00
float top , left ;
if ( orientation == GLToolbar :: Layout :: Top )
{
top = 0.5f * ( float ) cnv_size . get_height () * inv_zoom ;
left = - 0.5f * m_toolbar . get_width () * inv_zoom ;
}
else
{
top = ( - 0.5f * ( float ) cnv_size . get_height () + m_view_toolbar -> get_height ()) * inv_zoom ;
left = - 0.5f * m_toolbar . get_width () * inv_zoom ;
}
2018-07-31 12:25:00 +02:00
m_toolbar . set_position ( top , left );
break ;
}
case GLToolbar :: Layout :: Vertical :
{
// centers the toolbar on the right edge of the 3d scene
2018-12-17 10:55:14 +01:00
float top , left ;
if ( orientation == GLToolbar :: Layout :: Left )
{
top = 0.5f * m_toolbar . get_height () * inv_zoom ;
left = ( - 0.5f * ( float ) cnv_size . get_width ()) * inv_zoom ;
}
else
{
top = 0.5f * m_toolbar . get_height () * inv_zoom ;
left = ( 0.5f * ( float ) cnv_size . get_width () - m_toolbar . get_width ()) * inv_zoom ;
}
2018-07-31 12:25:00 +02:00
m_toolbar . set_position ( top , left );
break ;
}
}
2018-12-06 10:38:19 +01:00
if ( m_view_toolbar != nullptr )
{
2018-12-17 10:55:14 +01:00
// places the toolbar on the bottom-left corner of the 3d scene
2018-12-06 10:38:19 +01:00
float top = ( - 0.5f * ( float ) cnv_size . get_height () + m_view_toolbar -> get_height ()) * inv_zoom ;
float left = - 0.5f * ( float ) cnv_size . get_width () * inv_zoom ;
m_view_toolbar -> set_position ( top , left );
}
2018-07-31 12:25:00 +02:00
}
2018-11-22 15:29:59 +01:00
const Print * GLCanvas3D :: fff_print () const
{
return ( m_process == nullptr ) ? nullptr : m_process -> fff_print ();
}
const SLAPrint * GLCanvas3D :: sla_print () const
{
return ( m_process == nullptr ) ? nullptr : m_process -> sla_print ();
}
2018-05-09 10:47:04 +02:00
} // namespace GUI
} // namespace Slic3r