From 31cef208e0f82abdd20db48a147b39a715c83f70 Mon Sep 17 00:00:00 2001 From: Garux Date: Wed, 2 Aug 2017 16:26:21 +0300 Subject: [PATCH] Radiant: misc... * draggable renderable transform origin for translate, rotate & scale manipulators click w/o move = reset move is constraintable to axis with shift pressed * highlight manipulators' renderables on mouse hover * mouse_moved_epsilon system (0.5% of viewport size) for m1, tunnel selectors, manipulators (except freelook) to make them more fail-safe --- include/iselection.h | 2 +- radiant/camwindow.cpp | 1 + radiant/gtkmisc.cpp | 1 + radiant/renderstate.cpp | 4 +- radiant/selection.cpp | 512 +++++++++++++++++++++++++++++----------- radiant/selection.h | 1 + radiant/xywindow.cpp | 6 +- 7 files changed, 387 insertions(+), 140 deletions(-) diff --git a/include/iselection.h b/include/iselection.h index 99310d91..3a4e04b4 100644 --- a/include/iselection.h +++ b/include/iselection.h @@ -124,7 +124,7 @@ virtual void rotateSelected( const Quaternion& rotation, bool snapOrigin = false virtual void scaleSelected( const Vector3& scaling, bool snapOrigin = false ) = 0; virtual void pivotChanged() const = 0; -virtual void setCustomPivotOrigin( Vector3& point ) const = 0; +virtual void setCustomTransformOrigin( const Vector3& origin, const bool set[3] ) const = 0; }; #include "modulesystem.h" diff --git a/radiant/camwindow.cpp b/radiant/camwindow.cpp index f6b6270a..166a79ff 100644 --- a/radiant/camwindow.cpp +++ b/radiant/camwindow.cpp @@ -1008,6 +1008,7 @@ gboolean selection_motion_freemove( GtkWidget *widget, GdkEventMotion *event, Wi void selection_motion_freemove( gdouble x, gdouble y, guint state, void* data ){ //globalOutputStream() << "motion... "; CamWnd* camwnd = reinterpret_cast( data ); + camwnd->m_window_observer->setMouseMoved(); camwnd->m_window_observer->onMouseMotion( windowvector_for_widget_centre( camwnd->m_gl_widget ), modifiers_for_state( state ) ); } diff --git a/radiant/gtkmisc.cpp b/radiant/gtkmisc.cpp index dc9bfd04..7f9b52f2 100644 --- a/radiant/gtkmisc.cpp +++ b/radiant/gtkmisc.cpp @@ -36,6 +36,7 @@ #include #include +#include #include "math/vector.h" #include "os/path.h" diff --git a/radiant/renderstate.cpp b/radiant/renderstate.cpp index 8889c250..6eb3fa7a 100644 --- a/radiant/renderstate.cpp +++ b/radiant/renderstate.cpp @@ -2202,8 +2202,8 @@ void OpenGLShader::construct( const char* name ){ state.m_pointsize = 4; } else if ( string_equal( name + 1, "BIGPOINT" ) ) { - state.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHWRITE; - state.m_sort = OpenGLState::eSortControlFirst; + state.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_OVERRIDE; + state.m_sort = OpenGLState::eSortGUI1 + 1; state.m_pointsize = 6; } else if ( string_equal( name + 1, "PIVOT" ) ) { diff --git a/radiant/selection.cpp b/radiant/selection.cpp index 490b51c0..8f2801ec 100644 --- a/radiant/selection.cpp +++ b/radiant/selection.cpp @@ -192,7 +192,7 @@ float distance_for_axis( const Vector3& a, const Vector3& b, const Vector3& axis class Manipulatable { public: -virtual void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ) = 0; +virtual void Construct( const Matrix4& device2manip, const float x, const float y, const AABB& bounds, const Vector3& transform_origin ) = 0; virtual void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap, const bool snapbbox ) = 0; }; @@ -217,7 +217,7 @@ public: RotateFree( Rotatable& rotatable ) : m_rotatable( rotatable ){ } -void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){ +void Construct( const Matrix4& device2manip, const float x, const float y, const AABB& bounds, const Vector3& transform_origin ){ point_on_sphere( m_start, device2manip, x, y ); vector3_normalise( m_start ); } @@ -228,8 +228,8 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const if( snap ){ Vector3 axis( 0, 0, 0 ); for( std::size_t i = 0; i < 3; ++i ){ - if( current[i] == 0.0f ){ - axis[i] = 1.0f; + if( current[i] == 0.f ){ + axis[i] = 1.f; break; } } @@ -254,7 +254,7 @@ public: RotateAxis( Rotatable& rotatable ) : m_rotatable( rotatable ){ } -void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){ +void Construct( const Matrix4& device2manip, const float x, const float y, const AABB& bounds, const Vector3& transform_origin ){ point_on_sphere( m_start, device2manip, x, y ); constrain_to_axis( m_start, m_axis ); } @@ -302,7 +302,7 @@ public: TranslateAxis( Translatable& translatable ) : m_translatable( translatable ){ } -void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){ +void Construct( const Matrix4& device2manip, const float x, const float y, const AABB& bounds, const Vector3& transform_origin ){ point_on_axis( m_start, m_axis, device2manip, x, y ); m_bounds = bounds; } @@ -353,7 +353,7 @@ public: TranslateFree( Translatable& translatable ) : m_translatable( translatable ){ } -void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){ +void Construct( const Matrix4& device2manip, const float x, const float y, const AABB& bounds, const Vector3& transform_origin ){ point_on_plane( m_start, device2manip, x, y ); m_bounds = bounds; } @@ -365,10 +365,10 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const if( snap ){ for ( std::size_t i = 0; i < 3 ; ++i ){ if( fabs( current[i] ) >= fabs( current[(i + 1) % 3] ) ){ - current[(i + 1) % 3] = 0.0f; + current[(i + 1) % 3] = 0.f; } else{ - current[i] = 0.0f; + current[i] = 0.f; } } } @@ -380,7 +380,7 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const Vector3 mins( m_bounds.origin - m_bounds.extents ); //globalOutputStream() << "current: " << current << "\n"; for( std::size_t i = 0; i < 3; ++i ){ - if( fabs( current[i] ) > 0.000001f ){ + if( fabs( current[i] ) > 1e-6f ){ float snapto1 = float_snapped( maxs[i] + current[i] , grid ); float snapto2 = float_snapped( mins[i] + current[i] , grid ); @@ -420,7 +420,7 @@ public: ScaleAxis( Scalable& scalable ) : m_scalable( scalable ){ } -void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){ +void Construct( const Matrix4& device2manip, const float x, const float y, const AABB& bounds, const Vector3& transform_origin ){ point_on_axis( m_start, m_axis, device2manip, x, y ); m_choosen_extent = Vector3( @@ -439,9 +439,9 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const translation_local2object( delta, delta, manip2object ); vector3_snap( delta, GetSnapGridSize() ); - Vector3 start( vector3_snapped( m_start, GetSnapGridSize() != 0.0f ? GetSnapGridSize() : 0.001f ) ); + Vector3 start( vector3_snapped( m_start, GetSnapGridSize() != 0.f ? GetSnapGridSize() : 1e-3f ) ); for ( std::size_t i = 0; i < 3 ; ++i ){ //prevent snapping to 0 with big gridsize - if( float_snapped( m_start[i], 0.001f ) != 0.0f && start[i] == 0.0f ){ + if( float_snapped( m_start[i], 1e-3f ) != 0.f && start[i] == 0.f ){ start[i] = GetSnapGridSize(); } } @@ -463,7 +463,7 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const } if( snap ){ for( std::size_t i = 0; i < 3; i++ ){ - if( scale[i] == 1.0f ){ + if( scale[i] == 1.f ){ scale[i] = vector3_dot( scale, m_axis ); } } @@ -490,13 +490,13 @@ public: ScaleFree( Scalable& scalable ) : m_scalable( scalable ){ } -void Construct( const Matrix4& device2manip, const float x, const float y, const AABB bounds, const Vector3 transform_origin ){ +void Construct( const Matrix4& device2manip, const float x, const float y, const AABB& bounds, const Vector3& transform_origin ){ point_on_plane( m_start, device2manip, x, y ); m_choosen_extent = Vector3( - std::max( bounds.origin[0] + bounds.extents[0] - transform_origin[0], - bounds.origin[0] + bounds.extents[0] + transform_origin[0] ), - std::max( bounds.origin[1] + bounds.extents[1] - transform_origin[1], - bounds.origin[1] + bounds.extents[1] + transform_origin[1] ), - std::max( bounds.origin[2] + bounds.extents[2] - transform_origin[2], - bounds.origin[2] + bounds.extents[2] + transform_origin[2] ) + std::max( bounds.origin[0] + bounds.extents[0] - transform_origin[0], -( bounds.origin[0] - bounds.extents[0] - transform_origin[0] ) ), + std::max( bounds.origin[1] + bounds.extents[1] - transform_origin[1], -( bounds.origin[1] - bounds.extents[1] - transform_origin[1] ) ), + std::max( bounds.origin[2] + bounds.extents[2] - transform_origin[2], -( bounds.origin[2] - bounds.extents[2] - transform_origin[2] ) ) ); m_bounds = bounds; } @@ -508,9 +508,9 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const translation_local2object( delta, delta, manip2object ); vector3_snap( delta, GetSnapGridSize() ); - Vector3 start( vector3_snapped( m_start, GetSnapGridSize() != 0.0f ? GetSnapGridSize() : 0.001f ) ); + Vector3 start( vector3_snapped( m_start, GetSnapGridSize() != 0.f ? GetSnapGridSize() : 1e-3f ) ); for ( std::size_t i = 0; i < 3 ; ++i ){ //prevent snapping to 0 with big gridsize - if( float_snapped( m_start[i], 0.001f ) != 0.0f && start[i] == 0.0f ){ + if( float_snapped( m_start[i], 1e-3f ) != 0.f && start[i] == 0.f ){ start[i] = GetSnapGridSize(); } } @@ -552,7 +552,7 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const } for( std::size_t i = 0; i < 3; i++ ){ if( ignore_axis != i ){ - scale[i] = ( scale[i] < 0.0f ) ? -fabs( bestscale ) : fabs( bestscale ); + scale[i] = ( scale[i] < 0.f ) ? -fabs( bestscale ) : fabs( bestscale ); } } } @@ -825,6 +825,13 @@ void BestPoint( std::size_t count, Vector4 clipped[9], SelectionIntersection& be #endif } +void Point_BestPoint( const Matrix4& local2view, const PointVertex& vertex, SelectionIntersection& best ){ + Vector4 clipped; + if ( matrix4_clip_point( local2view, vertex3f_to_vector3( vertex.vertex ), clipped ) == c_CLIP_PASS ) { + assign_if_closer( best, select_point_from_clipped( clipped ) ); + } +} + void LineStrip_BestPoint( const Matrix4& local2view, const PointVertex* vertices, const std::size_t size, SelectionIntersection& best ){ Vector4 clipped[2]; for ( std::size_t i = 0; ( i + 1 ) < size; ++i ) @@ -1072,6 +1079,7 @@ SelectableBool m_selectable_y; SelectableBool m_selectable_z; SelectableBool m_selectable_screen; SelectableBool m_selectable_sphere; +Selectable* m_selectable_prev_ptr; Pivot2World m_pivot; Matrix4 m_local2world_x; Matrix4 m_local2world_y; @@ -1089,15 +1097,14 @@ RotateManipulator( Rotatable& rotatable, std::size_t segments, float radius ) : m_circle_y( ( segments << 2 ) + 1 ), m_circle_z( ( segments << 2 ) + 1 ), m_circle_screen( segments << 3 ), - m_circle_sphere( segments << 3 ){ + m_circle_sphere( segments << 3 ), + m_selectable_prev_ptr( 0 ){ draw_semicircle( segments, radius, m_circle_x.m_vertices.data(), RemapYZX() ); draw_semicircle( segments, radius, m_circle_y.m_vertices.data(), RemapZXY() ); draw_semicircle( segments, radius, m_circle_z.m_vertices.data(), RemapXYZ() ); draw_circle( segments, radius * 1.15f, m_circle_screen.m_vertices.data(), RemapXYZ() ); draw_circle( segments, radius, m_circle_sphere.m_vertices.data(), RemapXYZ() ); - - m_selectable_sphere.setSelected( true ); } @@ -1236,6 +1243,17 @@ void testSelect( const View& view, const Matrix4& pivot2world ){ if ( !selector.failed() ) { ( *selector.begin() ).second->setSelected( true ); + if( m_selectable_prev_ptr != ( *selector.begin() ).second ){ + m_selectable_prev_ptr = ( *selector.begin() ).second; + SceneChangeNotify(); + } + } + else{ + m_selectable_sphere.setSelected( true ); + if( m_selectable_prev_ptr != &m_selectable_sphere ){ + m_selectable_prev_ptr = &m_selectable_sphere; + SceneChangeNotify(); + } } } @@ -1266,6 +1284,7 @@ void setSelected( bool select ){ m_selectable_y.setSelected( select ); m_selectable_z.setSelected( select ); m_selectable_screen.setSelected( select ); + m_selectable_sphere.setSelected( select ); } bool isSelected() const { return m_selectable_x.isSelected() @@ -1468,6 +1487,7 @@ SelectableBool m_selectable_x; SelectableBool m_selectable_y; SelectableBool m_selectable_z; SelectableBool m_selectable_screen; +Selectable* m_selectable_prev_ptr; Pivot2World m_pivot; public: static Shader* m_state_wire; @@ -1478,7 +1498,8 @@ TranslateManipulator( Translatable& translatable, std::size_t segments, float le m_axis( translatable ), m_arrow_head_x( 3 * 2 * ( segments << 3 ) ), m_arrow_head_y( 3 * 2 * ( segments << 3 ) ), - m_arrow_head_z( 3 * 2 * ( segments << 3 ) ){ + m_arrow_head_z( 3 * 2 * ( segments << 3 ) ), + m_selectable_prev_ptr( 0 ){ draw_arrowline( length, m_arrow_x.m_line, 0 ); draw_arrowhead( segments, length, m_arrow_head_x.m_vertices.data(), TripleRemapXYZ(), TripleRemapXYZ() ); draw_arrowline( length, m_arrow_y.m_line, 1 ); @@ -1604,6 +1625,14 @@ void testSelect( const View& view, const Matrix4& pivot2world ){ if ( !selector.failed() ) { ( *selector.begin() ).second->setSelected( true ); + if( m_selectable_prev_ptr != ( *selector.begin() ).second ){ + m_selectable_prev_ptr = ( *selector.begin() ).second; + SceneChangeNotify(); + } + } + else if( m_selectable_prev_ptr ){ + m_selectable_prev_ptr = 0; + SceneChangeNotify(); } } @@ -1685,11 +1714,13 @@ SelectableBool m_selectable_x; SelectableBool m_selectable_y; SelectableBool m_selectable_z; SelectableBool m_selectable_screen; +Selectable* m_selectable_prev_ptr; Pivot2World m_pivot; public: ScaleManipulator( Scalable& scalable, std::size_t segments, float length ) : m_free( scalable ), - m_axis( scalable ){ + m_axis( scalable ), + m_selectable_prev_ptr( 0 ){ draw_arrowline( length, m_arrow_x.m_line, 0 ); draw_arrowline( length, m_arrow_y.m_line, 1 ); draw_arrowline( length, m_arrow_z.m_line, 2 ); @@ -1763,6 +1794,14 @@ void testSelect( const View& view, const Matrix4& pivot2world ){ if ( !selector.failed() ) { ( *selector.begin() ).second->setSelected( true ); + if( m_selectable_prev_ptr != ( *selector.begin() ).second ){ + m_selectable_prev_ptr = ( *selector.begin() ).second; + SceneChangeNotify(); + } + } + else if( m_selectable_prev_ptr ){ + m_selectable_prev_ptr = 0; + SceneChangeNotify(); } } @@ -2812,6 +2851,144 @@ bool isSelected() const { } }; +class TransformOriginTranslatable +{ +public: +virtual void transformOriginTranslate( const Vector3& translation, const bool set[3], const AABB& bounds ) = 0; +}; + +class TransformOriginTranslate : public Manipulatable +{ +private: +Vector3 m_start; +TransformOriginTranslatable& m_transformOriginTranslatable; +AABB m_bounds; +public: +TransformOriginTranslate( TransformOriginTranslatable& transformOriginTranslatable ) + : m_transformOriginTranslatable( transformOriginTranslatable ){ +} +void Construct( const Matrix4& device2manip, const float x, const float y, const AABB& bounds, const Vector3& transform_origin ){ + point_on_plane( m_start, device2manip, x, y ); + m_bounds = bounds; +} +void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const float x, const float y, const bool snap, const bool snapbbox ){ + Vector3 current; + point_on_plane( current, device2manip, x, y ); + current = vector3_subtracted( current, m_start ); + + if( snap ){ + for ( std::size_t i = 0; i < 3 ; ++i ){ + if( fabs( current[i] ) >= fabs( current[(i + 1) % 3] ) ){ + current[(i + 1) % 3] = 0.f; + } + else{ + current[i] = 0.f; + } + } + } + + bool set[3] = { true, true, true }; + for ( std::size_t i = 0; i < 3 ; ++i ){ + if( fabs( current[i] ) < 1e-3f ){ + set[i] = false; + } + } + + translation_local2object( current, current, manip2object ); + + m_transformOriginTranslatable.transformOriginTranslate( current, set, m_bounds ); +} +}; + +class TransformOriginManipulator : public Manipulator { + struct RenderablePoint : public OpenGLRenderable + { + PointVertex m_point; + RenderablePoint(): + m_point( vertex3f_identity ) { + } + void render( RenderStateFlags state ) const { + glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_point.colour ); + glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_point.vertex ); + glDrawArrays( GL_POINTS, 0, 1 ); + } + void setColour( const Colour4b & colour ) { + m_point.colour = colour; + } + }; + + TransformOriginTranslate m_translate; + RenderablePoint m_point; + SelectableBool m_selectable; + Selectable* m_selectable_prev_ptr; + Pivot2World m_pivot; +public: + static Shader* m_state; + + TransformOriginManipulator( TransformOriginTranslatable& transformOriginTranslatable ) : + m_translate( transformOriginTranslatable ), + m_selectable_prev_ptr( 0 ) { + } + + void UpdateColours() { + m_point.setColour( colourSelected( g_colour_screen, m_selectable.isSelected() ) ); + } + + void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ) { + m_pivot.update( pivot2world, volume.GetModelview(), volume.GetProjection(), volume.GetViewport() ); + + // temp hack + UpdateColours(); + + renderer.SetState( m_state, Renderer::eWireframeOnly ); + renderer.SetState( m_state, Renderer::eFullMaterials ); + + renderer.addRenderable( m_point, m_pivot.m_worldSpace ); + } + void testSelect( const View& view, const Matrix4& pivot2world ) { + m_pivot.update( pivot2world, view.GetModelview(), view.GetProjection(), view.GetViewport() ); + + SelectionPool selector; + { + Matrix4 local2view( matrix4_multiplied_by_matrix4( view.GetViewMatrix(), m_pivot.m_worldSpace ) ); + +#if defined( DEBUG_SELECTION ) + g_render_clipped.construct( view.GetViewMatrix() ); +#endif + { + SelectionIntersection best; + + Point_BestPoint( local2view, m_point.m_point, best ); + selector.addSelectable( best, &m_selectable ); + } + } + + if ( !selector.failed() ) { + ( *selector.begin() ).second->setSelected( true ); + if( m_selectable_prev_ptr != ( *selector.begin() ).second ){ + m_selectable_prev_ptr = ( *selector.begin() ).second; + SceneChangeNotify(); + } + } + else if( m_selectable_prev_ptr ){ + m_selectable_prev_ptr = 0; + SceneChangeNotify(); + } + } + + Manipulatable* GetManipulatable() { + return &m_translate; + } + + void setSelected( bool select ) { + m_selectable.setSelected( select ); + } + bool isSelected() const { + return m_selectable.isSelected(); + } +}; +Shader* TransformOriginManipulator::m_state; + class select_all : public scene::Graph::Walker { bool m_select; @@ -2856,6 +3033,7 @@ class RadiantSelectionSystem : public Translatable, public Rotatable, public Scalable, + public TransformOriginTranslatable, public Renderable { mutable Matrix4 m_pivot2world; @@ -2884,6 +3062,7 @@ RotateManipulator m_rotate_manipulator; ScaleManipulator m_scale_manipulator; DragManipulator m_drag_manipulator; ClipManipulator m_clip_manipulator; +mutable TransformOriginManipulator m_transformOrigin_manipulator; typedef SelectionList selection_t; selection_t m_selection; @@ -2892,7 +3071,9 @@ selection_t m_component_selection; Signal1 m_selectionChanged_callbacks; void ConstructPivot() const; -void setCustomPivotOrigin( Vector3& point ) const; +void ConstructPivotRotation() const; +void setCustomTransformOrigin( const Vector3& origin, const bool set[3] ) const; +void setCustomTransformOrigin( const Vector3& origin, const bool set[3], const AABB& bounds ) const; public: AABB getSelectionAABB() const; private: @@ -2929,6 +3110,7 @@ RadiantSelectionSystem() : m_translate_manipulator( *this, 2, 64 ), m_rotate_manipulator( *this, 8, 64 ), m_scale_manipulator( *this, 0, 64 ), + m_transformOrigin_manipulator( *this ), m_pivotChanged( false ), m_pivot_moving( false ), m_pivotIsCustom( false ){ @@ -3066,24 +3248,34 @@ void startMove(){ } bool SelectManipulator( const View& view, const float device_point[2], const float device_epsilon[2] ){ + bool movingOrigin = false; + if ( !nothingSelected() || ( ManipulatorMode() == eDrag && Mode() == eComponent ) ) { #if defined ( DEBUG_SELECTION ) g_render_clipped.destroy(); #endif + m_transformOrigin_manipulator.setSelected( false ); m_manipulator->setSelected( false ); if ( !nothingSelected() || ( ManipulatorMode() == eDrag && Mode() == eComponent ) ) { View scissored( view ); ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, device_epsilon ) ); - m_manipulator->testSelect( scissored, GetPivot2World() ); + + if( transformOrigin_isTranslatable() ){ + m_transformOrigin_manipulator.testSelect( scissored, GetPivot2World() ); + movingOrigin = m_transformOrigin_manipulator.isSelected(); + } + + if( !movingOrigin ) + m_manipulator->testSelect( scissored, GetPivot2World() ); } startMove(); m_pivot_moving = m_manipulator->isSelected(); - if ( m_pivot_moving ) { + if ( m_pivot_moving || movingOrigin ) { Pivot2World pivot; pivot.update( GetPivot2World(), view.GetModelview(), view.GetProjection(), view.GetViewport() ); @@ -3091,15 +3283,39 @@ bool SelectManipulator( const View& view, const float device_point[2], const flo Matrix4 device2manip; ConstructDevice2Manip( device2manip, m_pivot2world_start, view.GetModelview(), view.GetProjection(), view.GetViewport() ); - m_manipulator->GetManipulatable()->Construct( device2manip, device_point[0], device_point[1], getSelectionAABB(), vector4_to_vector3( GetPivot2World().t() ) ); + if( m_pivot_moving ){ + m_manipulator->GetManipulatable()->Construct( device2manip, device_point[0], device_point[1], getSelectionAABB(), vector4_to_vector3( GetPivot2World().t() ) ); - m_undo_begun = false; + m_undo_begun = false; + } + else if( movingOrigin ){ + m_transformOrigin_manipulator.GetManipulatable()->Construct( device2manip, device_point[0], device_point[1], getSelectionAABB(), vector4_to_vector3( GetPivot2World().t() ) ); + } } SceneChangeNotify(); } - return m_pivot_moving; + return m_pivot_moving || movingOrigin; +} + +void HighlightManipulator( const View& view, const float device_point[2], const float device_epsilon[2] ){ + if ( !nothingSelected() && transformOrigin_isTranslatable() ) { +#if defined ( DEBUG_SELECTION ) + g_render_clipped.destroy(); +#endif + + m_transformOrigin_manipulator.setSelected( false ); + m_manipulator->setSelected( false ); + + View scissored( view ); + ConstructSelectionTest( scissored, SelectionBoxForPoint( device_point, device_epsilon ) ); + + m_transformOrigin_manipulator.testSelect( scissored, GetPivot2World() ); + + if( !m_transformOrigin_manipulator.isSelected() ) + m_manipulator->testSelect( scissored, GetPivot2World() ); + } } void deselectAll(){ @@ -3150,7 +3366,7 @@ void SelectionPool_Select( SelectionPool& pool, bool select, float dist_epsilon 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" ); + ASSERT_MESSAGE( fabs( device_point[0] ) <= 1.f && fabs( device_point[1] ) <= 1.f, "point-selection error" ); if ( modifier == eReplace ) { deselectComponentsOrAll( face ); @@ -3282,7 +3498,7 @@ void SelectPoint( const View& view, const float device_point[2], const float dev } 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" ); + ASSERT_MESSAGE( fabs( device_point[0] ) <= 1.f && fabs( device_point[1] ) <= 1.f, "point-selection error" ); #if defined ( DEBUG_SELECTION ) g_render_clipped.destroy(); #endif @@ -3459,6 +3675,18 @@ void scaleSelected( const Vector3& scaling, bool snapOrigin = false ){ freezeTransforms(); } +bool transformOrigin_isTranslatable() const{ + return ManipulatorMode() == eScale + || ManipulatorMode() == eRotate + || ManipulatorMode() == eTranslate; +} + +void transformOriginTranslate( const Vector3& translation, const bool set[3], const AABB& bounds ){ + m_pivot2world = m_pivot2world_start; + setCustomTransformOrigin( translation + vector4_to_vector3( m_pivot2world_start.t() ), set, bounds ); + SceneChangeNotify(); +} + void MoveSelected( const View& view, const float device_point[2], bool snap, bool snapbbox ){ if ( m_manipulator->isSelected() ) { if ( !m_undo_begun ) { @@ -3470,6 +3698,11 @@ void MoveSelected( const View& view, const float device_point[2], bool snap, boo ConstructDevice2Manip( device2manip, m_pivot2world_start, view.GetModelview(), view.GetProjection(), view.GetViewport() ); m_manipulator->GetManipulatable()->Transform( m_manip2pivot_start, device2manip, device_point[0], device_point[1], snap, snapbbox ); } + else if( m_transformOrigin_manipulator.isSelected() ){ + Matrix4 device2manip; + ConstructDevice2Manip( device2manip, m_pivot2world_start, view.GetModelview(), view.GetProjection(), view.GetViewport() ); + m_transformOrigin_manipulator.GetManipulatable()->Transform( m_manip2pivot_start, device2manip, device_point[0], device_point[1], snap, snapbbox ); + } } /// \todo Support view-dependent nudge. @@ -3479,7 +3712,7 @@ void NudgeManipulator( const Vector3& nudge, const Vector3& view ){ // } } -void endMove(); +bool endMove(); void freezeTransforms(); void renderSolid( Renderer& renderer, const VolumeTest& volume ) const; @@ -3500,12 +3733,14 @@ static void constructStatic(){ TranslateManipulator::m_state_wire = GlobalShaderCache().capture( "$WIRE_OVERLAY" ); TranslateManipulator::m_state_fill = GlobalShaderCache().capture( "$FLATSHADE_OVERLAY" ); RotateManipulator::m_state_outer = GlobalShaderCache().capture( "$WIRE_OVERLAY" ); + TransformOriginManipulator::m_state = GlobalShaderCache().capture( "$BIGPOINT" ); } static void destroyStatic(){ #if defined( DEBUG_SELECTION ) GlobalShaderCache().release( "$DEBUG_CLIPPED" ); #endif + GlobalShaderCache().release( "$BIGPOINT" ); GlobalShaderCache().release( "$WIRE_OVERLAY" ); GlobalShaderCache().release( "$FLATSHADE_OVERLAY" ); GlobalShaderCache().release( "$WIRE_OVERLAY" ); @@ -3682,7 +3917,15 @@ void RadiantSelectionSystem::freezeTransforms(){ } -void RadiantSelectionSystem::endMove(){ +bool RadiantSelectionSystem::endMove(){ + if( m_transformOrigin_manipulator.isSelected() ){ + if( m_pivot2world == m_pivot2world_start ){ + m_pivotIsCustom = false; + pivotChanged(); + } + return true; + } + freezeTransforms(); if ( Mode() == ePrimitive ) { @@ -3723,7 +3966,7 @@ void RadiantSelectionSystem::endMove(){ GlobalUndoSystem().finish( command.c_str() ); } - + return false; } inline AABB Instance_getPivotBounds( scene::Instance& instance ){ @@ -3812,123 +4055,89 @@ inline void pivot_for_node( Matrix4& pivot, scene::Node& node, scene::Instance& } #endif +void RadiantSelectionSystem::ConstructPivotRotation() const { + 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; + } +} + void RadiantSelectionSystem::ConstructPivot() const { if ( !m_pivotChanged || m_pivot_moving || m_pivotIsCustom ) { return; } m_pivotChanged = false; - Vector3 m_object_pivot; - if ( !nothingSelected() ) { - { - AABB bounds; - if ( Mode() == eComponent ) { - Scene_BoundsSelectedComponent( GlobalSceneGraph(), bounds ); - } - else - { - Scene_BoundsSelected( GlobalSceneGraph(), bounds ); - } - m_object_pivot = bounds.origin; - } + AABB bounds( getSelectionAABB() ); + Vector3 m_object_pivot = bounds.origin; //vector3_snap( m_object_pivot, GetSnapGridSize() ); //globalOutputStream() << m_object_pivot << "\n"; m_pivot2world = matrix4_translation_for_vec3( m_object_pivot ); - 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; - } - + ConstructPivotRotation(); } } -void RadiantSelectionSystem::setCustomPivotOrigin( Vector3& point ) const { - 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"; +void RadiantSelectionSystem::setCustomTransformOrigin( const Vector3& origin, const bool set[3] ) const { + AABB bounds( getSelectionAABB() ); + setCustomTransformOrigin( origin, set, bounds ); +} + +void RadiantSelectionSystem::setCustomTransformOrigin( const Vector3& origin, const bool set[3], const AABB& bounds ) const { + if ( !nothingSelected() && transformOrigin_isTranslatable() ) { + + //globalOutputStream() << origin << "\n"; for( std::size_t i = 0; i < 3; i++ ){ - if( point[i] < 900000.0f ){ - float bestsnapDist = fabs( bounds.origin[i] - point[i] ); + float value = origin[i]; + if( set[i] ){ + float bestsnapDist = fabs( bounds.origin[i] - value ); float bestsnapTo = bounds.origin[i]; - float othersnapDist = fabs( bounds.origin[i] + bounds.extents[i] - point[i] ); + float othersnapDist = fabs( bounds.origin[i] + bounds.extents[i] - value ); if( othersnapDist < bestsnapDist ){ bestsnapDist = othersnapDist; bestsnapTo = bounds.origin[i] + bounds.extents[i]; } - othersnapDist = fabs( bounds.origin[i] - bounds.extents[i] - point[i] ); + othersnapDist = fabs( bounds.origin[i] - bounds.extents[i] - value ); if( othersnapDist < bestsnapDist ){ bestsnapDist = othersnapDist; bestsnapTo = bounds.origin[i] - bounds.extents[i]; } - othersnapDist = fabs( float_snapped( point[i], GetSnapGridSize() ) - point[i] ); + othersnapDist = fabs( float_snapped( value, GetSnapGridSize() ) - value ); if( othersnapDist < bestsnapDist ){ bestsnapDist = othersnapDist; - bestsnapTo = float_snapped( point[i], GetSnapGridSize() ); + bestsnapTo = float_snapped( value, GetSnapGridSize() ); } - point[i] = bestsnapTo; + value = bestsnapTo; - m_pivot2world[i + 12] = point[i]; //m_pivot2world.tx() .ty() .tz() + m_pivot2world[i + 12] = value; //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; + + ConstructPivotRotation(); } } @@ -3955,6 +4164,9 @@ void RadiantSelectionSystem::renderSolid( Renderer& renderer, const VolumeTest& renderer.SetState( m_state, Renderer::eWireframeOnly ); renderer.SetState( m_state, Renderer::eFullMaterials ); + if( transformOrigin_isTranslatable() ) + m_transformOrigin_manipulator.render( renderer, volume, GetPivot2World() ); + m_manipulator->render( renderer, volume, GetPivot2World() ); } @@ -4253,7 +4465,7 @@ void mouseDown( DeviceVector position ){ void mouseMoved( DeviceVector position ){ m_current = device_constrained( position ); - m_mouseMovedWhilePressed = true; + //m_mouseMovedWhilePressed = true; if( m_mouse2 ){ draw_area(); } @@ -4287,7 +4499,10 @@ DeviceVector m_epsilon; const View* m_view; ModifierFlags m_state; -Manipulator_() : m_state( c_modifierNone ){ +bool m_moving_transformOrigin; +bool m_mouseMovedWhilePressed; + +Manipulator_() : m_state( c_modifierNone ), m_moving_transformOrigin( false ), m_mouseMovedWhilePressed( false ) { } bool mouseDown( DeviceVector position ){ @@ -4295,12 +4510,13 @@ bool mouseDown( DeviceVector position ){ } void mouseMoved( DeviceVector position ){ - getSelectionSystem().MoveSelected( *m_view, &position[0], ( m_state & c_modifierShift ) == c_modifierShift, ( m_state & c_modifierControl ) == c_modifierControl ); + if( m_mouseMovedWhilePressed ) + getSelectionSystem().MoveSelected( *m_view, &position[0], ( m_state & c_modifierShift ) == c_modifierShift, ( m_state & c_modifierControl ) == c_modifierControl ); } typedef MemberCaller1 MouseMovedCaller; void mouseUp( DeviceVector position ){ - getSelectionSystem().endMove(); + m_moving_transformOrigin = getSelectionSystem().endMove(); g_mouseMovedCallback.clear(); g_mouseUpCallback.clear(); } @@ -4323,6 +4539,15 @@ void modifierDisable( ModifierFlags type ){ }; +inline bool mouse_moved_epsilon( const WindowVector& position, const DeviceVector& moveStart, int width, int height, float epsilon, float& move ){ + if( move > epsilon ) + return true; + const DeviceVector device( device_constrained( window_to_normalised_device( position, width, height ) ) ); + const float currentMove = std::max( fabs( device.x() - moveStart.x() ), fabs( device.y() - moveStart.y() ) ); + move = std::max( move, currentMove ); + //globalOutputStream() << move << "\n"; + return move > epsilon; +} class RadiantWindowObserver : public SelectionSystemWindowObserver { @@ -4336,12 +4561,18 @@ int m_height; bool m_mouse_down; +const float m_moveEpsilon; +float m_move; /* released move after m_moveEnd, for tunnel selector decision: eReplace or eCycle */ +float m_movePressed; /* pressed move after m_moveStart, for decision: m1 tunnel selector or manipulate and if to do tunnel selector at all */ +DeviceVector m_moveStart; +DeviceVector m_moveEnd; + public: Selector_ m_selector; Manipulator_ m_manipulator; TexManipulator_ m_texmanipulator; -RadiantWindowObserver() : m_mouse_down( false ){ +RadiantWindowObserver() : m_mouse_down( false ), m_moveEpsilon( .01f ){ } void release(){ delete this; @@ -4366,14 +4597,14 @@ void onMouseDown( const WindowVector& position, ButtonIdentifier button, Modifie //m_selector.m_mouseMoved = false; DeviceVector devicePosition( window_to_normalised_device( position, m_width, m_height ) ); - g_bAltResize_AltSelect = ( modifiers == c_modifierAlt ) ? true : false; + g_bAltResize_AltSelect = ( modifiers == c_modifierAlt ); if ( ( modifiers == c_modifier_manipulator || ( modifiers == c_modifierAlt && getSelectionSystem().Mode() != SelectionSystem::eComponent ) ) && m_manipulator.mouseDown( devicePosition ) ) { g_mouseMovedCallback.insert( MouseEventCallback( Manipulator_::MouseMovedCaller( m_manipulator ) ) ); g_mouseUpCallback.insert( MouseEventCallback( Manipulator_::MouseUpCaller( m_manipulator ) ) ); } else { - m_selector.m_mouse2 = ( button == c_button_select ) ? false : true; + m_selector.m_mouse2 = ( button != c_button_select ); m_selector.mouseDown( devicePosition ); g_mouseMovedCallback.insert( MouseEventCallback( Selector_::MouseMovedCaller( m_selector ) ) ); g_mouseUpCallback.insert( MouseEventCallback( Selector_::MouseUpCaller( m_selector ) ) ); @@ -4386,16 +4617,19 @@ void onMouseDown( const WindowVector& position, ButtonIdentifier button, Modifie m_texmanipulator.mouseDown( devicePosition ); g_mouseMovedCallback.insert( MouseEventCallback( TexManipulator_::MouseMovedCaller( m_texmanipulator ) ) ); g_mouseUpCallback.insert( MouseEventCallback( TexManipulator_::MouseUpCaller( m_texmanipulator ) ) ); - - } + m_moveStart = device_constrained( window_to_normalised_device( position, m_width, m_height ) ); + m_movePressed = 0.f; } void onMouseMotion( const WindowVector& position, ModifierFlags modifiers ){ - m_selector.m_mouseMoved = true; + m_selector.m_mouseMoved = mouse_moved_epsilon( position, m_moveEnd, m_width, m_height, m_moveEpsilon, m_move ); if ( m_mouse_down && !g_mouseMovedCallback.empty() ) { - m_selector.m_mouseMovedWhilePressed = true; + m_manipulator.m_mouseMovedWhilePressed = m_selector.m_mouseMovedWhilePressed = mouse_moved_epsilon( position, m_moveStart, m_width, m_height, m_moveEpsilon, m_movePressed ); g_mouseMovedCallback.get() ( window_to_normalised_device( position, m_width, m_height ) ); } + else{ + getSelectionSystem().HighlightManipulator( *m_manipulator.m_view, &window_to_normalised_device( position, m_width, m_height )[0], &m_manipulator.m_epsilon[0] ); + } } void onMouseUp( const WindowVector& position, ButtonIdentifier button, ModifierFlags modifiers ){ if ( ( button == c_button_select || button == c_button_select2 || button == c_button_texture ) && !g_mouseUpCallback.empty() ) { @@ -4408,12 +4642,17 @@ void onMouseUp( const WindowVector& position, ButtonIdentifier button, ModifierF 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 ) ){ + ( getSelectionSystem().Mode() != SelectionSystem::eComponent || getSelectionSystem().ManipulatorMode() != SelectionSystem::eDrag ) + && !m_manipulator.m_moving_transformOrigin ){ m_selector.testSelect_simpleM1( device_constrained( window_to_normalised_device( position, m_width, m_height ) ) ); } //getSelectionSystem().m_undo_begun = false; + m_manipulator.m_moving_transformOrigin = false; m_selector.m_mouseMoved = false; m_selector.m_mouseMovedWhilePressed = false; + m_manipulator.m_mouseMovedWhilePressed = false; + m_moveEnd = device_constrained( window_to_normalised_device( position, m_width, m_height ) ); + m_move = 0.f; } void onModifierDown( ModifierFlags type ){ m_selector.modifierEnable( type ); @@ -4425,6 +4664,11 @@ void onModifierUp( ModifierFlags type ){ m_manipulator.modifierDisable( type ); m_texmanipulator.modifierDisable( type ); } +/* TODO: support mouse_moved_epsilon in freelook too */ +void setMouseMoved(){ + m_move = 1.f; + m_movePressed = 1.f; +} }; diff --git a/radiant/selection.h b/radiant/selection.h index 16b9aa2f..a611b4a1 100644 --- a/radiant/selection.h +++ b/radiant/selection.h @@ -40,6 +40,7 @@ class SelectionSystemWindowObserver : public WindowObserver public: virtual void setView( const View& view ) = 0; virtual void setRectangleDrawCallback( const RectangleCallback& callback ) = 0; +virtual void setMouseMoved() = 0; }; SelectionSystemWindowObserver* NewWindowObserver(); diff --git a/radiant/xywindow.cpp b/radiant/xywindow.cpp index db1cef55..9ad9fdc7 100644 --- a/radiant/xywindow.cpp +++ b/radiant/xywindow.cpp @@ -1100,10 +1100,10 @@ void XYWnd::SetCustomPivotOrigin( int pointx, int pointy ){ XY_ToPoint( pointx, pointy, point ); VIEWTYPE viewtype = GetViewType(); const int nDim = ( viewtype == YZ ) ? 0 : ( ( viewtype == XZ ) ? 1 : 2 ); - //vector3_snap( point, GetSnapGridSize() ); - point[nDim] = 999999; + bool set[3] = { true, true, true }; + set[nDim] = false; - GlobalSelectionSystem().setCustomPivotOrigin( point ); + GlobalSelectionSystem().setCustomTransformOrigin( point, set ); SceneChangeNotify(); }