/* Copyright 2005 Nicholas Bishop * * This file is part of SharpConstruct. * * SharpConstruct is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * SharpConstruct is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with SharpConstruct; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "Brush.h" #include "Editor.hh" #include "GrabBrush.hh" #include "MeshHistory.h" #include "Preferences.h" #include #include #include #include using SharpConstruct::GInterface::Editor; using SharpConstruct::Optimized::Point3D; Editor::DisplayOptionsData::DisplayOptionsData( Editor& v ) : view_( v ), wireframe_( false ), smoothed_( false ), double_sided_( false ), speed_( false ), glout_( 0 ), background_color_( 0.3, 0.3, 0.3 ) {} Editor::DisplayOptionsData::~DisplayOptionsData() { Preferences::Instance().Set( "GraphicsMode", graphics_mode_ ); } bool Editor::DisplayOptionsData::Wireframe() const { return wireframe_; } bool Editor::DisplayOptionsData::Smoothed() const { return smoothed_; } bool Editor::DisplayOptionsData::DoubleSided() const { return double_sided_; } bool Editor::DisplayOptionsData::Speed() const { return speed_; } SharpConstruct::Output3D& Editor::DisplayOptionsData::Output() { return *glout_; } const SharpConstruct::Color& Editor::DisplayOptionsData::BackgroundColor() const { return background_color_; } void Editor::DisplayOptionsData::ToggleWireframe() { wireframe_ = !wireframe_; view_.Refresh( false, true ); } void Editor::DisplayOptionsData::ToggleSmoothed() { smoothed_ = !smoothed_; view_.Refresh( false, true ); } void Editor::DisplayOptionsData::ToggleDoubleSided() { double_sided_ = !double_sided_; view_.Refresh( false, true ); } void Editor::DisplayOptionsData::ToggleSpeed() { speed_ = !speed_; view_.Refresh( false, true ); } void Editor::DisplayOptionsData::SetGraphicsMode() { GraphicsModeType t( VBO ); const int pref( Preferences::Instance().Get( "GraphicsMode", 0 ) ); if( pref == 0 ) t = VBO; else if( pref == 1 ) t = VAR; SetGraphicsMode( t ); } void Editor::DisplayOptionsData::SetGraphicsMode( const GraphicsModeType t ) { graphics_mode_ = t; delete glout_; if( t == VAR ) glout_ = new SharpConstruct::VAR(); else { if( SharpConstruct::VBO::IsSupported() ) glout_ = new SharpConstruct::VBO(); else glout_ = new SharpConstruct::VAR(); } view_.Refresh( true, true ); } void Editor::DisplayOptionsData::SetBackgroundColor( const Color& c ) { background_color_ = c; view_.Refresh( false, true ); } Editor::Editor() : update_vert_norm_( true ), update_color_( true ), update_polygons_( true ), display_options_( *this ), dirty_( 0, 0, 0, 0 ), arc_ball_( 0.8 ), zoom_( 1 ) { MeshHistory::Instance().SignalEntireSectionChanged().connect( sigc::mem_fun( *this, &Editor::on_entire_section_changed_ ) ); add_events( Gdk::POINTER_MOTION_MASK | /*Gdk::POINTER_MOTION_HINT_MASK |*/ Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK ); set_extension_events( Gdk::EXTENSION_EVENTS_CURSOR ); Glib::RefPtr glconfig; glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB | Gdk::GL::MODE_DEPTH | Gdk::GL::MODE_DOUBLE); if (!glconfig) { glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB | Gdk::GL::MODE_DEPTH); if (!glconfig) { std::cerr << "*** Cannot find any OpenGL-capable visual.\n"; std::exit( 1 ); } } set_gl_capability( glconfig ); show(); } void Editor::Refresh( const bool vnc, const bool poly ) { cursor_.Update(); queue_draw(); if( vnc ) update_vert_norm_ = update_color_ = true; if( poly ) { rendered_ = false; update_polygons_ = true; recalc_all_projected_ = true; } } void Editor::ResetView() { Mesh& mesh( MeshHistory::Instance().GetCurrentMesh() ); mesh.CalculateBoundingSphere(); location_.Set( 0, 0 ); zoom_ = 1; arc_ball_.Reset(); Refresh( false, true ); } const Editor::DisplayOptionsData& Editor::DisplayOptions() const { return display_options_; } Editor::DisplayOptionsData& Editor::DisplayOptions() { return display_options_; } bool Editor::on_button_press_event( GdkEventButton* e ) { Gdk::EventMask event_mask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_RELEASE_MASK ); if( e->device->source == GDK_SOURCE_MOUSE ) event_mask |= Gdk::POINTER_MOTION_HINT_MASK; get_window()->pointer_grab( false, event_mask, e->time ); if( e->button == 1 ) { Brush& brush( MeshHistory::Instance(). GetCurrentMesh().CurrentBrush() ); Glib::RefPtr< Gdk::Device > device( Glib::wrap( e->device, true ) ); double pressure( 1 ); device->get_axis( *e->axes, Gdk::AXIS_PRESSURE, pressure ); brush.SetPressure( pressure ); /*if( Keyboard.Shift() ) brush.SetFlip( true );*/ bool do_col = false; if( ( brush.Mode() == Brush::DISPLACE || brush.Mode() == Brush::SMOOTH ) && brush.ColorEdit() ) do_col = true; if( brush.Mode() == Brush::GRAB ) MeshHistory::Instance().AddMesh( true, false, false ); else if( brush.PositionEdit() || brush.ColorEdit() ) MeshHistory::Instance().AddMesh( brush.PositionEdit(), do_col, false ); brush.StartSculpt( MeshHistory::Instance().GetCurrentMesh() ); if( brush.PositionEdit() != Brush::OFF ) update_vert_norm_ = true; if( do_col ) update_color_ = true; //ChangedMeshSignal(); //update_vertex_buffer_ = true; update_depth_storage_ = true; brush.SetFlip( false ); queue_draw(); /*Brush& brush( Mesh::CurrentBrush() ); Mesh& mesh( MeshHistory::Instance().GetCurrentMesh() ); brush.StartSculpt( mesh ); //ChangedMeshSignal(); //update_vertex_buffer_ = true; update_depth_storage_ = true;*/ } else if( e->button == 3 ) { double xnorm = last_mouse_location_.X(); double ynorm = last_mouse_location_.Y(); ArcBall::NormalizeCoordinates( xnorm, ynorm, get_width(), get_height() ); arc_ball_.BeginDrag( xnorm, ynorm ); } return true; } bool Editor::on_motion_notify_event( GdkEventMotion* event ) { if( !redraw_complete_ ) { update_mouse_(); queue_draw(); return true; } redraw_complete_ = false; if( event->is_hint ) update_mouse_(); else { mouse_loc_.set_x( event->x ); mouse_loc_.set_y( event->y ); //mouse_state_ = event->state; } const int x( mouse_loc_.get_x() ), y( mouse_loc_.get_y() ); Glib::RefPtr< Gdk::Device > device( Glib::wrap( event->device, true ) ); if( event->state & Gdk::BUTTON1_MASK ) { Brush& brush( Mesh::CurrentBrush() ); /*if( Keyboard.Control() ) { hide_corner_2_.SetX( Mouse.Location().X() ); hide_corner_2_.SetY( Mouse.Location().Y() ); } else*/ { if( event->state & Gdk::SHIFT_MASK ) brush.SetFlip( true ); if( device->get_source() == Gdk::SOURCE_ERASER ) brush.SetFlip( true ); double pressure( 1 ); device->get_axis( *event->axes, Gdk::AXIS_PRESSURE, pressure ); brush.SetPressure( pressure ); if( brush.Mode() == Brush::GRAB ) { GrabBrush::SetDelta( ( x - last_mouse_location_.X() ) / 1000.0, ( y - last_mouse_location_.Y() ) / 1000.0 ); } Mesh& mesh( MeshHistory::Instance().GetCurrentMesh() ); brush.ContinueSculpt( mesh ); brush.SetFlip( false ); bool do_col( false ); if( ( brush.Mode() == Brush::DISPLACE || brush.Mode() == Brush::SMOOTH ) && brush.ColorEdit() ) do_col = true; if( brush.PositionEdit() != Brush::OFF ) update_vert_norm_ = true; if( do_col ) update_color_ = true; } } else if( event->state & GDK_BUTTON2_MASK ) { location_.SetX( location_.X() + x - last_mouse_location_.X() ); location_.SetY( location_.Y() + y - last_mouse_location_.Y() ); } else if( event->state & GDK_BUTTON3_MASK ) { double xnorm = last_mouse_location_.X(); double ynorm = last_mouse_location_.Y(); ArcBall::NormalizeCoordinates( xnorm, ynorm, get_width(), get_height() ); arc_ball_.Drag( xnorm, ynorm ); rendered_ = false; } last_mouse_location_ = Math::Point2D< int >( x, y ); queue_draw(); return true; } bool Editor::on_button_release_event( GdkEventButton* e ) { get_window()->pointer_ungrab( e->time ); if( e->button == 3 ) { recalc_all_projected_ = true; update_depth_storage_ = true; rendered_ = false; } return true; } bool Editor::on_scroll_event( GdkEventScroll* e ) { Gtk::Widget::on_scroll_event( e ); const float zoom_delta( 0.1 ); if( e->direction == GDK_SCROLL_UP ) zoom_ -= zoom_delta; else if( e->direction == GDK_SCROLL_DOWN ) zoom_ += zoom_delta; if( zoom_ < 0.1 ) zoom_ = 0.1; queue_draw(); rendered_ = false; recalc_all_projected_ = true; update_depth_storage_ = true; return true; } void Editor::on_realize() { Gtk::DrawingArea::on_realize(); Glib::RefPtr glwindow = get_gl_window(); if (!glwindow->gl_begin(get_gl_context())) return; glEnable( GL_BLEND ); glEnable( GL_COLOR_MATERIAL ); glEnable( GL_DEPTH_TEST ); // Enable lighting glEnable( GL_LIGHTING ); glEnable( GL_LIGHT0 ); const float ambient[] = { 0.15, 0.15, 0.15, 1 }; const float diffuse[] = { 0.6, 0.6, 0.6, 1 }; const float specular[] = { 0, 0, 0, 0 }; const float position[] = { 0, 0, -1, 0 }; glLightfv( GL_LIGHT0, GL_AMBIENT, ambient ); glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse ); glLightfv( GL_LIGHT0, GL_SPECULAR, specular ); glLightfv( GL_LIGHT0, GL_POSITION, position ); cursor_.Update(); set_viewport_(); DisplayOptions().SetGraphicsMode(); get_window()->set_cursor( Gdk::Cursor( Gdk::CROSSHAIR ) ); glwindow->gl_end(); } bool Editor::on_configure_event( GdkEventConfigure* event ) { if ( !get_gl_window()->gl_begin( get_gl_context() ) ) return false; set_viewport_(); get_gl_window()->gl_end(); return true; } bool Editor::on_expose_event( GdkEventExpose* event ) { redraw_complete_ = true; static bool rerender( true ); Glib::RefPtr< Gdk::GL::Window > glwindow = get_gl_window(); if (!get_gl_window()->gl_begin(get_gl_context())) return false; set_viewport_(); orientate_(); glEnable( GL_LIGHTING ); if( DisplayOptions().Smoothed() ) glShadeModel( GL_SMOOTH ); else glShadeModel( GL_FLAT ); if( DisplayOptions().DoubleSided() ) { glDisable( GL_CULL_FACE ); glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE ); } else { glEnable( GL_CULL_FACE ); glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE ); } ////////////// const Color& c( DisplayOptions().BackgroundColor() ); glClearColor( c.Red, c.Green, c.Blue, 1 ); Math::Rect2D< int > bounds( selection_boundary_() ); Mesh& mesh( MeshHistory::Instance().GetCurrentMesh() ); if( rendered_ && display_options_.Speed() && mesh.ProjectedLocations().size() == mesh.VertexLocations().size() ) { std::vector< char > proc_triangles( mesh.Triangles().size(), false ); std::vector< char > proc_quads( mesh.Quads().size(), false ); const unsigned brdr( 1 ); glScissor( bounds.X() + brdr, get_height() - bounds.Bottom() + brdr, bounds.W() - brdr * 2, bounds.H() - brdr * 2 ); glEnable( GL_SCISSOR_TEST ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glDisable( GL_SCISSOR_TEST ); if( DisplayOptions().Wireframe() ) { glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1, 1 ); } // Draw speed triangles glBegin( GL_TRIANGLES ); for( unsigned i( 0 ); i < mesh.ProjectedLocations().size(); ++i ) { const Point3D& proj( mesh.ProjectedLocations()[ i ] ); if( bounds.Contains( Math::Point2D< int >( lroundf( proj.X() ), lroundf( get_height() - proj.Y() ) ) ) ) { for( unsigned j = 0; j < mesh.VertexUsers()[ i ].Triangles.size(); ++j ) { const unsigned triangle( mesh.VertexUsers()[ i ].Triangles[ j ] ); if( !proc_triangles[ triangle ] ) { for( unsigned j = 0; j <= 2; j++ ) { glColor3fv( &mesh.VertexColors()[ mesh.Triangles()[ triangle ].V[ j ] ].Red ); glNormal3fv( &mesh.VertexNormals()[ mesh.Triangles()[ triangle ].V[ j ] ].X() ); glVertex3fv( &mesh.VertexLocations()[ mesh.Triangles()[ triangle ].V[ j ] ].X() ); } proc_triangles[ triangle ] = true; } } } } glEnd(); // Draw speed quads glBegin( GL_QUADS ); for( unsigned i( 0 ); i < mesh.ProjectedLocations().size(); ++i ) { const Point3D& proj( mesh.ProjectedLocations()[ i ] ); if( bounds.Contains( Math::Point2D< int >( lroundf( proj.X() ), lroundf( get_height() - proj.Y() ) ) ) ) { for( unsigned j = 0; j < mesh.VertexUsers()[ i ].Quads.size(); ++j ) { const unsigned quad( mesh.VertexUsers()[ i ].Quads[ j ] ); if( !proc_quads[ quad ] ) { for( unsigned j = 0; j <= 3; j++ ) { glColor3fv( &mesh.VertexColors()[ mesh.Quads()[ quad ].V[ j ] ].Red ); glNormal3fv( &mesh.VertexNormals()[ mesh.Quads()[ quad ].V[ j ] ].X() ); glVertex3fv( &mesh.VertexLocations()[ mesh.Quads()[ quad ].V[ j ] ].X() ); } proc_quads[ quad ] = true; } } } } glEnd(); if( DisplayOptions().Wireframe() ) { glDisable( GL_POLYGON_OFFSET_FILL ); glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); glBlendFunc( GL_ZERO, GL_ZERO ); // Redraw triangles with lines glBegin( GL_TRIANGLES ); for( unsigned i = 0; i < proc_triangles.size(); ++i ) { if( proc_triangles[ i ] ) { for( unsigned j = 0; j <= 2; j++ ) { glColor3fv( &mesh.VertexColors()[ mesh.Triangles()[ i ].V[ j ] ].Red ); glNormal3fv( &mesh.VertexNormals()[ mesh.Triangles()[ i ].V[ j ] ].X() ); glVertex3fv( &mesh.VertexLocations()[ mesh.Triangles()[ i ].V[ j ] ].X() ); } } } glEnd(); // Redraw quads with lines glBegin( GL_QUADS ); for( unsigned i = 0; i < proc_quads.size(); ++i ) { if( proc_quads[ i ] ) { for( unsigned j = 0; j <= 3; j++ ) { glColor3fv( &mesh.VertexColors()[ mesh.Quads()[ i ].V[ j ] ].Red ); glNormal3fv( &mesh.VertexNormals()[ mesh.Quads()[ i ].V[ j ] ].X() ); glVertex3fv( &mesh.VertexLocations()[ mesh.Quads()[ i ].V[ j ] ].X() ); } } } glEnd(); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } rerender = false; } else { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); load_data_(); draw_model_(); rendered_ = true; rerender = true; } ////////////// //draw_model_(); project_points_(); glDisable( GL_LIGHTING ); // Store depth buffer if( Preferences::Instance().Get( "STORE_DEPTH", true ) && update_depth_storage_ ) { depth_storage_.resize( get_width() * get_height() ); glReadPixels( 0, 0, get_width(), get_height(), GL_DEPTH_COMPONENT, GL_FLOAT, &depth_storage_[ 0 ] ); update_depth_storage_ = false; //Mesh::SetDepthStorage = &depth_storage_; } // Return to normalcy glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluOrtho2D( 0, get_width(), get_height(), 0 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); #if 0 // DEBUG draw update boundary glColor4f( 1, 0, 0, 0.2 ); glBegin( GL_LINE_LOOP ); glVertex2f( bounds.X(), bounds.Y() ); glVertex2f( bounds.Right(), bounds.Y() ); glVertex2f( bounds.Right(), bounds.Bottom() ); glVertex2f( bounds.X(), bounds.Bottom() ); glEnd(); #endif cursor_.Update(); const int x( mouse_loc_.get_x() ), y( mouse_loc_.get_y() ); if( x > 0 && x < get_width() && y > 0 && y < get_height() ) cursor_.Draw( x, y ); else cursor_.Draw( get_width() / 2, get_height() / 2 ); // Draw symmetry-mode markers glLoadIdentity(); glBegin( GL_TRIANGLES ); { for( std::map< std::string, Math::Point2D< int > >::iterator i = symm_locs_.begin(); i != symm_locs_.end(); ++i ) { const Math::Point2D< float > sloc( i->second.X(), get_height() - i->second.Y() ); glColor3f( 0, 0, 0 ); glVertex2f( sloc.X(), sloc.Y() + 3 ); glColor3f( 1, 1, 1 ); glVertex2f( sloc.X() + 3, sloc.Y() - 3 ); glColor3f( 1, 0, 0 ); glVertex2f( sloc.X() - 3, sloc.Y() - 3 ); } } glEnd(); // Swap buffers. if (glwindow->is_double_buffered()) glwindow->swap_buffers(); else glFlush(); get_gl_window()->gl_end(); return true; } void Editor::on_size_allocate( Gtk::Allocation& a ) { DrawingArea::on_size_allocate( a ); Refresh( false, true ); } void Editor::on_entire_section_changed_( const bool vn, const bool c, const bool p ) { queue_draw(); if( vn ) update_vert_norm_ = true; if( c ) update_color_ = true; if( p ) { update_polygons_ = true; recalc_all_projected_ = true; } rendered_ = false; } void Editor::load_data_() { Mesh& mesh( MeshHistory::Instance().GetCurrentMesh() ); DisplayOptions().Output().Set( mesh, update_vert_norm_, update_color_, update_polygons_ ); update_vert_norm_ = false; update_color_ = false; update_polygons_ = false; } void Editor::draw_model_() { if( DisplayOptions().Wireframe() ) { glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1, 1 ); DisplayOptions().Output().Draw(); glDisable( GL_POLYGON_OFFSET_FILL ); glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); glBlendFunc( GL_ZERO, GL_ZERO ); DisplayOptions().Output().Draw(); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); } else DisplayOptions().Output().Draw(); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); /*{ glColor3f( 1, 1, 1 ); Mesh& mesh( MeshHistory::Instance().GetCurrentMesh() ); glBegin( GL_QUADS ); for( unsigned i = 0; i < mesh.VisibleElements().Quads; i++ ) { for( unsigned j = 0; j <= 3; j++ ) { glNormal3fv( &mesh.VertexNormals() [ mesh.Quads()[ i ] .V[ j ] ].X() ); glVertex3fv( &mesh.VertexLocations() [ mesh.Quads()[ i ] .V[ j ] ].X() ); } } glEnd(); }*/ } SharpConstruct::Math::Rect2D< int > Editor::selection_boundary_() const { static Math::Rect2D< int > previous( 0, 0, 0, 0 ); const Brush& brush( MeshHistory::Instance(). GetCurrentMesh().CurrentBrush() ); const unsigned radius( std::max( lroundf( brush.Radius() ), 10L ) ); // Find BBox around cur. mouse loc const int x( mouse_loc_.get_x() ), y( mouse_loc_.get_y() ); Math::Rect2D< int > cur( 0, 0, radius * 2, radius * 2 ); if( x > 0 && x < get_width() && y > 0 && y < get_height() ) { cur.SetX( mouse_loc_.get_x() - radius ); cur.SetY( mouse_loc_.get_y() - radius ); } else { cur.SetX( get_width() / 2 - radius ); cur.SetY( get_height() / 2 - radius ); } for( std::map< std::string, Math::Point2D< int > >::const_iterator i = symm_locs_.begin(); i != symm_locs_.end(); ++i ) { const Math::Point2D< int >& loc( i->second ); const Math::Rect2D< int > symm( loc.X() - radius, get_height() - loc.Y() - radius, radius * 2, radius * 2 ); cur = Math::BoundingRect( cur, symm ); } /*if( dirty_.W() ) { Math::Rect2D< int > d_upside_down( MainWindow::Instance().Dirty() ); d_upside_down.SetY( H() - ( d_upside_down.Y() - Y() ) + ( MainWindow::Instance().H() - Bottom() ) ); cur = Math::BoundingRect( cur, d_upside_down ); }*/ if( previous.W() == 0 ) previous = cur; Math::Rect2D< int > final( std::min( cur.X(), previous.X() ) - 2, std::min( cur.Y(), previous.Y() ) - 2, cur.W(), cur.H() ); final.AnchoredSetRight( std::max( cur.Right(), previous.Right() ) + 2 ); final.AnchoredSetBottom( std::max( cur.Bottom(), previous.Bottom() ) + 2 ); previous = cur; return final; } void Editor::set_viewport_() { glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glViewport( 0, 0, get_width(), get_height() ); float ratio( get_width() ); if( get_height() ) ratio /= get_height(); gluPerspective( 45 * zoom_, ratio, 0.1f, 50 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); } void Editor::orientate_() { Mesh& mesh( MeshHistory::Instance().GetCurrentMesh() ); double trackball_matrix[ 16 ]; unsigned col, row, index; glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); gluLookAt( 0, 0, mesh.Diameter() * 2, mesh.Center().X(), mesh.Center().Y(), mesh.Center().Z(), 0, 1, 0 ); const ArcBall::Transform* rot; if( mouse_state_ & Gdk::SHIFT_MASK && mouse_state_ & Gdk::BUTTON3_MASK ) rot = &arc_ball_.SnapRotation( arc_ball_.Transformation(), 45 ); else rot = &arc_ball_.Transformation(); for( col = 0, index = 0; col < 4; col++ ) { for( row = 0; row < 4; row++ ) trackball_matrix[ index++ ] = ( *rot )[ row ][ col ]; } glMultMatrixd( trackball_matrix ); const float amt( 100.0f ); glTranslatef( mesh.RightNormal().X() * ( location_.X() / amt ), mesh.RightNormal().Y() * ( location_.X() / amt ) , mesh.RightNormal().Z() * ( location_.X() / amt ) ); glTranslatef( mesh.UpNormal().X() * ( location_.Y() / amt ), mesh.UpNormal().Y() * ( location_.Y() / amt ), mesh.UpNormal().Z() * ( location_.Y() / amt ) ); } Point3D Editor::project_( const Point3D& in ) const { double x, y, z; gluProject( in.X(), in.Y(), in.Z(), model_matrix_, projection_matrix_, viewport_, &x, &y, &z ); return Point3D( x, y, z ); } SharpConstruct::Math::Point2D< int > Editor::project_2d_( const Point3D& in ) const { double x, y, z; gluProject( in.X(), in.Y(), in.Z(), model_matrix_, projection_matrix_, viewport_, &x, &y, &z ); return Math::Point2D< int >( lround( x ), lround( y ) ); } Point3D Editor::unproject_( const Optimized::Point3D& in ) const { double x, y, z; gluUnProject( in.X(), in.Y(), in.Z(), model_matrix_, projection_matrix_, viewport_, &x, &y, &z ); return Point3D( x, y, z ); } void Editor::project_points_() { Mesh& mesh = MeshHistory::Instance().GetCurrentMesh(); const Brush& brush( mesh.CurrentBrush() ); const unsigned radius( brush.Radius() ); glGetDoublev( GL_PROJECTION_MATRIX, projection_matrix_ ); glGetIntegerv( GL_VIEWPORT , viewport_ ); glGetDoublev( GL_MODELVIEW_MATRIX, model_matrix_ ); float depth; const int mx( mouse_loc_.get_x() ), my( mouse_loc_.get_y() ); const Math::Point2D< int > msloc( mx, my ); const Math::Point2D< int > mloc( mx, get_height() - my ); const unsigned floc = ( get_height() - msloc.Y() ) * get_width() + msloc.X(); if( Preferences::Instance().Get( "STORE_DEPTH", true ) ) { if( static_cast< int >( depth_storage_.size() ) < get_width() * get_height() || floc < 0 || floc >= depth_storage_.size() ) return; depth = depth_storage_[ floc ]; } else glReadPixels( mloc.X(), mloc.Y(), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth ); //prev_brush_loc_ = Mesh::CurrentSelection(); Mesh::CurrentSelection() = unproject_( Point3D( mloc.X(), mloc.Y(), depth ) ); // Find Mesh::_selection_radius Mesh::SetSelectionRadius( Mesh::CurrentSelection().Distance( unproject_( Point3D( mloc.X() + radius, mloc.Y(), depth ) ) ) ); project_normals_(); /*// Find the hide box, if needed Math::Rect2D< int > hider( 0, 0, abs( hide_corner_2_.X() - hide_corner_1_.X() ) + 2, abs( hide_corner_2_.Y() - hide_corner_1_.Y() ) + 2 ); if( hider.get_width() > 3 || hider.get_height() > 3 ) { hider.SetX( std::min( hide_corner_1_.X(), hide_corner_2_.X() ) ); hider.SetY( std::max( hide_corner_1_.Y(), hide_corner_2_.Y() ) ); hider.SetX( hider.X() - X() ); hider.SetY( get_height() - ( hider.Y() - Y() ) + ( MainWindow::Instance().get_height() - Bottom() ) ); hc_proj_[ 0 ] = unproject_( Point3D( hider.X(), hider.Y(), 1 ) ); hc_proj_[ 1 ] = unproject_( Point3D( hider.Right(), hider.Y(), 1 ) ); hc_proj_[ 2 ] = unproject_( Point3D( hider.Right(), hider.Bottom(), 1 ) ); hc_proj_[ 3 ] = unproject_( Point3D( hider.X(), hider.Bottom(), 1 ) ); hc_proj_[ 4 ] = unproject_( Point3D( hider.X(), hider.Y(), 0 ) ); hc_proj_[ 5 ] = unproject_( Point3D( hider.Right(), hider.Bottom(), 0 ) ); }*/ // Find symmetry points symm_locs_.clear(); const Point3D cs( Mesh::CurrentSelection() ); if( brush.Symmetry().X() ) symm_locs_[ "X" ] = project_2d_( Point3D( -cs.X(), cs.Y(), cs.Z() ) ); if( brush.Symmetry().Y() ) symm_locs_[ "Y" ] = project_2d_( Point3D( cs.X(), -cs.Y(), cs.Z() ) ); if( brush.Symmetry().Z() ) symm_locs_[ "Z" ] = project_2d_( Point3D( cs.X(), cs.Y(), -cs.Z() ) ); if( brush.Symmetry().X() && brush.Symmetry().Y() ) symm_locs_[ "XY" ] = project_2d_( Point3D( -cs.X(), -cs.Y(), cs.Z() ) ); if( brush.Symmetry().X() && brush.Symmetry().Z() ) symm_locs_[ "XZ" ] = project_2d_( Point3D( -cs.X(), cs.Y(), -cs.Z() ) ); if( brush.Symmetry().Y() && brush.Symmetry().Z() ) symm_locs_[ "YZ" ] = project_2d_( Point3D( cs.X(), -cs.Y(), -cs.Z() ) ); if( brush.Symmetry().X() && brush.Symmetry().Y() && brush.Symmetry().Z() ) symm_locs_[ "XYZ" ] = project_2d_( Point3D( -cs.X(), -cs.Y(), -cs.Z() ) ); { /*Optimized::Point3DVector& v( mesh._selection_tube ); v.resize( 6 ); const Math::Rect2D< int > bounds( selection_boundary_() ); v[ 0 ] = unproject_( Point3D( bounds.X(), bounds.Y(), 1 ) ); v[ 1 ] = unproject_( Point3D( bounds.Right(), bounds.Y(), 1 ) ); v[ 2 ] = unproject_( Point3D( bounds.Right(), bounds.Bottom(), 1 ) ); v[ 3 ] = unproject_( Point3D( bounds.X(), bounds.Bottom(), 1 ) ); v[ 4 ] = unproject_( Point3D( bounds.X(), bounds.Y(), 0 ) ); v[ 5 ] = unproject_( Point3D( bounds.Right(), bounds.Bottom(), 0 ) );*/ } // This code finds and stores the projected 2D locations // of all the mesh's vertices. Rather slow. if( DisplayOptions().Speed() ) { if( recalc_all_projected_ || mesh.ProjectedLocations().size() != mesh.VertexLocations().size() ) { mesh.ProjectedLocations().resize( mesh.VertexLocations().size() ); for( unsigned i = 0; i < mesh.VertexLocations().size(); i++ ) { mesh.ProjectedLocations()[ i ] = project_( Point3D( mesh.VertexLocations()[ i ].X(), mesh.VertexLocations()[ i ].Y(), mesh.VertexLocations()[ i ].Z() ) ); } recalc_all_projected_ = false; } else { if( mesh.ModifiedVertexIndices().size() ) { for( unsigned i = 0; i < mesh.ModifiedVertexIndices().size(); i++ ) { const unsigned ndx( mesh.ModifiedVertexIndices()[ i ] ); mesh.ProjectedLocations()[ ndx ] = project_( Point3D( mesh.VertexLocations()[ ndx ].X(), mesh.VertexLocations()[ ndx ].Y(), mesh.VertexLocations()[ ndx ].Z() ) ); } } } } } void Editor::project_normals_() { GLdouble model_matrix[ 16 ]; double projected_x, projected_y, projected_z; double zero_loc[ 3 ]; glGetDoublev( GL_MODELVIEW_MATRIX, model_matrix ); gluUnProject( 0, 0, 0, model_matrix, projection_matrix_, viewport_, &zero_loc[ 0 ], &zero_loc[ 1 ], &zero_loc[ 2 ] ); // Update Mesh::RightNormal() gluUnProject( 1, 0, 0, model_matrix, projection_matrix_, viewport_, &projected_x, &projected_y, &projected_z ); Mesh::RightNormal() = Optimized::Normal3D( projected_x, projected_y, projected_z ); Mesh::RightNormal().X() -= zero_loc[ 0 ]; Mesh::RightNormal().Y() -= zero_loc[ 1 ]; Mesh::RightNormal().Z() -= zero_loc[ 2 ]; Normalize( Mesh::RightNormal() ); // Update Mesh::UpNormal() gluUnProject( 0, -1, 0, model_matrix, projection_matrix_, viewport_, &projected_x, &projected_y, &projected_z ); Mesh::UpNormal() = Optimized::Normal3D( projected_x, projected_y, projected_z ); Mesh::UpNormal().X() -= zero_loc[ 0 ]; Mesh::UpNormal().Y() -= zero_loc[ 1 ]; Mesh::UpNormal().Z() -= zero_loc[ 2 ]; Normalize( Mesh::UpNormal() ); // Update Mesh::ZNormal() gluUnProject( 0, 0, 1, model_matrix, projection_matrix_, viewport_, &projected_x, &projected_y, &projected_z ); Mesh::ZNormal() = Optimized::Normal3D( projected_x, projected_y, projected_z ); Mesh::ZNormal().X() -= zero_loc[ 0 ]; Mesh::ZNormal().Y() -= zero_loc[ 1 ]; Mesh::ZNormal().Z() -= zero_loc[ 2 ]; Normalize( Mesh::ZNormal() ); } void Editor::update_mouse_() { int mx, my; Gdk::ModifierType mask; get_window()->get_pointer( mx, my, mask ); mouse_loc_.set_x( mx ); mouse_loc_.set_y( my ); mouse_state_ = mask; }