-/// \brief Returns the absolute index of the \p faceVertex.
-std::size_t absoluteIndex( FaceVertexId faceVertex ){
- std::size_t index = 0;
- for ( std::size_t i = 0; i < faceVertex.getFace(); ++i )
- {
- index += m_faces[i]->getWinding().numpoints;
- }
- return index + faceVertex.getVertex();
-}
-
-void appendFaces( const Faces& other ){
- clear();
- for ( Faces::const_iterator i = other.begin(); i != other.end(); ++i )
- {
- push_back( *i );
- }
-}
-
-/// \brief The undo memento for a brush stores only the list of face references - the faces are not copied.
-class BrushUndoMemento : public UndoMemento
-{
-public:
-BrushUndoMemento( const Faces& faces ) : m_faces( faces ){
-}
-void release(){
- delete this;
-}
-
-Faces m_faces;
-};
-
-void undoSave(){
- if ( m_map != 0 ) {
- m_map->changed();
- }
- if ( m_undoable_observer != 0 ) {
- m_undoable_observer->save( this );
- }
-}
-
-UndoMemento* exportState() const {
- return new BrushUndoMemento( m_faces );
-}
-
-void importState( const UndoMemento* state ){
- undoSave();
- appendFaces( static_cast<const BrushUndoMemento*>( state )->m_faces );
- planeChanged();
-
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->DEBUG_verify();
- }
-}
-
-bool isDetail(){
- return !m_faces.empty() && m_faces.front()->isDetail();
-}
-
-/// \brief Appends a copy of \p face to the end of the face list.
-Face* addFace( const Face& face ){
- if ( m_faces.size() == c_brush_maxFaces ) {
- return 0;
- }
- undoSave();
- push_back( FaceSmartPointer( new Face( face, this ) ) );
- m_faces.back()->setDetail( isDetail() );
- planeChanged();
- return m_faces.back();
-}
-
-/// \brief Appends a new face constructed from the parameters to the end of the face list.
-Face* addPlane( const Vector3& p0, const Vector3& p1, const Vector3& p2, const char* shader, const TextureProjection& projection ){
- if ( m_faces.size() == c_brush_maxFaces ) {
- return 0;
- }
- undoSave();
- push_back( FaceSmartPointer( new Face( p0, p1, p2, shader, projection, this ) ) );
- m_faces.back()->setDetail( isDetail() );
- planeChanged();
- return m_faces.back();
-}
-
-static void constructStatic( EBrushType type ){
- m_type = type;
- Face::m_type = type;
- FacePlane::m_type = type;
-
- g_bp_globals.m_texdefTypeId = TEXDEFTYPEID_QUAKE;
- if ( m_type == eBrushTypeQuake3BP || m_type == eBrushTypeDoom3 || m_type == eBrushTypeQuake4 ) {
- g_bp_globals.m_texdefTypeId = TEXDEFTYPEID_BRUSHPRIMITIVES;
- // g_brush_texturelock_enabled = true; // bad idea, this overrides user setting
- }
- else if ( m_type == eBrushTypeHalfLife ) {
- g_bp_globals.m_texdefTypeId = TEXDEFTYPEID_HALFLIFE;
- // g_brush_texturelock_enabled = true; // bad idea, this overrides user setting
- }
-
- Face::m_quantise = ( m_type == eBrushTypeQuake ) ? quantiseInteger : quantiseFloating;
-
- m_state_point = GlobalShaderCache().capture( "$POINT" );
-}
-static void destroyStatic(){
- GlobalShaderCache().release( "$POINT" );
-}
-
-std::size_t DEBUG_size(){
- return m_faces.size();
-}
-
-typedef Faces::const_iterator const_iterator;
-
-const_iterator begin() const {
- return m_faces.begin();
-}
-const_iterator end() const {
- return m_faces.end();
-}
-
-Face* back(){
- return m_faces.back();
-}
-const Face* back() const {
- return m_faces.back();
-}
-void reserve( std::size_t count ){
- m_faces.reserve( count );
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->reserve( count );
- }
-}
-void push_back( Faces::value_type face ){
- m_faces.push_back( face );
- if ( m_instanceCounter.m_count != 0 ) {
- m_faces.back()->instanceAttach( m_map );
- }
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->push_back( *face );
- ( *i )->DEBUG_verify();
- }
-}
-void pop_back(){
- if ( m_instanceCounter.m_count != 0 ) {
- m_faces.back()->instanceDetach( m_map );
- }
- m_faces.pop_back();
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->pop_back();
- ( *i )->DEBUG_verify();
- }
-}
-void erase( std::size_t index ){
- if ( m_instanceCounter.m_count != 0 ) {
- m_faces[index]->instanceDetach( m_map );
- }
- m_faces.erase( m_faces.begin() + index );
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->erase( index );
- ( *i )->DEBUG_verify();
- }
-}
-void connectivityChanged(){
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->connectivityChanged();
- }
-}
-
-
-void clear(){
- undoSave();
- if ( m_instanceCounter.m_count != 0 ) {
- forEachFace_instanceDetach( m_map );
- }
- m_faces.clear();
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->clear();
- ( *i )->DEBUG_verify();
- }
-}
-std::size_t size() const {
- return m_faces.size();
-}
-bool empty() const {
- return m_faces.empty();
-}
-
-/// \brief Returns true if any face of the brush contributes to the final B-Rep.
-bool hasContributingFaces() const {
- for ( const_iterator i = begin(); i != end(); ++i )
- {
- if ( ( *i )->contributes() ) {
- return true;
- }
- }
- return false;
-}
-
-/// \brief Removes faces that do not contribute to the brush. This is useful for cleaning up after CSG operations on the brush.
-/// Note: removal of empty faces is not performed during direct brush manipulations, because it would make a manipulation irreversible if it created an empty face.
-void removeEmptyFaces(){
- evaluateBRep();
-
- {
- std::size_t i = 0;
- while ( i < m_faces.size() )
- {
- if ( !m_faces[i]->contributes() ) {
- erase( i );
- planeChanged();
- }
- else
- {
- ++i;
- }
- }
- }
-}
-
-/// \brief Constructs \p winding from the intersection of \p plane with the other planes of the brush.
-void windingForClipPlane( Winding& winding, const Plane3& plane ) const {
- FixedWinding buffer[2];
- bool swap = false;
-
- // get a poly that covers an effectively infinite area
- Winding_createInfinite( buffer[swap], plane, m_maxWorldCoord + 1 );
-
- // chop the poly by all of the other faces
- {
- for ( std::size_t i = 0; i < m_faces.size(); ++i )
- {
- const Face& clip = *m_faces[i];
-
- if ( plane3_equal( clip.plane3(), plane )
- || !plane3_valid( clip.plane3() ) || !plane_unique( i )
- || plane3_opposing( plane, clip.plane3() ) ) {
- continue;
- }
-
- buffer[!swap].clear();
-
-#if BRUSH_CONNECTIVITY_DEBUG
- globalOutputStream() << "clip vs face: " << i << "\n";
-#endif
-
- {
- // flip the plane, because we want to keep the back side
- Plane3 clipPlane( vector3_negated( clip.plane3().normal() ), -clip.plane3().dist() );
- Winding_Clip( buffer[swap], plane, clipPlane, i, buffer[!swap] );
- }
-
-#if BRUSH_CONNECTIVITY_DEBUG
- for ( FixedWinding::Points::iterator k = buffer[!swap].points.begin(), j = buffer[!swap].points.end() - 1; k != buffer[!swap].points.end(); j = k, ++k )
- {
- if ( vector3_length_squared( vector3_subtracted( ( *k ).vertex, ( *j ).vertex ) ) < 1 ) {
- globalOutputStream() << "v: " << std::distance( buffer[!swap].points.begin(), j ) << " tiny edge adjacent to face " << ( *j ).adjacent << "\n";
- }
- }
-#endif
-
- //ASSERT_MESSAGE(buffer[!swap].numpoints != 1, "created single-point winding");
-
- swap = !swap;
- }
- }
-
- Winding_forFixedWinding( winding, buffer[swap] );
-
-#if BRUSH_CONNECTIVITY_DEBUG
- Winding_printConnectivity( winding );
-
- for ( Winding::iterator i = winding.begin(), j = winding.end() - 1; i != winding.end(); j = i, ++i )
- {
- if ( vector3_length_squared( vector3_subtracted( ( *i ).vertex, ( *j ).vertex ) ) < 1 ) {
- globalOutputStream() << "v: " << std::distance( winding.begin(), j ) << " tiny edge adjacent to face " << ( *j ).adjacent << "\n";
- }
- }
-#endif
-}
-
-void update_wireframe( RenderableWireframe& wire, const bool* faces_visible ) const {
- wire.m_faceVertex.resize( m_edge_indices.size() );
- wire.m_vertices = m_uniqueVertexPoints.data();
- wire.m_size = 0;
- for ( std::size_t i = 0; i < m_edge_faces.size(); ++i )
- {
- if ( faces_visible[m_edge_faces[i].first]
- || faces_visible[m_edge_faces[i].second] ) {
- wire.m_faceVertex[wire.m_size++] = m_edge_indices[i];
- }
- }
-}
-
-
-void update_faces_wireframe( Array<PointVertex>& wire, const bool* faces_visible ) const {
- std::size_t count = 0;
- for ( std::size_t i = 0; i < m_faceCentroidPoints.size(); ++i )
- {
- if ( faces_visible[i] ) {
- ++count;
- }
- }
-
- wire.resize( count );
- Array<PointVertex>::iterator p = wire.begin();
- for ( std::size_t i = 0; i < m_faceCentroidPoints.size(); ++i )
- {
- if ( faces_visible[i] ) {
- *p++ = m_faceCentroidPoints[i];
- }
- }
-}
-
-/// \brief Makes this brush a deep-copy of the \p other.
-void copy( const Brush& other ){
- for ( Faces::const_iterator i = other.m_faces.begin(); i != other.m_faces.end(); ++i )
- {
- addFace( *( *i ) );
- }
- planeChanged();
-}
-
-private:
-void edge_push_back( FaceVertexId faceVertex ){
- m_select_edges.push_back( SelectableEdge( m_faces, faceVertex ) );
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->edge_push_back( m_select_edges.back() );
- }
-}
-void edge_clear(){
- m_select_edges.clear();
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->edge_clear();
- }
-}
-void vertex_push_back( FaceVertexId faceVertex ){
- m_select_vertices.push_back( SelectableVertex( m_faces, faceVertex ) );
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->vertex_push_back( m_select_vertices.back() );
- }
-}
-void vertex_clear(){
- m_select_vertices.clear();
- for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
- {
- ( *i )->vertex_clear();
- }
-}
-
-/// \brief Returns true if the face identified by \p index is preceded by another plane that takes priority over it.
-bool plane_unique( std::size_t index ) const {
- // duplicate plane
- for ( std::size_t i = 0; i < m_faces.size(); ++i )
- {
- if ( index != i && !plane3_inside( m_faces[index]->plane3(), m_faces[i]->plane3(), index < i ) ) {
- return false;
- }
- }
- return true;
-}
-
-/// \brief Removes edges that are smaller than the tolerance used when generating brush windings.
-void removeDegenerateEdges(){
- for ( std::size_t i = 0; i < m_faces.size(); ++i )
- {
- Winding& winding = m_faces[i]->getWinding();
- for ( Winding::iterator j = winding.begin(); j != winding.end(); )
- {
- std::size_t index = std::distance( winding.begin(), j );
- std::size_t next = Winding_next( winding, index );
- if ( Edge_isDegenerate( winding[index].vertex, winding[next].vertex ) ) {
-#if BRUSH_DEGENERATE_DEBUG
- globalOutputStream() << "Brush::buildWindings: face " << i << ": degenerate edge adjacent to " << winding[index].adjacent << "\n";
-#endif
- Winding& other = m_faces[winding[index].adjacent]->getWinding();
- std::size_t adjacent = Winding_FindAdjacent( other, i );
- if ( adjacent != c_brush_maxFaces ) {
- other.erase( other.begin() + adjacent );
- }
- winding.erase( j );
- }
- else
- {
- ++j;
- }
- }
- }
-}
-
-/// \brief Invalidates faces that have only two vertices in their winding, while preserving edge-connectivity information.
-void removeDegenerateFaces(){
- // save adjacency info for degenerate faces
- for ( std::size_t i = 0; i < m_faces.size(); ++i )
- {
- Winding& degen = m_faces[i]->getWinding();
-
- if ( degen.numpoints == 2 ) {
-#if BRUSH_DEGENERATE_DEBUG
- globalOutputStream() << "Brush::buildWindings: face " << i << ": degenerate winding adjacent to " << degen[0].adjacent << ", " << degen[1].adjacent << "\n";
-#endif
- // this is an "edge" face, where the plane touches the edge of the brush
- {
- Winding& winding = m_faces[degen[0].adjacent]->getWinding();
- std::size_t index = Winding_FindAdjacent( winding, i );
- if ( index != c_brush_maxFaces ) {
-#if BRUSH_DEGENERATE_DEBUG
- globalOutputStream() << "Brush::buildWindings: face " << degen[0].adjacent << ": remapping adjacent " << winding[index].adjacent << " to " << degen[1].adjacent << "\n";
-#endif
- winding[index].adjacent = degen[1].adjacent;
- }
- }
-
- {
- Winding& winding = m_faces[degen[1].adjacent]->getWinding();
- std::size_t index = Winding_FindAdjacent( winding, i );
- if ( index != c_brush_maxFaces ) {
-#if BRUSH_DEGENERATE_DEBUG
- globalOutputStream() << "Brush::buildWindings: face " << degen[1].adjacent << ": remapping adjacent " << winding[index].adjacent << " to " << degen[0].adjacent << "\n";
-#endif
- winding[index].adjacent = degen[0].adjacent;
- }
- }
-
- degen.resize( 0 );
- }
- }
-}
-
-/// \brief Removes edges that have the same adjacent-face as their immediate neighbour.
-void removeDuplicateEdges(){
- // verify face connectivity graph
- for ( std::size_t i = 0; i < m_faces.size(); ++i )
- {
- //if(m_faces[i]->contributes())
- {
- Winding& winding = m_faces[i]->getWinding();
- for ( std::size_t j = 0; j != winding.numpoints; )
- {
- std::size_t next = Winding_next( winding, j );
- if ( winding[j].adjacent == winding[next].adjacent ) {
-#if BRUSH_DEGENERATE_DEBUG
- globalOutputStream() << "Brush::buildWindings: face " << i << ": removed duplicate edge adjacent to face " << winding[j].adjacent << "\n";
-#endif
- winding.erase( winding.begin() + next );
- }
- else
- {
- ++j;
- }
- }
- }
- }
-}
-
-/// \brief Removes edges that do not have a matching pair in their adjacent-face.
-void verifyConnectivityGraph(){
- // verify face connectivity graph
- for ( std::size_t i = 0; i < m_faces.size(); ++i )
- {
- //if(m_faces[i]->contributes())
- {
- Winding& winding = m_faces[i]->getWinding();
- for ( Winding::iterator j = winding.begin(); j != winding.end(); )
- {
-#if BRUSH_CONNECTIVITY_DEBUG
- globalOutputStream() << "Brush::buildWindings: face " << i << ": adjacent to face " << ( *j ).adjacent << "\n";
-#endif
- // remove unidirectional graph edges
- if ( ( *j ).adjacent == c_brush_maxFaces
- || Winding_FindAdjacent( m_faces[( *j ).adjacent]->getWinding(), i ) == c_brush_maxFaces ) {
-#if BRUSH_CONNECTIVITY_DEBUG
- globalOutputStream() << "Brush::buildWindings: face " << i << ": removing unidirectional connectivity graph edge adjacent to face " << ( *j ).adjacent << "\n";
-#endif
- winding.erase( j );
- }
- else
- {
- ++j;
- }
- }
- }
- }
-}
-
-/// \brief Returns true if the brush is a finite volume. A brush without a finite volume extends past the maximum world bounds and is not valid.
-bool isBounded(){
- for ( const_iterator i = begin(); i != end(); ++i )
- {
- if ( !( *i )->is_bounded() ) {
- return false;
- }
- }
- return true;
-}
-
-/// \brief Constructs the polygon windings for each face of the brush. Also updates the brush bounding-box and face texture-coordinates.
-bool buildWindings(){
-
- {
- m_aabb_local = AABB();
-
- for ( std::size_t i = 0; i < m_faces.size(); ++i )
- {
- Face& f = *m_faces[i];
-
- if ( !plane3_valid( f.plane3() ) || !plane_unique( i ) ) {
- f.getWinding().resize( 0 );
- }
- else
- {
-#if BRUSH_CONNECTIVITY_DEBUG
- globalOutputStream() << "face: " << i << "\n";
-#endif
- windingForClipPlane( f.getWinding(), f.plane3() );
-
- // update brush bounds
- const Winding& winding = f.getWinding();
- for ( Winding::const_iterator i = winding.begin(); i != winding.end(); ++i )
- {
- aabb_extend_by_point_safe( m_aabb_local, ( *i ).vertex );
- }
-
- // update texture coordinates
- f.EmitTextureCoordinates();
- }
- }
- }
-
- bool degenerate = !isBounded();
-
- if ( !degenerate ) {
- // clean up connectivity information.
- // these cleanups must be applied in a specific order.
- removeDegenerateEdges();
- removeDegenerateFaces();
- removeDuplicateEdges();
- verifyConnectivityGraph();
- }
-
- return degenerate;
-}
-
-/// \brief Constructs the face windings and updates anything that depends on them.
-void buildBRep();
-};
-
-
-
-class FaceInstance;
-
-class FaceInstanceSet
-{
-typedef SelectionList<FaceInstance> FaceInstances;
-FaceInstances m_faceInstances;
-public:
-void insert( FaceInstance& faceInstance ){
- m_faceInstances.append( faceInstance );
-}
-void erase( FaceInstance& faceInstance ){
- m_faceInstances.erase( faceInstance );
-}
-
-template<typename Functor>
-void foreach( Functor functor ){
- for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
- {
- functor( *( *i ) );
- }
-}
-
-bool empty() const {
- return m_faceInstances.empty();
-}
-FaceInstance& last() const {
- return m_faceInstances.back();
-}
-};
-
-extern FaceInstanceSet g_SelectedFaceInstances;
-
-typedef std::list<std::size_t> VertexSelection;
-
-inline VertexSelection::iterator VertexSelection_find( VertexSelection& self, std::size_t value ){
- return std::find( self.begin(), self.end(), value );
-}
-
-inline VertexSelection::const_iterator VertexSelection_find( const VertexSelection& self, std::size_t value ){
- return std::find( self.begin(), self.end(), value );
-}
-
-inline VertexSelection::iterator VertexSelection_insert( VertexSelection& self, std::size_t value ){
- VertexSelection::iterator i = VertexSelection_find( self, value );
- if ( i == self.end() ) {
- self.push_back( value );
- return --self.end();
- }
- return i;
-}
-inline void VertexSelection_erase( VertexSelection& self, std::size_t value ){
- VertexSelection::iterator i = VertexSelection_find( self, value );
- if ( i != self.end() ) {
- self.erase( i );
- }
-}
-
-inline bool triangle_reversed( std::size_t x, std::size_t y, std::size_t z ){
- return !( ( x < y && y < z ) || ( z < x && x < y ) || ( y < z && z < x ) );
-}
-template<typename Element>
-inline Vector3 triangle_cross( const BasicVector3<Element>& x, const BasicVector3<Element> y, const BasicVector3<Element>& z ){
- return vector3_cross( y - x, z - x );
-}
-template<typename Element>
-inline bool triangles_same_winding( const BasicVector3<Element>& x1, const BasicVector3<Element> y1, const BasicVector3<Element>& z1, const BasicVector3<Element>& x2, const BasicVector3<Element> y2, const BasicVector3<Element>& z2 ){
- return vector3_dot( triangle_cross( x1, y1, z1 ), triangle_cross( x2, y2, z2 ) ) > 0;
-}
-
-
-typedef const Plane3* PlanePointer;
-typedef PlanePointer* PlanesIterator;
-
-class VectorLightList : public LightList
-{
-typedef std::vector<const RendererLight*> Lights;
-Lights m_lights;
-public:
-void addLight( const RendererLight& light ){
- m_lights.push_back( &light );
-}
-void clear(){
- m_lights.clear();
-}
-void evaluateLights() const {
-}
-void lightsChanged() const {
-}
-void forEachLight( const RendererLightCallback& callback ) const {
- for ( Lights::const_iterator i = m_lights.begin(); i != m_lights.end(); ++i )
- {
- callback( *( *i ) );
- }
-}
-};
-
-class FaceInstance
-{
-Face* m_face;
-ObservedSelectable m_selectable;
-ObservedSelectable m_selectableVertices;
-ObservedSelectable m_selectableEdges;
-SelectionChangeCallback m_selectionChanged;
-
-VertexSelection m_vertexSelection;
-VertexSelection m_edgeSelection;
-
-public:
-mutable VectorLightList m_lights;
-
-FaceInstance( Face& face, const SelectionChangeCallback& observer ) :
- m_face( &face ),
- m_selectable( SelectedChangedCaller( *this ) ),
- m_selectableVertices( observer ),
- m_selectableEdges( observer ),
- m_selectionChanged( observer ){
-}
-FaceInstance( const FaceInstance& other ) :
- m_face( other.m_face ),
- m_selectable( SelectedChangedCaller( *this ) ),
- m_selectableVertices( other.m_selectableVertices ),
- m_selectableEdges( other.m_selectableEdges ),
- m_selectionChanged( other.m_selectionChanged ){
-}
-FaceInstance& operator=( const FaceInstance& other ){
- m_face = other.m_face;
- return *this;
-}
-
-Face& getFace(){
- return *m_face;
-}
-const Face& getFace() const {
- return *m_face;
-}
-
-void selectedChanged( const Selectable& selectable ){
- if ( selectable.isSelected() ) {
- g_SelectedFaceInstances.insert( *this );
- }
- else
- {
- g_SelectedFaceInstances.erase( *this );
- }
- m_selectionChanged( selectable );
-}
-typedef MemberCaller1<FaceInstance, const Selectable&, &FaceInstance::selectedChanged> SelectedChangedCaller;
-
-bool selectedVertices() const {
- return !m_vertexSelection.empty();
-}
-bool selectedEdges() const {
- return !m_edgeSelection.empty();
-}
-bool isSelected() const {
- return m_selectable.isSelected();
-}
-
-bool selectedComponents() const {
- return selectedVertices() || selectedEdges() || isSelected();
-}
-bool selectedComponents( SelectionSystem::EComponentMode mode ) const {
- switch ( mode )
- {
- case SelectionSystem::eVertex:
- return selectedVertices();
- case SelectionSystem::eEdge:
- return selectedEdges();
- case SelectionSystem::eFace:
- return isSelected();
- default:
- return false;
- }
-}
-void setSelected( SelectionSystem::EComponentMode mode, bool select ){
- switch ( mode )
- {
- case SelectionSystem::eFace:
- m_selectable.setSelected( select );
- break;
- case SelectionSystem::eVertex:
- ASSERT_MESSAGE( !select, "select-all not supported" );
-
- m_vertexSelection.clear();
- m_selectableVertices.setSelected( false );
- break;
- case SelectionSystem::eEdge:
- ASSERT_MESSAGE( !select, "select-all not supported" );
-
- m_edgeSelection.clear();
- m_selectableEdges.setSelected( false );
- break;
- default:
- break;
- }
-}
-
-template<typename Functor>
-void SelectedVertices_foreach( Functor functor ) const {
- for ( VertexSelection::const_iterator i = m_vertexSelection.begin(); i != m_vertexSelection.end(); ++i )
- {
- std::size_t index = Winding_FindAdjacent( getFace().getWinding(), *i );
- if ( index != c_brush_maxFaces ) {
- functor( getFace().getWinding()[index].vertex );
- }
- }
-}
-template<typename Functor>
-void SelectedEdges_foreach( Functor functor ) const {
- for ( VertexSelection::const_iterator i = m_edgeSelection.begin(); i != m_edgeSelection.end(); ++i )
- {
- std::size_t index = Winding_FindAdjacent( getFace().getWinding(), *i );
- if ( index != c_brush_maxFaces ) {
- const Winding& winding = getFace().getWinding();
- std::size_t adjacent = Winding_next( winding, index );
- functor( vector3_mid( winding[index].vertex, winding[adjacent].vertex ) );
- }
- }
-}
-template<typename Functor>
-void SelectedFaces_foreach( Functor functor ) const {
- if ( isSelected() ) {
- functor( centroid() );
- }
-}
-
-template<typename Functor>
-void SelectedComponents_foreach( Functor functor ) const {
- SelectedVertices_foreach( functor );
- SelectedEdges_foreach( functor );
- SelectedFaces_foreach( functor );
-}
-
-void iterate_selected( AABB& aabb ) const {
- SelectedComponents_foreach([&](const Vector3 &point) {
- aabb_extend_by_point_safe(aabb, point);
- });
-}
-
-void iterate_selected( RenderablePointVector& points ) const {
- SelectedComponents_foreach([&](const Vector3 &point) {
- const Colour4b colour_selected(0, 0, 255, 255);
- points.push_back(pointvertex_for_windingpoint(point, colour_selected));
- });
-}
-
-bool intersectVolume( const VolumeTest& volume, const Matrix4& localToWorld ) const {
- return m_face->intersectVolume( volume, localToWorld );
-}
-
-void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
- if ( !m_face->isFiltered() && m_face->contributes() && intersectVolume( volume, localToWorld ) ) {
- renderer.PushState();
- if ( selectedComponents() ) {
- renderer.Highlight( Renderer::eFace );
- }
- m_face->render( renderer, localToWorld );
- renderer.PopState();
- }
-}
-
-void testSelect( SelectionTest& test, SelectionIntersection& best ){
- if ( !m_face->isFiltered() ) {
- m_face->testSelect( test, best );
- }
-}
-void testSelect( Selector& selector, SelectionTest& test ){
- SelectionIntersection best;
- testSelect( test, best );
- if ( best.valid() ) {
- Selector_add( selector, m_selectable, best );
- }
-}
-void testSelect_centroid( Selector& selector, SelectionTest& test ){
- if ( m_face->contributes() && !m_face->isFiltered() ) {
- SelectionIntersection best;
- m_face->testSelect_centroid( test, best );
- if ( best.valid() ) {
- Selector_add( selector, m_selectable, best );
- }
- }
-}
-
-void selectPlane( Selector& selector, const Line& line, PlanesIterator first, PlanesIterator last, const PlaneCallback& selectedPlaneCallback ){
- for ( Winding::const_iterator i = getFace().getWinding().begin(); i != getFace().getWinding().end(); ++i )
- {
- Vector3 v( vector3_subtracted( line_closest_point( line, ( *i ).vertex ), ( *i ).vertex ) );
- double dot = vector3_dot( getFace().plane3().normal(), v );
- if ( dot <= 0 ) {
- return;
- }
- }
-
- Selector_add( selector, m_selectable );
-
- selectedPlaneCallback( getFace().plane3() );
-}
-void selectReversedPlane( Selector& selector, const SelectedPlanes& selectedPlanes ){
- if ( selectedPlanes.contains( plane3_flipped( getFace().plane3() ) ) ) {
- Selector_add( selector, m_selectable );
- }
-}
-
-void transformComponents( const Matrix4& matrix ){
- if ( isSelected() ) {
- m_face->transform( matrix, false );
- }
- if ( selectedVertices() ) {
- if ( m_vertexSelection.size() == 1 ) {
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[1] );
- m_face->assign_planepts( m_face->m_move_planeptsTransformed );
- }
- else if ( m_vertexSelection.size() == 2 ) {
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[1] );
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[2] );
- m_face->assign_planepts( m_face->m_move_planeptsTransformed );
- }
- else if ( m_vertexSelection.size() >= 3 ) {
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[0] );
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[1] );
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[2] );
- m_face->assign_planepts( m_face->m_move_planeptsTransformed );
- }
- }
- if ( selectedEdges() ) {
- if ( m_edgeSelection.size() == 1 ) {
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[0] );
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[1] );
- m_face->assign_planepts( m_face->m_move_planeptsTransformed );
- }
- else if ( m_edgeSelection.size() >= 2 ) {
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[0] );
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[1] );
- matrix4_transform_point( matrix, m_face->m_move_planeptsTransformed[2] );
- m_face->assign_planepts( m_face->m_move_planeptsTransformed );
- }
- }
-}
-
-void snapto( float snap ){
- m_face->snapto( snap );
-}
-
-void snapComponents( float snap ){
- if ( isSelected() ) {
- snapto( snap );
- }
- if ( selectedVertices() ) {
- vector3_snap( m_face->m_move_planepts[0], snap );
- vector3_snap( m_face->m_move_planepts[1], snap );
- vector3_snap( m_face->m_move_planepts[2], snap );
- m_face->assign_planepts( m_face->m_move_planepts );
- planepts_assign( m_face->m_move_planeptsTransformed, m_face->m_move_planepts );
- m_face->freezeTransform();
- }
- if ( selectedEdges() ) {
- vector3_snap( m_face->m_move_planepts[0], snap );
- vector3_snap( m_face->m_move_planepts[1], snap );
- vector3_snap( m_face->m_move_planepts[2], snap );
- m_face->assign_planepts( m_face->m_move_planepts );
- planepts_assign( m_face->m_move_planeptsTransformed, m_face->m_move_planepts );
- m_face->freezeTransform();
- }
-}
-void update_move_planepts_vertex( std::size_t index ){
- m_face->update_move_planepts_vertex( index, m_face->m_move_planepts );
-}
-void update_move_planepts_vertex2( std::size_t index, std::size_t other ){
- const std::size_t numpoints = m_face->getWinding().numpoints;
- ASSERT_MESSAGE( index < numpoints, "select_vertex: invalid index" );
-
- const std::size_t opposite = Winding_Opposite( m_face->getWinding(), index, other );
-
- if ( triangle_reversed( index, other, opposite ) ) {
- std::swap( index, other );
- }
-
- ASSERT_MESSAGE(
- triangles_same_winding(
- m_face->getWinding()[opposite].vertex,
- m_face->getWinding()[index].vertex,
- m_face->getWinding()[other].vertex,
- m_face->getWinding()[0].vertex,
- m_face->getWinding()[1].vertex,
- m_face->getWinding()[2].vertex
- ),
- "update_move_planepts_vertex2: error"
- );
-
- m_face->m_move_planepts[0] = m_face->getWinding()[opposite].vertex;
- m_face->m_move_planepts[1] = m_face->getWinding()[index].vertex;
- m_face->m_move_planepts[2] = m_face->getWinding()[other].vertex;
- planepts_quantise( m_face->m_move_planepts, GRID_MIN ); // winding points are very inaccurate
-}
-void update_selection_vertex(){
- if ( m_vertexSelection.size() == 0 ) {
- m_selectableVertices.setSelected( false );
- }
- else
- {
- m_selectableVertices.setSelected( true );
-
- if ( m_vertexSelection.size() == 1 ) {
- std::size_t index = Winding_FindAdjacent( getFace().getWinding(), *m_vertexSelection.begin() );
-
- if ( index != c_brush_maxFaces ) {
- update_move_planepts_vertex( index );
- }
- }
- else if ( m_vertexSelection.size() == 2 ) {
- std::size_t index = Winding_FindAdjacent( getFace().getWinding(), *m_vertexSelection.begin() );
- std::size_t other = Winding_FindAdjacent( getFace().getWinding(), *( ++m_vertexSelection.begin() ) );
-
- if ( index != c_brush_maxFaces
- && other != c_brush_maxFaces ) {
- update_move_planepts_vertex2( index, other );
- }
- }
- }
-}
-void select_vertex( std::size_t index, bool select ){
- if ( select ) {
- VertexSelection_insert( m_vertexSelection, getFace().getWinding()[index].adjacent );
- }
- else
- {
- VertexSelection_erase( m_vertexSelection, getFace().getWinding()[index].adjacent );
- }
-
- SceneChangeNotify();
- update_selection_vertex();
-}
-
-bool selected_vertex( std::size_t index ) const {
- return VertexSelection_find( m_vertexSelection, getFace().getWinding()[index].adjacent ) != m_vertexSelection.end();
-}
-
-void update_move_planepts_edge( std::size_t index ){
- std::size_t numpoints = m_face->getWinding().numpoints;
- ASSERT_MESSAGE( index < numpoints, "select_edge: invalid index" );
-
- std::size_t adjacent = Winding_next( m_face->getWinding(), index );
- std::size_t opposite = Winding_Opposite( m_face->getWinding(), index );
- m_face->m_move_planepts[0] = m_face->getWinding()[index].vertex;
- m_face->m_move_planepts[1] = m_face->getWinding()[adjacent].vertex;
- m_face->m_move_planepts[2] = m_face->getWinding()[opposite].vertex;
- planepts_quantise( m_face->m_move_planepts, GRID_MIN ); // winding points are very inaccurate
-}
-void update_selection_edge(){
- if ( m_edgeSelection.size() == 0 ) {
- m_selectableEdges.setSelected( false );
- }
- else
- {
- m_selectableEdges.setSelected( true );
-
- if ( m_edgeSelection.size() == 1 ) {
- std::size_t index = Winding_FindAdjacent( getFace().getWinding(), *m_edgeSelection.begin() );
-
- if ( index != c_brush_maxFaces ) {
- update_move_planepts_edge( index );
- }
- }
- }
-}
-void select_edge( std::size_t index, bool select ){
- if ( select ) {
- VertexSelection_insert( m_edgeSelection, getFace().getWinding()[index].adjacent );
- }
- else
- {
- VertexSelection_erase( m_edgeSelection, getFace().getWinding()[index].adjacent );
- }
-
- SceneChangeNotify();
- update_selection_edge();
-}
-
-bool selected_edge( std::size_t index ) const {
- return VertexSelection_find( m_edgeSelection, getFace().getWinding()[index].adjacent ) != m_edgeSelection.end();
-}
-
-const Vector3& centroid() const {
- return m_face->centroid();
-}
-
-void connectivityChanged(){
- // This occurs when a face is added or removed.
- // The current vertex and edge selections no longer valid and must be cleared.
- m_vertexSelection.clear();
- m_selectableVertices.setSelected( false );
- m_edgeSelection.clear();
- m_selectableEdges.setSelected( false );
-}
-};
-
-class BrushClipPlane : public OpenGLRenderable
-{
-Plane3 m_plane;
-Winding m_winding;
-static Shader* m_state;
-public:
-static void constructStatic(){
- m_state = GlobalShaderCache().capture( "$CLIPPER_OVERLAY" );
-}
-static void destroyStatic(){
- GlobalShaderCache().release( "$CLIPPER_OVERLAY" );
-}
-
-void setPlane( const Brush& brush, const Plane3& plane ){
- m_plane = plane;
- if ( plane3_valid( m_plane ) ) {
- brush.windingForClipPlane( m_winding, m_plane );
- }
- else
- {
- m_winding.resize( 0 );
- }
-}
-
-void render( RenderStateFlags state ) const {
- if ( ( state & RENDER_FILL ) != 0 ) {
- Winding_Draw( m_winding, m_plane.normal(), state );
- }
- else
- {
- Winding_DrawWireframe( m_winding );
-
- // also draw a line indicating the direction of the cut
- Vector3 lineverts[2];
- Winding_Centroid( m_winding, m_plane, lineverts[0] );
- lineverts[1] = vector3_added( lineverts[0], vector3_scaled( m_plane.normal(), Brush::m_maxWorldCoord * 4 ) );
-
- glVertexPointer( 3, GL_FLOAT, sizeof( Vector3 ), &lineverts[0] );
- glDrawArrays( GL_LINES, 0, GLsizei( 2 ) );
- }
-}
-
-void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
- renderer.SetState( m_state, Renderer::eWireframeOnly );
- renderer.SetState( m_state, Renderer::eFullMaterials );
- renderer.addRenderable( *this, localToWorld );
-}
-};
-
-inline void Face_addLight( const FaceInstance& face, const Matrix4& localToWorld, const RendererLight& light ){
- const Plane3& facePlane = face.getFace().plane3();
- const Vector3& origin = light.aabb().origin;
- Plane3 tmp( plane3_transformed( Plane3( facePlane.normal(), -facePlane.dist() ), localToWorld ) );
- if ( !plane3_test_point( tmp, origin )
- || !plane3_test_point( tmp, vector3_added( origin, light.offset() ) ) ) {
- face.m_lights.addLight( light );
- }
-}
-
-
-
-typedef std::vector<FaceInstance> FaceInstances;
-
-class EdgeInstance : public Selectable
-{
-FaceInstances& m_faceInstances;
-SelectableEdge* m_edge;
-
-void select_edge( bool select ){
- FaceVertexId faceVertex = m_edge->m_faceVertex;
- m_faceInstances[faceVertex.getFace()].select_edge( faceVertex.getVertex(), select );
- faceVertex = next_edge( m_edge->m_faces, faceVertex );
- m_faceInstances[faceVertex.getFace()].select_edge( faceVertex.getVertex(), select );
-}
-bool selected_edge() const {
- FaceVertexId faceVertex = m_edge->m_faceVertex;
- if ( !m_faceInstances[faceVertex.getFace()].selected_edge( faceVertex.getVertex() ) ) {
- return false;
- }
- faceVertex = next_edge( m_edge->m_faces, faceVertex );
- if ( !m_faceInstances[faceVertex.getFace()].selected_edge( faceVertex.getVertex() ) ) {
- return false;
- }
-
- return true;
-}
-
-public:
-EdgeInstance( FaceInstances& faceInstances, SelectableEdge& edge )
- : m_faceInstances( faceInstances ), m_edge( &edge ){
-}
-EdgeInstance& operator=( const EdgeInstance& other ){
- m_edge = other.m_edge;
- return *this;
-}
-
-void setSelected( bool select ){
- select_edge( select );
-}
-bool isSelected() const {
- return selected_edge();
-}
-
-
-void testSelect( Selector& selector, SelectionTest& test ){
- SelectionIntersection best;
- m_edge->testSelect( test, best );
- if ( best.valid() ) {
- Selector_add( selector, *this, best );
- }
-}
-};
-
-class VertexInstance : public Selectable
-{
-FaceInstances& m_faceInstances;
-SelectableVertex* m_vertex;
-
-void select_vertex( bool select ){
- FaceVertexId faceVertex = m_vertex->m_faceVertex;
- do
- {
- m_faceInstances[faceVertex.getFace()].select_vertex( faceVertex.getVertex(), select );
- faceVertex = next_vertex( m_vertex->m_faces, faceVertex );
- }
- while ( faceVertex.getFace() != m_vertex->m_faceVertex.getFace() );
-}
-bool selected_vertex() const {
- FaceVertexId faceVertex = m_vertex->m_faceVertex;
- do
- {
- if ( !m_faceInstances[faceVertex.getFace()].selected_vertex( faceVertex.getVertex() ) ) {
- return false;
- }
- faceVertex = next_vertex( m_vertex->m_faces, faceVertex );
- }
- while ( faceVertex.getFace() != m_vertex->m_faceVertex.getFace() );
- return true;
-}
-
-public:
-VertexInstance( FaceInstances& faceInstances, SelectableVertex& vertex )
- : m_faceInstances( faceInstances ), m_vertex( &vertex ){
-}
-VertexInstance& operator=( const VertexInstance& other ){
- m_vertex = other.m_vertex;
- return *this;
-}
-
-void setSelected( bool select ){
- select_vertex( select );
-}
-bool isSelected() const {
- return selected_vertex();
-}
-
-void testSelect( Selector& selector, SelectionTest& test ){
- SelectionIntersection best;
- m_vertex->testSelect( test, best );
- if ( best.valid() ) {
- Selector_add( selector, *this, best );
- }
-}
-};
-
-class BrushInstanceVisitor
-{
-public:
-virtual void visit( FaceInstance& face ) const = 0;
-};
-
-class BrushInstance :
- public BrushObserver,
- public scene::Instance,
- public Selectable,
- public Renderable,
- public SelectionTestable,
- public ComponentSelectionTestable,
- public ComponentEditable,
- public ComponentSnappable,
- public PlaneSelectable,
- public LightCullable
-{
-class TypeCasts
-{
-InstanceTypeCastTable m_casts;
-public:
-TypeCasts(){
- InstanceStaticCast<BrushInstance, Selectable>::install( m_casts );
- InstanceContainedCast<BrushInstance, Bounded>::install( m_casts );
- InstanceContainedCast<BrushInstance, Cullable>::install( m_casts );
- InstanceStaticCast<BrushInstance, Renderable>::install( m_casts );
- InstanceStaticCast<BrushInstance, SelectionTestable>::install( m_casts );
- InstanceStaticCast<BrushInstance, ComponentSelectionTestable>::install( m_casts );
- InstanceStaticCast<BrushInstance, ComponentEditable>::install( m_casts );
- InstanceStaticCast<BrushInstance, ComponentSnappable>::install( m_casts );
- InstanceStaticCast<BrushInstance, PlaneSelectable>::install( m_casts );
- InstanceIdentityCast<BrushInstance>::install( m_casts );
- InstanceContainedCast<BrushInstance, Transformable>::install( m_casts );
-}
-InstanceTypeCastTable& get(){
- return m_casts;
-}
-};