]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/selection.cpp
Merge commit '87f4eea939309a5ea1972323e237d23afdf01104' into master-merge
[xonotic/netradiant.git] / radiant / selection.cpp
index df18daa9c5ef23a908cc40e83b84b62eb09617c9..e8d16c95be6f031f7fb22d5144f4f545b5b21a9e 100644 (file)
@@ -328,6 +328,8 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const
 }
 };
 
+void GetSelectionAABB( AABB& bounds );
+const Matrix4& ssGetPivot2World();
 
 class Scalable
 {
@@ -343,14 +345,27 @@ private:
 Vector3 m_start;
 Vector3 m_axis;
 Scalable& m_scalable;
+
+Vector3 m_choosen_extent;
+
 public:
 ScaleAxis( Scalable& scalable )
        : m_scalable( scalable ){
 }
 void Construct( const Matrix4& device2manip, const float x, const float y ){
        point_on_axis( m_start, m_axis, device2manip, x, y );
+
+       AABB aabb;
+       GetSelectionAABB( aabb );
+       Vector3 transform_origin = vector4_to_vector3( ssGetPivot2World().t() );
+       m_choosen_extent = Vector3( std::max( aabb.origin[0] + aabb.extents[0] - transform_origin[0], - aabb.origin[0] + aabb.extents[0] + transform_origin[0] ),
+                                       std::max( aabb.origin[1] + aabb.extents[1] - transform_origin[1], - aabb.origin[1] + aabb.extents[1] + transform_origin[1] ),
+                                       std::max( aabb.origin[2] + aabb.extents[2] - transform_origin[2], - aabb.origin[2] + aabb.extents[2] + transform_origin[2] )
+                                                       );
+
 }
 void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y ){
+       //globalOutputStream() << "manip2object: " << manip2object << "  device2manip: " << device2manip << "  x: " << x << "  y:" << y <<"\n";
        Vector3 current;
        point_on_axis( current, m_axis, device2manip, x, y );
        Vector3 delta = vector3_subtracted( current, m_start );
@@ -359,11 +374,19 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const
        vector3_snap( delta, GetSnapGridSize() );
 
        Vector3 start( vector3_snapped( m_start, GetSnapGridSize() ) );
+       //globalOutputStream() << "start: " << start << "   delta: " << delta <<"\n";
        Vector3 scale(
                start[0] == 0 ? 1 : 1 + delta[0] / start[0],
                start[1] == 0 ? 1 : 1 + delta[1] / start[1],
                start[2] == 0 ? 1 : 1 + delta[2] / start[2]
                );
+
+       for( std::size_t i = 0; i < 3; i++ ){
+               if( m_choosen_extent[i] > 0.0625 ){ //epsilon to prevent too high scale for set of models, having really small extent, formed by origins
+                       scale[i] = ( m_choosen_extent[i] + delta[i] ) / m_choosen_extent[i];
+               }
+       }
+
        m_scalable.scale( scale );
 }
 
@@ -377,12 +400,23 @@ class ScaleFree : public Manipulatable
 private:
 Vector3 m_start;
 Scalable& m_scalable;
+
+Vector3 m_choosen_extent;
+
 public:
 ScaleFree( Scalable& scalable )
        : m_scalable( scalable ){
 }
 void Construct( const Matrix4& device2manip, const float x, const float y ){
        point_on_plane( m_start, device2manip, x, y );
+
+       AABB aabb;
+       GetSelectionAABB( aabb );
+       Vector3 transform_origin = vector4_to_vector3( ssGetPivot2World().t() );
+       m_choosen_extent = Vector3( std::max( aabb.origin[0] + aabb.extents[0] - transform_origin[0], - aabb.origin[0] + aabb.extents[0] + transform_origin[0] ),
+                                       std::max( aabb.origin[1] + aabb.extents[1] - transform_origin[1], - aabb.origin[1] + aabb.extents[1] + transform_origin[1] ),
+                                       std::max( aabb.origin[2] + aabb.extents[2] - transform_origin[2], - aabb.origin[2] + aabb.extents[2] + transform_origin[2] )
+                                                       );
 }
 void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y ){
        Vector3 current;
@@ -398,6 +432,13 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const
                start[1] == 0 ? 1 : 1 + delta[1] / start[1],
                start[2] == 0 ? 1 : 1 + delta[2] / start[2]
                );
+
+       for( std::size_t i = 0; i < 3; i++ ){
+               if( m_choosen_extent[i] > 0.0625 ){
+                       scale[i] = ( m_choosen_extent[i] + delta[i] ) / m_choosen_extent[i];
+               }
+       }
+
        m_scalable.scale( scale );
 }
 };
@@ -2536,6 +2577,7 @@ Rotation m_rotation;
 Scale m_scale;
 public:
 static Shader* m_state;
+bool m_bPreferPointEntsIn2D;
 private:
 EManipulatorMode m_manipulator_mode;
 Manipulator* m_manipulator;
@@ -2561,8 +2603,13 @@ selection_t m_component_selection;
 Signal1<const Selectable&> m_selectionChanged_callbacks;
 
 void ConstructPivot() const;
+void setCustomPivotOrigin( Vector3& point ) const;
+public:
+void getSelectionAABB( AABB& bounds ) const;
+private:
 mutable bool m_pivotChanged;
 bool m_pivot_moving;
+mutable bool m_pivotIsCustom;
 
 void Scene_TestSelect( Selector& selector, SelectionTest& test, const View& view, SelectionSystem::EMode mode, SelectionSystem::EComponentMode componentMode );
 
@@ -2579,9 +2626,12 @@ enum EModifier
        eToggle,
        eReplace,
        eCycle,
+       eSelect,
+       eDeselect,
 };
 
 RadiantSelectionSystem() :
+       m_bPreferPointEntsIn2D( true ),
        m_undo_begun( false ),
        m_mode( ePrimitive ),
        m_componentmode( eDefault ),
@@ -2591,7 +2641,8 @@ RadiantSelectionSystem() :
        m_rotate_manipulator( *this, 8, 64 ),
        m_scale_manipulator( *this, 0, 64 ),
        m_pivotChanged( false ),
-       m_pivot_moving( false ){
+       m_pivot_moving( false ),
+       m_pivotIsCustom( false ){
        SetManipulatorMode( eTranslate );
        pivotChanged();
        addSelectionChangeCallback( PivotChangedSelectionCaller( *this ) );
@@ -2623,6 +2674,7 @@ EComponentMode ComponentMode() const {
        return m_componentmode;
 }
 void SetManipulatorMode( EManipulatorMode mode ){
+       m_pivotIsCustom = false;
        m_manipulator_mode = mode;
        switch ( m_manipulator_mode )
        {
@@ -2771,18 +2823,29 @@ void deselectAll(){
        }
 }
 
+void deselectComponentsOrAll( bool components ){
+       if ( components ) {
+               setSelectedAllComponents( false );
+       }
+       else
+       {
+               deselectAll();
+       }
+}
+
 void SelectPoint( const View& view, const float device_point[2], const float device_epsilon[2], RadiantSelectionSystem::EModifier modifier, bool face ){
+       //globalOutputStream() << device_point[0] << "   " << device_point[1] << "\n";
        ASSERT_MESSAGE( fabs( device_point[0] ) <= 1.0f && fabs( device_point[1] ) <= 1.0f, "point-selection error" );
+
        if ( modifier == eReplace ) {
-               if ( face ) {
-                       setSelectedAllComponents( false );
-               }
-               else
-               {
-                       deselectAll();
-               }
+               deselectComponentsOrAll( face );
        }
-
+/*
+//nothingSelected() doesn't consider faces, selected in non-component mode, m
+       if ( modifier == eCycle && nothingSelected() ){
+               modifier = eReplace;
+       }
+*/
   #if defined ( DEBUG_SELECTION )
        g_render_clipped.destroy();
   #endif
@@ -2793,52 +2856,61 @@ void SelectPoint( const View& view, const float device_point[2], const float dev
 
                SelectionVolume volume( scissored );
                SelectionPool selector;
-               if ( face ) {
-                       Scene_TestSelect_Component( selector, volume, scissored, eFace );
-               }
-               else
-               {
-                       Scene_TestSelect( selector, volume, scissored, Mode(), ComponentMode() );
-               }
+               SelectionPool selector_point_ents;
+               const bool prefer_point_ents = m_bPreferPointEntsIn2D && Mode() == ePrimitive && !view.fill() && !face
+                       && ( modifier == RadiantSelectionSystem::eReplace || modifier == RadiantSelectionSystem::eSelect || modifier == RadiantSelectionSystem::eDeselect );
 
-               if ( !selector.failed() ) {
+               if( prefer_point_ents ){
+                       Scene_TestSelect( selector_point_ents, volume, scissored, eEntity, ComponentMode() );
+               }
+               if( prefer_point_ents && !selector_point_ents.failed() ){
                        switch ( modifier )
                        {
-                       case RadiantSelectionSystem::eToggle:
-                       {
-                               SelectableSortedSet::iterator best = selector.begin();
-                               // toggle selection of the object with least depth
-                               if ( ( *best ).second->isSelected() ) {
-                                       ( *best ).second->setSelected( false );
-                               }
-                               else{
-                                       ( *best ).second->setSelected( true );
-                               }
-                       }
-                       break;
                        // if cycle mode not enabled, enable it
                        case RadiantSelectionSystem::eReplace:
                        {
                                // select closest
-                               ( *selector.begin() ).second->setSelected( true );
+                               ( *selector_point_ents.begin() ).second->setSelected( true );
                        }
                        break;
-                       // select the next object in the list from the one already selected
-                       case RadiantSelectionSystem::eCycle:
+                       case RadiantSelectionSystem::eSelect:
                        {
-                               SelectionPool::iterator i = selector.begin();
-                               while ( i != selector.end() )
+                               SelectionPool::iterator best = selector_point_ents.begin();
+                               if( !( *best ).second->isSelected() ){
+                                       ( *best ).second->setSelected( true );
+                               }
+                               SelectionPool::iterator i = best;
+                               ++i;
+                               while ( i != selector_point_ents.end() )
                                {
-                                       if ( ( *i ).second->isSelected() ) {
-                                               ( *i ).second->setSelected( false );
-                                               ++i;
-                                               if ( i != selector.end() ) {
-                                                       i->second->setSelected( true );
+                                       if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
+                                               if( !( *i ).second->isSelected() ){
+                                                       ( *i ).second->setSelected( true );
                                                }
-                                               else
-                                               {
-                                                       selector.begin()->second->setSelected( true );
+                                       }
+                                       else{
+                                               break;
+                                       }
+                                       ++i;
+                               }
+                       }
+                       break;
+                       case RadiantSelectionSystem::eDeselect:
+                       {
+                               SelectionPool::iterator best = selector_point_ents.begin();
+                               if( ( *best ).second->isSelected() ){
+                                       ( *best ).second->setSelected( false );
+                               }
+                               SelectionPool::iterator i = best;
+                               ++i;
+                               while ( i != selector_point_ents.end() )
+                               {
+                                       if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
+                                               if( ( *i ).second->isSelected() ){
+                                                       ( *i ).second->setSelected( false );
                                                }
+                                       }
+                                       else{
                                                break;
                                        }
                                        ++i;
@@ -2849,19 +2921,191 @@ void SelectPoint( const View& view, const float device_point[2], const float dev
                                break;
                        }
                }
+               else{
+                       if ( face ){
+                               Scene_TestSelect_Component( selector, volume, scissored, eFace );
+                       }
+                       else{
+                               Scene_TestSelect( selector, volume, scissored, Mode(), ComponentMode() );
+                       }
+
+                       if ( !selector.failed() ) {
+                               switch ( modifier )
+                               {
+                               case RadiantSelectionSystem::eToggle:
+                               {
+                                       SelectableSortedSet::iterator best = selector.begin();
+                                       // toggle selection of the object with least depth
+                                       if ( ( *best ).second->isSelected() ) {
+                                               ( *best ).second->setSelected( false );
+                                       }
+                                       else{
+                                               ( *best ).second->setSelected( true );
+                                       }
+                               }
+                               break;
+                               // if cycle mode not enabled, enable it
+                               case RadiantSelectionSystem::eReplace:
+                               {
+                                       // select closest
+                                       ( *selector.begin() ).second->setSelected( true );
+                               }
+                               break;
+                               // select the next object in the list from the one already selected
+                               case RadiantSelectionSystem::eCycle:
+                               {
+                                       bool CycleSelectionOccured = false;
+                                       SelectionPool::iterator i = selector.begin();
+                                       while ( i != selector.end() )
+                                       {
+                                               if ( ( *i ).second->isSelected() ) {
+                                                       deselectComponentsOrAll( face );
+                                                       ++i;
+                                                       if ( i != selector.end() ) {
+                                                               i->second->setSelected( true );
+                                                       }
+                                                       else
+                                                       {
+                                                               selector.begin()->second->setSelected( true );
+                                                       }
+                                                       CycleSelectionOccured = true;
+                                                       break;
+                                               }
+                                               ++i;
+                                       }
+                                       if( !CycleSelectionOccured ){
+                                               deselectComponentsOrAll( face );
+                                               ( *selector.begin() ).second->setSelected( true );
+                                       }
+                               }
+                               break;
+                               case RadiantSelectionSystem::eSelect:
+                               {
+                                       SelectionPool::iterator best = selector.begin();
+                                       if( !( *best ).second->isSelected() ){
+                                               ( *best ).second->setSelected( true );
+                                       }
+                                       SelectionPool::iterator i = best;
+                                       ++i;
+                                       while ( i != selector.end() )
+                                       {
+                                               if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
+                                                       if( !( *i ).second->isSelected() ){
+                                                               ( *i ).second->setSelected( true );
+                                                       }
+                                               }
+                                               else{
+                                                       break;
+                                               }
+                                               ++i;
+                                       }
+                               }
+                               break;
+                               case RadiantSelectionSystem::eDeselect:
+                               {
+                                       SelectionPool::iterator best = selector.begin();
+                                       if( ( *best ).second->isSelected() ){
+                                               ( *best ).second->setSelected( false );
+                                       }
+                                       SelectionPool::iterator i = best;
+                                       ++i;
+                                       while ( i != selector.end() )
+                                       {
+                                               if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
+                                                       if( ( *i ).second->isSelected() ){
+                                                               ( *i ).second->setSelected( false );
+                                                       }
+                                               }
+                                               else{
+                                                       break;
+                                               }
+                                               ++i;
+                                       }
+                               }
+                               break;
+                               default:
+                                       break;
+                               }
+                       }
+                       else if( modifier == eCycle ){
+                               deselectComponentsOrAll( face );
+                       }
+               }
        }
 }
 
-void SelectArea( const View& view, const float device_point[2], const float device_delta[2], RadiantSelectionSystem::EModifier modifier, bool face ){
-       if ( modifier == eReplace ) {
-               if ( face ) {
-                       setSelectedAllComponents( false );
+bool SelectPoint_InitPaint( const View& view, const float device_point[2], const float device_epsilon[2], bool face ){
+       ASSERT_MESSAGE( fabs( device_point[0] ) <= 1.0f && fabs( device_point[1] ) <= 1.0f, "point-selection error" );
+  #if defined ( DEBUG_SELECTION )
+       g_render_clipped.destroy();
+  #endif
+
+       {
+               View scissored( view );
+               ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, device_epsilon ) );
+
+               SelectionVolume volume( scissored );
+               SelectionPool selector;
+               SelectionPool selector_point_ents;
+               const bool prefer_point_ents = m_bPreferPointEntsIn2D && Mode() == ePrimitive && !view.fill() && !face;
+
+               if( prefer_point_ents ){
+                       Scene_TestSelect( selector_point_ents, volume, scissored, eEntity, ComponentMode() );
                }
-               else
-               {
-                       deselectAll();
+               if( prefer_point_ents && !selector_point_ents.failed() ){
+                       SelectableSortedSet::iterator best = selector_point_ents.begin();
+                       const bool wasSelected = ( *best ).second->isSelected();
+                       ( *best ).second->setSelected( !wasSelected );
+                       SelectableSortedSet::iterator i = best;
+                       ++i;
+                       while ( i != selector_point_ents.end() )
+                       {
+                               if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
+                                       ( *i ).second->setSelected( !wasSelected );
+                               }
+                               else{
+                                       break;
+                               }
+                               ++i;
+                       }
+                       return !wasSelected;
+               }
+               else{//do primitives, if ents failed
+                       if ( face ){
+                               Scene_TestSelect_Component( selector, volume, scissored, eFace );
+                       }
+                       else{
+                               Scene_TestSelect( selector, volume, scissored, Mode(), ComponentMode() );
+                       }
+                       if ( !selector.failed() ){
+                               SelectableSortedSet::iterator best = selector.begin();
+                               const bool wasSelected = ( *best ).second->isSelected();
+                               ( *best ).second->setSelected( !wasSelected );
+                               SelectableSortedSet::iterator i = best;
+                               ++i;
+                               while ( i != selector.end() )
+                               {
+                                       if( ( *i ).first.equalEpsilon( ( *best ).first, 0.25f, 0.000001f ) ){
+                                               ( *i ).second->setSelected( !wasSelected );
+                                       }
+                                       else{
+                                               break;
+                                       }
+                                       ++i;
+                               }
+                               return !wasSelected;
+                       }
+                       else{
+                               return true;
+                       }
                }
        }
+}
+
+void SelectArea( const View& view, const float device_point[2], const float device_delta[2], RadiantSelectionSystem::EModifier modifier, bool face ){
+       if ( modifier == eReplace ) {
+               deselectComponentsOrAll( face );
+       }
 
   #if defined ( DEBUG_SELECTION )
        g_render_clipped.destroy();
@@ -2955,7 +3199,12 @@ void outputScale( TextOutputStream& ostream ){
        ostream << " -scale " << m_scale.x() << " " << m_scale.y() << " " << m_scale.z();
 }
 
-void rotateSelected( const Quaternion& rotation ){
+void rotateSelected( const Quaternion& rotation, bool snapOrigin ){
+       if( snapOrigin && !m_pivotIsCustom ){
+               m_pivot2world.tx() = float_snapped( m_pivot2world.tx(), GetSnapGridSize() );
+               m_pivot2world.ty() = float_snapped( m_pivot2world.ty(), GetSnapGridSize() );
+               m_pivot2world.tz() = float_snapped( m_pivot2world.tz(), GetSnapGridSize() );
+       }
        startMove();
        rotate( rotation );
        freezeTransforms();
@@ -3037,7 +3286,7 @@ inline RadiantSelectionSystem& getSelectionSystem(){
 }
 }
 
-
+#include "map.h"
 
 class testselect_entity_visible : public scene::Graph::Walker
 {
@@ -3048,6 +3297,10 @@ testselect_entity_visible( Selector& selector, SelectionTest& test )
        : m_selector( selector ), m_test( test ){
 }
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       if( path.top().get_pointer() == Map_GetWorldspawn( g_map ) ||
+               node_is_group( path.top().get() ) ){
+               return false;
+       }
        Selectable* selectable = Instance_getSelectable( instance );
        if ( selectable != 0
                 && Node_isEntity( path.top() ) ) {
@@ -3315,7 +3568,7 @@ inline void pivot_for_node( Matrix4& pivot, scene::Node& node, scene::Instance&
 #endif
 
 void RadiantSelectionSystem::ConstructPivot() const {
-       if ( !m_pivotChanged || m_pivot_moving ) {
+       if ( !m_pivotChanged || m_pivot_moving || m_pivotIsCustom ) {
                return;
        }
        m_pivotChanged = false;
@@ -3335,7 +3588,8 @@ void RadiantSelectionSystem::ConstructPivot() const {
                        m_object_pivot = bounds.origin;
                }
 
-               vector3_snap( m_object_pivot, GetSnapGridSize() );
+               //vector3_snap( m_object_pivot, GetSnapGridSize() );
+               //globalOutputStream() << m_object_pivot << "\n";
                m_pivot2world = matrix4_translation_for_vec3( m_object_pivot );
 
                switch ( m_manipulator_mode )
@@ -3366,6 +3620,106 @@ void RadiantSelectionSystem::ConstructPivot() const {
        }
 }
 
+void RadiantSelectionSystem::setCustomPivotOrigin( Vector3& point ) const {
+       /*if ( !m_pivotChanged || m_pivot_moving ) {
+               return;
+       }*/
+       //m_pivotChanged = false;
+
+       if ( !nothingSelected() && ( m_manipulator_mode == eTranslate || m_manipulator_mode == eRotate || m_manipulator_mode == eScale ) ) {
+               AABB bounds;
+               if ( Mode() == eComponent ) {
+                       Scene_BoundsSelectedComponent( GlobalSceneGraph(), bounds );
+               }
+               else
+               {
+                       Scene_BoundsSelected( GlobalSceneGraph(), bounds );
+               }
+               //globalOutputStream() << point << "\n";
+               const float gridsize = GetSnapGridSize();
+               //const float bbox_epsilon = gridsize / 4.0;
+
+               for( std::size_t i = 0; i < 3; i++ ){
+                       if( point[i] < 900000 ){
+                               float bestsnapDist = fabs( bounds.origin[i] - point[i] );
+                               float bestsnapTo = bounds.origin[i];
+                               float othersnapDist = fabs( bounds.origin[i] + bounds.extents[i] - point[i] );
+                               if( othersnapDist < bestsnapDist ){
+                                       bestsnapDist = othersnapDist;
+                                       bestsnapTo = bounds.origin[i] + bounds.extents[i];
+                               }
+                               othersnapDist = fabs( bounds.origin[i] - bounds.extents[i] - point[i] );
+                               if( othersnapDist < bestsnapDist ){
+                                       bestsnapDist = othersnapDist;
+                                       bestsnapTo = bounds.origin[i] - bounds.extents[i];
+                               }
+                               othersnapDist = fabs( float_snapped( point[i], gridsize ) - point[i] );
+                               if( othersnapDist < bestsnapDist ){
+                                       bestsnapDist = othersnapDist;
+                                       bestsnapTo = float_snapped( point[i], gridsize );
+                               }
+                               point[i] = bestsnapTo;
+
+/*                             if( float_equal_epsilon( point[i], bestsnapTo, bbox_epsilon ) ){
+                                       point[i] = bestsnapTo;
+                               }
+                               else{
+                                       point[i] = float_snapped( point[i], gridsize );
+                               }
+                               */
+                               m_pivot2world[i + 12] = point[i]; //m_pivot2world.tx() .ty() .tz()
+                       }
+               }
+
+               switch ( m_manipulator_mode )
+               {
+               case eTranslate:
+                       break;
+               case eRotate:
+                       if ( Mode() == eComponent ) {
+                               matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() );
+                       }
+                       else
+                       {
+                               matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() );
+                       }
+                       break;
+               case eScale:
+                       if ( Mode() == eComponent ) {
+                               matrix4_assign_rotation_for_pivot( m_pivot2world, m_component_selection.back() );
+                       }
+                       else
+                       {
+                               matrix4_assign_rotation_for_pivot( m_pivot2world, m_selection.back() );
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       m_pivotIsCustom = true;
+}
+
+void RadiantSelectionSystem::getSelectionAABB( AABB& bounds ) const {
+       if ( !nothingSelected() ) {
+               if ( Mode() == eComponent ) {
+                       Scene_BoundsSelectedComponent( GlobalSceneGraph(), bounds );
+               }
+               else
+               {
+                       Scene_BoundsSelected( GlobalSceneGraph(), bounds );
+               }
+       }
+}
+
+void GetSelectionAABB( AABB& bounds ){
+       getSelectionSystem().getSelectionAABB( bounds );
+}
+
+const Matrix4& ssGetPivot2World(){
+       return getSelectionSystem().GetPivot2World();
+}
+
 void RadiantSelectionSystem::renderSolid( Renderer& renderer, const VolumeTest& volume ) const {
        //if(view->TestPoint(m_object_pivot))
        if ( !nothingSelected() ) {
@@ -3385,12 +3739,26 @@ void RadiantSelectionSystem::renderSolid( Renderer& renderer, const VolumeTest&
 #endif
 }
 
+#include "preferencesystem.h"
+#include "preferences.h"
+
+void SelectionSystem_constructPreferences( PreferencesPage& page ){
+       page.appendCheckBox( "", "Prefer point entities in 2D", getSelectionSystem().m_bPreferPointEntsIn2D );
+}
+void SelectionSystem_constructPage( PreferenceGroup& group ){
+       PreferencesPage page( group.createPage( "Selection", "Selection System Settings" ) );
+       SelectionSystem_constructPreferences( page );
+}
+void SelectionSystem_registerPreferencesPage(){
+       PreferencesDialog_addSettingsPage( FreeCaller<void(PreferenceGroup&), SelectionSystem_constructPage>() );
+}
+
+
 
 void SelectionSystem_OnBoundsChanged(){
        getSelectionSystem().pivotChanged();
 }
 
-
 SignalHandlerId SelectionSystem_boundsChanged;
 
 void SelectionSystem_Construct(){
@@ -3401,6 +3769,9 @@ void SelectionSystem_Construct(){
        SelectionSystem_boundsChanged = GlobalSceneGraph().addBoundsChangedCallback( FreeCaller<void(), SelectionSystem_OnBoundsChanged>() );
 
        GlobalShaderCache().attachRenderable( getSelectionSystem() );
+
+       GlobalPreferenceSystem().registerPreference( "PreferPointEntsIn2D", make_property_string( getSelectionSystem().m_bPreferPointEntsIn2D ) );
+       SelectionSystem_registerPreferencesPage();
 }
 
 void SelectionSystem_Destroy(){
@@ -3449,6 +3820,7 @@ Single<MouseEventCallback> g_mouseUpCallback;
 
 #if 1
 const ButtonIdentifier c_button_select = c_buttonLeft;
+const ButtonIdentifier c_button_select2 = c_buttonRight;
 const ModifierFlags c_modifier_manipulator = c_modifierNone;
 const ModifierFlags c_modifier_toggle = c_modifierShift;
 const ModifierFlags c_modifier_replace = c_modifierShift | c_modifierAlt;
@@ -3472,11 +3844,13 @@ const ModifierFlags c_modifier_copy_texture = c_modifierNone;
 class Selector_
 {
 RadiantSelectionSystem::EModifier modifier_for_state( ModifierFlags state ){
-       if ( state == c_modifier_toggle || state == c_modifier_toggle_face ) {
-               return RadiantSelectionSystem::eToggle;
-       }
-       if ( state == c_modifier_replace || state == c_modifier_replace_face ) {
-               return RadiantSelectionSystem::eReplace;
+       if ( ( state == c_modifier_toggle || state == c_modifier_toggle_face || state == c_modifier_face ) ) {
+               if( m_mouse2 ){
+                       return RadiantSelectionSystem::eReplace;
+               }
+               else{
+                       return RadiantSelectionSystem::eToggle;
+               }
        }
        return RadiantSelectionSystem::eManipulator;
 }
@@ -3497,12 +3871,15 @@ public:
 DeviceVector m_start;
 DeviceVector m_current;
 DeviceVector m_epsilon;
-std::size_t m_unmoved_replaces;
 ModifierFlags m_state;
+bool m_mouse2;
+bool m_mouseMoved;
+bool m_mouseMovedWhilePressed;
+bool m_paintSelect;
 const View* m_view;
 RectangleCallback m_window_update;
 
-Selector_() : m_start( 0.0f, 0.0f ), m_current( 0.0f, 0.0f ), m_unmoved_replaces( 0 ), m_state( c_modifierNone ){
+Selector_() : m_start( 0.0f, 0.0f ), m_current( 0.0f, 0.0f ), m_state( c_modifierNone ), m_mouse2( false ), m_mouseMoved( false ), m_mouseMovedWhilePressed( false ){
 }
 
 void draw_area(){
@@ -3515,11 +3892,11 @@ void testSelect( DeviceVector position ){
                DeviceVector delta( position - m_start );
                if ( fabs( delta.x() ) > m_epsilon.x() && fabs( delta.y() ) > m_epsilon.y() ) {
                        DeviceVector delta( position - m_start );
-                       getSelectionSystem().SelectArea( *m_view, &m_start[0], &delta[0], modifier, ( m_state & c_modifier_face ) != c_modifierNone );
+                       //getSelectionSystem().SelectArea( *m_view, &m_start[0], &delta[0], modifier, ( m_state & c_modifier_face ) != c_modifierNone );
+                       getSelectionSystem().SelectArea( *m_view, &m_start[0], &delta[0], RadiantSelectionSystem::eToggle, ( m_state & c_modifier_face ) != c_modifierNone );
                }
-               else
-               {
-                       if ( modifier == RadiantSelectionSystem::eReplace && m_unmoved_replaces++ > 0 ) {
+               else if( !m_mouseMovedWhilePressed ){
+                       if ( modifier == RadiantSelectionSystem::eReplace && !m_mouseMoved ) {
                                modifier = RadiantSelectionSystem::eCycle;
                        }
                        getSelectionSystem().SelectPoint( *m_view, &position[0], &m_epsilon[0], modifier, ( m_state & c_modifier_face ) != c_modifierNone );
@@ -3530,8 +3907,20 @@ void testSelect( DeviceVector position ){
        draw_area();
 }
 
+void testSelect_simpleM1( DeviceVector position ){
+       /*RadiantSelectionSystem::EModifier modifier = RadiantSelectionSystem::eReplace;
+       DeviceVector delta( position - m_start );
+       if ( fabs( delta.x() ) < m_epsilon.x() && fabs( delta.y() ) < m_epsilon.y() ) {
+               modifier = RadiantSelectionSystem::eCycle;
+       }
+       getSelectionSystem().SelectPoint( *m_view, &position[0], &m_epsilon[0], modifier, false );*/
+       getSelectionSystem().SelectPoint( *m_view, &position[0], &m_epsilon[0], m_mouseMoved ? RadiantSelectionSystem::eReplace : RadiantSelectionSystem::eCycle, false );
+       m_start = m_current = device_constrained( position );
+}
+
+
 bool selecting() const {
-       return m_state != c_modifier_manipulator;
+       return m_state != c_modifier_manipulator && m_mouse2;
 }
 
 void setState( ModifierFlags state ){
@@ -3555,16 +3944,32 @@ void modifierDisable( ModifierFlags type ){
 
 void mouseDown( DeviceVector position ){
        m_start = m_current = device_constrained( position );
+       if( !m_mouse2 && m_state != c_modifierNone ){
+               m_paintSelect = getSelectionSystem().SelectPoint_InitPaint( *m_view, &position[0], &m_epsilon[0], ( m_state & c_modifier_face ) != c_modifierNone );
+       }
 }
 
 void mouseMoved( DeviceVector position ){
        m_current = device_constrained( position );
-       draw_area();
+       m_mouseMovedWhilePressed = true;
+       if( m_mouse2 ){
+               draw_area();
+       }
+       else if( m_state != c_modifier_manipulator ){
+               getSelectionSystem().SelectPoint( *m_view, &m_current[0], &m_epsilon[0],
+                                                                               m_paintSelect ? RadiantSelectionSystem::eSelect : RadiantSelectionSystem::eDeselect,
+                                                                               ( m_state & c_modifier_face ) != c_modifierNone );
+       }
 }
 typedef MemberCaller<Selector_, void(DeviceVector), &Selector_::mouseMoved> MouseMovedCaller;
 
 void mouseUp( DeviceVector position ){
-       testSelect( device_constrained( position ) );
+       if( m_mouse2 ){
+               testSelect( device_constrained( position ) );
+       }
+       else{
+               m_start = m_current = DeviceVector( 0.0f, 0.0f );
+       }
 
        g_mouseMovedCallback.clear();
        g_mouseUpCallback.clear();
@@ -3634,8 +4039,9 @@ void onSizeChanged( int width, int height ){
        m_selector.m_epsilon = m_manipulator.m_epsilon = epsilon;
 }
 void onMouseDown( const WindowVector& position, ButtonIdentifier button, ModifierFlags modifiers ){
-       if ( button == c_button_select ) {
+       if ( button == c_button_select || ( button == c_button_select2 && modifiers != c_modifierNone ) ) {
                m_mouse_down = true;
+               //m_selector.m_mouseMoved = false;
 
                DeviceVector devicePosition( window_to_normalised_device( position, m_width, m_height ) );
                if ( modifiers == c_modifier_manipulator && m_manipulator.mouseDown( devicePosition ) ) {
@@ -3644,6 +4050,12 @@ void onMouseDown( const WindowVector& position, ButtonIdentifier button, Modifie
                }
                else
                {
+                       if ( button == c_button_select ) {
+                               m_selector.m_mouse2 = false;
+                       }
+                       else{
+                               m_selector.m_mouse2 = true;
+                       }
                        m_selector.mouseDown( devicePosition );
                        g_mouseMovedCallback.insert( MouseEventCallback( Selector_::MouseMovedCaller( m_selector ) ) );
                        g_mouseUpCallback.insert( MouseEventCallback( Selector_::MouseUpCaller( m_selector ) ) );
@@ -3665,18 +4077,29 @@ void onMouseDown( const WindowVector& position, ButtonIdentifier button, Modifie
        }
 }
 void onMouseMotion( const WindowVector& position, ModifierFlags modifiers ){
-       m_selector.m_unmoved_replaces = 0;
-
+       m_selector.m_mouseMoved = true;
        if ( m_mouse_down && !g_mouseMovedCallback.empty() ) {
+               m_selector.m_mouseMovedWhilePressed = true;
                g_mouseMovedCallback.get() ( window_to_normalised_device( position, m_width, m_height ) );
        }
 }
 void onMouseUp( const WindowVector& position, ButtonIdentifier button, ModifierFlags modifiers ){
-       if ( button == c_button_select && !g_mouseUpCallback.empty() ) {
+       if ( ( button == c_button_select || button == c_button_select2 ) && !g_mouseUpCallback.empty() ) {
                m_mouse_down = false;
 
                g_mouseUpCallback.get() ( window_to_normalised_device( position, m_width, m_height ) );
        }
+       //L button w/o scene changed = tunnel selection
+       if( // !getSelectionSystem().m_undo_begun &&
+               modifiers == c_modifierNone && button == c_button_select &&
+               //( !m_selector.m_mouseMoved || !m_mouse_down ) &&
+               !m_selector.m_mouseMovedWhilePressed &&
+               ( getSelectionSystem().Mode() != SelectionSystem::eComponent || getSelectionSystem().ManipulatorMode() != SelectionSystem::eDrag ) ){
+               m_selector.testSelect_simpleM1( device_constrained( window_to_normalised_device( position, m_width, m_height ) ) );
+       }
+       //getSelectionSystem().m_undo_begun = false;
+       m_selector.m_mouseMoved = false;
+       m_selector.m_mouseMovedWhilePressed = false;
 }
 void onModifierDown( ModifierFlags type ){
        m_selector.modifierEnable( type );