2019-03-15 12:53:15 +01:00
#include "GLGizmoBase.hpp"
2019-03-20 13:51:25 +01:00
#include "slic3r/GUI/GLCanvas3D.hpp"
2019-03-15 12:53:15 +01:00
#include <GL/glew.h>
#include "slic3r/GUI/GUI_App.hpp"
2020-01-28 10:20:37 +01:00
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "slic3r/GUI/MeshUtils.hpp"
2019-03-15 12:53:15 +01:00
// TODO: Display tooltips quicker on Linux
namespace Slic3r {
namespace GUI {
2019-05-10 11:13:55 +02:00
const float GLGizmoBase :: Grabber :: SizeFactor = 0.05f ;
2019-03-15 12:53:15 +01:00
const float GLGizmoBase :: Grabber :: MinHalfSize = 1.5f ;
const float GLGizmoBase :: Grabber :: DraggingScaleFactor = 1.25f ;
GLGizmoBase :: Grabber :: Grabber ()
: center ( Vec3d :: Zero ())
, angles ( Vec3d :: Zero ())
, dragging ( false )
, enabled ( true )
{
color [ 0 ] = 1.0f ;
color [ 1 ] = 1.0f ;
color [ 2 ] = 1.0f ;
2019-08-07 14:15:38 +02:00
color [ 3 ] = 1.0f ;
2019-03-15 12:53:15 +01:00
}
void GLGizmoBase :: Grabber :: render ( bool hover , float size ) const
{
2019-08-07 14:15:38 +02:00
float render_color [ 4 ];
2019-03-15 12:53:15 +01:00
if ( hover )
{
render_color [ 0 ] = 1.0f - color [ 0 ];
render_color [ 1 ] = 1.0f - color [ 1 ];
render_color [ 2 ] = 1.0f - color [ 2 ];
2019-08-07 14:15:38 +02:00
render_color [ 3 ] = color [ 3 ];
2019-03-15 12:53:15 +01:00
}
else
2019-08-07 14:15:38 +02:00
:: memcpy (( void * ) render_color , ( const void * ) color , 4 * sizeof ( float ));
2019-03-15 12:53:15 +01:00
render ( size , render_color , true );
}
float GLGizmoBase :: Grabber :: get_half_size ( float size ) const
{
return std :: max ( size * SizeFactor , MinHalfSize );
}
float GLGizmoBase :: Grabber :: get_dragging_half_size ( float size ) const
{
2019-04-15 15:05:26 +02:00
return get_half_size ( size ) * DraggingScaleFactor ;
2019-03-15 12:53:15 +01:00
}
void GLGizmoBase :: Grabber :: render ( float size , const float * render_color , bool use_lighting ) const
{
float half_size = dragging ? get_dragging_half_size ( size ) : get_half_size ( size );
if ( use_lighting )
2019-03-27 14:42:09 +01:00
glsafe ( :: glEnable ( GL_LIGHTING ));
2019-03-15 12:53:15 +01:00
2019-08-07 14:15:38 +02:00
glsafe ( :: glColor4fv ( render_color ));
2019-03-15 12:53:15 +01:00
2019-03-27 14:42:09 +01:00
glsafe ( :: glPushMatrix ());
glsafe ( :: glTranslated ( center ( 0 ), center ( 1 ), center ( 2 )));
2019-03-15 12:53:15 +01:00
2019-03-27 14:42:09 +01:00
glsafe ( :: glRotated ( Geometry :: rad2deg ( angles ( 2 )), 0.0 , 0.0 , 1.0 ));
glsafe ( :: glRotated ( Geometry :: rad2deg ( angles ( 1 )), 0.0 , 1.0 , 0.0 ));
glsafe ( :: glRotated ( Geometry :: rad2deg ( angles ( 0 )), 1.0 , 0.0 , 0.0 ));
2019-03-15 12:53:15 +01:00
// face min x
2019-03-27 14:42:09 +01:00
glsafe ( :: glPushMatrix ());
glsafe ( :: glTranslatef ( - ( GLfloat ) half_size , 0.0f , 0.0f ));
glsafe ( :: glRotatef ( - 90.0f , 0.0f , 1.0f , 0.0f ));
2019-03-15 12:53:15 +01:00
render_face ( half_size );
2019-03-27 14:42:09 +01:00
glsafe ( :: glPopMatrix ());
2019-03-15 12:53:15 +01:00
// face max x
2019-03-27 14:42:09 +01:00
glsafe ( :: glPushMatrix ());
glsafe ( :: glTranslatef (( GLfloat ) half_size , 0.0f , 0.0f ));
glsafe ( :: glRotatef ( 90.0f , 0.0f , 1.0f , 0.0f ));
2019-03-15 12:53:15 +01:00
render_face ( half_size );
2019-03-27 14:42:09 +01:00
glsafe ( :: glPopMatrix ());
2019-03-15 12:53:15 +01:00
// face min y
2019-03-27 14:42:09 +01:00
glsafe ( :: glPushMatrix ());
glsafe ( :: glTranslatef ( 0.0f , - ( GLfloat ) half_size , 0.0f ));
glsafe ( :: glRotatef ( 90.0f , 1.0f , 0.0f , 0.0f ));
2019-03-15 12:53:15 +01:00
render_face ( half_size );
2019-03-27 14:42:09 +01:00
glsafe ( :: glPopMatrix ());
2019-03-15 12:53:15 +01:00
// face max y
2019-03-27 14:42:09 +01:00
glsafe ( :: glPushMatrix ());
glsafe ( :: glTranslatef ( 0.0f , ( GLfloat ) half_size , 0.0f ));
glsafe ( :: glRotatef ( - 90.0f , 1.0f , 0.0f , 0.0f ));
2019-03-15 12:53:15 +01:00
render_face ( half_size );
2019-03-27 14:42:09 +01:00
glsafe ( :: glPopMatrix ());
2019-03-15 12:53:15 +01:00
// face min z
2019-03-27 14:42:09 +01:00
glsafe ( :: glPushMatrix ());
glsafe ( :: glTranslatef ( 0.0f , 0.0f , - ( GLfloat ) half_size ));
glsafe ( :: glRotatef ( 180.0f , 1.0f , 0.0f , 0.0f ));
2019-03-15 12:53:15 +01:00
render_face ( half_size );
2019-03-27 14:42:09 +01:00
glsafe ( :: glPopMatrix ());
2019-03-15 12:53:15 +01:00
// face max z
2019-03-27 14:42:09 +01:00
glsafe ( :: glPushMatrix ());
glsafe ( :: glTranslatef ( 0.0f , 0.0f , ( GLfloat ) half_size ));
2019-03-15 12:53:15 +01:00
render_face ( half_size );
2019-03-27 14:42:09 +01:00
glsafe ( :: glPopMatrix ());
2019-03-15 12:53:15 +01:00
2019-03-27 14:42:09 +01:00
glsafe ( :: glPopMatrix ());
2019-03-15 12:53:15 +01:00
if ( use_lighting )
2019-03-27 14:42:09 +01:00
glsafe ( :: glDisable ( GL_LIGHTING ));
2019-03-15 12:53:15 +01:00
}
void GLGizmoBase :: Grabber :: render_face ( float half_size ) const
{
:: glBegin ( GL_TRIANGLES );
:: glNormal3f ( 0.0f , 0.0f , 1.0f );
:: glVertex3f ( - ( GLfloat ) half_size , - ( GLfloat ) half_size , 0.0f );
:: glVertex3f (( GLfloat ) half_size , - ( GLfloat ) half_size , 0.0f );
:: glVertex3f (( GLfloat ) half_size , ( GLfloat ) half_size , 0.0f );
:: glVertex3f (( GLfloat ) half_size , ( GLfloat ) half_size , 0.0f );
:: glVertex3f ( - ( GLfloat ) half_size , ( GLfloat ) half_size , 0.0f );
:: glVertex3f ( - ( GLfloat ) half_size , - ( GLfloat ) half_size , 0.0f );
2019-03-27 14:42:09 +01:00
glsafe ( :: glEnd ());
2019-03-15 12:53:15 +01:00
}
2019-07-17 08:38:48 +02:00
2019-12-17 15:57:24 +01:00
GLGizmoBase :: GLGizmoBase ( GLCanvas3D & parent , const std :: string & icon_filename , unsigned int sprite_id , CommonGizmosData * common_data_ptr )
2019-03-15 12:53:15 +01:00
: m_parent ( parent )
, m_group_id ( - 1 )
, m_state ( Off )
, m_shortcut_key ( 0 )
, m_icon_filename ( icon_filename )
, m_sprite_id ( sprite_id )
, m_hover_id ( - 1 )
, m_dragging ( false )
, m_imgui ( wxGetApp (). imgui ())
2019-08-26 09:06:21 +02:00
, m_first_input_window_render ( true )
2019-12-17 15:57:24 +01:00
, m_c ( common_data_ptr )
2019-03-15 12:53:15 +01:00
{
2019-08-07 14:15:38 +02:00
:: memcpy (( void * ) m_base_color , ( const void * ) DEFAULT_BASE_COLOR , 4 * sizeof ( float ));
:: memcpy (( void * ) m_drag_color , ( const void * ) DEFAULT_DRAG_COLOR , 4 * sizeof ( float ));
:: memcpy (( void * ) m_highlight_color , ( const void * ) DEFAULT_HIGHLIGHT_COLOR , 4 * sizeof ( float ));
2019-03-15 12:53:15 +01:00
}
void GLGizmoBase :: set_hover_id ( int id )
{
if ( m_grabbers . empty () || ( id < ( int ) m_grabbers . size ()))
{
m_hover_id = id ;
on_set_hover_id ();
}
}
void GLGizmoBase :: set_highlight_color ( const float * color )
{
if ( color != nullptr )
2019-08-07 14:15:38 +02:00
:: memcpy (( void * ) m_highlight_color , ( const void * ) color , 4 * sizeof ( float ));
2019-03-15 12:53:15 +01:00
}
void GLGizmoBase :: enable_grabber ( unsigned int id )
{
2019-07-23 11:22:54 +02:00
if ( id < m_grabbers . size ())
2019-03-15 12:53:15 +01:00
m_grabbers [ id ]. enabled = true ;
on_enable_grabber ( id );
}
void GLGizmoBase :: disable_grabber ( unsigned int id )
{
2019-07-23 11:22:54 +02:00
if ( id < m_grabbers . size ())
2019-03-15 12:53:15 +01:00
m_grabbers [ id ]. enabled = false ;
on_disable_grabber ( id );
}
2019-07-17 12:06:23 +02:00
void GLGizmoBase :: start_dragging ()
2019-03-15 12:53:15 +01:00
{
m_dragging = true ;
for ( int i = 0 ; i < ( int ) m_grabbers . size (); ++ i )
{
m_grabbers [ i ]. dragging = ( m_hover_id == i );
}
2019-07-17 12:06:23 +02:00
on_start_dragging ();
2019-03-15 12:53:15 +01:00
}
void GLGizmoBase :: stop_dragging ()
{
m_dragging = false ;
for ( int i = 0 ; i < ( int ) m_grabbers . size (); ++ i )
{
m_grabbers [ i ]. dragging = false ;
}
on_stop_dragging ();
}
2019-07-17 12:06:23 +02:00
void GLGizmoBase :: update ( const UpdateData & data )
2019-03-15 12:53:15 +01:00
{
if ( m_hover_id != - 1 )
2019-07-17 12:06:23 +02:00
on_update ( data );
2019-03-15 12:53:15 +01:00
}
2019-08-07 14:15:38 +02:00
std :: array < float , 4 > GLGizmoBase :: picking_color_component ( unsigned int id ) const
2019-03-15 12:53:15 +01:00
{
static const float INV_255 = 1.0f / 255.0f ;
id = BASE_ID - id ;
if ( m_group_id > - 1 )
id -= m_group_id ;
// color components are encoded to match the calculation of volume_id made into GLCanvas3D::_picking_pass()
2019-08-07 14:15:38 +02:00
return std :: array < float , 4 > {
float (( id >> 0 ) & 0xff ) * INV_255 , // red
float (( id >> 8 ) & 0xff ) * INV_255 , // green
float (( id >> 16 ) & 0xff ) * INV_255 , // blue
float ( picking_checksum_alpha_channel ( id & 0xff , ( id >> 8 ) & 0xff , ( id >> 16 ) & 0xff )) * INV_255 // checksum for validating against unwanted alpha blending and multi sampling
};
2019-03-15 12:53:15 +01:00
}
void GLGizmoBase :: render_grabbers ( const BoundingBoxf3 & box ) const
{
2019-03-25 15:03:35 +01:00
render_grabbers (( float )(( box . size ()( 0 ) + box . size ()( 1 ) + box . size ()( 2 )) / 3.0 ));
2019-03-15 12:53:15 +01:00
}
void GLGizmoBase :: render_grabbers ( float size ) const
{
for ( int i = 0 ; i < ( int ) m_grabbers . size (); ++ i )
{
if ( m_grabbers [ i ]. enabled )
m_grabbers [ i ]. render (( m_hover_id == i ), size );
}
}
void GLGizmoBase :: render_grabbers_for_picking ( const BoundingBoxf3 & box ) const
{
2019-03-25 15:03:35 +01:00
float mean_size = ( float )(( box . size ()( 0 ) + box . size ()( 1 ) + box . size ()( 2 )) / 3.0 );
2019-03-15 12:53:15 +01:00
for ( unsigned int i = 0 ; i < ( unsigned int ) m_grabbers . size (); ++ i )
{
if ( m_grabbers [ i ]. enabled )
{
2019-08-07 14:15:38 +02:00
std :: array < float , 4 > color = picking_color_component ( i );
2019-03-15 12:53:15 +01:00
m_grabbers [ i ]. color [ 0 ] = color [ 0 ];
m_grabbers [ i ]. color [ 1 ] = color [ 1 ];
m_grabbers [ i ]. color [ 2 ] = color [ 2 ];
2019-08-07 14:15:38 +02:00
m_grabbers [ i ]. color [ 3 ] = color [ 3 ];
2019-03-25 15:03:35 +01:00
m_grabbers [ i ]. render_for_picking ( mean_size );
2019-03-15 12:53:15 +01:00
}
}
}
void GLGizmoBase :: set_tooltip ( const std :: string & tooltip ) const
{
m_parent . set_tooltip ( tooltip );
}
std :: string GLGizmoBase :: format ( float value , unsigned int decimals ) const
{
return Slic3r :: string_printf ( "%.*f" , decimals , value );
}
2019-08-26 09:06:21 +02:00
void GLGizmoBase :: render_input_window ( float x , float y , float bottom_limit )
{
on_render_input_window ( x , y , bottom_limit );
if ( m_first_input_window_render )
{
// for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame
// so, we forces another frame rendering the first time the imgui window is shown
m_parent . set_as_dirty ();
m_first_input_window_render = false ;
}
}
2019-08-07 14:15:38 +02:00
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
unsigned char picking_checksum_alpha_channel ( unsigned char red , unsigned char green , unsigned char blue )
{
// 8 bit hash for the color
unsigned char b = (((( 37 * red ) + green ) & 0x0ff ) * 37 + blue ) & 0x0ff ;
// Increase enthropy by a bit reversal
b = ( b & 0xF0 ) >> 4 | ( b & 0x0F ) << 4 ;
b = ( b & 0xCC ) >> 2 | ( b & 0x33 ) << 2 ;
b = ( b & 0xAA ) >> 1 | ( b & 0x55 ) << 1 ;
// Flip every second bit to increase the enthropy even more.
b ^= 0x55 ;
return b ;
}
2020-01-28 10:20:37 +01:00
2020-01-30 14:12:52 +01:00
bool CommonGizmosData :: update_from_backend ( GLCanvas3D & canvas , ModelObject * model_object )
2020-01-28 10:20:37 +01:00
{
2020-01-30 14:12:52 +01:00
recent_update = false ;
if ( m_model_object != model_object
|| ( model_object && m_model_object_id != model_object -> id ())) {
m_model_object = model_object ;
m_print_object_idx = - 1 ;
m_mesh_raycaster . reset ();
m_object_clipper . reset ();
m_supports_clipper . reset ();
2020-01-30 15:25:15 +01:00
m_old_mesh = nullptr ;
m_mesh = nullptr ;
m_backend_mesh_transformed . clear ();
2020-01-30 14:12:52 +01:00
if ( m_model_object ) {
m_active_instance = canvas . get_selection (). get_instance_idx ();
m_active_instance_bb_radius = m_model_object -> instance_bounding_box ( m_active_instance ). radius ();
}
recent_update = true ;
}
if ( ! m_model_object || ! canvas . get_selection (). is_from_single_instance ())
2020-01-28 10:20:37 +01:00
return false ;
int old_po_idx = m_print_object_idx ;
// First we need a pointer to the respective SLAPrintObject. The index into objects vector is
// cached so we don't have todo it on each render. We only search for the po if needed:
if ( m_print_object_idx < 0 || ( int ) canvas . sla_print () -> objects (). size () != m_print_objects_count ) {
m_print_objects_count = canvas . sla_print () -> objects (). size ();
m_print_object_idx = - 1 ;
for ( const SLAPrintObject * po : canvas . sla_print () -> objects ()) {
++ m_print_object_idx ;
if ( po -> model_object () -> id () == m_model_object -> id ())
break ;
}
}
m_mesh = nullptr ;
// Load either the model_object mesh, or one provided by the backend
// This mesh does not account for the possible Z up SLA offset.
2020-01-28 12:41:48 +01:00
// The backend mesh needs to be transformed and because a pointer to it is
// saved, a copy is stored as a member (FIXME)
if ( m_print_object_idx >= 0 ) {
const SLAPrintObject * po = canvas . sla_print () -> objects ()[ m_print_object_idx ];
if ( po -> is_step_done ( slaposHollowing )) {
m_backend_mesh_transformed = po -> get_mesh_to_print ();
m_backend_mesh_transformed . transform ( canvas . sla_print () -> sla_trafo ( * m_model_object ). inverse ());
m_mesh = & m_backend_mesh_transformed ;
}
}
2020-01-28 10:20:37 +01:00
2020-01-28 12:41:48 +01:00
if ( ! m_mesh ) {
2020-01-28 10:20:37 +01:00
m_mesh = & m_model_object -> volumes . front () -> mesh ();
2020-01-28 12:41:48 +01:00
m_backend_mesh_transformed . clear ();
}
2020-01-28 10:20:37 +01:00
m_model_object_id = m_model_object -> id ();
if ( m_mesh != m_old_mesh ) {
2020-01-30 14:31:22 +01:00
wxBusyCursor wait ;
2020-01-28 10:20:37 +01:00
m_mesh_raycaster . reset ( new MeshRaycaster ( * m_mesh ));
m_object_clipper . reset ();
m_supports_clipper . reset ();
m_old_mesh = m_mesh ;
2020-01-30 14:12:52 +01:00
recent_update = true ;
2020-01-28 10:20:37 +01:00
return true ;
}
2020-01-30 14:12:52 +01:00
if ( ! recent_update )
recent_update = m_print_object_idx < 0 && old_po_idx >= 0 ;
2020-01-28 12:41:48 +01:00
return m_print_object_idx < 0 ? old_po_idx >= 0 : false ;
2020-01-28 10:20:37 +01:00
}
2019-03-15 12:53:15 +01:00
} // namespace GUI
} // namespace Slic3r