- scene::Node* m_node;
- typedef UniqueSet<BrushObserver*> Observers;
- Observers m_observers;
- UndoObserver* m_undoable_observer;
- MapFile* m_map;
-
- // state
- Faces m_faces;
- // ----
-
- // cached data compiled from state
- Array<PointVertex> m_faceCentroidPoints;
- RenderablePointArray m_render_faces;
-
- Array<PointVertex> m_uniqueVertexPoints;
- typedef std::vector<SelectableVertex> SelectableVertices;
- SelectableVertices m_select_vertices;
- RenderablePointArray m_render_vertices;
-
- Array<PointVertex> m_uniqueEdgePoints;
- typedef std::vector<SelectableEdge> SelectableEdges;
- SelectableEdges m_select_edges;
- RenderablePointArray m_render_edges;
-
- Array<EdgeRenderIndices> m_edge_indices;
- Array<EdgeFaces> m_edge_faces;
-
- AABB m_aabb_local;
- // ----
-
- Callback m_evaluateTransform;
- Callback m_boundsChanged;
-
- mutable bool m_planeChanged; // b-rep evaluation required
- mutable bool m_transformChanged; // transform evaluation required
- // ----
-
-public:
- STRING_CONSTANT(Name, "Brush");
-
- Callback m_lightsChanged;
-
- // static data
- static Shader* m_state_point;
- // ----
-
- static EBrushType m_type;
- static double m_maxWorldCoord;
-
- Brush(scene::Node& node, const Callback& evaluateTransform, const Callback& boundsChanged) :
- m_node(&node),
- m_undoable_observer(0),
- m_map(0),
- m_render_faces(m_faceCentroidPoints, GL_POINTS),
- m_render_vertices(m_uniqueVertexPoints, GL_POINTS),
- m_render_edges(m_uniqueEdgePoints, GL_POINTS),
- m_planeChanged(false),
- m_transformChanged(false),
- m_evaluateTransform(evaluateTransform),
- m_boundsChanged(boundsChanged)
- {
- planeChanged();
- }
- Brush(const Brush& other, scene::Node& node, const Callback& evaluateTransform, const Callback& boundsChanged) :
- m_node(&node),
- m_undoable_observer(0),
- m_map(0),
- m_render_faces(m_faceCentroidPoints, GL_POINTS),
- m_render_vertices(m_uniqueVertexPoints, GL_POINTS),
- m_render_edges(m_uniqueEdgePoints, GL_POINTS),
- m_planeChanged(false),
- m_transformChanged(false),
- m_evaluateTransform(evaluateTransform),
- m_boundsChanged(boundsChanged)
- {
- copy(other);
- }
- Brush(const Brush& other) :
- TransformNode(other),
- Bounded(other),
- Cullable(other),
- Undoable(other),
- FaceObserver(other),
- Filterable(other),
- Nameable(other),
- BrushDoom3(other),
- m_node(0),
- m_undoable_observer(0),
- m_map(0),
- m_render_faces(m_faceCentroidPoints, GL_POINTS),
- m_render_vertices(m_uniqueVertexPoints, GL_POINTS),
- m_render_edges(m_uniqueEdgePoints, GL_POINTS),
- m_planeChanged(false),
- m_transformChanged(false)
- {
- copy(other);
- }
- ~Brush()
- {
- ASSERT_MESSAGE(m_observers.empty(), "Brush::~Brush: observers still attached");
- }
-
- // assignment not supported
- Brush& operator=(const Brush& other);
-
- void setDoom3GroupOrigin(const Vector3& origin)
- {
- //globalOutputStream() << "func_static origin before: " << m_funcStaticOrigin << " after: " << origin << "\n";
- for(Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i)
- {
- (*i)->getPlane().m_funcStaticOrigin = origin;
- (*i)->getPlane().updateTranslated();
- (*i)->planeChanged();
- }
- planeChanged();
- }
-
- void attach(BrushObserver& observer)
- {
- for(Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i)
- {
- observer.push_back(*(*i));
- }
-
- for(SelectableEdges::iterator i = m_select_edges.begin(); i !=m_select_edges.end(); ++i)
- {
- observer.edge_push_back(*i);
- }
-
- for(SelectableVertices::iterator i = m_select_vertices.begin(); i != m_select_vertices.end(); ++i)
- {
- observer.vertex_push_back(*i);
- }
-
- m_observers.insert(&observer);
- }
- void detach(BrushObserver& observer)
- {
- m_observers.erase(&observer);
- }
-
- void forEachFace(const BrushVisitor& visitor) const
- {
- for(Faces::const_iterator i = m_faces.begin(); i != m_faces.end(); ++i)
- {
- visitor.visit(*(*i));
- }
- }
-
- void forEachFace_instanceAttach(MapFile* map) const
- {
- for(Faces::const_iterator i = m_faces.begin(); i != m_faces.end(); ++i)
- {
- (*i)->instanceAttach(map);
- }
- }
- void forEachFace_instanceDetach(MapFile* map) const
- {
- for(Faces::const_iterator i = m_faces.begin(); i != m_faces.end(); ++i)
- {
- (*i)->instanceDetach(map);
- }
- }
-
- InstanceCounter m_instanceCounter;
- void instanceAttach(const scene::Path& path)
- {
- if(++m_instanceCounter.m_count == 1)
- {
- m_map = path_find_mapfile(path.begin(), path.end());
- m_undoable_observer = GlobalUndoSystem().observer(this);
- GlobalFilterSystem().registerFilterable(*this);
- forEachFace_instanceAttach(m_map);
- }
- else
- {
- ASSERT_MESSAGE(path_find_mapfile(path.begin(), path.end()) == m_map, "node is instanced across more than one file");
- }
- }
- void instanceDetach(const scene::Path& path)
- {
- if(--m_instanceCounter.m_count == 0)
- {
- forEachFace_instanceDetach(m_map);
- GlobalFilterSystem().unregisterFilterable(*this);
- m_map = 0;
- m_undoable_observer = 0;
- GlobalUndoSystem().release(this);
- }
- }
-
- // nameable
- const char* name() const
- {
- return "brush";
- }
- void attach(const NameCallback& callback)
- {
- }
- void detach(const NameCallback& callback)
- {
- }
-
- // filterable
- void updateFiltered()
- {
- if(m_node != 0)
- {
- if(brush_filtered(*this))
- {
- m_node->enable(scene::Node::eFiltered);
- }
- else
- {
- m_node->disable(scene::Node::eFiltered);
- }
- }
- }
-
- // observer
- void planeChanged()
- {
- m_planeChanged = true;
- aabbChanged();
- m_lightsChanged();
- }
- void shaderChanged()
- {
- updateFiltered();
- }
-
- void evaluateBRep() const
- {
- if(m_planeChanged)
- {
- m_planeChanged = false;
- const_cast<Brush*>(this)->buildBRep();
- }
- }
-
- void transformChanged()
- {
- m_transformChanged = true;
- planeChanged();
- }
- typedef MemberCaller<Brush, &Brush::transformChanged> TransformChangedCaller;
-
- void evaluateTransform()
- {
- if(m_transformChanged)
- {
- m_transformChanged = false;
- revertTransform();
- m_evaluateTransform();
- }
- }
- const Matrix4& localToParent() const
- {
- return g_matrix4_identity;
- }
- void aabbChanged()
- {
- m_boundsChanged();
- }
- const AABB& localAABB() const
- {
- evaluateBRep();
- return m_aabb_local;
- }
-
- VolumeIntersectionValue intersectVolume(const VolumeTest& test, const Matrix4& localToWorld) const
- {
- return test.TestAABB(m_aabb_local, localToWorld);
- }
-
- void renderComponents(SelectionSystem::EComponentMode mode, Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const
- {
- switch(mode)
- {
- case SelectionSystem::eVertex:
- renderer.addRenderable(m_render_vertices, localToWorld);
- break;
- case SelectionSystem::eEdge:
- renderer.addRenderable(m_render_edges, localToWorld);
- break;
- case SelectionSystem::eFace:
- renderer.addRenderable(m_render_faces, localToWorld);
- break;
- default:
- break;
- }
- }
-
- void transform(const Matrix4& matrix)
- {
- bool mirror = matrix4_handedness(matrix) == MATRIX4_LEFTHANDED;
-
- for(Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i)
- {
- (*i)->transform(matrix, mirror);
- }
- }
- void snapto(float snap)
- {
- for(Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i)
- {
- (*i)->snapto(snap);
- }
- }
- void revertTransform()
- {
- for(Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i)
- {
- (*i)->revertTransform();
- }
- }
- void freezeTransform()
- {
- for(Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i)
- {
- (*i)->freezeTransform();
- }
- }
-
- /// \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;
- }
- else if(m_type == eBrushTypeHalfLife)
- {
- g_bp_globals.m_texdefTypeId = TEXDEFTYPEID_HALFLIFE;
- g_brush_texturelock_enabled = true;
- }
-
- 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();
+ scene::Node* m_node;
+ typedef UniqueSet<BrushObserver*> Observers;
+ Observers m_observers;
+ UndoObserver* m_undoable_observer;
+ MapFile* m_map;
+
+ // state
+ Faces m_faces;
+ // ----
+
+ // cached data compiled from state
+ Array<PointVertex> m_faceCentroidPoints;
+ RenderablePointArray m_render_faces;
+
+ Array<PointVertex> m_uniqueVertexPoints;
+ typedef std::vector<SelectableVertex> SelectableVertices;
+ SelectableVertices m_select_vertices;
+ RenderablePointArray m_render_vertices;
+
+ Array<PointVertex> m_uniqueEdgePoints;
+ typedef std::vector<SelectableEdge> SelectableEdges;
+ SelectableEdges m_select_edges;
+ RenderablePointArray m_render_edges;
+
+ Array<EdgeRenderIndices> m_edge_indices;
+ Array<EdgeFaces> m_edge_faces;
+
+ AABB m_aabb_local;
+ // ----
+
+ Callback<void()> m_evaluateTransform;
+ Callback<void()> m_boundsChanged;
+
+ mutable bool m_planeChanged; // b-rep evaluation required
+ mutable bool m_transformChanged; // transform evaluation required
+ // ----
+
+public:
+ STRING_CONSTANT( Name, "Brush" );
+
+ Callback<void()> m_lightsChanged;
+
+ // static data
+ static Shader* m_state_point;
+ // ----
+
+ static EBrushType m_type;
+ static double m_maxWorldCoord;
+
+ Brush( scene::Node& node, const Callback<void()>& evaluateTransform, const Callback<void()>& boundsChanged ) :
+ m_node( &node ),
+ m_undoable_observer( 0 ),
+ m_map( 0 ),
+ m_render_faces( m_faceCentroidPoints, GL_POINTS ),
+ m_render_vertices( m_uniqueVertexPoints, GL_POINTS ),
+ m_render_edges( m_uniqueEdgePoints, GL_POINTS ),
+ m_evaluateTransform( evaluateTransform ),
+ m_boundsChanged( boundsChanged ),
+ m_planeChanged( false ),
+ m_transformChanged( false ){
+ planeChanged();
+ }
+ Brush( const Brush& other, scene::Node& node, const Callback<void()>& evaluateTransform, const Callback<void()>& boundsChanged ) :
+ m_node( &node ),
+ m_undoable_observer( 0 ),
+ m_map( 0 ),
+ m_render_faces( m_faceCentroidPoints, GL_POINTS ),
+ m_render_vertices( m_uniqueVertexPoints, GL_POINTS ),
+ m_render_edges( m_uniqueEdgePoints, GL_POINTS ),
+ m_evaluateTransform( evaluateTransform ),
+ m_boundsChanged( boundsChanged ),
+ m_planeChanged( false ),
+ m_transformChanged( false ){
+ copy( other );
+ }
+
+ Brush( const Brush& other ) :
+ TransformNode( other ),
+ Bounded( other ),
+ Cullable( other ),
+ Snappable(),
+ Undoable( other ),
+ FaceObserver( other ),
+ Filterable( other ),
+ Nameable( other ),
+ BrushDoom3( other ),
+ m_node( 0 ),
+ m_undoable_observer( 0 ),
+ m_map( 0 ),
+ m_render_faces( m_faceCentroidPoints, GL_POINTS ),
+ m_render_vertices( m_uniqueVertexPoints, GL_POINTS ),
+ m_render_edges( m_uniqueEdgePoints, GL_POINTS ),
+ m_planeChanged( false ),
+ m_transformChanged( false ){
+ copy( other );
+ }
+
+ ~Brush(){
+ ASSERT_MESSAGE( m_observers.empty(), "Brush::~Brush: observers still attached" );
+ }
+
+ // assignment not supported
+ Brush& operator=( const Brush& other );
+
+ void setDoom3GroupOrigin( const Vector3& origin ){
+ //globalOutputStream() << "func_static origin before: " << m_funcStaticOrigin << " after: " << origin << "\n";
+ for ( Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i )
+ {
+ ( *i )->getPlane().m_funcStaticOrigin = origin;
+ ( *i )->getPlane().updateTranslated();
+ ( *i )->planeChanged();
+ }
+ planeChanged();
+ }
+
+ void attach( BrushObserver& observer ){
+ for ( Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i )
+ {
+ observer.push_back( *( *i ) );
+ }
+
+ for ( SelectableEdges::iterator i = m_select_edges.begin(); i != m_select_edges.end(); ++i )
+ {
+ observer.edge_push_back( *i );
+ }
+
+ for ( SelectableVertices::iterator i = m_select_vertices.begin(); i != m_select_vertices.end(); ++i )
+ {
+ observer.vertex_push_back( *i );
+ }
+
+ m_observers.insert( &observer );
+ }
+
+ void detach( BrushObserver& observer ){
+ m_observers.erase( &observer );
+ }
+
+ void forEachFace( const BrushVisitor& visitor ) const {
+ for ( Faces::const_iterator i = m_faces.begin(); i != m_faces.end(); ++i )
+ {
+ visitor.visit( *( *i ) );
+ }
+ }
+
+ void forEachFace_instanceAttach( MapFile* map ) const {
+ for ( Faces::const_iterator i = m_faces.begin(); i != m_faces.end(); ++i )
+ {
+ ( *i )->instanceAttach( map );
+ }
+ }
+
+ void forEachFace_instanceDetach( MapFile* map ) const {
+ for ( Faces::const_iterator i = m_faces.begin(); i != m_faces.end(); ++i )
+ {
+ ( *i )->instanceDetach( map );
+ }
+ }
+
+ InstanceCounter m_instanceCounter;
+
+ void instanceAttach( const scene::Path& path ){
+ if ( ++m_instanceCounter.m_count == 1 ) {
+ m_map = path_find_mapfile( path.begin(), path.end() );
+ m_undoable_observer = GlobalUndoSystem().observer( this );
+ GlobalFilterSystem().registerFilterable( *this );
+ forEachFace_instanceAttach( m_map );
+ }
+ else
+ {
+ ASSERT_MESSAGE( path_find_mapfile( path.begin(), path.end() ) == m_map, "node is instanced across more than one file" );
+ }
+ }
+
+ void instanceDetach( const scene::Path& path ){
+ if ( --m_instanceCounter.m_count == 0 ) {
+ forEachFace_instanceDetach( m_map );
+ GlobalFilterSystem().unregisterFilterable( *this );
+ m_map = 0;
+ m_undoable_observer = 0;
+ GlobalUndoSystem().release( this );
+ }
+ }
+
+ // nameable
+ const char* name() const {
+ return "brush";
+ }
+
+ void attach( const NameCallback& callback ){
+ }
+
+ void detach( const NameCallback& callback ){
+ }
+
+ // filterable
+ void updateFiltered(){
+ if ( m_node != 0 ) {
+ if ( brush_filtered( *this ) ) {
+ m_node->enable( scene::Node::eFiltered );
+ }
+ else
+ {
+ m_node->disable( scene::Node::eFiltered );
+ }
+ }
+ }
+
+ // observer
+ void planeChanged(){
+ m_planeChanged = true;
+ aabbChanged();
+ m_lightsChanged();
+ }
+
+ void shaderChanged(){
+ updateFiltered();
+ planeChanged();
+ }
+
+ void evaluateBRep() const {
+ if ( m_planeChanged ) {
+ m_planeChanged = false;
+ const_cast<Brush*>( this )->buildBRep();
+ }
+ }
+
+ void transformChanged(){
+ m_transformChanged = true;
+ planeChanged();
+ }
+
+ typedef MemberCaller<Brush, void(), &Brush::transformChanged> TransformChangedCaller;
+
+ void evaluateTransform(){
+ if ( m_transformChanged ) {
+ m_transformChanged = false;
+ revertTransform();
+ m_evaluateTransform();
+ }
+ }
+
+ const Matrix4& localToParent() const {
+ return g_matrix4_identity;
+ }
+
+ void aabbChanged(){
+ m_boundsChanged();
+ }
+
+ const AABB& localAABB() const {
+ evaluateBRep();
+ return m_aabb_local;
+ }
+
+ VolumeIntersectionValue intersectVolume( const VolumeTest& test, const Matrix4& localToWorld ) const {
+ return test.TestAABB( m_aabb_local, localToWorld );
+ }
+
+ void renderComponents( SelectionSystem::EComponentMode mode, Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
+ switch ( mode )
+ {
+ case SelectionSystem::eVertex:
+ renderer.addRenderable( m_render_vertices, localToWorld );
+ break;
+ case SelectionSystem::eEdge:
+ renderer.addRenderable( m_render_edges, localToWorld );
+ break;
+ case SelectionSystem::eFace:
+ renderer.addRenderable( m_render_faces, localToWorld );
+ break;
+ default:
+ break;
+ }
+ }
+
+ void transform( const Matrix4& matrix ){
+ bool mirror = matrix4_handedness( matrix ) == MATRIX4_LEFTHANDED;
+
+ for ( Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i )
+ {
+ ( *i )->transform( matrix, mirror );
+ }
+ }
+
+ void snapto( float snap ){
+ for ( Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i )
+ {
+ ( *i )->snapto( snap );
+ }
+ }
+
+ void revertTransform(){
+ for ( Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i )
+ {
+ ( *i )->revertTransform();
+ }
+ }
+
+ void freezeTransform(){
+ for ( Faces::iterator i = m_faces.begin(); i != m_faces.end(); ++i )
+ {
+ ( *i )->freezeTransform();
+ }
+ }
+
+ /// \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();