diff --git a/include/selectable.h b/include/selectable.h index daf0491c..fa63d447 100644 --- a/include/selectable.h +++ b/include/selectable.h @@ -302,6 +302,7 @@ virtual void selectReversedPlanes( Selector& selector, const SelectedPlanes& sel virtual void bestPlaneDirect( SelectionTest& test, Plane3& plane, SelectionIntersection& intersection ) = 0; virtual void bestPlaneIndirect( SelectionTest& test, Plane3& plane, Vector3& intersection, float& dist ) = 0; virtual void selectByPlane( const Plane3& plane ) = 0; +virtual void gatherPolygonsByPlane( const Plane3& plane, std::vector>& polygons ) const = 0; }; diff --git a/libs/dragplanes.h b/libs/dragplanes.h index c8e4d568..dc00413f 100644 --- a/libs/dragplanes.h +++ b/libs/dragplanes.h @@ -227,28 +227,28 @@ void bestPlaneIndirect( const AABB& aabb, SelectionTest& test, Plane3& plane, Ve */ const std::size_t edges[24] = { - 0, 1, - 1, 2, - 2, 3, - 3, 0, + 0, 1, // x + 3, 2, + 7, 6, 4, 5, - 5, 6, - 6, 7, + 2, 1, // y + 3, 0, + 6, 5, 7, 4, - 0, 4, - 1, 5, - 2, 6, - 3, 7, + 4, 0, // z + 5, 1, + 6, 2, + 7, 3, }; const std::size_t adjacent_planes[24] = { 4, 2, - 4, 0, 4, 3, - 4, 1, - 5, 2, - 5, 0, 5, 3, + 5, 2, + 4, 0, + 4, 1, + 5, 0, 5, 1, 1, 2, 2, 0, @@ -256,31 +256,55 @@ void bestPlaneIndirect( const AABB& aabb, SelectionTest& test, Plane3& plane, Ve 3, 1, }; + float dot = 1; + const bool some_extent_zero = aabb.extents[0] == 0 || aabb.extents[1] == 0 || aabb.extents[2] == 0; for ( std::size_t i = 0; i < 24; ++++i ){ Line line( corners[edges[i]], corners[edges[i + 1]] ); - if( matrix4_clip_line_by_nearplane( test.getVolume().GetViewMatrix(), line ) == 2 ){ + if( aabb.extents[i / 8] != 0.f && matrix4_clip_line_by_nearplane( test.getVolume().GetViewMatrix(), line ) == 2 ){ const Vector3 intersection_new = line_closest_point( line, g_vector3_identity ); const float dist_new = vector3_length_squared( intersection_new ); - if( dist_new < dist ){ + const float dot_new = fabs( vector3_dot( vector3_normalised( intersection_new ), vector3_normalised( line.end - line.start ) ) ); + //effective epsilon is rather big: optimized 32 bit build is using doubles implicitly (floats might be straightly checked for equality); same code in brush.h is cool with way smaller epsilon + if( dist - dist_new > 1e-2f // new dist noticeably smaller + || ( float_equal_epsilon( dist_new, dist, 1e-2f ) && dot_new < dot ) ){ // or ambiguous case. Resolve it by dot comparison const Plane3& plane1 = planes[adjacent_planes[i]]; const Plane3& plane2 = planes[adjacent_planes[i + 1]]; - if( plane3_distance_to_point( plane1, test.getVolume().getViewer() ) <= 0 ){ - if( aabb.extents[( ( adjacent_planes[i] >> 1 ) << 1 ) / 2] == 0 ) /* select the other, if zero bound */ - plane = plane2; - else - plane = plane1; - intersection = intersection_new; - dist = dist_new; - } - else{ - if( plane3_distance_to_point( plane2, test.getVolume().getViewer() ) <= 0 ){ - if( aabb.extents[( ( adjacent_planes[i + 1] >> 1 ) << 1 ) / 2] == 0 ) /* select the other, if zero bound */ - plane = plane1; - else - plane = plane2; + + auto assign_plane = [&plane, &intersection, intersection_new, &dist, dist_new, &dot, dot_new]( const Plane3& plane_new ){ + plane = plane_new; intersection = intersection_new; dist = dist_new; + dot = dot_new; + }; + + if( test.getVolume().fill() ){ + if( plane3_distance_to_point( plane1, test.getVolume().getViewer() ) <= 0 ){ + if( aabb.extents[adjacent_planes[i] / 2] == 0 ) /* select the other, if zero bound */ + assign_plane( plane2 ); + else + assign_plane( plane1 ); } + else if( plane3_distance_to_point( plane2, test.getVolume().getViewer() ) <= 0 ){ + if( aabb.extents[adjacent_planes[i + 1] / 2] == 0 ) /* select the other, if zero bound */ + assign_plane( plane1 ); + else + assign_plane( plane2 ); + } + } + else if( some_extent_zero || fabs( vector3_length_squared( line.end - line.start ) ) > 1e-3 ){ + if( fabs( vector3_dot( plane1.normal(), test.getVolume().getViewDir() ) ) < fabs( vector3_dot( plane2.normal(), test.getVolume().getViewDir() ) ) ){ + if( aabb.extents[adjacent_planes[i] / 2] == 0 ) /* select the other, if zero bound */ + assign_plane( plane2 ); + else + assign_plane( plane1 ); + } + else{ + if( aabb.extents[adjacent_planes[i + 1] / 2] == 0 ) /* select the other, if zero bound */ + assign_plane( plane1 ); + else + assign_plane( plane2 ); + } + } } } @@ -294,9 +318,38 @@ void selectByPlane( const AABB& aabb, const Plane3& plane, const Matrix4& rotati for ( std::size_t i = 0; i < 6; ++i ){ if( plane3_equal( plane, planes[i] ) || plane3_equal( plane, plane3_flipped( planes[i] ) ) ){ m_selectables[i].setSelected( true ); + return; } } } +void gatherPolygonsByPlane( const AABB& aabb, const Plane3& plane, std::vector>& polygons, const Matrix4& rotation = g_matrix4_identity ) const { + Vector3 corners[8]; + aabb_corners_oriented( aabb, rotation, corners ); + + Plane3 planes[6]; + aabb_planes_oriented( aabb, rotation, planes ); + + const std::size_t indices[24] = { + 2, 1, 5, 6, //+x //right + 3, 7, 4, 0, //-x //left + 1, 0, 4, 5, //+y //front + 3, 2, 6, 7, //-y //back + 0, 1, 2, 3, //+z //top + 7, 6, 5, 4, //-z //bottom + }; + + for ( std::size_t i = 0; i < 6; ++i ){ + if( plane3_equal( plane, planes[i] ) || plane3_equal( plane, plane3_flipped( planes[i] ) ) ){ + const std::size_t index = i * 4; + polygons.emplace_back( std::initializer_list( { corners[indices[index]], + corners[indices[index + 1]], + corners[indices[index + 2]], + corners[indices[index + 3]] } ) ); + return; + } + } + +} AABB evaluateResize( const Vector3& translation ) const { Vector3 min = m_bounds.origin - m_bounds.extents; diff --git a/libs/scenelib.h b/libs/scenelib.h index de5a5081..f8d6f7b7 100644 --- a/libs/scenelib.h +++ b/libs/scenelib.h @@ -40,6 +40,7 @@ class Selector; class SelectionTest; +class SelectionIntersection; class ComponentSelectionTestable { @@ -49,6 +50,7 @@ STRING_CONSTANT( Name, "ComponentSelectionTestable" ); virtual bool isSelectedComponents() const = 0; virtual void setSelectedComponents( bool select, SelectionSystem::EComponentMode mode ) = 0; virtual void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ) = 0; +virtual void gatherComponentsHighlight( std::vector>& polygons, SelectionIntersection& intersection, SelectionTest& test, SelectionSystem::EComponentMode mode ) const = 0; }; typedef std::function Vector3Callback; diff --git a/plugins/entity/doom3group.cpp b/plugins/entity/doom3group.cpp index b7d47198..4a9dbb16 100644 --- a/plugins/entity/doom3group.cpp +++ b/plugins/entity/doom3group.cpp @@ -544,6 +544,8 @@ void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSys m_curveCatmullRom.testSelect( selector, test ); } } +void gatherComponentsHighlight( std::vector>& polygons, SelectionIntersection& intersection, SelectionTest& test, SelectionSystem::EComponentMode mode ) const { +} void transformComponents( const Matrix4& matrix ){ if ( m_curveNURBS.isSelected() ) { diff --git a/plugins/entity/light.cpp b/plugins/entity/light.cpp index f9a362fa..c5b170eb 100644 --- a/plugins/entity/light.cpp +++ b/plugins/entity/light.cpp @@ -1884,6 +1884,11 @@ void selectByPlane( const Plane3& plane ){ m_dragPlanes.selectByPlane( m_contained.aabb(), plane, rotation() ); } } +void gatherPolygonsByPlane( const Plane3& plane, std::vector>& polygons ) const { + if ( g_lightType == LIGHTTYPE_DOOM3 ) { + m_dragPlanes.gatherPolygonsByPlane( m_contained.aabb(), plane, polygons, rotation() ); + } +} bool isSelectedComponents() const { @@ -1906,6 +1911,8 @@ void setSelectedComponents( bool select, SelectionSystem::EComponentMode mode ){ } void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ){ } +void gatherComponentsHighlight( std::vector>& polygons, SelectionIntersection& intersection, SelectionTest& test, SelectionSystem::EComponentMode mode ) const { +} void selectedChangedComponent( const Selectable& selectable ){ GlobalSelectionSystem().getObserver ( SelectionSystem::eComponent )( selectable ); diff --git a/radiant/brush.h b/radiant/brush.h index 095c0881..6b5e3cbe 100644 --- a/radiant/brush.h +++ b/radiant/brush.h @@ -1205,7 +1205,7 @@ void testSelect( SelectionTest& test, SelectionIntersection& best ){ Winding_testSelect( m_winding, test, best, m_plane.getPlanePoints() ); } -void testSelect_centroid( SelectionTest& test, SelectionIntersection& best ){ +void testSelect_centroid( SelectionTest& test, SelectionIntersection& best ) const { test.TestPoint( m_centroid, best ); } @@ -1502,12 +1502,12 @@ inline FaceVertexId next_vertex( const Faces& faces, FaceVertexId faceVertex ){ class SelectableEdge { +public: Vector3 getEdge() const { const Winding& winding = getFace().getWinding(); return vector3_mid( winding[m_faceVertex.getVertex()].vertex, winding[Winding_next( winding, m_faceVertex.getVertex() )].vertex ); } -public: Faces& m_faces; FaceVertexId m_faceVertex; @@ -1530,11 +1530,11 @@ void testSelect( SelectionTest& test, SelectionIntersection& best ){ class SelectableVertex { +public: Vector3 getVertex() const { return getFace().getWinding()[m_faceVertex.getVertex()].vertex; } -public: Faces& m_faces; FaceVertexId m_faceVertex; @@ -2859,7 +2859,7 @@ void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& localT } } -void testSelect( SelectionTest& test, SelectionIntersection& best ){ +void testSelect( SelectionTest& test, SelectionIntersection& best ) const { if ( !m_face->isFiltered() ) { m_face->testSelect( test, best ); } @@ -3217,46 +3217,48 @@ void testSelect( Selector& selector, SelectionTest& test ){ Selector_add( selector, *this, best ); } } +void gatherComponentsHighlight( std::vector>& polygons, SelectionIntersection& intersection, SelectionTest& test ) const { + SelectionIntersection best; + m_edge->testSelect( test, best ); + if ( SelectionIntersection_closer( best, intersection ) ) { + intersection = best; + polygons.clear(); + polygons.emplace_back( std::initializer_list( { m_edge->getEdge() } ) ); + } +} -void bestPlaneIndirect( const SelectionTest& test, Plane3& plane, Vector3& intersection, float& dist ) const { +void bestPlaneIndirect( const SelectionTest& test, Plane3& plane, Vector3& intersection, float& dist, float& dot ) const { const Winding& winding = m_edge->getFace().getWinding(); FaceVertexId faceVertex = m_edge->m_faceVertex; Line line( winding[faceVertex.getVertex()].vertex, winding[Winding_next( winding, faceVertex.getVertex() )].vertex ); if( matrix4_clip_line_by_nearplane( test.getVolume().GetViewMatrix(), line ) == 2 ){ const Vector3 intersection_new = line_closest_point( line, g_vector3_identity ); const float dist_new = vector3_length_squared( intersection_new ); - if( dist_new < dist ){ + const float dot_new = fabs( vector3_dot( vector3_normalised( intersection_new ), vector3_normalised( line.end - line.start ) ) ); + if( dist - dist_new > 1e-6f // new dist noticeably smaller + || ( float_equal_epsilon( dist_new, dist, 1e-6f ) && dot_new < dot ) ){ // or ambiguous case. Resolve it by dot comparison + const Plane3& plane1 = m_faceInstances[faceVertex.getFace()].getFace().plane3(); + faceVertex = next_edge( m_edge->m_faces, faceVertex ); + const Plane3& plane2 = m_faceInstances[faceVertex.getFace()].getFace().plane3(); + + auto assign_plane = [&plane, &intersection, intersection_new, &dist, dist_new, &dot, dot_new]( const Plane3& plane_new ){ + plane = plane_new; + intersection = intersection_new; + dist = dist_new; + dot = dot_new; + }; + if( test.getVolume().fill() ){ - const Plane3& plane1 = m_faceInstances[faceVertex.getFace()].getFace().plane3(); - if( plane3_distance_to_point( plane1, test.getVolume().getViewer() ) <= 0 ){ - plane = plane1; - intersection = intersection_new; - dist = dist_new; - } - else{ - faceVertex = next_edge( m_edge->m_faces, faceVertex ); - const Plane3& plane2 = m_faceInstances[faceVertex.getFace()].getFace().plane3(); - if( plane3_distance_to_point( plane2, test.getVolume().getViewer() ) <= 0 ){ - plane = plane2; - intersection = intersection_new; - dist = dist_new; - } - } + if( plane3_distance_to_point( plane1, test.getVolume().getViewer() ) <= 0 ) + assign_plane( plane1 ); + else if( plane3_distance_to_point( plane2, test.getVolume().getViewer() ) <= 0 ) + assign_plane( plane2 ); } - else{ - const Plane3& plane1 = m_faceInstances[faceVertex.getFace()].getFace().plane3(); - faceVertex = next_edge( m_edge->m_faces, faceVertex ); - const Plane3& plane2 = m_faceInstances[faceVertex.getFace()].getFace().plane3(); - if( fabs( vector3_dot( plane1.normal(), test.getVolume().getViewDir() ) ) < fabs( vector3_dot( plane2.normal(), test.getVolume().getViewDir() ) ) ){ - plane = plane1; - intersection = intersection_new; - dist = dist_new; - } - else{ - plane = plane2; - intersection = intersection_new; - dist = dist_new; - } + else if( fabs( vector3_length_squared( line.end - line.start ) ) > 1e-3 ){ + if( fabs( vector3_dot( plane1.normal(), test.getVolume().getViewDir() ) ) < fabs( vector3_dot( plane2.normal(), test.getVolume().getViewDir() ) ) ) + assign_plane( plane1 ); + else + assign_plane( plane2 ); } } } @@ -3313,6 +3315,15 @@ void testSelect( Selector& selector, SelectionTest& test ){ Selector_add( selector, *this, best ); } } +void gatherComponentsHighlight( std::vector>& polygons, SelectionIntersection& intersection, SelectionTest& test ) const { + SelectionIntersection best; + m_vertex->testSelect( test, best ); + if ( SelectionIntersection_closer( best, intersection ) ) { + intersection = best; + polygons.clear(); + polygons.emplace_back( std::initializer_list( { m_vertex->getVertex() } ) ); + } +} void selectVerticesOfFace( const FaceInstance& faceinstance ){ FaceVertexId faceVertex = m_vertex->m_faceVertex; @@ -3320,13 +3331,14 @@ void selectVerticesOfFace( const FaceInstance& faceinstance ){ { if( &faceinstance == &m_faceInstances[faceVertex.getFace()] ){ setSelected( true ); + return; } faceVertex = next_vertex( m_vertex->m_faces, faceVertex ); } while ( faceVertex.getFace() != m_vertex->m_faceVertex.getFace() ); } void gather( Brush::VertexModeVertices& vertexModeVertices ) const { - vertexModeVertices.emplace_back( m_vertex->getFace().getWinding()[m_vertex->m_faceVertex.getVertex()].vertex, isSelected() ); + vertexModeVertices.emplace_back( m_vertex->getVertex(), isSelected() ); FaceVertexId faceVertex = m_vertex->m_faceVertex; do { @@ -3336,7 +3348,7 @@ void gather( Brush::VertexModeVertices& vertexModeVertices ) const { while ( faceVertex.getFace() != m_vertex->m_faceVertex.getFace() ); } bool vertex_select( const Vector3& vertex ){ - if( vector3_length_squared( vertex - m_vertex->getFace().getWinding()[m_vertex->m_faceVertex.getVertex()].vertex ) < ( 0.1 * 0.1 ) ){ + if( vector3_length_squared( vertex - m_vertex->getVertex() ) < ( 0.1 * 0.1 ) ){ setSelected( true ); return true; } @@ -3778,6 +3790,71 @@ void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSys break; } } +void gatherComponentsHighlight( std::vector>& polygons, SelectionIntersection& intersection, SelectionTest& test, SelectionSystem::EComponentMode mode ) const { + test.BeginMesh( localToWorld() ); + + switch ( mode ) + { + case SelectionSystem::eVertex: + { + for ( const VertexInstance& i : m_vertexInstances ) + { + i.gatherComponentsHighlight( polygons, intersection, test ); + } + } + break; + case SelectionSystem::eEdge: + { + for ( const EdgeInstance& i : m_edgeInstances ) + { + i.gatherComponentsHighlight( polygons, intersection, test ); + } + } + break; + case SelectionSystem::eFace: + { + if ( test.getVolume().fill() ) { + const Plane3* plane = nullptr; + bool newint = false; + for ( const FaceInstance& i : m_faceInstances ) + { + SelectionIntersection best; + i.testSelect( test, best ); + if( best.valid() && intersection.equalEpsilon( best, 0.25f, 2e-6f ) ){ + plane = &i.getFace().plane3(); + } + else if ( SelectionIntersection_closer( best, intersection ) ) { + intersection = best; + newint = true; + plane = &i.getFace().plane3(); + } + } + if( plane ){ + if( newint || ( !polygons.empty() && polygons.back().size() >= 3 && !plane3_equal( *plane, plane3_for_points( polygons.back().data() ) ) ) ){ + polygons.clear(); + } + gatherPolygonsByPlane( *plane, polygons, false ); + } + } + else + { + for ( const FaceInstance& i : m_faceInstances ) + { + SelectionIntersection best; + i.getFace().testSelect_centroid( test, best ); + if ( SelectionIntersection_closer( best, intersection ) ) { + intersection = best; + polygons.clear(); + polygons.emplace_back( std::initializer_list( { i.getFace().centroid() } ) ); + } + } + } + } + break; + default: + break; + } +} void invertComponentSelection( SelectionSystem::EComponentMode mode ){ switch ( mode ) @@ -3867,9 +3944,10 @@ void bestPlaneDirect( SelectionTest& test, Plane3& plane, SelectionIntersection& } void bestPlaneIndirect( SelectionTest& test, Plane3& plane, Vector3& intersection, float& dist ){ test.BeginMesh( localToWorld() ); + float dot = 1; for ( EdgeInstances::iterator i = m_edgeInstances.begin(); i != m_edgeInstances.end(); ++i ) { - ( *i ).bestPlaneIndirect( test, plane, intersection, dist ); + ( *i ).bestPlaneIndirect( test, plane, intersection, dist, dot ); } } void selectByPlane( const Plane3& plane ){ @@ -3877,6 +3955,20 @@ void selectByPlane( const Plane3& plane ){ if( plane3_equal( plane, fi.getFace().plane3() ) || plane3_equal( plane, plane3_flipped( fi.getFace().plane3() ) ) ) fi.setSelected( SelectionSystem::eFace, true ); } +void gatherPolygonsByPlane( const Plane3& plane, std::vector>& polygons ) const { + gatherPolygonsByPlane( plane, polygons, true ); +} +void gatherPolygonsByPlane( const Plane3& plane, std::vector>& polygons, const bool reversed_plane_also ) const { + for ( const FaceInstance& fi : m_faceInstances ) + if( plane3_equal( plane, fi.getFace().plane3() ) || ( reversed_plane_also && plane3_equal( plane, plane3_flipped( fi.getFace().plane3() ) ) ) ) + if( fi.getFace().contributes() ){ + const Winding& winding = fi.getFace().getWinding(); + polygons.emplace_back( winding.numpoints ); + for( std::size_t i = 0; i < winding.numpoints; ++i ){ + polygons.back()[i] = winding[i].vertex; + } + } +} void selectVerticesOnPlane( const Plane3& plane ){ for ( FaceInstance& fi : m_faceInstances ) diff --git a/radiant/camwindow.cpp b/radiant/camwindow.cpp index 25cb9309..53a2bf67 100644 --- a/radiant/camwindow.cpp +++ b/radiant/camwindow.cpp @@ -906,6 +906,7 @@ void render( RenderStateFlags state ) const { glPolygonOffset( 1, -1 ); glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( Colour4b ), colorarr1.data() ); glDrawArrays( GL_QUADS, start0? 2 : 0, GLsizei( count - ( start0? 2 : 4 ) ) ); + glPolygonOffset( -1, 1 ); // restore default } } diff --git a/radiant/patch.h b/radiant/patch.h index d1622cda..1b2ca3af 100644 --- a/radiant/patch.h +++ b/radiant/patch.h @@ -1543,6 +1543,47 @@ void setSelectedComponents( bool select, SelectionSystem::EComponentMode mode ){ m_dragPlanes.setSelected( select ); } } +void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ){ + test.BeginMesh( localToWorld() ); + + switch ( mode ) + { + case SelectionSystem::eVertex: + { + for ( PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i ) + { + ( *i ).testSelect( selector, test ); + } + } + break; + default: + break; + } +} +void gatherComponentsHighlight( std::vector>& polygons, SelectionIntersection& intersection, SelectionTest& test, SelectionSystem::EComponentMode mode ) const { + test.BeginMesh( localToWorld() ); + + switch ( mode ) + { + case SelectionSystem::eVertex: + { + for ( const PatchControlInstance& pci : m_ctrl_instances ) + { + SelectionIntersection best; + test.TestPoint( pci.m_ctrl->m_vertex, best ); + if ( SelectionIntersection_closer( best, intersection ) ) { + intersection = best; + polygons.clear(); + polygons.emplace_back( std::initializer_list( { pci.m_ctrl->m_vertex } ) ); + } + } + } + break; + default: + break; + } +} + const AABB& getSelectedComponentsBounds() const { m_aabb_component = AABB(); @@ -1564,26 +1605,8 @@ void gatherSelectedComponents( const Vector3Callback& callback ) const { } } -void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ){ - test.BeginMesh( localToWorld() ); - - switch ( mode ) - { - case SelectionSystem::eVertex: - { - for ( PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i ) - { - ( *i ).testSelect( selector, test ); - } - } - break; - default: - break; - } -} - -bool selectedVertices(){ - for ( PatchControlInstances::iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i ) +bool selectedVertices() const { + for ( PatchControlInstances::const_iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i ) { if ( ( *i ).m_selectable.isSelected() ) { return true; @@ -1637,6 +1660,9 @@ void bestPlaneIndirect( SelectionTest& test, Plane3& plane, Vector3& intersectio void selectByPlane( const Plane3& plane ){ m_dragPlanes.selectByPlane( m_patch.localAABB(), plane ); } +void gatherPolygonsByPlane( const Plane3& plane, std::vector>& polygons ) const { + m_dragPlanes.gatherPolygonsByPlane( m_patch.localAABB(), plane, polygons ); +} void snapComponents( float snap ){ diff --git a/radiant/renderstate.cpp b/radiant/renderstate.cpp index a8747216..df40fc31 100644 --- a/radiant/renderstate.cpp +++ b/radiant/renderstate.cpp @@ -2325,6 +2325,20 @@ void OpenGLShader::construct( const char* name ){ state.m_sort = OpenGLState::eSortTranslucent; } #endif // 0 + else if ( string_equal( name + 1, "PLANE_WIRE_OVERLAY" ) ) { + state.m_colour = Vector4( 1, 1, 0, 1 ); + state.m_state = RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_DEPTHTEST | RENDER_OFFSETLINE; + state.m_sort = OpenGLState::eSortGUI1; + state.m_depthfunc = GL_LEQUAL; + state.m_linewidth = 2; + + OpenGLState& hiddenLine = appendDefaultPass(); + hiddenLine.m_colour = state.m_colour; + hiddenLine.m_state = RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_DEPTHTEST | RENDER_LINESTIPPLE; + hiddenLine.m_sort = OpenGLState::eSortGUI0; + hiddenLine.m_depthfunc = GL_GREATER; + hiddenLine.m_linestipple_factor = 2; + } else if ( string_equal( name + 1, "WIRE_OVERLAY" ) ) { #if 0 state.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_DEPTHTEST; diff --git a/radiant/selection.cpp b/radiant/selection.cpp index 85be8329..6a61c6bf 100644 --- a/radiant/selection.cpp +++ b/radiant/selection.cpp @@ -407,6 +407,9 @@ void Transform( const Matrix4& manip2object, const Matrix4& device2manip, const Vector3 current = g_vector3_axes[m_axisZ] * vector3_dot( m_planeSelected.normal(), ( point_on_plane( m_planeZ, m_view->GetViewMatrix(), x, y ) - m_startZ ) ) * ( m_planeSelected.normal()[m_axisZ] >= 0? 1 : -1 ); + if( !std::isfinite( current[0] ) || !std::isfinite( current[1] ) || !std::isfinite( current[2] ) ) // catch INF case, is likely with top of the box in 2D + return; + if( snapbbox ) aabb_snap_translation( current, m_bounds ); else @@ -2951,10 +2954,7 @@ inline const Functor& Scene_forEachVisibleSelectedPlaneselectable( const Functor return functor; } -bool Scene_forEachPlaneSelectable_selectPlanes2( SelectionTest& test, TranslateAxis2& translateAxis ){ - Plane3 plane( 0, 0, 0, 0 ); - Vector3 intersectionPoint( FLT_MAX, FLT_MAX, FLT_MAX ); - +void Scene_forEachPlaneSelectable_bestPlane( SelectionTest& test, Plane3& plane, Vector3& intersectionPoint ){ SelectionIntersection intersection; auto bestPlaneDirect = [&test, &plane, &intersection]( PlaneSelectable& planeSelectable ){ planeSelectable.bestPlaneDirect( test, plane, intersection ); @@ -2967,6 +2967,12 @@ bool Scene_forEachPlaneSelectable_selectPlanes2( SelectionTest& test, TranslateA }; Scene_forEachVisibleSelectedPlaneselectable( bestPlaneIndirect ); } +} + +bool Scene_forEachPlaneSelectable_selectPlanes2( SelectionTest& test, TranslateAxis2& translateAxis ){ + Plane3 plane( 0, 0, 0, 0 ); + Vector3 intersectionPoint( FLT_MAX, FLT_MAX, FLT_MAX ); + Scene_forEachPlaneSelectable_bestPlane( test, plane, intersectionPoint ); if( plane3_valid( plane ) ){ if( intersectionPoint == Vector3( FLT_MAX, FLT_MAX, FLT_MAX ) ){ // direct @@ -2988,23 +2994,24 @@ bool Scene_forEachPlaneSelectable_selectPlanes2( SelectionTest& test, TranslateA } -bool Scene_forEachBrush_setupExtrude( SelectionTest& test, DragExtrudeFaces& extrudeFaces ){ - Plane3 plane( 0, 0, 0, 0 ); - Vector3 intersectionPoint( FLT_MAX, FLT_MAX, FLT_MAX ); - - if( g_SelectedFaceInstances.empty() ){ - SelectionIntersection intersection; - auto bestPlaneDirect = [&test, &plane, &intersection]( BrushInstance& brushInstance ){ - brushInstance.bestPlaneDirect( test, plane, intersection ); +void Scene_forEachSelectedBrush_bestPlane( SelectionTest& test, Plane3& plane, Vector3& intersectionPoint ){ + SelectionIntersection intersection; + auto bestPlaneDirect = [&test, &plane, &intersection]( BrushInstance& brushInstance ){ + brushInstance.bestPlaneDirect( test, plane, intersection ); + }; + Scene_forEachVisibleSelectedBrush( bestPlaneDirect ); + if( !plane3_valid( plane ) ){ + float dist( FLT_MAX ); + auto bestPlaneIndirect = [&test, &plane, &intersectionPoint, &dist]( BrushInstance& brushInstance ){ + brushInstance.bestPlaneIndirect( test, plane, intersectionPoint, dist ); }; - Scene_forEachVisibleSelectedBrush( bestPlaneDirect ); - if( !plane3_valid( plane ) ){ - float dist( FLT_MAX ); - auto bestPlaneIndirect = [&test, &plane, &intersectionPoint, &dist]( BrushInstance& brushInstance ){ - brushInstance.bestPlaneIndirect( test, plane, intersectionPoint, dist ); - }; - Scene_forEachVisibleSelectedBrush( bestPlaneIndirect ); - } + Scene_forEachVisibleSelectedBrush( bestPlaneIndirect ); + } +} + +void Scene_forEachBrush_bestPlane( SelectionTest& test, Plane3& plane, Vector3& intersectionPoint ){ + if( g_SelectedFaceInstances.empty() ){ + Scene_forEachSelectedBrush_bestPlane( test, plane, intersectionPoint ); } else{ SelectionIntersection intersection; @@ -3023,6 +3030,12 @@ bool Scene_forEachBrush_setupExtrude( SelectionTest& test, DragExtrudeFaces& ext Scene_forEachVisibleBrush( GlobalSceneGraph(), bestPlaneIndirect ); } } +} + +bool Scene_forEachBrush_setupExtrude( SelectionTest& test, DragExtrudeFaces& extrudeFaces ){ + Plane3 plane( 0, 0, 0, 0 ); + Vector3 intersectionPoint( FLT_MAX, FLT_MAX, FLT_MAX ); + Scene_forEachBrush_bestPlane( test, plane, intersectionPoint ); if( plane3_valid( plane ) ){ if( intersectionPoint == Vector3( FLT_MAX, FLT_MAX, FLT_MAX ) ){ // direct @@ -4201,20 +4214,8 @@ bool selection_selectVerticesOrFaceVertices( SelectionTest& test ){ } /* otherwise select vertices of brush faces, which lay on best plane */ Plane3 plane( 0, 0, 0, 0 ); - SelectionIntersection intersection; - auto bestPlaneDirect = [&test, &plane, &intersection]( BrushInstance& brushInstance ){ - brushInstance.bestPlaneDirect( test, plane, intersection ); - }; - Scene_forEachVisibleSelectedBrush( bestPlaneDirect ); - - if( !plane3_valid( plane ) ){ - Vector3 intersectionPoint; - float dist( FLT_MAX ); - auto bestPlaneIndirect = [&test, &plane, &intersectionPoint, &dist]( BrushInstance& brushInstance ){ - brushInstance.bestPlaneIndirect( test, plane, intersectionPoint, dist ); - }; - Scene_forEachVisibleSelectedBrush( bestPlaneIndirect ); - } + Vector3 intersectionPoint( FLT_MAX, FLT_MAX, FLT_MAX ); + Scene_forEachSelectedBrush_bestPlane( test, plane, intersectionPoint ); if( plane3_valid( plane ) ){ auto selectVerticesOnPlane = [&plane]( BrushInstance& brushInstance ){ @@ -4227,6 +4228,29 @@ bool selection_selectVerticesOrFaceVertices( SelectionTest& test ){ } +template +class ComponentSelectionTestableVisibleSelectedVisitor : public SelectionSystem::Visitor +{ +const Functor& m_functor; +public: +ComponentSelectionTestableVisibleSelectedVisitor( const Functor& functor ) : m_functor( functor ){ +} +void visit( scene::Instance& instance ) const { + ComponentSelectionTestable* componentSelectionTestable = Instance_getComponentSelectionTestable( instance ); + if ( componentSelectionTestable != 0 + && instance.path().top().get().visible() ) { + m_functor( *componentSelectionTestable ); + } +} +}; + +template +inline const Functor& Scene_forEachVisibleSelectedComponentSelectionTestable( const Functor& functor ){ + GlobalSelectionSystem().foreachSelected( ComponentSelectionTestableVisibleSelectedVisitor( functor ) ); + return functor; +} + + static ModifierFlags g_modifiers = c_modifierNone; //AltDragManipulatorResize, extrude, uvtool skew, select primitives in component modes static bool g_bTmpComponentMode = false; @@ -4247,8 +4271,11 @@ bool m_extrudeFaces; public: -DragManipulator( Translatable& translatable ) : m_freeResize( m_resize ), m_axisResize( m_resize ), m_freeDragXY_Z( translatable ){ +static Shader* m_state_wire; + +DragManipulator( Translatable& translatable ) : m_freeResize( m_resize ), m_axisResize( m_resize ), m_freeDragXY_Z( translatable ), m_renderCircle( 2 << 3 ){ setSelected( false ); + draw_circle( m_renderCircle.m_vertices.size() >> 3, 5, m_renderCircle.m_vertices.data(), RemapXYZ() ); } Manipulatable* GetManipulatable(){ @@ -4268,16 +4295,18 @@ void testSelect( const View& view, const Matrix4& pivot2world ){ SelectionPool selector; SelectionVolume test( view ); - if( g_modifiers == ( c_modifierAlt | c_modifierControl ) && ( GlobalSelectionSystem().countSelected() != 0 || !g_SelectedFaceInstances.empty() ) ){ // extrude + if( g_modifiers == ( c_modifierAlt | c_modifierControl ) + && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive + && ( GlobalSelectionSystem().countSelected() != 0 || !g_SelectedFaceInstances.empty() ) ){ // extrude m_extrudeFaces = Scene_forEachBrush_setupExtrude( test, m_dragExtrudeFaces ); } else if( GlobalSelectionSystem().countSelected() != 0 ){ if ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ){ if( g_modifiers == c_modifierAlt ){ - if( view.fill() ){ + if( view.fill() ){ // alt resize m_selected2 = Scene_forEachPlaneSelectable_selectPlanes2( test, m_axisResize ); } - else{ + else{ // alt vertices drag m_selected = selection_selectVerticesOrFaceVertices( test ); } } @@ -4295,7 +4324,7 @@ void testSelect( const View& view, const Matrix4& pivot2world ){ } } } - else{ + else{ // components BestSelector bestSelector; Scene_TestSelect_Component_Selected( bestSelector, test, view, GlobalSelectionSystem().ComponentMode() ); /* drag components */ for ( Selectable* s : bestSelector.best() ){ @@ -4353,7 +4382,126 @@ void setSelected( bool select ){ bool isSelected() const { return m_dragSelected || m_selected || m_selected2 || m_newBrush || m_extrudeFaces; } + +void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& pivot2world ){ + if( !m_polygons.empty() ){ + renderer.SetState( m_state_wire, Renderer::eWireframeOnly ); + renderer.SetState( m_state_wire, Renderer::eFullMaterials ); + if( m_polygons.back().size() == 1 ){ + Pivot2World_viewplaneSpace( m_renderCircle.m_viewplaneSpace, matrix4_translation_for_vec3( m_polygons.back()[0] ), volume.GetModelview(), volume.GetProjection(), volume.GetViewport() ); + renderer.addRenderable( m_renderCircle, m_renderCircle.m_viewplaneSpace ); + } + else{ + renderer.addRenderable( m_renderPoly, g_matrix4_identity ); + } + } +} +void highlight( const View& view ){ + SelectionVolume test( view ); + std::vector> polygons; + /* conditions structure respects one in testSelect() */ + if( g_modifiers == ( c_modifierAlt | c_modifierControl ) + && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive + && ( GlobalSelectionSystem().countSelected() != 0 || !g_SelectedFaceInstances.empty() ) ){ // extrude + Plane3 plane( 0, 0, 0, 0 ); + Vector3 intersectionPoint( FLT_MAX, FLT_MAX, FLT_MAX ); + Scene_forEachBrush_bestPlane( test, plane, intersectionPoint ); + + if( plane3_valid( plane ) ){ + auto gatherPolygonsByPlane = [plane, &polygons]( BrushInstance& brushInstance ){ + if( brushInstance.isSelected() || brushInstance.isSelectedComponents() ) + brushInstance.gatherPolygonsByPlane( plane, polygons, false ); + }; + Scene_forEachVisibleBrush( GlobalSceneGraph(), gatherPolygonsByPlane ); + } + } + else if( GlobalSelectionSystem().countSelected() != 0 ){ + if ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ){ + if( g_modifiers == c_modifierAlt ){ + if( view.fill() ){ // alt resize + Plane3 plane( 0, 0, 0, 0 ); + Vector3 intersectionPoint( FLT_MAX, FLT_MAX, FLT_MAX ); + Scene_forEachPlaneSelectable_bestPlane( test, plane, intersectionPoint ); + + if( plane3_valid( plane ) ){ + auto gatherPolygonsByPlane = [plane, &polygons]( PlaneSelectable& planeSelectable ){ + planeSelectable.gatherPolygonsByPlane( plane, polygons ); + }; + Scene_forEachVisibleSelectedPlaneselectable( gatherPolygonsByPlane ); + } + + } + else{ // alt vertices drag + SelectionIntersection intersection; + const SelectionSystem::EComponentMode mode = SelectionSystem::eVertex; + auto gatherComponentsHighlight = [&polygons, &intersection, &test, mode]( const ComponentSelectionTestable& componentSelectionTestable ){ + componentSelectionTestable.gatherComponentsHighlight( polygons, intersection, test, mode ); + }; + Scene_forEachVisibleSelectedComponentSelectionTestable( gatherComponentsHighlight ); + + if( polygons.empty() ){ + Plane3 plane( 0, 0, 0, 0 ); + Vector3 intersectionPoint( FLT_MAX, FLT_MAX, FLT_MAX ); + Scene_forEachSelectedBrush_bestPlane( test, plane, intersectionPoint ); + + if( plane3_valid( plane ) ){ + auto gatherPolygonsByPlane = [plane, &polygons]( BrushInstance& brushInstance ){ + brushInstance.gatherPolygonsByPlane( plane, polygons ); + }; + Scene_forEachVisibleSelectedBrush( gatherPolygonsByPlane ); + } + } + } + } + } + else{ // components + SelectionIntersection intersection; + const SelectionSystem::EComponentMode mode = GlobalSelectionSystem().ComponentMode(); + auto gatherComponentsHighlight = [&polygons, &intersection, &test, mode]( const ComponentSelectionTestable& componentSelectionTestable ){ + componentSelectionTestable.gatherComponentsHighlight( polygons, intersection, test, mode ); + }; + Scene_forEachVisibleSelectedComponentSelectionTestable( gatherComponentsHighlight ); + } + } + + if( m_polygons != polygons ){ + m_polygons.swap( polygons ); + SceneChangeNotify(); + } +} +private: +std::vector> m_polygons; +struct RenderablePoly: public OpenGLRenderable +{ + const std::vector>& m_polygons; + + RenderablePoly( const std::vector>& polygons ) : m_polygons( polygons ){ + } + void render( RenderStateFlags state ) const { + glPolygonOffset( -2, -2 ); + for( const auto& poly : m_polygons ){ + glVertexPointer( 3, GL_FLOAT, sizeof( m_polygons[0][0] ), poly[0].data() ); + glDrawArrays( GL_POLYGON, 0, GLsizei( poly.size() ) ); + } + glPolygonOffset( -1, 1 ); // restore default + } }; +RenderablePoly m_renderPoly{ m_polygons }; +struct RenderableCircle : public OpenGLRenderable +{ + Array m_vertices; + Matrix4 m_viewplaneSpace; + + RenderableCircle( std::size_t size ) : m_vertices( size ){ + } + void render( RenderStateFlags state ) const { + glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_vertices.data()->vertex ); + glDrawArrays( GL_LINE_LOOP, 0, GLsizei( m_vertices.size() ) ); + } +}; +RenderableCircle m_renderCircle; +}; +Shader* DragManipulator::m_state_wire; @@ -6874,7 +7022,11 @@ bool SelectManipulator( const View& view, const float device_point[2], const flo void HighlightManipulator( const View& view, const float device_point[2], const float device_epsilon[2] ){ Manipulatable::assign_static( view, device_point, device_epsilon ); //this b4 m_manipulator calls! - if ( ( !nothingSelected() && transformOrigin_isTranslatable() ) || ManipulatorMode() == eClip || ManipulatorMode() == eBuild || ManipulatorMode() == eUV ) { + if ( ( !nothingSelected() && transformOrigin_isTranslatable() ) + || ManipulatorMode() == eClip + || ManipulatorMode() == eBuild + || ManipulatorMode() == eUV + || ManipulatorMode() == eDrag ) { #if defined ( DEBUG_SELECTION ) g_render_clipped.destroy(); #endif @@ -6900,6 +7052,9 @@ void HighlightManipulator( const View& view, const float device_point[2], const else if( ManipulatorMode() == eUV ){ m_manipulator->testSelect( scissored, GetPivot2World() ); } + else if( ManipulatorMode() == eDrag ){ + m_drag_manipulator.highlight( scissored ); + } } } @@ -7358,12 +7513,14 @@ static void constructStatic(){ UVManipulator::m_state_point = GlobalShaderCache().capture( "$BIGPOINT" ); RenderablePivot::StaticShader::instance() = GlobalShaderCache().capture( "$PIVOT" ); UVManipulator::m_state_line = GlobalShaderCache().capture( "$BLENDLINE" ); + DragManipulator::m_state_wire = GlobalShaderCache().capture( "$PLANE_WIRE_OVERLAY" ); } static void destroyStatic(){ #if defined( DEBUG_SELECTION ) GlobalShaderCache().release( "$DEBUG_CLIPPED" ); #endif + GlobalShaderCache().release( "$PLANE_WIRE_OVERLAY" ); GlobalShaderCache().release( "$BLENDLINE" ); GlobalShaderCache().release( "$PIVOT" ); GlobalShaderCache().release( "$BIGPOINT" ); @@ -7805,7 +7962,11 @@ AABB RadiantSelectionSystem::getSelectionAABB() const { void RadiantSelectionSystem::renderSolid( Renderer& renderer, const VolumeTest& volume ) const { //if(view->TestPoint(m_object_pivot)) - if ( !nothingSelected() || ManipulatorMode() == eClip || ManipulatorMode() == eBuild || ManipulatorMode() == eUV ) { + if ( !nothingSelected() + || ManipulatorMode() == eClip + || ManipulatorMode() == eBuild + || ManipulatorMode() == eUV + || ManipulatorMode() == eDrag ) { renderer.Highlight( Renderer::ePrimitive, false ); renderer.Highlight( Renderer::eFace, false );