X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=radiant%2Fbrush.h;h=96401a02f8c012b4b0089d6a0a30587bc7045e83;hb=7418c509072f68d1cfb77dab863ef3396a5d20db;hp=0fb6ffe225c2b0d3dbe45d0a8b3d5fb08a07079f;hpb=e4287c28bb2dafedc81c66e63951d947cfbeb225;p=xonotic%2Fnetradiant.git diff --git a/radiant/brush.h b/radiant/brush.h index 0fb6ffe2..96401a02 100644 --- a/radiant/brush.h +++ b/radiant/brush.h @@ -66,17 +66,16 @@ #include "brush_primit.h" const unsigned int BRUSH_DETAIL_FLAG = 27; -const unsigned int BRUSH_DETAIL_MASK = ( 1 << BRUSH_DETAIL_FLAG ); - -enum EBrushType -{ - eBrushTypeQuake, - eBrushTypeQuake2, - eBrushTypeQuake3, - eBrushTypeQuake3BP, - eBrushTypeDoom3, - eBrushTypeQuake4, - eBrushTypeHalfLife, +const unsigned int BRUSH_DETAIL_MASK = (1 << BRUSH_DETAIL_FLAG); + +enum EBrushType { + eBrushTypeQuake, + eBrushTypeQuake2, + eBrushTypeQuake3, + eBrushTypeQuake3BP, + eBrushTypeDoom3, + eBrushTypeQuake4, + eBrushTypeHalfLife, }; @@ -84,77 +83,79 @@ enum EBrushType #define BRUSH_DEGENERATE_DEBUG 0 template -inline TextOuputStreamType& ostream_write( TextOuputStreamType& ostream, const Matrix4& m ){ - return ostream << "(" << m[0] << " " << m[1] << " " << m[2] << " " << m[3] << ", " - << m[4] << " " << m[5] << " " << m[6] << " " << m[7] << ", " - << m[8] << " " << m[9] << " " << m[10] << " " << m[11] << ", " - << m[12] << " " << m[13] << " " << m[14] << " " << m[15] << ")"; +inline TextOuputStreamType &ostream_write(TextOuputStreamType &ostream, const Matrix4 &m) +{ + return ostream << "(" << m[0] << " " << m[1] << " " << m[2] << " " << m[3] << ", " + << m[4] << " " << m[5] << " " << m[6] << " " << m[7] << ", " + << m[8] << " " << m[9] << " " << m[10] << " " << m[11] << ", " + << m[12] << " " << m[13] << " " << m[14] << " " << m[15] << ")"; } -inline void print_vector3( const Vector3& v ){ - globalOutputStream() << "( " << v.x() << " " << v.y() << " " << v.z() << " )\n"; +inline void print_vector3(const Vector3 &v) +{ + globalOutputStream() << "( " << v.x() << " " << v.y() << " " << v.z() << " )\n"; } -inline void print_3x3( const Matrix4& m ){ - globalOutputStream() << "( " << m.xx() << " " << m.xy() << " " << m.xz() << " ) " - << "( " << m.yx() << " " << m.yy() << " " << m.yz() << " ) " - << "( " << m.zx() << " " << m.zy() << " " << m.zz() << " )\n"; +inline void print_3x3(const Matrix4 &m) +{ + globalOutputStream() << "( " << m.xx() << " " << m.xy() << " " << m.xz() << " ) " + << "( " << m.yx() << " " << m.yy() << " " << m.yz() << " ) " + << "( " << m.zx() << " " << m.zy() << " " << m.zz() << " )\n"; } - -inline bool texdef_sane( const texdef_t& texdef ){ - return fabs( texdef.shift[0] ) < ( 1 << 16 ) - && fabs( texdef.shift[1] ) < ( 1 << 16 ); +inline bool texdef_sane(const texdef_t &texdef) +{ + return fabs(texdef.shift[0]) < (1 << 16) + && fabs(texdef.shift[1]) < (1 << 16); } -inline void Winding_DrawWireframe( const Winding& winding ){ - glVertexPointer( 3, GL_FLOAT, sizeof( WindingVertex ), &winding.points.data()->vertex ); - glDrawArrays( GL_LINE_LOOP, 0, GLsizei( winding.numpoints ) ); +inline void Winding_DrawWireframe(const Winding &winding) +{ + glVertexPointer(3, GL_FLOAT, sizeof(WindingVertex), &winding.points.data()->vertex); + glDrawArrays(GL_LINE_LOOP, 0, GLsizei(winding.numpoints)); } -inline void Winding_Draw( const Winding& winding, const Vector3& normal, RenderStateFlags state ){ - glVertexPointer( 3, GL_FLOAT, sizeof( WindingVertex ), &winding.points.data()->vertex ); - - if ( ( state & RENDER_BUMP ) != 0 ) { - Vector3 normals[c_brush_maxFaces]; - typedef Vector3* Vector3Iter; - for ( Vector3Iter i = normals, end = normals + winding.numpoints; i != end; ++i ) - { - *i = normal; - } - if ( GlobalShaderCache().useShaderLanguage() ) { - glNormalPointer( GL_FLOAT, sizeof( Vector3 ), normals ); - glVertexAttribPointerARB( c_attr_TexCoord0, 2, GL_FLOAT, 0, sizeof( WindingVertex ), &winding.points.data()->texcoord ); - glVertexAttribPointerARB( c_attr_Tangent, 3, GL_FLOAT, 0, sizeof( WindingVertex ), &winding.points.data()->tangent ); - glVertexAttribPointerARB( c_attr_Binormal, 3, GL_FLOAT, 0, sizeof( WindingVertex ), &winding.points.data()->bitangent ); - } - else - { - glVertexAttribPointerARB( 11, 3, GL_FLOAT, 0, sizeof( Vector3 ), normals ); - glVertexAttribPointerARB( 8, 2, GL_FLOAT, 0, sizeof( WindingVertex ), &winding.points.data()->texcoord ); - glVertexAttribPointerARB( 9, 3, GL_FLOAT, 0, sizeof( WindingVertex ), &winding.points.data()->tangent ); - glVertexAttribPointerARB( 10, 3, GL_FLOAT, 0, sizeof( WindingVertex ), &winding.points.data()->bitangent ); - } - } - else - { - if ( state & RENDER_LIGHTING ) { - Vector3 normals[c_brush_maxFaces]; - typedef Vector3* Vector3Iter; - for ( Vector3Iter i = normals, last = normals + winding.numpoints; i != last; ++i ) - { - *i = normal; - } - glNormalPointer( GL_FLOAT, sizeof( Vector3 ), normals ); - } - - if ( state & RENDER_TEXTURE ) { - glTexCoordPointer( 2, GL_FLOAT, sizeof( WindingVertex ), &winding.points.data()->texcoord ); - } - } +inline void Winding_Draw(const Winding &winding, const Vector3 &normal, RenderStateFlags state) +{ + glVertexPointer(3, GL_FLOAT, sizeof(WindingVertex), &winding.points.data()->vertex); + + if ((state & RENDER_BUMP) != 0) { + Vector3 normals[c_brush_maxFaces]; + typedef Vector3 *Vector3Iter; + for (Vector3Iter i = normals, end = normals + winding.numpoints; i != end; ++i) { + *i = normal; + } + if (GlobalShaderCache().useShaderLanguage()) { + glNormalPointer(GL_FLOAT, sizeof(Vector3), normals); + glVertexAttribPointerARB(c_attr_TexCoord0, 2, GL_FLOAT, 0, sizeof(WindingVertex), + &winding.points.data()->texcoord); + glVertexAttribPointerARB(c_attr_Tangent, 3, GL_FLOAT, 0, sizeof(WindingVertex), + &winding.points.data()->tangent); + glVertexAttribPointerARB(c_attr_Binormal, 3, GL_FLOAT, 0, sizeof(WindingVertex), + &winding.points.data()->bitangent); + } else { + glVertexAttribPointerARB(11, 3, GL_FLOAT, 0, sizeof(Vector3), normals); + glVertexAttribPointerARB(8, 2, GL_FLOAT, 0, sizeof(WindingVertex), &winding.points.data()->texcoord); + glVertexAttribPointerARB(9, 3, GL_FLOAT, 0, sizeof(WindingVertex), &winding.points.data()->tangent); + glVertexAttribPointerARB(10, 3, GL_FLOAT, 0, sizeof(WindingVertex), &winding.points.data()->bitangent); + } + } else { + if (state & RENDER_LIGHTING) { + Vector3 normals[c_brush_maxFaces]; + typedef Vector3 *Vector3Iter; + for (Vector3Iter i = normals, last = normals + winding.numpoints; i != last; ++i) { + *i = normal; + } + glNormalPointer(GL_FLOAT, sizeof(Vector3), normals); + } + + if (state & RENDER_TEXTURE) { + glTexCoordPointer(2, GL_FLOAT, sizeof(WindingVertex), &winding.points.data()->texcoord); + } + } #if 0 - if ( state & RENDER_FILL ) { + if ( state & RENDER_FILL ) { glDrawArrays( GL_TRIANGLE_FAN, 0, GLsizei( winding.numpoints ) ); } else @@ -162,11 +163,11 @@ inline void Winding_Draw( const Winding& winding, const Vector3& normal, RenderS glDrawArrays( GL_LINE_LOOP, 0, GLsizei( winding.numpoints ) ); } #else - glDrawArrays( GL_POLYGON, 0, GLsizei( winding.numpoints ) ); + glDrawArrays(GL_POLYGON, 0, GLsizei(winding.numpoints)); #endif #if 0 - const Winding& winding = winding; + const Winding& winding = winding; if ( state & RENDER_FILL ) { glBegin( GL_POLYGON ); @@ -196,1137 +197,1285 @@ inline void Winding_Draw( const Winding& winding, const Vector3& normal, RenderS typedef DoubleVector3 PlanePoints[3]; -inline bool planepts_equal( const PlanePoints planepts, const PlanePoints other ){ - return planepts[0] == other[0] && planepts[1] == other[1] && planepts[2] == other[2]; -} - -inline void planepts_assign( PlanePoints planepts, const PlanePoints other ){ - planepts[0] = other[0]; - planepts[1] = other[1]; - planepts[2] = other[2]; -} - -inline void planepts_quantise( PlanePoints planepts, double snap ){ - vector3_snap( planepts[0], snap ); - vector3_snap( planepts[1], snap ); - vector3_snap( planepts[2], snap ); -} - -inline float vector3_max_component( const Vector3& vec3 ){ - return std::max( fabsf( vec3[0] ), std::max( fabsf( vec3[1] ), fabsf( vec3[2] ) ) ); +inline bool planepts_equal(const PlanePoints planepts, const PlanePoints other) +{ + return planepts[0] == other[0] && planepts[1] == other[1] && planepts[2] == other[2]; } -inline void edge_snap( Vector3& edge, double snap ){ - float scale = static_cast( ceil( fabs( snap / vector3_max_component( edge ) ) ) ); - if ( scale > 0.0f ) { - vector3_scale( edge, scale ); - } - vector3_snap( edge, snap ); +inline void planepts_assign(PlanePoints planepts, const PlanePoints other) +{ + planepts[0] = other[0]; + planepts[1] = other[1]; + planepts[2] = other[2]; } -inline void planepts_snap( PlanePoints planepts, double snap ){ - Vector3 edge01( vector3_subtracted( planepts[1], planepts[0] ) ); - Vector3 edge12( vector3_subtracted( planepts[2], planepts[1] ) ); - Vector3 edge20( vector3_subtracted( planepts[0], planepts[2] ) ); - - double length_squared_01 = vector3_dot( edge01, edge01 ); - double length_squared_12 = vector3_dot( edge12, edge12 ); - double length_squared_20 = vector3_dot( edge20, edge20 ); - - vector3_snap( planepts[0], snap ); - - if ( length_squared_01 < length_squared_12 ) { - if ( length_squared_12 < length_squared_20 ) { - edge_snap( edge01, snap ); - edge_snap( edge12, snap ); - planepts[1] = vector3_added( planepts[0], edge01 ); - planepts[2] = vector3_added( planepts[1], edge12 ); - } - else - { - edge_snap( edge20, snap ); - edge_snap( edge01, snap ); - planepts[1] = vector3_added( planepts[0], edge20 ); - planepts[2] = vector3_added( planepts[1], edge01 ); - } - } - else - { - if ( length_squared_01 < length_squared_20 ) { - edge_snap( edge01, snap ); - edge_snap( edge12, snap ); - planepts[1] = vector3_added( planepts[0], edge01 ); - planepts[2] = vector3_added( planepts[1], edge12 ); - } - else - { - edge_snap( edge12, snap ); - edge_snap( edge20, snap ); - planepts[1] = vector3_added( planepts[0], edge12 ); - planepts[2] = vector3_added( planepts[1], edge20 ); - } - } +inline void planepts_quantise(PlanePoints planepts, double snap) +{ + vector3_snap(planepts[0], snap); + vector3_snap(planepts[1], snap); + vector3_snap(planepts[2], snap); } -inline PointVertex pointvertex_for_planept( const DoubleVector3& point, const Colour4b& colour ){ - return PointVertex( - Vertex3f( - static_cast( point.x() ), - static_cast( point.y() ), - static_cast( point.z() ) - ), - colour - ); +inline float vector3_max_component(const Vector3 &vec3) +{ + return std::max(fabsf(vec3[0]), std::max(fabsf(vec3[1]), fabsf(vec3[2]))); } -inline PointVertex pointvertex_for_windingpoint( const Vector3& point, const Colour4b& colour ){ - return PointVertex( - vertex3f_for_vector3( point ), - colour - ); +inline void edge_snap(Vector3 &edge, double snap) +{ + float scale = static_cast( ceil(fabs(snap / vector3_max_component(edge)))); + if (scale > 0.0f) { + vector3_scale(edge, scale); + } + vector3_snap(edge, snap); } -inline bool check_plane_is_integer( const PlanePoints& planePoints ){ - return !float_is_integer( planePoints[0][0] ) - || !float_is_integer( planePoints[0][1] ) - || !float_is_integer( planePoints[0][2] ) - || !float_is_integer( planePoints[1][0] ) - || !float_is_integer( planePoints[1][1] ) - || !float_is_integer( planePoints[1][2] ) - || !float_is_integer( planePoints[2][0] ) - || !float_is_integer( planePoints[2][1] ) - || !float_is_integer( planePoints[2][2] ); +inline void planepts_snap(PlanePoints planepts, double snap) +{ + Vector3 edge01(vector3_subtracted(planepts[1], planepts[0])); + Vector3 edge12(vector3_subtracted(planepts[2], planepts[1])); + Vector3 edge20(vector3_subtracted(planepts[0], planepts[2])); + + double length_squared_01 = vector3_dot(edge01, edge01); + double length_squared_12 = vector3_dot(edge12, edge12); + double length_squared_20 = vector3_dot(edge20, edge20); + + vector3_snap(planepts[0], snap); + + if (length_squared_01 < length_squared_12) { + if (length_squared_12 < length_squared_20) { + edge_snap(edge01, snap); + edge_snap(edge12, snap); + planepts[1] = vector3_added(planepts[0], edge01); + planepts[2] = vector3_added(planepts[1], edge12); + } else { + edge_snap(edge20, snap); + edge_snap(edge01, snap); + planepts[1] = vector3_added(planepts[0], edge20); + planepts[2] = vector3_added(planepts[1], edge01); + } + } else { + if (length_squared_01 < length_squared_20) { + edge_snap(edge01, snap); + edge_snap(edge12, snap); + planepts[1] = vector3_added(planepts[0], edge01); + planepts[2] = vector3_added(planepts[1], edge12); + } else { + edge_snap(edge12, snap); + edge_snap(edge20, snap); + planepts[1] = vector3_added(planepts[0], edge12); + planepts[2] = vector3_added(planepts[1], edge20); + } + } +} + +inline PointVertex pointvertex_for_planept(const DoubleVector3 &point, const Colour4b &colour) +{ + return PointVertex( + Vertex3f( + static_cast( point.x()), + static_cast( point.y()), + static_cast( point.z()) + ), + colour + ); } -inline void brush_check_shader( const char* name ){ - if ( !shader_valid( name ) ) { - globalErrorStream() << "brush face has invalid texture name: '" << name << "'\n"; - } +inline PointVertex pointvertex_for_windingpoint(const Vector3 &point, const Colour4b &colour) +{ + return PointVertex( + vertex3f_for_vector3(point), + colour + ); } -class FaceShaderObserver +inline bool check_plane_is_integer(const PlanePoints &planePoints) { -public: -virtual void realiseShader() = 0; -virtual void unrealiseShader() = 0; -}; - -class FaceShaderObserverRealise + return !float_is_integer(planePoints[0][0]) + || !float_is_integer(planePoints[0][1]) + || !float_is_integer(planePoints[0][2]) + || !float_is_integer(planePoints[1][0]) + || !float_is_integer(planePoints[1][1]) + || !float_is_integer(planePoints[1][2]) + || !float_is_integer(planePoints[2][0]) + || !float_is_integer(planePoints[2][1]) + || !float_is_integer(planePoints[2][2]); +} + +inline void brush_check_shader(const char *name) { -public: -void operator()( FaceShaderObserver& observer ) const { - observer.realiseShader(); + if (!shader_valid(name)) { + globalErrorStream() << "brush face has invalid texture name: '" << name << "'\n"; + } } -}; -class FaceShaderObserverUnrealise -{ +class FaceShaderObserver { public: -void operator()( FaceShaderObserver& observer ) const { - observer.unrealiseShader(); -} + virtual void realiseShader() = 0; + + virtual void unrealiseShader() = 0; }; typedef ReferencePair FaceShaderObserverPair; -class ContentsFlagsValue -{ +class ContentsFlagsValue { public: -ContentsFlagsValue(){ -} -ContentsFlagsValue( int surfaceFlags, int contentFlags, int value, bool specified ) : - m_surfaceFlags( surfaceFlags ), - m_contentFlags( contentFlags ), - m_value( value ), - m_specified( specified ){ -} -int m_surfaceFlags; -int m_contentFlags; -int m_value; -bool m_specified; + ContentsFlagsValue() + { + } + + ContentsFlagsValue(int surfaceFlags, int contentFlags, int value, bool specified) : + m_surfaceFlags(surfaceFlags), + m_contentFlags(contentFlags), + m_value(value), + m_specified(specified) + { + } + + int m_surfaceFlags; + int m_contentFlags; + int m_value; + bool m_specified; }; -inline void ContentsFlagsValue_assignMasked( ContentsFlagsValue& flags, const ContentsFlagsValue& other ){ - bool detail = bitfield_enabled( flags.m_contentFlags, BRUSH_DETAIL_MASK ); - flags = other; - if ( detail ) { - flags.m_contentFlags = bitfield_enable( flags.m_contentFlags, BRUSH_DETAIL_MASK ); - } - else - { - flags.m_contentFlags = bitfield_disable( flags.m_contentFlags, BRUSH_DETAIL_MASK ); - } +inline void ContentsFlagsValue_assignMasked(ContentsFlagsValue &flags, const ContentsFlagsValue &other) +{ + bool detail = bitfield_enabled(flags.m_contentFlags, BRUSH_DETAIL_MASK); + flags = other; + if (detail) { + flags.m_contentFlags = bitfield_enable(flags.m_contentFlags, BRUSH_DETAIL_MASK); + } else { + flags.m_contentFlags = bitfield_disable(flags.m_contentFlags, BRUSH_DETAIL_MASK); + } } -class FaceShader : public ModuleObserver -{ +class FaceShader : public ModuleObserver { public: -class SavedState -{ -public: -CopiedString m_shader; -ContentsFlagsValue m_flags; - -SavedState( const FaceShader& faceShader ){ - m_shader = faceShader.getShader(); - m_flags = faceShader.m_flags; -} + class SavedState { + public: + CopiedString m_shader; + ContentsFlagsValue m_flags; + + SavedState(const FaceShader &faceShader) + { + m_shader = faceShader.getShader(); + m_flags = faceShader.m_flags; + } + + void exportState(FaceShader &faceShader) const + { + faceShader.setShader(m_shader.c_str()); + faceShader.m_flags = m_flags; + } + }; + + CopiedString m_shader; + Shader *m_state; + ContentsFlagsValue m_flags; + FaceShaderObserverPair m_observers; + bool m_instanced; + bool m_realised; + + FaceShader(const char *shader, const ContentsFlagsValue &flags = ContentsFlagsValue(0, 0, 0, false)) : + m_shader(shader), + m_state(0), + m_flags(flags), + m_instanced(false), + m_realised(false) + { + captureShader(); + } + + ~FaceShader() + { + releaseShader(); + } -void exportState( FaceShader& faceShader ) const { - faceShader.setShader( m_shader.c_str() ); - faceShader.setFlags( m_flags ); -} -}; - -CopiedString m_shader; -Shader* m_state; -ContentsFlagsValue m_flags; -FaceShaderObserverPair m_observers; -bool m_instanced; -bool m_realised; - -FaceShader( const char* shader, const ContentsFlagsValue& flags = ContentsFlagsValue( 0, 0, 0, false ) ) : - m_shader( shader ), - m_state( 0 ), - m_flags( flags ), - m_instanced( false ), - m_realised( false ){ - captureShader(); -} -~FaceShader(){ - releaseShader(); -} // copy-construction not supported -FaceShader( const FaceShader& other ); - -void instanceAttach(){ - m_instanced = true; - m_state->incrementUsed(); -} -void instanceDetach(){ - m_state->decrementUsed(); - m_instanced = false; -} - -void captureShader(){ - ASSERT_MESSAGE( m_state == 0, "shader cannot be captured" ); - brush_check_shader( m_shader.c_str() ); - m_state = GlobalShaderCache().capture( m_shader.c_str() ); - m_state->attach( *this ); -} -void releaseShader(){ - ASSERT_MESSAGE( m_state != 0, "shader cannot be released" ); - m_state->detach( *this ); - GlobalShaderCache().release( m_shader.c_str() ); - m_state = 0; -} - -void realise(){ - ASSERT_MESSAGE( !m_realised, "FaceTexdef::realise: already realised" ); - m_realised = true; - m_observers.forEach( FaceShaderObserverRealise() ); -} -void unrealise(){ - ASSERT_MESSAGE( m_realised, "FaceTexdef::unrealise: already unrealised" ); - m_observers.forEach( FaceShaderObserverUnrealise() ); - m_realised = false; -} - -void attach( FaceShaderObserver& observer ){ - m_observers.attach( observer ); - if ( m_realised ) { - observer.realiseShader(); - } -} - -void detach( FaceShaderObserver& observer ){ - if ( m_realised ) { - observer.unrealiseShader(); - } - m_observers.detach( observer ); -} - -const char* getShader() const { - return m_shader.c_str(); -} -void setShader( const char* name ){ - if ( m_instanced ) { - m_state->decrementUsed(); - } - releaseShader(); - m_shader = name; - captureShader(); - if ( m_instanced ) { - m_state->incrementUsed(); - } -} -ContentsFlagsValue getFlags() const { - ASSERT_MESSAGE( m_realised, "FaceShader::getFlags: flags not valid when unrealised" ); - if ( !m_flags.m_specified ) { - return ContentsFlagsValue( - m_state->getTexture().surfaceFlags, - m_state->getTexture().contentFlags, - m_state->getTexture().value, - true - ); - } - return m_flags; -} -void setFlags( const ContentsFlagsValue& flags ){ - ASSERT_MESSAGE( m_realised, "FaceShader::setFlags: flags not valid when unrealised" ); - ContentsFlagsValue_assignMasked( m_flags, flags ); -} - -Shader* state() const { - return m_state; -} - -std::size_t width() const { - if ( m_realised ) { - return m_state->getTexture().width; - } - return 1; -} -std::size_t height() const { - if ( m_realised ) { - return m_state->getTexture().height; - } - return 1; -} -unsigned int shaderFlags() const { - if ( m_realised ) { - return m_state->getFlags(); - } - return 0; -} + FaceShader(const FaceShader &other); + + void instanceAttach() + { + m_instanced = true; + m_state->incrementUsed(); + } + + void instanceDetach() + { + m_state->decrementUsed(); + m_instanced = false; + } + + void captureShader() + { + ASSERT_MESSAGE(m_state == 0, "shader cannot be captured"); + brush_check_shader(m_shader.c_str()); + m_state = GlobalShaderCache().capture(m_shader.c_str()); + m_state->attach(*this); + } + + void releaseShader() + { + ASSERT_MESSAGE(m_state != 0, "shader cannot be released"); + m_state->detach(*this); + GlobalShaderCache().release(m_shader.c_str()); + m_state = 0; + } + + void realise() + { + ASSERT_MESSAGE(!m_realised, "FaceTexdef::realise: already realised"); + m_realised = true; + m_observers.forEach([](FaceShaderObserver &observer) { + observer.realiseShader(); + }); + } + + void unrealise() + { + ASSERT_MESSAGE(m_realised, "FaceTexdef::unrealise: already unrealised"); + m_observers.forEach([](FaceShaderObserver &observer) { + observer.unrealiseShader(); + }); + m_realised = false; + } + + void attach(FaceShaderObserver &observer) + { + m_observers.attach(observer); + if (m_realised) { + observer.realiseShader(); + } + } + + void detach(FaceShaderObserver &observer) + { + if (m_realised) { + observer.unrealiseShader(); + } + m_observers.detach(observer); + } + + const char *getShader() const + { + return m_shader.c_str(); + } + + void setShader(const char *name) + { + if (m_instanced) { + m_state->decrementUsed(); + } + releaseShader(); + m_shader = name; + captureShader(); + if (m_instanced) { + m_state->incrementUsed(); + } + } + + ContentsFlagsValue getFlags() const + { + ASSERT_MESSAGE(m_realised, "FaceShader::getFlags: flags not valid when unrealised"); + if (!m_flags.m_specified) { + return ContentsFlagsValue( + m_state->getTexture().surfaceFlags, + m_state->getTexture().contentFlags, + m_state->getTexture().value, + true + ); + } + return m_flags; + } + + void setFlags(const ContentsFlagsValue &flags) + { + ASSERT_MESSAGE(m_realised, "FaceShader::setFlags: flags not valid when unrealised"); + ContentsFlagsValue_assignMasked(m_flags, flags); + } + + Shader *state() const + { + return m_state; + } + + std::size_t width() const + { + if (m_realised) { + return m_state->getTexture().width; + } + return 1; + } + + std::size_t height() const + { + if (m_realised) { + return m_state->getTexture().height; + } + return 1; + } + + unsigned int shaderFlags() const + { + if (m_realised) { + return m_state->getFlags(); + } + return 0; + } }; - - -class FaceTexdef : public FaceShaderObserver -{ +class FaceTexdef : public FaceShaderObserver { // not copyable -FaceTexdef( const FaceTexdef& other ); -// not assignable -FaceTexdef& operator=( const FaceTexdef& other ); -public: -class SavedState -{ -public: -TextureProjection m_projection; - -SavedState( const FaceTexdef& faceTexdef ){ - m_projection = faceTexdef.m_projection; -} - -void exportState( FaceTexdef& faceTexdef ) const { - Texdef_Assign( faceTexdef.m_projection, m_projection ); -} -}; - -FaceShader& m_shader; -TextureProjection m_projection; -bool m_projectionInitialised; -bool m_scaleApplied; - -FaceTexdef( - FaceShader& shader, - const TextureProjection& projection, - bool projectionInitialised = true - ) : - m_shader( shader ), - m_projection( projection ), - m_projectionInitialised( projectionInitialised ), - m_scaleApplied( false ){ - m_shader.attach( *this ); -} -~FaceTexdef(){ - m_shader.detach( *this ); -} - -void addScale(){ - ASSERT_MESSAGE( !m_scaleApplied, "texture scale aready added" ); - m_scaleApplied = true; - m_projection.m_brushprimit_texdef.addScale( m_shader.width(), m_shader.height() ); -} -void removeScale(){ - ASSERT_MESSAGE( m_scaleApplied, "texture scale aready removed" ); - m_scaleApplied = false; - m_projection.m_brushprimit_texdef.removeScale( m_shader.width(), m_shader.height() ); -} - -void realiseShader(){ - if ( m_projectionInitialised && !m_scaleApplied ) { - addScale(); - } -} -void unrealiseShader(){ - if ( m_projectionInitialised && m_scaleApplied ) { - removeScale(); - } -} - -void setTexdef( const TextureProjection& projection ){ - removeScale(); - Texdef_Assign( m_projection, projection ); - addScale(); -} - -void shift( float s, float t ){ - ASSERT_MESSAGE( texdef_sane( m_projection.m_texdef ), "FaceTexdef::shift: bad texdef" ); - removeScale(); - Texdef_Shift( m_projection, s, t ); - addScale(); -} - -void scale( float s, float t ){ - removeScale(); - Texdef_Scale( m_projection, s, t ); - addScale(); -} - -void rotate( float angle ){ - removeScale(); - Texdef_Rotate( m_projection, angle ); - addScale(); -} - -void fit( const Vector3& normal, const Winding& winding, float s_repeat, float t_repeat ){ - Texdef_FitTexture( m_projection, m_shader.width(), m_shader.height(), normal, winding, s_repeat, t_repeat ); -} - -void emitTextureCoordinates( Winding& winding, const Vector3& normal, const Matrix4& localToWorld ){ - Texdef_EmitTextureCoordinates( m_projection, m_shader.width(), m_shader.height(), winding, normal, localToWorld ); -} + FaceTexdef(const FaceTexdef &other); -void transform( const Plane3& plane, const Matrix4& matrix ){ - removeScale(); - Texdef_transformLocked( m_projection, m_shader.width(), m_shader.height(), plane, matrix ); - addScale(); -} +// not assignable + FaceTexdef &operator=(const FaceTexdef &other); -TextureProjection normalised() const { - brushprimit_texdef_t tmp( m_projection.m_brushprimit_texdef ); - tmp.removeScale( m_shader.width(), m_shader.height() ); - return TextureProjection( m_projection.m_texdef, tmp, m_projection.m_basis_s, m_projection.m_basis_t ); -} -void setBasis( const Vector3& normal ){ - Matrix4 basis; - Normal_GetTransform( normal, basis ); - m_projection.m_basis_s = Vector3( basis.xx(), basis.yx(), basis.zx() ); - m_projection.m_basis_t = Vector3( -basis.xy(), -basis.yy(), -basis.zy() ); -} +public: + class SavedState { + public: + TextureProjection m_projection; + + SavedState(const FaceTexdef &faceTexdef) + { + m_projection = faceTexdef.m_projection; + } + + void exportState(FaceTexdef &faceTexdef) const + { + Texdef_Assign(faceTexdef.m_projection, m_projection); + } + }; + + FaceShader &m_shader; + TextureProjection m_projection; + bool m_projectionInitialised; + bool m_scaleApplied; + + FaceTexdef( + FaceShader &shader, + const TextureProjection &projection, + bool projectionInitialised = true + ) : + m_shader(shader), + m_projection(projection), + m_projectionInitialised(projectionInitialised), + m_scaleApplied(false) + { + m_shader.attach(*this); + } + + ~FaceTexdef() + { + m_shader.detach(*this); + } + + void addScale() + { + ASSERT_MESSAGE(!m_scaleApplied, "texture scale aready added"); + m_scaleApplied = true; + m_projection.m_brushprimit_texdef.addScale(m_shader.width(), m_shader.height()); + } + + void removeScale() + { + ASSERT_MESSAGE(m_scaleApplied, "texture scale aready removed"); + m_scaleApplied = false; + m_projection.m_brushprimit_texdef.removeScale(m_shader.width(), m_shader.height()); + } + + void realiseShader() + { + if (m_projectionInitialised && !m_scaleApplied) { + addScale(); + } + } + + void unrealiseShader() + { + if (m_projectionInitialised && m_scaleApplied) { + removeScale(); + } + } + + void setTexdef(const TextureProjection &projection) + { + removeScale(); + Texdef_Assign(m_projection, projection); + addScale(); + } + + void shift(float s, float t) + { + ASSERT_MESSAGE(texdef_sane(m_projection.m_texdef), "FaceTexdef::shift: bad texdef"); + removeScale(); + Texdef_Shift(m_projection, s, t); + addScale(); + } + + void scale(float s, float t) + { + removeScale(); + Texdef_Scale(m_projection, s, t); + addScale(); + } + + void rotate(float angle) + { + removeScale(); + Texdef_Rotate(m_projection, angle); + addScale(); + } + + void fit(const Vector3 &normal, const Winding &winding, float s_repeat, float t_repeat) + { + Texdef_FitTexture(m_projection, m_shader.width(), m_shader.height(), normal, winding, s_repeat, t_repeat); + } + + void emitTextureCoordinates(Winding &winding, const Vector3 &normal, const Matrix4 &localToWorld) + { + Texdef_EmitTextureCoordinates(m_projection, m_shader.width(), m_shader.height(), winding, normal, localToWorld); + } + + void transform(const Plane3 &plane, const Matrix4 &matrix) + { + removeScale(); + Texdef_transformLocked(m_projection, m_shader.width(), m_shader.height(), plane, matrix); + addScale(); + } + + TextureProjection normalised() const + { + brushprimit_texdef_t tmp(m_projection.m_brushprimit_texdef); + tmp.removeScale(m_shader.width(), m_shader.height()); + return TextureProjection(m_projection.m_texdef, tmp, m_projection.m_basis_s, m_projection.m_basis_t); + } + + void setBasis(const Vector3 &normal) + { + Matrix4 basis; + Normal_GetTransform(normal, basis); + m_projection.m_basis_s = Vector3(basis.xx(), basis.yx(), basis.zx()); + m_projection.m_basis_t = Vector3(-basis.xy(), -basis.yy(), -basis.zy()); + } }; -inline void planepts_print( const PlanePoints& planePoints, TextOutputStream& ostream ){ - ostream << "( " << planePoints[0][0] << " " << planePoints[0][1] << " " << planePoints[0][2] << " ) " - << "( " << planePoints[1][0] << " " << planePoints[1][1] << " " << planePoints[1][2] << " ) " - << "( " << planePoints[2][0] << " " << planePoints[2][1] << " " << planePoints[2][2] << " )"; -} - - -inline Plane3 Plane3_applyTranslation( const Plane3& plane, const Vector3& translation ){ - Plane3 tmp( plane3_translated( Plane3( plane.normal(), -plane.dist() ), translation ) ); - return Plane3( tmp.normal(), -tmp.dist() ); +inline void planepts_print(const PlanePoints &planePoints, TextOutputStream &ostream) +{ + ostream << "( " << planePoints[0][0] << " " << planePoints[0][1] << " " << planePoints[0][2] << " ) " + << "( " << planePoints[1][0] << " " << planePoints[1][1] << " " << planePoints[1][2] << " ) " + << "( " << planePoints[2][0] << " " << planePoints[2][1] << " " << planePoints[2][2] << " )"; } -inline Plane3 Plane3_applyTransform( const Plane3& plane, const Matrix4& matrix ){ - Plane3 tmp( plane3_transformed( Plane3( plane.normal(), -plane.dist() ), matrix ) ); - return Plane3( tmp.normal(), -tmp.dist() ); -} -class FacePlane +inline Plane3 Plane3_applyTranslation(const Plane3 &plane, const Vector3 &translation) { -PlanePoints m_planepts; -Plane3 m_planeCached; -Plane3 m_plane; -public: -Vector3 m_funcStaticOrigin; - -static EBrushType m_type; - -static bool isDoom3Plane(){ - return FacePlane::m_type == eBrushTypeDoom3 || FacePlane::m_type == eBrushTypeQuake4; + Plane3 tmp(plane3_translated(Plane3(plane.normal(), -plane.dist()), translation)); + return Plane3(tmp.normal(), -tmp.dist()); } -class SavedState +inline Plane3 Plane3_applyTransform(const Plane3 &plane, const Matrix4 &matrix) { -public: -PlanePoints m_planepts; -Plane3 m_plane; - -SavedState( const FacePlane& facePlane ){ - if ( facePlane.isDoom3Plane() ) { - m_plane = facePlane.m_plane; - } - else - { - planepts_assign( m_planepts, facePlane.planePoints() ); - } -} - -void exportState( FacePlane& facePlane ) const { - if ( facePlane.isDoom3Plane() ) { - facePlane.m_plane = m_plane; - facePlane.updateTranslated(); - } - else - { - planepts_assign( facePlane.planePoints(), m_planepts ); - facePlane.MakePlane(); - } -} -}; - -FacePlane() : m_funcStaticOrigin( 0, 0, 0 ){ -} -FacePlane( const FacePlane& other ) : m_funcStaticOrigin( 0, 0, 0 ){ - if ( !isDoom3Plane() ) { - planepts_assign( m_planepts, other.m_planepts ); - MakePlane(); - } - else - { - m_plane = other.m_plane; - updateTranslated(); - } + Plane3 tmp(plane3_transformed(Plane3(plane.normal(), -plane.dist()), matrix)); + return Plane3(tmp.normal(), -tmp.dist()); } -void MakePlane(){ - if ( !isDoom3Plane() ) { +class FacePlane { + PlanePoints m_planepts; + Plane3 m_planeCached; + Plane3 m_plane; +public: + Vector3 m_funcStaticOrigin; + + static EBrushType m_type; + + static bool isDoom3Plane() + { + return FacePlane::m_type == eBrushTypeDoom3 || FacePlane::m_type == eBrushTypeQuake4; + } + + class SavedState { + public: + PlanePoints m_planepts; + Plane3 m_plane; + + SavedState(const FacePlane &facePlane) + { + if (facePlane.isDoom3Plane()) { + m_plane = facePlane.m_plane; + } else { + planepts_assign(m_planepts, facePlane.planePoints()); + } + } + + void exportState(FacePlane &facePlane) const + { + if (facePlane.isDoom3Plane()) { + facePlane.m_plane = m_plane; + facePlane.updateTranslated(); + } else { + planepts_assign(facePlane.planePoints(), m_planepts); + facePlane.MakePlane(); + } + } + }; + + FacePlane() : m_funcStaticOrigin(0, 0, 0) + { + } + + FacePlane(const FacePlane &other) : m_funcStaticOrigin(0, 0, 0) + { + if (!isDoom3Plane()) { + planepts_assign(m_planepts, other.m_planepts); + MakePlane(); + } else { + m_plane = other.m_plane; + updateTranslated(); + } + } + + void MakePlane() + { + if (!isDoom3Plane()) { #if 0 - if ( check_plane_is_integer( m_planepts ) ) { + if ( check_plane_is_integer( m_planepts ) ) { globalErrorStream() << "non-integer planepts: "; planepts_print( m_planepts, globalErrorStream() ); globalErrorStream() << "\n"; } #endif - m_planeCached = plane3_for_points( m_planepts ); - } -} - -void reverse(){ - if ( !isDoom3Plane() ) { - vector3_swap( m_planepts[0], m_planepts[2] ); - MakePlane(); - } - else - { - m_planeCached = plane3_flipped( m_plane ); - updateSource(); - } -} -void transform( const Matrix4& matrix, bool mirror ){ - if ( !isDoom3Plane() ) { + m_planeCached = plane3_for_points(m_planepts); + } + } + + void reverse() + { + if (!isDoom3Plane()) { + vector3_swap(m_planepts[0], m_planepts[2]); + MakePlane(); + } else { + m_planeCached = plane3_flipped(m_plane); + updateSource(); + } + } + + void transform(const Matrix4 &matrix, bool mirror) + { + if (!isDoom3Plane()) { #if 0 - bool off = check_plane_is_integer( planePoints() ); + bool off = check_plane_is_integer( planePoints() ); #endif - matrix4_transform_point( matrix, m_planepts[0] ); - matrix4_transform_point( matrix, m_planepts[1] ); - matrix4_transform_point( matrix, m_planepts[2] ); + matrix4_transform_point(matrix, m_planepts[0]); + matrix4_transform_point(matrix, m_planepts[1]); + matrix4_transform_point(matrix, m_planepts[2]); - if ( mirror ) { - reverse(); - } + if (mirror) { + reverse(); + } #if 0 - if ( check_plane_is_integer( planePoints() ) ) { + if ( check_plane_is_integer( planePoints() ) ) { if ( !off ) { globalErrorStream() << "caused by transform\n"; } } #endif - MakePlane(); - } - else - { - m_planeCached = Plane3_applyTransform( m_planeCached, matrix ); - updateSource(); - } -} -void offset( float offset ){ - if ( !isDoom3Plane() ) { - Vector3 move( vector3_scaled( m_planeCached.normal(), -offset ) ); - - vector3_subtract( m_planepts[0], move ); - vector3_subtract( m_planepts[1], move ); - vector3_subtract( m_planepts[2], move ); - - MakePlane(); - } - else - { - m_planeCached.d += offset; - updateSource(); - } -} - -void updateTranslated(){ - m_planeCached = Plane3_applyTranslation( m_plane, m_funcStaticOrigin ); -} -void updateSource(){ - m_plane = Plane3_applyTranslation( m_planeCached, vector3_negated( m_funcStaticOrigin ) ); -} - - -PlanePoints& planePoints(){ - return m_planepts; -} -const PlanePoints& planePoints() const { - return m_planepts; -} -const Plane3& plane3() const { - return m_planeCached; -} -void setDoom3Plane( const Plane3& plane ){ - m_plane = plane; - updateTranslated(); -} -const Plane3& getDoom3Plane() const { - return m_plane; -} - -void copy( const FacePlane& other ){ - if ( !isDoom3Plane() ) { - planepts_assign( m_planepts, other.m_planepts ); - MakePlane(); - } - else - { - m_planeCached = other.m_plane; - updateSource(); - } -} -void copy( const Vector3& p0, const Vector3& p1, const Vector3& p2 ){ - if ( !isDoom3Plane() ) { - m_planepts[0] = p0; - m_planepts[1] = p1; - m_planepts[2] = p2; - MakePlane(); - } - else - { - m_planeCached = plane3_for_points( p2, p1, p0 ); - updateSource(); - } -} + MakePlane(); + } else { + m_planeCached = Plane3_applyTransform(m_planeCached, matrix); + updateSource(); + } + } + + void offset(float offset) + { + if (!isDoom3Plane()) { + Vector3 move(vector3_scaled(m_planeCached.normal(), -offset)); + + vector3_subtract(m_planepts[0], move); + vector3_subtract(m_planepts[1], move); + vector3_subtract(m_planepts[2], move); + + MakePlane(); + } else { + m_planeCached.d += offset; + updateSource(); + } + } + + void updateTranslated() + { + m_planeCached = Plane3_applyTranslation(m_plane, m_funcStaticOrigin); + } + + void updateSource() + { + m_plane = Plane3_applyTranslation(m_planeCached, vector3_negated(m_funcStaticOrigin)); + } + + + PlanePoints &planePoints() + { + return m_planepts; + } + + const PlanePoints &planePoints() const + { + return m_planepts; + } + + const Plane3 &plane3() const + { + return m_planeCached; + } + + void setDoom3Plane(const Plane3 &plane) + { + m_plane = plane; + updateTranslated(); + } + + const Plane3 &getDoom3Plane() const + { + return m_plane; + } + + void copy(const FacePlane &other) + { + if (!isDoom3Plane()) { + planepts_assign(m_planepts, other.m_planepts); + MakePlane(); + } else { + m_planeCached = other.m_plane; + updateSource(); + } + } + + void copy(const Vector3 &p0, const Vector3 &p1, const Vector3 &p2) + { + if (!isDoom3Plane()) { + m_planepts[0] = p0; + m_planepts[1] = p1; + m_planepts[2] = p2; + MakePlane(); + } else { + m_planeCached = plane3_for_points(p2, p1, p0); + updateSource(); + } + } }; -inline void Winding_testSelect( Winding& winding, SelectionTest& test, SelectionIntersection& best ){ - test.TestPolygon( VertexPointer( reinterpret_cast( &winding.points.data()->vertex ), sizeof( WindingVertex ) ), winding.numpoints, best ); +inline void Winding_testSelect(Winding &winding, SelectionTest &test, SelectionIntersection &best) +{ + test.TestPolygon(VertexPointer(reinterpret_cast( &winding.points.data()->vertex ), + sizeof(WindingVertex)), winding.numpoints, best); } const double GRID_MIN = 0.125; -inline double quantiseInteger( double f ){ - return float_to_integer( f ); +inline double quantiseInteger(double f) +{ + return float_to_integer(f); } -inline double quantiseFloating( double f ){ - return float_snapped( f, 1.f / ( 1 << 16 ) ); +inline double quantiseFloating(double f) +{ + return float_snapped(f, 1.f / (1 << 16)); } -typedef double ( *QuantiseFunc )( double f ); +typedef double ( *QuantiseFunc )(double f); class Face; -class FaceFilter -{ +class FaceFilter { public: -virtual bool filter( const Face& face ) const = 0; + virtual bool filter(const Face &face) const = 0; }; -bool face_filtered( Face& face ); -void add_face_filter( FaceFilter& filter, int mask, bool invert = false ); +bool face_filtered(Face &face); -void Brush_addTextureChangedCallback( const SignalHandler& callback ); -void Brush_textureChanged(); +void add_face_filter(FaceFilter &filter, int mask, bool invert = false); +void Brush_addTextureChangedCallback(const SignalHandler &callback); -extern bool g_brush_texturelock_enabled; +void Brush_textureChanged(); -class FaceObserver -{ -public: -virtual void planeChanged() = 0; -virtual void connectivityChanged() = 0; -virtual void shaderChanged() = 0; -virtual void evaluateTransform() = 0; -}; -class Face : - public OpenGLRenderable, - public Filterable, - public Undoable, - public FaceShaderObserver -{ -std::size_t m_refcount; +extern bool g_brush_texturelock_enabled; -class SavedState : public UndoMemento -{ +class FaceObserver { public: -FacePlane::SavedState m_planeState; -FaceTexdef::SavedState m_texdefState; -FaceShader::SavedState m_shaderState; + virtual void planeChanged() = 0; -SavedState( const Face& face ) : m_planeState( face.getPlane() ), m_texdefState( face.getTexdef() ), m_shaderState( face.getShader() ){ -} + virtual void connectivityChanged() = 0; -void exportState( Face& face ) const { - m_planeState.exportState( face.getPlane() ); - m_shaderState.exportState( face.getShader() ); - m_texdefState.exportState( face.getTexdef() ); -} + virtual void shaderChanged() = 0; -void release(){ - delete this; -} + virtual void evaluateTransform() = 0; }; +class Face : + public OpenGLRenderable, + public Filterable, + public Undoable, + public FaceShaderObserver { + std::size_t m_refcount; + + class SavedState : public UndoMemento { + public: + FacePlane::SavedState m_planeState; + FaceTexdef::SavedState m_texdefState; + FaceShader::SavedState m_shaderState; + + SavedState(const Face &face) : m_planeState(face.getPlane()), m_texdefState(face.getTexdef()), + m_shaderState(face.getShader()) + { + } + + void exportState(Face &face) const + { + m_planeState.exportState(face.getPlane()); + m_shaderState.exportState(face.getShader()); + m_texdefState.exportState(face.getTexdef()); + } + + void release() + { + delete this; + } + }; + public: -static QuantiseFunc m_quantise; -static EBrushType m_type; + static QuantiseFunc m_quantise; + static EBrushType m_type; -PlanePoints m_move_planepts; -PlanePoints m_move_planeptsTransformed; + PlanePoints m_move_planepts; + PlanePoints m_move_planeptsTransformed; private: -FacePlane m_plane; -FacePlane m_planeTransformed; -FaceShader m_shader; -FaceTexdef m_texdef; -TextureProjection m_texdefTransformed; + FacePlane m_plane; + FacePlane m_planeTransformed; + FaceShader m_shader; + FaceTexdef m_texdef; + TextureProjection m_texdefTransformed; -Winding m_winding; -Vector3 m_centroid; -bool m_filtered; + Winding m_winding; + Vector3 m_centroid; + bool m_filtered; -FaceObserver* m_observer; -UndoObserver* m_undoable_observer; -MapFile* m_map; + FaceObserver *m_observer; + UndoObserver *m_undoable_observer; + MapFile *m_map; // assignment not supported -Face& operator=( const Face& other ); + Face &operator=(const Face &other); + // copy-construction not supported -Face( const Face& other ); + Face(const Face &other); public: -Face( FaceObserver* observer ) : - m_refcount( 0 ), - m_shader( texdef_name_default() ), - m_texdef( m_shader, TextureProjection(), false ), - m_filtered( false ), - m_observer( observer ), - m_undoable_observer( 0 ), - m_map( 0 ){ - m_shader.attach( *this ); - m_plane.copy( Vector3( 0, 0, 0 ), Vector3( 64, 0, 0 ), Vector3( 0, 64, 0 ) ); - m_texdef.setBasis( m_plane.plane3().normal() ); - planeChanged(); -} -Face( - const Vector3& p0, - const Vector3& p1, - const Vector3& p2, - const char* shader, - const TextureProjection& projection, - FaceObserver* observer - ) : - m_refcount( 0 ), - m_shader( shader ), - m_texdef( m_shader, projection ), - m_observer( observer ), - m_undoable_observer( 0 ), - m_map( 0 ){ - m_shader.attach( *this ); - m_plane.copy( p0, p1, p2 ); - m_texdef.setBasis( m_plane.plane3().normal() ); - planeChanged(); - updateFiltered(); -} -Face( const Face& other, FaceObserver* observer ) : - m_refcount( 0 ), - m_shader( other.m_shader.getShader(), other.m_shader.m_flags ), - m_texdef( m_shader, other.getTexdef().normalised() ), - m_observer( observer ), - m_undoable_observer( 0 ), - m_map( 0 ){ - m_shader.attach( *this ); - m_plane.copy( other.m_plane ); - planepts_assign( m_move_planepts, other.m_move_planepts ); - m_texdef.setBasis( m_plane.plane3().normal() ); - planeChanged(); - updateFiltered(); -} -~Face(){ - m_shader.detach( *this ); -} + Face(FaceObserver *observer) : + m_refcount(0), + m_shader(texdef_name_default()), + m_texdef(m_shader, TextureProjection(), false), + m_filtered(false), + m_observer(observer), + m_undoable_observer(0), + m_map(0) + { + m_shader.attach(*this); + m_plane.copy(Vector3(0, 0, 0), Vector3(64, 0, 0), Vector3(0, 64, 0)); + m_texdef.setBasis(m_plane.plane3().normal()); + planeChanged(); + } + + Face( + const Vector3 &p0, + const Vector3 &p1, + const Vector3 &p2, + const char *shader, + const TextureProjection &projection, + FaceObserver *observer + ) : + m_refcount(0), + m_shader(shader), + m_texdef(m_shader, projection), + m_observer(observer), + m_undoable_observer(0), + m_map(0) + { + m_shader.attach(*this); + m_plane.copy(p0, p1, p2); + m_texdef.setBasis(m_plane.plane3().normal()); + planeChanged(); + updateFiltered(); + } + + Face(const Face &other, FaceObserver *observer) : + m_refcount(0), + m_shader(other.m_shader.getShader(), other.m_shader.m_flags), + m_texdef(m_shader, other.getTexdef().normalised()), + m_observer(observer), + m_undoable_observer(0), + m_map(0) + { + m_shader.attach(*this); + m_plane.copy(other.m_plane); + planepts_assign(m_move_planepts, other.m_move_planepts); + m_texdef.setBasis(m_plane.plane3().normal()); + planeChanged(); + updateFiltered(); + } + + ~Face() + { + m_shader.detach(*this); + } + + void planeChanged() + { + revertTransform(); + m_observer->planeChanged(); + } + + void realiseShader() + { + m_observer->shaderChanged(); + } + + void unrealiseShader() + { + } + + void instanceAttach(MapFile *map) + { + m_shader.instanceAttach(); + m_map = map; + m_undoable_observer = GlobalUndoSystem().observer(this); + GlobalFilterSystem().registerFilterable(*this); + } + + void instanceDetach(MapFile *map) + { + GlobalFilterSystem().unregisterFilterable(*this); + m_undoable_observer = 0; + GlobalUndoSystem().release(this); + m_map = 0; + m_shader.instanceDetach(); + } + + void render(RenderStateFlags state) const + { + Winding_Draw(m_winding, m_planeTransformed.plane3().normal(), state); + } + + void updateFiltered() + { + m_filtered = face_filtered(*this); + } + + bool isFiltered() const + { + return m_filtered; + } + + void undoSave() + { + if (m_map != 0) { + m_map->changed(); + } + if (m_undoable_observer != 0) { + m_undoable_observer->save(this); + } + } -void planeChanged(){ - revertTransform(); - m_observer->planeChanged(); -} - -void realiseShader(){ - m_observer->shaderChanged(); -} -void unrealiseShader(){ -} +// undoable + UndoMemento *exportState() const + { + return new SavedState(*this); + } + + void importState(const UndoMemento *data) + { + undoSave(); + + static_cast( data )->exportState(*this); + + planeChanged(); + m_observer->connectivityChanged(); + texdefChanged(); + m_observer->shaderChanged(); + updateFiltered(); + } + + void IncRef() + { + ++m_refcount; + } + + void DecRef() + { + if (--m_refcount == 0) { + delete this; + } + } + + void flipWinding() + { + m_plane.reverse(); + planeChanged(); + } + + bool intersectVolume(const VolumeTest &volume, const Matrix4 &localToWorld) const + { + return volume.TestPlane(Plane3(plane3().normal(), -plane3().dist()), localToWorld); + } + + void render(Renderer &renderer, const Matrix4 &localToWorld) const + { + renderer.SetState(m_shader.state(), Renderer::eFullMaterials); + renderer.addRenderable(*this, localToWorld); + } + + void transform(const Matrix4 &matrix, bool mirror) + { + if (g_brush_texturelock_enabled) { + Texdef_transformLocked(m_texdefTransformed, m_shader.width(), m_shader.height(), m_plane.plane3(), matrix); + } + + m_planeTransformed.transform(matrix, mirror); -void instanceAttach( MapFile* map ){ - m_shader.instanceAttach(); - m_map = map; - m_undoable_observer = GlobalUndoSystem().observer( this ); - GlobalFilterSystem().registerFilterable( *this ); -} -void instanceDetach( MapFile* map ){ - GlobalFilterSystem().unregisterFilterable( *this ); - m_undoable_observer = 0; - GlobalUndoSystem().release( this ); - m_map = 0; - m_shader.instanceDetach(); -} +#if 0 + ASSERT_MESSAGE( projectionaxis_for_normal( normal ) == projectionaxis_for_normal( plane3().normal() ), "bleh" ); +#endif + m_observer->planeChanged(); -void render( RenderStateFlags state ) const { - Winding_Draw( m_winding, m_planeTransformed.plane3().normal(), state ); -} + if (g_brush_texturelock_enabled) { + Brush_textureChanged(); + } + } -void updateFiltered(){ - m_filtered = face_filtered( *this ); -} -bool isFiltered() const { - return m_filtered; -} - -void undoSave(){ - if ( m_map != 0 ) { - m_map->changed(); - } - if ( m_undoable_observer != 0 ) { - m_undoable_observer->save( this ); - } -} - -// undoable -UndoMemento* exportState() const { - return new SavedState( *this ); -} -void importState( const UndoMemento* data ){ - undoSave(); - - static_cast( data )->exportState( *this ); - - planeChanged(); - m_observer->connectivityChanged(); - texdefChanged(); - m_observer->shaderChanged(); - updateFiltered(); -} - -void IncRef(){ - ++m_refcount; -} -void DecRef(){ - if ( --m_refcount == 0 ) { - delete this; - } -} - -void flipWinding(){ - m_plane.reverse(); - planeChanged(); -} - -bool intersectVolume( const VolumeTest& volume, const Matrix4& localToWorld ) const { - return volume.TestPlane( Plane3( plane3().normal(), -plane3().dist() ), localToWorld ); -} - -void render( Renderer& renderer, const Matrix4& localToWorld ) const { - renderer.SetState( m_shader.state(), Renderer::eFullMaterials ); - renderer.addRenderable( *this, localToWorld ); -} - -void transform( const Matrix4& matrix, bool mirror ){ - if ( g_brush_texturelock_enabled ) { - Texdef_transformLocked( m_texdefTransformed, m_shader.width(), m_shader.height(), m_plane.plane3(), matrix ); - } - - m_planeTransformed.transform( matrix, mirror ); - -#if 0 - ASSERT_MESSAGE( projectionaxis_for_normal( normal ) == projectionaxis_for_normal( plane3().normal() ), "bleh" ); -#endif - m_observer->planeChanged(); - - if ( g_brush_texturelock_enabled ) { - Brush_textureChanged(); - } -} - -void assign_planepts( const PlanePoints planepts ){ - m_planeTransformed.copy( planepts[0], planepts[1], planepts[2] ); - m_observer->planeChanged(); -} + void assign_planepts(const PlanePoints planepts) + { + m_planeTransformed.copy(planepts[0], planepts[1], planepts[2]); + m_observer->planeChanged(); + } /// \brief Reverts the transformable state of the brush to identity. -void revertTransform(){ - m_planeTransformed = m_plane; - planepts_assign( m_move_planeptsTransformed, m_move_planepts ); - m_texdefTransformed = m_texdef.m_projection; -} -void freezeTransform(){ - undoSave(); - m_plane = m_planeTransformed; - planepts_assign( m_move_planepts, m_move_planeptsTransformed ); - m_texdef.m_projection = m_texdefTransformed; -} - -void update_move_planepts_vertex( std::size_t index, PlanePoints planePoints ){ - std::size_t numpoints = getWinding().numpoints; - ASSERT_MESSAGE( index < numpoints, "update_move_planepts_vertex: invalid index" ); - - std::size_t opposite = Winding_Opposite( getWinding(), index ); - std::size_t adjacent = Winding_wrap( getWinding(), opposite + numpoints - 1 ); - planePoints[0] = getWinding()[opposite].vertex; - planePoints[1] = getWinding()[index].vertex; - planePoints[2] = getWinding()[adjacent].vertex; - // winding points are very inaccurate, so they must be quantised before using them to generate the face-plane - planepts_quantise( planePoints, GRID_MIN ); -} - -void snapto( float snap ){ - if ( contributes() ) { + void revertTransform() + { + m_planeTransformed = m_plane; + planepts_assign(m_move_planeptsTransformed, m_move_planepts); + m_texdefTransformed = m_texdef.m_projection; + } + + void freezeTransform() + { + undoSave(); + m_plane = m_planeTransformed; + planepts_assign(m_move_planepts, m_move_planeptsTransformed); + m_texdef.m_projection = m_texdefTransformed; + } + + void update_move_planepts_vertex(std::size_t index, PlanePoints planePoints) + { + std::size_t numpoints = getWinding().numpoints; + ASSERT_MESSAGE(index < numpoints, "update_move_planepts_vertex: invalid index"); + + std::size_t opposite = Winding_Opposite(getWinding(), index); + std::size_t adjacent = Winding_wrap(getWinding(), opposite + numpoints - 1); + planePoints[0] = getWinding()[opposite].vertex; + planePoints[1] = getWinding()[index].vertex; + planePoints[2] = getWinding()[adjacent].vertex; + // winding points are very inaccurate, so they must be quantised before using them to generate the face-plane + planepts_quantise(planePoints, GRID_MIN); + } + + void snapto(float snap) + { + if (contributes()) { #if 0 - ASSERT_MESSAGE( plane3_valid( m_plane.plane3() ), "invalid plane before snap to grid" ); + ASSERT_MESSAGE( plane3_valid( m_plane.plane3() ), "invalid plane before snap to grid" ); planepts_snap( m_plane.planePoints(), snap ); ASSERT_MESSAGE( plane3_valid( m_plane.plane3() ), "invalid plane after snap to grid" ); #else - PlanePoints planePoints; - update_move_planepts_vertex( 0, planePoints ); - vector3_snap( planePoints[0], snap ); - vector3_snap( planePoints[1], snap ); - vector3_snap( planePoints[2], snap ); - assign_planepts( planePoints ); - freezeTransform(); + PlanePoints planePoints; + update_move_planepts_vertex(0, planePoints); + vector3_snap(planePoints[0], snap); + vector3_snap(planePoints[1], snap); + vector3_snap(planePoints[2], snap); + assign_planepts(planePoints); + freezeTransform(); #endif - SceneChangeNotify(); - if ( !plane3_valid( m_plane.plane3() ) ) { - globalErrorStream() << "WARNING: invalid plane after snap to grid\n"; - } - } -} - -void testSelect( SelectionTest& test, SelectionIntersection& best ){ - Winding_testSelect( m_winding, test, best ); -} - -void testSelect_centroid( SelectionTest& test, SelectionIntersection& best ){ - test.TestPoint( m_centroid, best ); -} - -void shaderChanged(){ - EmitTextureCoordinates(); - Brush_textureChanged(); - m_observer->shaderChanged(); - updateFiltered(); - planeChanged(); - SceneChangeNotify(); -} - -const char* GetShader() const { - return m_shader.getShader(); -} -void SetShader( const char* name ){ - undoSave(); - m_shader.setShader( name ); - shaderChanged(); -} - -void revertTexdef(){ - m_texdefTransformed = m_texdef.m_projection; -} -void texdefChanged(){ - revertTexdef(); - EmitTextureCoordinates(); - Brush_textureChanged(); -} - -void GetTexdef( TextureProjection& projection ) const { - projection = m_texdef.normalised(); -} -void SetTexdef( const TextureProjection& projection ){ - undoSave(); - m_texdef.setTexdef( projection ); - texdefChanged(); -} - -void GetFlags( ContentsFlagsValue& flags ) const { - flags = m_shader.getFlags(); -} -void SetFlags( const ContentsFlagsValue& flags ){ - undoSave(); - m_shader.setFlags( flags ); - m_observer->shaderChanged(); - updateFiltered(); -} - -void ShiftTexdef( float s, float t ){ - undoSave(); - m_texdef.shift( s, t ); - texdefChanged(); -} - -void ScaleTexdef( float s, float t ){ - undoSave(); - m_texdef.scale( s, t ); - texdefChanged(); -} - -void RotateTexdef( float angle ){ - undoSave(); - m_texdef.rotate( angle ); - texdefChanged(); -} - -void FitTexture( float s_repeat, float t_repeat ){ - undoSave(); - m_texdef.fit( m_plane.plane3().normal(), m_winding, s_repeat, t_repeat ); - texdefChanged(); -} - -void EmitTextureCoordinates(){ - Texdef_EmitTextureCoordinates( m_texdefTransformed, m_shader.width(), m_shader.height(), m_winding, plane3().normal(), g_matrix4_identity ); -} - - -const Vector3& centroid() const { - return m_centroid; -} - -void construct_centroid(){ - Winding_Centroid( m_winding, plane3(), m_centroid ); -} - -const Winding& getWinding() const { - return m_winding; -} -Winding& getWinding(){ - return m_winding; -} - -const Plane3& plane3() const { - m_observer->evaluateTransform(); - return m_planeTransformed.plane3(); -} -FacePlane& getPlane(){ - return m_plane; -} -const FacePlane& getPlane() const { - return m_plane; -} -FaceTexdef& getTexdef(){ - return m_texdef; -} -const FaceTexdef& getTexdef() const { - return m_texdef; -} -FaceShader& getShader(){ - return m_shader; -} -const FaceShader& getShader() const { - return m_shader; -} - -bool isDetail() const { - return ( m_shader.m_flags.m_contentFlags & BRUSH_DETAIL_MASK ) != 0; -} -void setDetail( bool detail ){ - undoSave(); - if ( detail && !isDetail() ) { - m_shader.m_flags.m_contentFlags |= BRUSH_DETAIL_MASK; - } - else if ( !detail && isDetail() ) { - m_shader.m_flags.m_contentFlags &= ~BRUSH_DETAIL_MASK; - } - m_observer->shaderChanged(); -} - -bool contributes() const { - return m_winding.numpoints > 2; -} -bool is_bounded() const { - for ( Winding::const_iterator i = m_winding.begin(); i != m_winding.end(); ++i ) - { - if ( ( *i ).adjacent == c_brush_maxFaces ) { - return false; - } - } - return true; -} + SceneChangeNotify(); + if (!plane3_valid(m_plane.plane3())) { + globalErrorStream() << "WARNING: invalid plane after snap to grid\n"; + } + } + } + + void testSelect(SelectionTest &test, SelectionIntersection &best) + { + Winding_testSelect(m_winding, test, best); + } + + void testSelect_centroid(SelectionTest &test, SelectionIntersection &best) + { + test.TestPoint(m_centroid, best); + } + + void shaderChanged() + { + EmitTextureCoordinates(); + Brush_textureChanged(); + m_observer->shaderChanged(); + updateFiltered(); + planeChanged(); + SceneChangeNotify(); + } + + const char *GetShader() const + { + return m_shader.getShader(); + } + + void SetShader(const char *name) + { + undoSave(); + m_shader.setShader(name); + shaderChanged(); + } + + void revertTexdef() + { + m_texdefTransformed = m_texdef.m_projection; + } + + void texdefChanged() + { + revertTexdef(); + EmitTextureCoordinates(); + Brush_textureChanged(); + } + + void GetTexdef(TextureProjection &projection) const + { + projection = m_texdef.normalised(); + } + + void SetTexdef(const TextureProjection &projection) + { + undoSave(); + m_texdef.setTexdef(projection); + texdefChanged(); + } + + void GetFlags(ContentsFlagsValue &flags) const + { + flags = m_shader.getFlags(); + } + + void SetFlags(const ContentsFlagsValue &flags) + { + undoSave(); + m_shader.setFlags(flags); + m_observer->shaderChanged(); + updateFiltered(); + } + + void ShiftTexdef(float s, float t) + { + undoSave(); + m_texdef.shift(s, t); + texdefChanged(); + } + + void ScaleTexdef(float s, float t) + { + undoSave(); + m_texdef.scale(s, t); + texdefChanged(); + } + + void RotateTexdef(float angle) + { + undoSave(); + m_texdef.rotate(angle); + texdefChanged(); + } + + void FitTexture(float s_repeat, float t_repeat) + { + undoSave(); + m_texdef.fit(m_plane.plane3().normal(), m_winding, s_repeat, t_repeat); + texdefChanged(); + } + + void EmitTextureCoordinates() + { + Texdef_EmitTextureCoordinates(m_texdefTransformed, m_shader.width(), m_shader.height(), m_winding, + plane3().normal(), g_matrix4_identity); + } + + + const Vector3 ¢roid() const + { + return m_centroid; + } + + void construct_centroid() + { + Winding_Centroid(m_winding, plane3(), m_centroid); + } + + const Winding &getWinding() const + { + return m_winding; + } + + Winding &getWinding() + { + return m_winding; + } + + const Plane3 &plane3() const + { + m_observer->evaluateTransform(); + return m_planeTransformed.plane3(); + } + + FacePlane &getPlane() + { + return m_plane; + } + + const FacePlane &getPlane() const + { + return m_plane; + } + + FaceTexdef &getTexdef() + { + return m_texdef; + } + + const FaceTexdef &getTexdef() const + { + return m_texdef; + } + + FaceShader &getShader() + { + return m_shader; + } + + const FaceShader &getShader() const + { + return m_shader; + } + + bool isDetail() const + { + return (m_shader.m_flags.m_contentFlags & BRUSH_DETAIL_MASK) != 0; + } + + void setDetail(bool detail) + { + undoSave(); + if (detail && !isDetail()) { + m_shader.m_flags.m_contentFlags |= BRUSH_DETAIL_MASK; + } else if (!detail && isDetail()) { + m_shader.m_flags.m_contentFlags &= ~BRUSH_DETAIL_MASK; + } + m_observer->shaderChanged(); + } + + bool contributes() const + { + return m_winding.numpoints > 2; + } + + bool is_bounded() const + { + for (Winding::const_iterator i = m_winding.begin(); i != m_winding.end(); ++i) { + if ((*i).adjacent == c_brush_maxFaces) { + return false; + } + } + return true; + } }; -class FaceVertexId -{ -std::size_t m_face; -std::size_t m_vertex; +class FaceVertexId { + std::size_t m_face; + std::size_t m_vertex; public: -FaceVertexId( std::size_t face, std::size_t vertex ) - : m_face( face ), m_vertex( vertex ){ -} - -std::size_t getFace() const { - return m_face; -} -std::size_t getVertex() const { - return m_vertex; -} + FaceVertexId(std::size_t face, std::size_t vertex) + : m_face(face), m_vertex(vertex) + { + } + + std::size_t getFace() const + { + return m_face; + } + + std::size_t getVertex() const + { + return m_vertex; + } }; typedef std::size_t faceIndex_t; -struct EdgeRenderIndices -{ - RenderIndex first; - RenderIndex second; +struct EdgeRenderIndices { + RenderIndex first; + RenderIndex second; - EdgeRenderIndices() - : first( 0 ), second( 0 ){ - } - EdgeRenderIndices( const RenderIndex _first, const RenderIndex _second ) - : first( _first ), second( _second ){ - } + EdgeRenderIndices() + : first(0), second(0) + { + } + + EdgeRenderIndices(const RenderIndex _first, const RenderIndex _second) + : first(_first), second(_second) + { + } }; -struct EdgeFaces -{ - faceIndex_t first; - faceIndex_t second; +struct EdgeFaces { + faceIndex_t first; + faceIndex_t second; - EdgeFaces() - : first( c_brush_maxFaces ), second( c_brush_maxFaces ){ - } - EdgeFaces( const faceIndex_t _first, const faceIndex_t _second ) - : first( _first ), second( _second ){ - } + EdgeFaces() + : first(c_brush_maxFaces), second(c_brush_maxFaces) + { + } + + EdgeFaces(const faceIndex_t _first, const faceIndex_t _second) + : first(_first), second(_second) + { + } }; -class RenderableWireframe : public OpenGLRenderable -{ +class RenderableWireframe : public OpenGLRenderable { public: -void render( RenderStateFlags state ) const { + void render(RenderStateFlags state) const + { #if 1 - glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( PointVertex ), &m_vertices->colour ); - glVertexPointer( 3, GL_FLOAT, sizeof( PointVertex ), &m_vertices->vertex ); - glDrawElements( GL_LINES, GLsizei( m_size << 1 ), RenderIndexTypeID, m_faceVertex.data() ); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(PointVertex), &m_vertices->colour); + glVertexPointer(3, GL_FLOAT, sizeof(PointVertex), &m_vertices->vertex); + glDrawElements(GL_LINES, GLsizei(m_size << 1), RenderIndexTypeID, m_faceVertex.data()); #else - glBegin( GL_LINES ); + glBegin( GL_LINES ); for ( std::size_t i = 0; i < m_size; ++i ) { glVertex3fv( &m_vertices[m_faceVertex[i].first].vertex.x ); @@ -1334,2362 +1483,2581 @@ void render( RenderStateFlags state ) const { } glEnd(); #endif -} + } -Array m_faceVertex; -std::size_t m_size; -const PointVertex* m_vertices; + Array m_faceVertex; + std::size_t m_size; + const PointVertex *m_vertices; }; class Brush; -typedef std::vector brush_vector_t; -class BrushFilter -{ +typedef std::vector brush_vector_t; + +class BrushFilter { public: -virtual bool filter( const Brush& brush ) const = 0; + virtual bool filter(const Brush &brush) const = 0; }; -bool brush_filtered( Brush& brush ); -void add_brush_filter( BrushFilter& filter, int mask, bool invert = false ); +bool brush_filtered(Brush &brush); + +void add_brush_filter(BrushFilter &filter, int mask, bool invert = false); /// \brief Returns true if 'self' takes priority when building brush b-rep. -inline bool plane3_inside( const Plane3& self, const Plane3& other, bool selfIsLater ){ - if ( vector3_equal_epsilon( self.normal(), other.normal(), 0.001 ) ) { - // same plane? prefer the one with smaller index - if ( self.dist() == other.dist() ) { - return selfIsLater; - } - return self.dist() < other.dist(); - } - return true; +inline bool plane3_inside(const Plane3 &self, const Plane3 &other, bool selfIsLater) +{ + if (vector3_equal_epsilon(self.normal(), other.normal(), 0.001)) { + // same plane? prefer the one with smaller index + if (self.dist() == other.dist()) { + return selfIsLater; + } + return self.dist() < other.dist(); + } + return true; } typedef SmartPointer FaceSmartPointer; typedef std::vector Faces; /// \brief Returns the unique-id of the edge adjacent to \p faceVertex in the edge-pair for the set of \p faces. -inline FaceVertexId next_edge( const Faces& faces, FaceVertexId faceVertex ){ - std::size_t adjacent_face = faces[faceVertex.getFace()]->getWinding()[faceVertex.getVertex()].adjacent; - std::size_t adjacent_vertex = Winding_FindAdjacent( faces[adjacent_face]->getWinding(), faceVertex.getFace() ); +inline FaceVertexId next_edge(const Faces &faces, FaceVertexId faceVertex) +{ + std::size_t adjacent_face = faces[faceVertex.getFace()]->getWinding()[faceVertex.getVertex()].adjacent; + std::size_t adjacent_vertex = Winding_FindAdjacent(faces[adjacent_face]->getWinding(), faceVertex.getFace()); - ASSERT_MESSAGE( adjacent_vertex != c_brush_maxFaces, "connectivity data invalid" ); - if ( adjacent_vertex == c_brush_maxFaces ) { - return faceVertex; - } + ASSERT_MESSAGE(adjacent_vertex != c_brush_maxFaces, "connectivity data invalid"); + if (adjacent_vertex == c_brush_maxFaces) { + return faceVertex; + } - return FaceVertexId( adjacent_face, adjacent_vertex ); + return FaceVertexId(adjacent_face, adjacent_vertex); } /// \brief Returns the unique-id of the vertex adjacent to \p faceVertex in the vertex-ring for the set of \p faces. -inline FaceVertexId next_vertex( const Faces& faces, FaceVertexId faceVertex ){ - FaceVertexId nextEdge = next_edge( faces, faceVertex ); - return FaceVertexId( nextEdge.getFace(), Winding_next( faces[nextEdge.getFace()]->getWinding(), nextEdge.getVertex() ) ); -} - -class SelectableEdge +inline FaceVertexId next_vertex(const Faces &faces, FaceVertexId faceVertex) { -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; - -SelectableEdge( Faces& faces, FaceVertexId faceVertex ) - : m_faces( faces ), m_faceVertex( faceVertex ){ -} -SelectableEdge& operator=( const SelectableEdge& other ){ - m_faceVertex = other.m_faceVertex; - return *this; -} - -Face& getFace() const { - return *m_faces[m_faceVertex.getFace()]; -} - -void testSelect( SelectionTest& test, SelectionIntersection& best ){ - test.TestPoint( getEdge(), best ); + FaceVertexId nextEdge = next_edge(faces, faceVertex); + return FaceVertexId(nextEdge.getFace(), + Winding_next(faces[nextEdge.getFace()]->getWinding(), nextEdge.getVertex())); } -}; -class SelectableVertex -{ -Vector3 getVertex() const { - return getFace().getWinding()[m_faceVertex.getVertex()].vertex; -} +class SelectableEdge { + 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; - -SelectableVertex( Faces& faces, FaceVertexId faceVertex ) - : m_faces( faces ), m_faceVertex( faceVertex ){ -} -SelectableVertex& operator=( const SelectableVertex& other ){ - m_faceVertex = other.m_faceVertex; - return *this; -} - -Face& getFace() const { - return *m_faces[m_faceVertex.getFace()]; -} - -void testSelect( SelectionTest& test, SelectionIntersection& best ){ - test.TestPoint( getVertex(), best ); -} + Faces &m_faces; + FaceVertexId m_faceVertex; + + SelectableEdge(Faces &faces, FaceVertexId faceVertex) + : m_faces(faces), m_faceVertex(faceVertex) + { + } + + SelectableEdge &operator=(const SelectableEdge &other) + { + m_faceVertex = other.m_faceVertex; + return *this; + } + + Face &getFace() const + { + return *m_faces[m_faceVertex.getFace()]; + } + + void testSelect(SelectionTest &test, SelectionIntersection &best) + { + test.TestPoint(getEdge(), best); + } }; -class BrushObserver -{ -public: -virtual void reserve( std::size_t size ) = 0; -virtual void clear() = 0; -virtual void push_back( Face& face ) = 0; -virtual void pop_back() = 0; -virtual void erase( std::size_t index ) = 0; -virtual void connectivityChanged() = 0; - -virtual void edge_clear() = 0; -virtual void edge_push_back( SelectableEdge& edge ) = 0; - -virtual void vertex_clear() = 0; -virtual void vertex_push_back( SelectableVertex& vertex ) = 0; - -virtual void DEBUG_verify() const = 0; -}; +class SelectableVertex { + Vector3 getVertex() const + { + return getFace().getWinding()[m_faceVertex.getVertex()].vertex; + } -class BrushVisitor -{ public: -virtual void visit( Face& face ) const = 0; + Faces &m_faces; + FaceVertexId m_faceVertex; + + SelectableVertex(Faces &faces, FaceVertexId faceVertex) + : m_faces(faces), m_faceVertex(faceVertex) + { + } + + SelectableVertex &operator=(const SelectableVertex &other) + { + m_faceVertex = other.m_faceVertex; + return *this; + } + + Face &getFace() const + { + return *m_faces[m_faceVertex.getFace()]; + } + + void testSelect(SelectionTest &test, SelectionIntersection &best) + { + test.TestPoint(getVertex(), best); + } }; -class Brush : - public TransformNode, - public Bounded, - public Cullable, - public Snappable, - public Undoable, - public FaceObserver, - public Filterable, - public Nameable, - public BrushDoom3 -{ -private: -scene::Node* m_node; -typedef UniqueSet Observers; -Observers m_observers; -UndoObserver* m_undoable_observer; -MapFile* m_map; - -// state -Faces m_faces; -// ---- - -// cached data compiled from state -Array m_faceCentroidPoints; -RenderablePointArray m_render_faces; - -Array m_uniqueVertexPoints; -typedef std::vector SelectableVertices; -SelectableVertices m_select_vertices; -RenderablePointArray m_render_vertices; - -Array m_uniqueEdgePoints; -typedef std::vector SelectableEdges; -SelectableEdges m_select_edges; -RenderablePointArray m_render_edges; - -Array m_edge_indices; -Array 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 -// ---- - +class BrushObserver { 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_evaluateTransform( evaluateTransform ), - m_boundsChanged( boundsChanged ), - m_planeChanged( false ), - m_transformChanged( false ){ - 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_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 ); - } -} + virtual void reserve(std::size_t size) = 0; -// nameable -const char* name() const { - return "brush"; -} -void attach( const NameCallback& callback ){ -} -void detach( const NameCallback& callback ){ -} + virtual void clear() = 0; -// filterable -void updateFiltered(){ - if ( m_node != 0 ) { - if ( brush_filtered( *this ) ) { - m_node->enable( scene::Node::eFiltered ); - } - else - { - m_node->disable( scene::Node::eFiltered ); - } - } -} + virtual void push_back(Face &face) = 0; -// observer -void planeChanged(){ - m_planeChanged = true; - aabbChanged(); - m_lightsChanged(); -} -void shaderChanged(){ - updateFiltered(); - planeChanged(); -} + virtual void pop_back() = 0; -void evaluateBRep() const { - if ( m_planeChanged ) { - m_planeChanged = false; - const_cast( this )->buildBRep(); - } -} + virtual void erase(std::size_t index) = 0; -void transformChanged(){ - m_transformChanged = true; - planeChanged(); -} -typedef MemberCaller TransformChangedCaller; + virtual void connectivityChanged() = 0; -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; -} + virtual void edge_clear() = 0; -VolumeIntersectionValue intersectVolume( const VolumeTest& test, const Matrix4& localToWorld ) const { - return test.TestAABB( m_aabb_local, localToWorld ); -} + virtual void edge_push_back(SelectableEdge &edge) = 0; -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; - } -} + virtual void vertex_clear() = 0; -void transform( const Matrix4& matrix ){ - bool mirror = matrix4_handedness( matrix ) == MATRIX4_LEFTHANDED; + virtual void vertex_push_back(SelectableVertex &vertex) = 0; - 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(); - } -} + virtual void DEBUG_verify() const = 0; +}; -/// \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(); -} +class BrushVisitor { +public: + virtual void visit(Face &face) const = 0; +}; -void appendFaces( const Faces& other ){ - clear(); - for ( Faces::const_iterator i = other.begin(); i != other.end(); ++i ) - { - push_back( *i ); - } -} +class Brush : + public TransformNode, + public Bounded, + public Cullable, + public Snappable, + public Undoable, + public FaceObserver, + public Filterable, + public Nameable, + public BrushDoom3 { +private: + scene::Node *m_node; + typedef UniqueSet Observers; + Observers m_observers; + UndoObserver *m_undoable_observer; + MapFile *m_map; -/// \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( 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& 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::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 FaceInstances; -FaceInstances m_faceInstances; -public: -void insert( FaceInstance& faceInstance ){ - m_faceInstances.append( faceInstance ); -} -void erase( FaceInstance& faceInstance ){ - m_faceInstances.erase( faceInstance ); -} - -template -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 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 -inline Vector3 triangle_cross( const BasicVector3& x, const BasicVector3 y, const BasicVector3& z ){ - return vector3_cross( y - x, z - x ); -} -template -inline bool triangles_same_winding( const BasicVector3& x1, const BasicVector3 y1, const BasicVector3& z1, const BasicVector3& x2, const BasicVector3 y2, const BasicVector3& 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 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 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 -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 -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 -void SelectedFaces_foreach( Functor functor ) const { - if ( isSelected() ) { - functor( centroid() ); - } -} - -template -void SelectedComponents_foreach( Functor functor ) const { - SelectedVertices_foreach( functor ); - SelectedEdges_foreach( functor ); - SelectedFaces_foreach( functor ); -} - -void iterate_selected( AABB& aabb ) const { - SelectedComponents_foreach( AABBExtendByPoint( aabb ) ); -} - -class RenderablePointVectorPushBack -{ -RenderablePointVector& m_points; -public: -RenderablePointVectorPushBack( RenderablePointVector& points ) : m_points( points ){ -} -void operator()( const Vector3& point ) const { - const Colour4b colour_selected( 0, 0, 255, 255 ); - m_points.push_back( pointvertex_for_windingpoint( point, colour_selected ) ); -} -}; - -void iterate_selected( RenderablePointVector& points ) const { - SelectedComponents_foreach( RenderablePointVectorPushBack( points ) ); -} - -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 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::install( m_casts ); - InstanceContainedCast::install( m_casts ); - InstanceContainedCast::install( m_casts ); - InstanceStaticCast::install( m_casts ); - InstanceStaticCast::install( m_casts ); - InstanceStaticCast::install( m_casts ); - InstanceStaticCast::install( m_casts ); - InstanceStaticCast::install( m_casts ); - InstanceStaticCast::install( m_casts ); - InstanceIdentityCast::install( m_casts ); - InstanceContainedCast::install( m_casts ); -} -InstanceTypeCastTable& get(){ - return m_casts; -} -}; +// state + Faces m_faces; +// ---- +// cached data compiled from state + Array m_faceCentroidPoints; + RenderablePointArray m_render_faces; -Brush& m_brush; + Array m_uniqueVertexPoints; + typedef std::vector SelectableVertices; + SelectableVertices m_select_vertices; + RenderablePointArray m_render_vertices; -FaceInstances m_faceInstances; + Array m_uniqueEdgePoints; + typedef std::vector SelectableEdges; + SelectableEdges m_select_edges; + RenderablePointArray m_render_edges; -typedef std::vector EdgeInstances; -EdgeInstances m_edgeInstances; -typedef std::vector VertexInstances; -VertexInstances m_vertexInstances; + Array m_edge_indices; + Array m_edge_faces; -ObservedSelectable m_selectable; + AABB m_aabb_local; +// ---- -mutable RenderableWireframe m_render_wireframe; -mutable RenderablePointVector m_render_selected; -mutable AABB m_aabb_component; -mutable Array m_faceCentroidPointsCulled; -RenderablePointArray m_render_faces_wireframe; -mutable bool m_viewChanged; // requires re-evaluation of view-dependent cached data + Callback m_evaluateTransform; + Callback m_boundsChanged; -BrushClipPlane m_clipPlane; + mutable bool m_planeChanged; // b-rep evaluation required + mutable bool m_transformChanged; // transform evaluation required +// ---- -static Shader* m_state_selpoint; +public: + STRING_CONSTANT(Name, "Brush"); -const LightList* m_lightList; + Callback m_lightsChanged; -TransformModifier m_transform; +// static data + static Shader *m_state_point; +// ---- -BrushInstance( const BrushInstance& other ); // NOT COPYABLE -BrushInstance& operator=( const BrushInstance& other ); // NOT ASSIGNABLE -public: -static Counter* m_counter; + 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_evaluateTransform(evaluateTransform), + m_boundsChanged(boundsChanged), + m_planeChanged(false), + m_transformChanged(false) + { + 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_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"); + } -typedef LazyStatic StaticTypeCasts; +// 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); + } + } -void lightsChanged(){ - m_lightList->lightsChanged(); -} -typedef MemberCaller LightsChangedCaller; - -STRING_CONSTANT( Name, "BrushInstance" ); - -BrushInstance( const scene::Path& path, scene::Instance* parent, Brush& brush ) : - Instance( path, parent, this, StaticTypeCasts::instance().get() ), - m_brush( brush ), - m_selectable( SelectedChangedCaller( *this ) ), - m_render_selected( GL_POINTS ), - m_render_faces_wireframe( m_faceCentroidPointsCulled, GL_POINTS ), - m_viewChanged( false ), - m_transform( Brush::TransformChangedCaller( m_brush ), ApplyTransformCaller( *this ) ){ - m_brush.instanceAttach( Instance::path() ); - m_brush.attach( *this ); - m_counter->increment(); - - m_lightList = &GlobalShaderCache().attach( *this ); - m_brush.m_lightsChanged = LightsChangedCaller( *this ); ///\todo Make this work with instancing. - - Instance::setTransformChangedCallback( LightsChangedCaller( *this ) ); -} -~BrushInstance(){ - Instance::setTransformChangedCallback( Callback() ); +// nameable + const char *name() const + { + return "brush"; + } - m_brush.m_lightsChanged = Callback(); - GlobalShaderCache().detach( *this ); + void attach(const NameCallback &callback) + { + } - m_counter->decrement(); - m_brush.detach( *this ); - m_brush.instanceDetach( Instance::path() ); -} + void detach(const NameCallback &callback) + { + } -Brush& getBrush(){ - return m_brush; -} -const Brush& getBrush() const { - return m_brush; -} +// filterable + void updateFiltered() + { + if (m_node != 0) { + if (brush_filtered(*this)) { + m_node->enable(scene::Node::eFiltered); + } else { + m_node->disable(scene::Node::eFiltered); + } + } + } -Bounded& get( NullType){ - return m_brush; -} -Cullable& get( NullType){ - return m_brush; -} -Transformable& get( NullType){ - return m_transform; -} +// observer + void planeChanged() + { + m_planeChanged = true; + aabbChanged(); + m_lightsChanged(); + } + + void shaderChanged() + { + updateFiltered(); + planeChanged(); + } + + void evaluateBRep() const + { + if (m_planeChanged) { + m_planeChanged = false; + const_cast( this )->buildBRep(); + } + } + + void transformChanged() + { + m_transformChanged = true; + planeChanged(); + } + + typedef MemberCaller 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(); + } + } -void selectedChanged( const Selectable& selectable ){ - GlobalSelectionSystem().getObserver ( SelectionSystem::ePrimitive )( selectable ); - GlobalSelectionSystem().onSelectedChanged( *this, selectable ); +/// \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); + } + } - Instance::selectedChanged(); -} -typedef MemberCaller1 SelectedChangedCaller; +/// \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( 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(); + } -void selectedChangedComponent( const Selectable& selectable ){ - GlobalSelectionSystem().getObserver ( SelectionSystem::eComponent )( selectable ); - GlobalSelectionSystem().onComponentSelection( *this, selectable ); -} -typedef MemberCaller1 SelectedChangedComponentCaller; +/// \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(); + } -const BrushInstanceVisitor& forEachFaceInstance( const BrushInstanceVisitor& visitor ){ - for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - visitor.visit( *i ); - } - return visitor; -} +/// \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(); + } -static void constructStatic(){ - m_state_selpoint = GlobalShaderCache().capture( "$SELPOINT" ); -} -static void destroyStatic(){ - GlobalShaderCache().release( "$SELPOINT" ); -} +/// \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; + } -void clear(){ - m_faceInstances.clear(); -} -void reserve( std::size_t size ){ - m_faceInstances.reserve( size ); -} +/// \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; + } + } + } + } -void push_back( Face& face ){ - m_faceInstances.push_back( FaceInstance( face, SelectedChangedComponentCaller( *this ) ) ); -} -void pop_back(){ - ASSERT_MESSAGE( !m_faceInstances.empty(), "erasing invalid element" ); - m_faceInstances.pop_back(); -} -void erase( std::size_t index ){ - ASSERT_MESSAGE( index < m_faceInstances.size(), "erasing invalid element" ); - m_faceInstances.erase( m_faceInstances.begin() + index ); -} -void connectivityChanged(){ - for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).connectivityChanged(); - } -} +/// \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; -void edge_clear(){ - m_edgeInstances.clear(); -} -void edge_push_back( SelectableEdge& edge ){ - m_edgeInstances.push_back( EdgeInstance( m_faceInstances, edge ) ); -} + // get a poly that covers an effectively infinite area + Winding_createInfinite(buffer[swap], plane, m_maxWorldCoord + 1); -void vertex_clear(){ - m_vertexInstances.clear(); -} -void vertex_push_back( SelectableVertex& vertex ){ - m_vertexInstances.push_back( VertexInstance( m_faceInstances, vertex ) ); -} + // 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]; -void DEBUG_verify() const { - ASSERT_MESSAGE( m_faceInstances.size() == m_brush.DEBUG_size(), "FATAL: mismatch" ); -} + if (plane3_equal(clip.plane3(), plane) + || !plane3_valid(clip.plane3()) || !plane_unique(i) + || plane3_opposing(plane, clip.plane3())) { + continue; + } -bool isSelected() const { - return m_selectable.isSelected(); -} -void setSelected( bool select ){ - m_selectable.setSelected( select ); -} + buffer[!swap].clear(); -void update_selected() const { - m_render_selected.clear(); - for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - if ( ( *i ).getFace().contributes() ) { - ( *i ).iterate_selected( m_render_selected ); - } - } -} +#if BRUSH_CONNECTIVITY_DEBUG + globalOutputStream() << "clip vs face: " << i << "\n"; +#endif -void evaluateViewDependent( const VolumeTest& volume, const Matrix4& localToWorld ) const { - if ( m_viewChanged ) { - m_viewChanged = false; + { + // 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]); + } - bool faces_visible[c_brush_maxFaces]; - { - bool* j = faces_visible; - for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i, ++j ) +#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 ) { - *j = ( *i ).intersectVolume( volume, localToWorld ); + 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"; + } } - } - - m_brush.update_wireframe( m_render_wireframe, faces_visible ); - m_brush.update_faces_wireframe( m_faceCentroidPointsCulled, faces_visible ); - } -} - -void renderComponentsSelected( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const { - m_brush.evaluateBRep(); +#endif - update_selected(); - if ( !m_render_selected.empty() ) { - renderer.Highlight( Renderer::ePrimitive, false ); - renderer.SetState( m_state_selpoint, Renderer::eWireframeOnly ); - renderer.SetState( m_state_selpoint, Renderer::eFullMaterials ); - renderer.addRenderable( m_render_selected, localToWorld ); - } -} + //ASSERT_MESSAGE(buffer[!swap].numpoints != 1, "created single-point winding"); -void renderComponents( Renderer& renderer, const VolumeTest& volume ) const { - m_brush.evaluateBRep(); + swap = !swap; + } + } - const Matrix4& localToWorld = Instance::localToWorld(); + Winding_forFixedWinding(winding, buffer[swap]); - renderer.SetState( m_brush.m_state_point, Renderer::eWireframeOnly ); - renderer.SetState( m_brush.m_state_point, Renderer::eFullMaterials ); +#if BRUSH_CONNECTIVITY_DEBUG + Winding_printConnectivity( winding ); - if ( volume.fill() && GlobalSelectionSystem().ComponentMode() == SelectionSystem::eFace ) { - evaluateViewDependent( volume, localToWorld ); - renderer.addRenderable( m_render_faces_wireframe, localToWorld ); - } - else + for ( Winding::iterator i = winding.begin(), j = winding.end() - 1; i != winding.end(); j = i, ++i ) { - m_brush.renderComponents( GlobalSelectionSystem().ComponentMode(), renderer, volume, localToWorld ); + 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 &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::iterator p = wire.begin(); + for (std::size_t i = 0; i < m_faceCentroidPoints.size(); ++i) { + if (faces_visible[i]) { + *p++ = m_faceCentroidPoints[i]; + } + } + } -void renderClipPlane( Renderer& renderer, const VolumeTest& volume ) const { - if ( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eClip && isSelected() ) { - m_clipPlane.render( renderer, volume, localToWorld() ); - } -} +/// \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(); + } -void renderCommon( Renderer& renderer, const VolumeTest& volume ) const { - bool componentMode = GlobalSelectionSystem().Mode() == SelectionSystem::eComponent; +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(); + } + } - if ( componentMode && isSelected() ) { - renderComponents( renderer, volume ); - } +/// \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; + } - if ( parentSelected() ) { - if ( !componentMode ) { - renderer.Highlight( Renderer::eFace ); - } - renderer.Highlight( Renderer::ePrimitive ); - } -} +/// \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 °en = 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; + } + } -void renderSolid( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const { - //renderCommon(renderer, volume); + degen.resize(0); + } + } + } - m_lightList->evaluateLights(); +/// \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; + } + } + } + } + } - for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - renderer.setLights( ( *i ).m_lights ); - ( *i ).render( renderer, volume, localToWorld ); - } +/// \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; + } + } + } + } + } - renderComponentsSelected( renderer, volume, localToWorld ); -} +/// \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; + } -void renderWireframe( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const { - //renderCommon(renderer, volume); +/// \brief Constructs the polygon windings for each face of the brush. Also updates the brush bounding-box and face texture-coordinates. + bool buildWindings() + { - evaluateViewDependent( volume, localToWorld ); + { + m_aabb_local = AABB(); - if ( m_render_wireframe.m_size != 0 ) { - renderer.addRenderable( m_render_wireframe, localToWorld ); - } + for (std::size_t i = 0; i < m_faces.size(); ++i) { + Face &f = *m_faces[i]; - renderComponentsSelected( renderer, volume, localToWorld ); -} + 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(); +}; -void renderSolid( Renderer& renderer, const VolumeTest& volume ) const { - m_brush.evaluateBRep(); - renderClipPlane( renderer, volume ); +class FaceInstance; - renderSolid( renderer, volume, localToWorld() ); -} +class FaceInstanceSet { + typedef SelectionList FaceInstances; + FaceInstances m_faceInstances; +public: + void insert(FaceInstance &faceInstance) + { + m_faceInstances.append(faceInstance); + } + + void erase(FaceInstance &faceInstance) + { + m_faceInstances.erase(faceInstance); + } + + template + 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(); + } +}; -void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const { - m_brush.evaluateBRep(); +extern FaceInstanceSet g_SelectedFaceInstances; - renderClipPlane( renderer, volume ); +typedef std::list VertexSelection; - renderWireframe( renderer, volume, localToWorld() ); +inline VertexSelection::iterator VertexSelection_find(VertexSelection &self, std::size_t value) +{ + return std::find(self.begin(), self.end(), value); } -void viewChanged() const { - m_viewChanged = true; +inline VertexSelection::const_iterator VertexSelection_find(const VertexSelection &self, std::size_t value) +{ + return std::find(self.begin(), self.end(), value); } -void testSelect( Selector& selector, SelectionTest& test ){ - test.BeginMesh( localToWorld() ); +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; +} - SelectionIntersection best; - for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).testSelect( test, best ); - } - if ( best.valid() ) { - selector.addIntersection( best ); - } +inline void VertexSelection_erase(VertexSelection &self, std::size_t value) +{ + VertexSelection::iterator i = VertexSelection_find(self, value); + if (i != self.end()) { + self.erase(i); + } } -bool isSelectedComponents() const { - for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - if ( ( *i ).selectedComponents() ) { - return true; - } - } - return false; +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)); } -void setSelectedComponents( bool select, SelectionSystem::EComponentMode mode ){ - for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).setSelected( mode, select ); - } + +template +inline Vector3 +triangle_cross(const BasicVector3 &x, const BasicVector3 y, const BasicVector3 &z) +{ + return vector3_cross(y - x, z - x); } -void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ){ - test.BeginMesh( localToWorld() ); - switch ( mode ) - { - case SelectionSystem::eVertex: - { - for ( VertexInstances::iterator i = m_vertexInstances.begin(); i != m_vertexInstances.end(); ++i ) - { - ( *i ).testSelect( selector, test ); - } - } - break; - case SelectionSystem::eEdge: - { - for ( EdgeInstances::iterator i = m_edgeInstances.begin(); i != m_edgeInstances.end(); ++i ) - { - ( *i ).testSelect( selector, test ); - } - } - break; - case SelectionSystem::eFace: - { - if ( test.getVolume().fill() ) { - for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).testSelect( selector, test ); - } - } - else - { - for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).testSelect_centroid( selector, test ); - } - } - } - break; - default: - break; - } +template +inline bool +triangles_same_winding(const BasicVector3 &x1, const BasicVector3 y1, const BasicVector3 &z1, + const BasicVector3 &x2, const BasicVector3 y2, const BasicVector3 &z2) +{ + return vector3_dot(triangle_cross(x1, y1, z1), triangle_cross(x2, y2, z2)) > 0; } -void selectPlanes( Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback ){ - test.BeginMesh( localToWorld() ); - PlanePointer brushPlanes[c_brush_maxFaces]; - PlanesIterator j = brushPlanes; +typedef const Plane3 *PlanePointer; +typedef PlanePointer *PlanesIterator; - for ( Brush::const_iterator i = m_brush.begin(); i != m_brush.end(); ++i ) - { - *j++ = &( *i )->plane3(); - } +class VectorLightList : public LightList { + typedef std::vector 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)); + } + } +}; - for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).selectPlane( selector, Line( test.getNear(), test.getFar() ), brushPlanes, j, selectedPlaneCallback ); - } -} -void selectReversedPlanes( Selector& selector, const SelectedPlanes& selectedPlanes ){ - for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).selectReversedPlane( selector, selectedPlanes ); - } -} +class FaceInstance { + Face *m_face; + ObservedSelectable m_selectable; + ObservedSelectable m_selectableVertices; + ObservedSelectable m_selectableEdges; + SelectionChangeCallback m_selectionChanged; + VertexSelection m_vertexSelection; + VertexSelection m_edgeSelection; -void transformComponents( const Matrix4& matrix ){ - for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).transformComponents( matrix ); - } -} -const AABB& getSelectedComponentsBounds() const { - m_aabb_component = AABB(); +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 MemberCaller 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 + 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 + 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 + void SelectedFaces_foreach(Functor functor) const + { + if (isSelected()) { + functor(centroid()); + } + } + + template + 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 ¢roid() 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); + } +}; - for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).iterate_selected( m_aabb_component ); - } +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); + } +}; - return m_aabb_component; +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); + } } -void snapComponents( float snap ){ - for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).snapComponents( snap ); - } -} -void evaluateTransform(){ - Matrix4 matrix( m_transform.calculateTransform() ); - //globalOutputStream() << "matrix: " << matrix << "\n"; - if ( m_transform.getType() == TRANSFORM_PRIMITIVE ) { - m_brush.transform( matrix ); - } - else - { - transformComponents( matrix ); - } -} -void applyTransform(){ - m_brush.revertTransform(); - evaluateTransform(); - m_brush.freezeTransform(); -} -typedef MemberCaller ApplyTransformCaller; +typedef std::vector FaceInstances; -void setClipPlane( const Plane3& plane ){ - m_clipPlane.setPlane( m_brush, plane ); -} +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; + } -bool testLight( const RendererLight& light ) const { - return light.testAABB( worldAABB() ); -} -void insertLight( const RendererLight& light ){ - const Matrix4& localToWorld = Instance::localToWorld(); - for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - Face_addLight( *i, localToWorld, light ); - } -} -void clearLights(){ - for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) - { - ( *i ).m_lights.clear(); - } -} +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::install(m_casts); + InstanceContainedCast::install(m_casts); + InstanceContainedCast::install(m_casts); + InstanceStaticCast::install(m_casts); + InstanceStaticCast::install(m_casts); + InstanceStaticCast::install(m_casts); + InstanceStaticCast::install(m_casts); + InstanceStaticCast::install(m_casts); + InstanceStaticCast::install(m_casts); + InstanceIdentityCast::install(m_casts); + InstanceContainedCast::install(m_casts); + } + + InstanceTypeCastTable &get() + { + return m_casts; + } + }; + + + Brush &m_brush; + + FaceInstances m_faceInstances; + + typedef std::vector EdgeInstances; + EdgeInstances m_edgeInstances; + typedef std::vector VertexInstances; + VertexInstances m_vertexInstances; + + ObservedSelectable m_selectable; + + mutable RenderableWireframe m_render_wireframe; + mutable RenderablePointVector m_render_selected; + mutable AABB m_aabb_component; + mutable Array m_faceCentroidPointsCulled; + RenderablePointArray m_render_faces_wireframe; + mutable bool m_viewChanged; // requires re-evaluation of view-dependent cached data + + BrushClipPlane m_clipPlane; + + static Shader *m_state_selpoint; + + const LightList *m_lightList; + + TransformModifier m_transform; + + BrushInstance(const BrushInstance &other); // NOT COPYABLE + BrushInstance &operator=(const BrushInstance &other); // NOT ASSIGNABLE +public: + static Counter *m_counter; + + typedef LazyStatic StaticTypeCasts; + + void lightsChanged() + { + m_lightList->lightsChanged(); + } + + typedef MemberCaller LightsChangedCaller; + + STRING_CONSTANT(Name, "BrushInstance"); + + BrushInstance(const scene::Path &path, scene::Instance *parent, Brush &brush) : + Instance(path, parent, this, StaticTypeCasts::instance().get()), + m_brush(brush), + m_selectable(SelectedChangedCaller(*this)), + m_render_selected(GL_POINTS), + m_render_faces_wireframe(m_faceCentroidPointsCulled, GL_POINTS), + m_viewChanged(false), + m_transform(Brush::TransformChangedCaller(m_brush), ApplyTransformCaller(*this)) + { + m_brush.instanceAttach(Instance::path()); + m_brush.attach(*this); + m_counter->increment(); + + m_lightList = &GlobalShaderCache().attach(*this); + m_brush.m_lightsChanged = LightsChangedCaller(*this); ///\todo Make this work with instancing. + + Instance::setTransformChangedCallback(LightsChangedCaller(*this)); + } + + ~BrushInstance() + { + Instance::setTransformChangedCallback(Callback()); + + m_brush.m_lightsChanged = Callback(); + GlobalShaderCache().detach(*this); + + m_counter->decrement(); + m_brush.detach(*this); + m_brush.instanceDetach(Instance::path()); + } + + Brush &getBrush() + { + return m_brush; + } + + const Brush &getBrush() const + { + return m_brush; + } + + Bounded &get(NullType) + { + return m_brush; + } + + Cullable &get(NullType) + { + return m_brush; + } + + Transformable &get(NullType) + { + return m_transform; + } + + void selectedChanged(const Selectable &selectable) + { + GlobalSelectionSystem().getObserver(SelectionSystem::ePrimitive)(selectable); + GlobalSelectionSystem().onSelectedChanged(*this, selectable); + + Instance::selectedChanged(); + } + + typedef MemberCaller SelectedChangedCaller; + + void selectedChangedComponent(const Selectable &selectable) + { + GlobalSelectionSystem().getObserver(SelectionSystem::eComponent)(selectable); + GlobalSelectionSystem().onComponentSelection(*this, selectable); + } + + typedef MemberCaller SelectedChangedComponentCaller; + + const BrushInstanceVisitor &forEachFaceInstance(const BrushInstanceVisitor &visitor) + { + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + visitor.visit(*i); + } + return visitor; + } + + static void constructStatic() + { + m_state_selpoint = GlobalShaderCache().capture("$SELPOINT"); + } + + static void destroyStatic() + { + GlobalShaderCache().release("$SELPOINT"); + } + + void clear() + { + m_faceInstances.clear(); + } + + void reserve(std::size_t size) + { + m_faceInstances.reserve(size); + } + + void push_back(Face &face) + { + m_faceInstances.push_back(FaceInstance(face, SelectedChangedComponentCaller(*this))); + } + + void pop_back() + { + ASSERT_MESSAGE(!m_faceInstances.empty(), "erasing invalid element"); + m_faceInstances.pop_back(); + } + + void erase(std::size_t index) + { + ASSERT_MESSAGE(index < m_faceInstances.size(), "erasing invalid element"); + m_faceInstances.erase(m_faceInstances.begin() + index); + } + + void connectivityChanged() + { + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).connectivityChanged(); + } + } + + void edge_clear() + { + m_edgeInstances.clear(); + } + + void edge_push_back(SelectableEdge &edge) + { + m_edgeInstances.push_back(EdgeInstance(m_faceInstances, edge)); + } + + void vertex_clear() + { + m_vertexInstances.clear(); + } + + void vertex_push_back(SelectableVertex &vertex) + { + m_vertexInstances.push_back(VertexInstance(m_faceInstances, vertex)); + } + + void DEBUG_verify() const + { + ASSERT_MESSAGE(m_faceInstances.size() == m_brush.DEBUG_size(), "FATAL: mismatch"); + } + + bool isSelected() const + { + return m_selectable.isSelected(); + } + + void setSelected(bool select) + { + m_selectable.setSelected(select); + } + + void update_selected() const + { + m_render_selected.clear(); + for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + if ((*i).getFace().contributes()) { + (*i).iterate_selected(m_render_selected); + } + } + } + + void evaluateViewDependent(const VolumeTest &volume, const Matrix4 &localToWorld) const + { + if (m_viewChanged) { + m_viewChanged = false; + + bool faces_visible[c_brush_maxFaces]; + { + bool *j = faces_visible; + for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i, ++j) { + *j = (*i).intersectVolume(volume, localToWorld); + } + } + + m_brush.update_wireframe(m_render_wireframe, faces_visible); + m_brush.update_faces_wireframe(m_faceCentroidPointsCulled, faces_visible); + } + } + + void renderComponentsSelected(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld) const + { + m_brush.evaluateBRep(); + + update_selected(); + if (!m_render_selected.empty()) { + renderer.Highlight(Renderer::ePrimitive, false); + renderer.SetState(m_state_selpoint, Renderer::eWireframeOnly); + renderer.SetState(m_state_selpoint, Renderer::eFullMaterials); + renderer.addRenderable(m_render_selected, localToWorld); + } + } + + void renderComponents(Renderer &renderer, const VolumeTest &volume) const + { + m_brush.evaluateBRep(); + + const Matrix4 &localToWorld = Instance::localToWorld(); + + renderer.SetState(m_brush.m_state_point, Renderer::eWireframeOnly); + renderer.SetState(m_brush.m_state_point, Renderer::eFullMaterials); + + if (volume.fill() && GlobalSelectionSystem().ComponentMode() == SelectionSystem::eFace) { + evaluateViewDependent(volume, localToWorld); + renderer.addRenderable(m_render_faces_wireframe, localToWorld); + } else { + m_brush.renderComponents(GlobalSelectionSystem().ComponentMode(), renderer, volume, localToWorld); + } + } + + void renderClipPlane(Renderer &renderer, const VolumeTest &volume) const + { + if (GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eClip && isSelected()) { + m_clipPlane.render(renderer, volume, localToWorld()); + } + } + + void renderCommon(Renderer &renderer, const VolumeTest &volume) const + { + bool componentMode = GlobalSelectionSystem().Mode() == SelectionSystem::eComponent; + + if (componentMode && isSelected()) { + renderComponents(renderer, volume); + } + + if (parentSelected()) { + if (!componentMode) { + renderer.Highlight(Renderer::eFace); + } + renderer.Highlight(Renderer::ePrimitive); + } + } + + void renderSolid(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld) const + { + //renderCommon(renderer, volume); + + m_lightList->evaluateLights(); + + for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + renderer.setLights((*i).m_lights); + (*i).render(renderer, volume, localToWorld); + } + + renderComponentsSelected(renderer, volume, localToWorld); + } + + void renderWireframe(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld) const + { + //renderCommon(renderer, volume); + + evaluateViewDependent(volume, localToWorld); + + if (m_render_wireframe.m_size != 0) { + renderer.addRenderable(m_render_wireframe, localToWorld); + } + + renderComponentsSelected(renderer, volume, localToWorld); + } + + void renderSolid(Renderer &renderer, const VolumeTest &volume) const + { + m_brush.evaluateBRep(); + + renderClipPlane(renderer, volume); + + renderSolid(renderer, volume, localToWorld()); + } + + void renderWireframe(Renderer &renderer, const VolumeTest &volume) const + { + m_brush.evaluateBRep(); + + renderClipPlane(renderer, volume); + + renderWireframe(renderer, volume, localToWorld()); + } + + void viewChanged() const + { + m_viewChanged = true; + } + + void testSelect(Selector &selector, SelectionTest &test) + { + test.BeginMesh(localToWorld()); + + SelectionIntersection best; + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).testSelect(test, best); + } + if (best.valid()) { + selector.addIntersection(best); + } + } + + bool isSelectedComponents() const + { + for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + if ((*i).selectedComponents()) { + return true; + } + } + return false; + } + + void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode) + { + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).setSelected(mode, select); + } + } + + void testSelectComponents(Selector &selector, SelectionTest &test, SelectionSystem::EComponentMode mode) + { + test.BeginMesh(localToWorld()); + + switch (mode) { + case SelectionSystem::eVertex: { + for (VertexInstances::iterator i = m_vertexInstances.begin(); i != m_vertexInstances.end(); ++i) { + (*i).testSelect(selector, test); + } + } + break; + case SelectionSystem::eEdge: { + for (EdgeInstances::iterator i = m_edgeInstances.begin(); i != m_edgeInstances.end(); ++i) { + (*i).testSelect(selector, test); + } + } + break; + case SelectionSystem::eFace: { + if (test.getVolume().fill()) { + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).testSelect(selector, test); + } + } else { + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).testSelect_centroid(selector, test); + } + } + } + break; + default: + break; + } + } + + void selectPlanes(Selector &selector, SelectionTest &test, const PlaneCallback &selectedPlaneCallback) + { + test.BeginMesh(localToWorld()); + + PlanePointer brushPlanes[c_brush_maxFaces]; + PlanesIterator j = brushPlanes; + + for (Brush::const_iterator i = m_brush.begin(); i != m_brush.end(); ++i) { + *j++ = &(*i)->plane3(); + } + + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).selectPlane(selector, Line(test.getNear(), test.getFar()), brushPlanes, j, selectedPlaneCallback); + } + } + + void selectReversedPlanes(Selector &selector, const SelectedPlanes &selectedPlanes) + { + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).selectReversedPlane(selector, selectedPlanes); + } + } + + + void transformComponents(const Matrix4 &matrix) + { + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).transformComponents(matrix); + } + } + + const AABB &getSelectedComponentsBounds() const + { + m_aabb_component = AABB(); + + for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).iterate_selected(m_aabb_component); + } + + return m_aabb_component; + } + + void snapComponents(float snap) + { + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).snapComponents(snap); + } + } + + void evaluateTransform() + { + Matrix4 matrix(m_transform.calculateTransform()); + //globalOutputStream() << "matrix: " << matrix << "\n"; + + if (m_transform.getType() == TRANSFORM_PRIMITIVE) { + m_brush.transform(matrix); + } else { + transformComponents(matrix); + } + } + + void applyTransform() + { + m_brush.revertTransform(); + evaluateTransform(); + m_brush.freezeTransform(); + } + + typedef MemberCaller ApplyTransformCaller; + + void setClipPlane(const Plane3 &plane) + { + m_clipPlane.setPlane(m_brush, plane); + } + + bool testLight(const RendererLight &light) const + { + return light.testAABB(worldAABB()); + } + + void insertLight(const RendererLight &light) + { + const Matrix4 &localToWorld = Instance::localToWorld(); + for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + Face_addLight(*i, localToWorld, light); + } + } + + void clearLights() + { + for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).m_lights.clear(); + } + } }; -inline BrushInstance* Instance_getBrush( scene::Instance& instance ){ - return InstanceTypeCast::cast( instance ); +inline BrushInstance *Instance_getBrush(scene::Instance &instance) +{ + return InstanceTypeCast::cast(instance); } template -class BrushSelectedVisitor : public SelectionSystem::Visitor -{ -const Functor& m_functor; +class BrushSelectedVisitor : public SelectionSystem::Visitor { + const Functor &m_functor; public: -BrushSelectedVisitor( const Functor& functor ) : m_functor( functor ){ -} -void visit( scene::Instance& instance ) const { - BrushInstance* brush = Instance_getBrush( instance ); - if ( brush != 0 ) { - m_functor( *brush ); - } -} + BrushSelectedVisitor(const Functor &functor) : m_functor(functor) + { + } + + void visit(scene::Instance &instance) const + { + BrushInstance *brush = Instance_getBrush(instance); + if (brush != 0) { + m_functor(*brush); + } + } }; template -inline const Functor& Scene_forEachSelectedBrush( const Functor& functor ){ - GlobalSelectionSystem().foreachSelected( BrushSelectedVisitor( functor ) ); - return functor; +inline const Functor &Scene_forEachSelectedBrush(const Functor &functor) +{ + GlobalSelectionSystem().foreachSelected(BrushSelectedVisitor(functor)); + return functor; } template -class BrushVisibleSelectedVisitor : public SelectionSystem::Visitor -{ -const Functor& m_functor; +class BrushVisibleSelectedVisitor : public SelectionSystem::Visitor { + const Functor &m_functor; public: -BrushVisibleSelectedVisitor( const Functor& functor ) : m_functor( functor ){ -} -void visit( scene::Instance& instance ) const { - BrushInstance* brush = Instance_getBrush( instance ); - if ( brush != 0 - && instance.path().top().get().visible() ) { - m_functor( *brush ); - } -} + BrushVisibleSelectedVisitor(const Functor &functor) : m_functor(functor) + { + } + + void visit(scene::Instance &instance) const + { + BrushInstance *brush = Instance_getBrush(instance); + if (brush != 0 + && instance.path().top().get().visible()) { + m_functor(*brush); + } + } }; template -inline const Functor& Scene_forEachVisibleSelectedBrush( const Functor& functor ){ - GlobalSelectionSystem().foreachSelected( BrushVisibleSelectedVisitor( functor ) ); - return functor; +inline const Functor &Scene_forEachVisibleSelectedBrush(const Functor &functor) +{ + GlobalSelectionSystem().foreachSelected(BrushVisibleSelectedVisitor(functor)); + return functor; } -class BrushForEachFace -{ -const BrushInstanceVisitor& m_visitor; +class BrushForEachFace { + const BrushInstanceVisitor &m_visitor; public: -BrushForEachFace( const BrushInstanceVisitor& visitor ) : m_visitor( visitor ){ -} -void operator()( BrushInstance& brush ) const { - brush.forEachFaceInstance( m_visitor ); -} + BrushForEachFace(const BrushInstanceVisitor &visitor) : m_visitor(visitor) + { + } + + void operator()(BrushInstance &brush) const + { + brush.forEachFaceInstance(m_visitor); + } }; template -class FaceInstanceVisitFace : public BrushInstanceVisitor -{ -const Functor& functor; +class FaceInstanceVisitFace : public BrushInstanceVisitor { + const Functor &functor; public: -FaceInstanceVisitFace( const Functor& functor ) - : functor( functor ){ -} -void visit( FaceInstance& face ) const { - functor( face.getFace() ); -} + FaceInstanceVisitFace(const Functor &functor) + : functor(functor) + { + } + + void visit(FaceInstance &face) const + { + functor(face.getFace()); + } }; template -inline const Functor& Brush_forEachFace( BrushInstance& brush, const Functor& functor ){ - brush.forEachFaceInstance( FaceInstanceVisitFace( functor ) ); - return functor; +inline const Functor &Brush_forEachFace(BrushInstance &brush, const Functor &functor) +{ + brush.forEachFaceInstance(FaceInstanceVisitFace(functor)); + return functor; } template -class FaceVisitAll : public BrushVisitor -{ -const Functor& functor; +class FaceVisitAll : public BrushVisitor { + const Functor &functor; public: -FaceVisitAll( const Functor& functor ) - : functor( functor ){ -} -void visit( Face& face ) const { - functor( face ); -} + FaceVisitAll(const Functor &functor) + : functor(functor) + { + } + + void visit(Face &face) const + { + functor(face); + } }; template -inline const Functor& Brush_forEachFace( const Brush& brush, const Functor& functor ){ - brush.forEachFace( FaceVisitAll( functor ) ); - return functor; +inline const Functor &Brush_forEachFace(const Brush &brush, const Functor &functor) +{ + brush.forEachFace(FaceVisitAll(functor)); + return functor; } template -inline const Functor& Brush_forEachFace( Brush& brush, const Functor& functor ){ - brush.forEachFace( FaceVisitAll( functor ) ); - return functor; +inline const Functor &Brush_forEachFace(Brush &brush, const Functor &functor) +{ + brush.forEachFace(FaceVisitAll(functor)); + return functor; } template -class FaceInstanceVisitAll : public BrushInstanceVisitor -{ -const Functor& functor; +class FaceInstanceVisitAll : public BrushInstanceVisitor { + const Functor &functor; public: -FaceInstanceVisitAll( const Functor& functor ) - : functor( functor ){ -} -void visit( FaceInstance& face ) const { - functor( face ); -} + FaceInstanceVisitAll(const Functor &functor) + : functor(functor) + { + } + + void visit(FaceInstance &face) const + { + functor(face); + } }; template -inline const Functor& Brush_ForEachFaceInstance( BrushInstance& brush, const Functor& functor ){ - brush.forEachFaceInstance( FaceInstanceVisitAll( functor ) ); - return functor; +inline const Functor &Brush_ForEachFaceInstance(BrushInstance &brush, const Functor &functor) +{ + brush.forEachFaceInstance(FaceInstanceVisitAll(functor)); + return functor; } template -inline const Functor& Scene_forEachBrush( scene::Graph& graph, const Functor& functor ){ - graph.traverse( InstanceWalker< InstanceApply >( functor ) ); - return functor; +inline const Functor &Scene_forEachBrush(scene::Graph &graph, const Functor &functor) +{ + graph.traverse(InstanceWalker >(functor)); + return functor; } template -class InstanceIfVisible : public Functor -{ +class InstanceIfVisible : public Functor { public: -InstanceIfVisible( const Functor& functor ) : Functor( functor ){ -} -void operator()( scene::Instance& instance ){ - if ( instance.path().top().get().visible() ) { - Functor::operator()( instance ); - } -} + InstanceIfVisible(const Functor &functor) : Functor(functor) + { + } + + void operator()(scene::Instance &instance) + { + if (instance.path().top().get().visible()) { + Functor::operator()(instance); + } + } }; template -class BrushVisibleWalker : public scene::Graph::Walker -{ -const Functor& m_functor; +class BrushVisibleWalker : public scene::Graph::Walker { + const Functor &m_functor; public: -BrushVisibleWalker( const Functor& functor ) : m_functor( functor ){ -} -bool pre( const scene::Path& path, scene::Instance& instance ) const { - if ( path.top().get().visible() ) { - BrushInstance* brush = Instance_getBrush( instance ); - if ( brush != 0 ) { - m_functor( *brush ); - } - } - return true; -} + BrushVisibleWalker(const Functor &functor) : m_functor(functor) + { + } + + bool pre(const scene::Path &path, scene::Instance &instance) const + { + if (path.top().get().visible()) { + BrushInstance *brush = Instance_getBrush(instance); + if (brush != 0) { + m_functor(*brush); + } + } + return true; + } }; template -inline const Functor& Scene_forEachVisibleBrush( scene::Graph& graph, const Functor& functor ){ - graph.traverse( BrushVisibleWalker( functor ) ); - return functor; +inline const Functor &Scene_forEachVisibleBrush(scene::Graph &graph, const Functor &functor) +{ + graph.traverse(BrushVisibleWalker(functor)); + return functor; } template -inline const Functor& Scene_ForEachBrush_ForEachFace( scene::Graph& graph, const Functor& functor ){ - Scene_forEachBrush( graph, BrushForEachFace( FaceInstanceVisitFace( functor ) ) ); - return functor; +inline const Functor &Scene_ForEachBrush_ForEachFace(scene::Graph &graph, const Functor &functor) +{ + Scene_forEachBrush(graph, BrushForEachFace(FaceInstanceVisitFace(functor))); + return functor; } // d1223m template -inline const Functor& Scene_ForEachBrush_ForEachFaceInstance( scene::Graph& graph, const Functor& functor ){ - Scene_forEachBrush( graph, BrushForEachFace( FaceInstanceVisitAll( functor ) ) ); - return functor; +inline const Functor &Scene_ForEachBrush_ForEachFaceInstance(scene::Graph &graph, const Functor &functor) +{ + Scene_forEachBrush(graph, BrushForEachFace(FaceInstanceVisitAll(functor))); + return functor; } template -inline const Functor& Scene_ForEachSelectedBrush_ForEachFace( scene::Graph& graph, const Functor& functor ){ - Scene_forEachSelectedBrush( BrushForEachFace( FaceInstanceVisitFace( functor ) ) ); - return functor; +inline const Functor &Scene_ForEachSelectedBrush_ForEachFace(scene::Graph &graph, const Functor &functor) +{ + Scene_forEachSelectedBrush(BrushForEachFace(FaceInstanceVisitFace(functor))); + return functor; } template -inline const Functor& Scene_ForEachSelectedBrush_ForEachFaceInstance( scene::Graph& graph, const Functor& functor ){ - Scene_forEachSelectedBrush( BrushForEachFace( FaceInstanceVisitAll( functor ) ) ); - return functor; +inline const Functor &Scene_ForEachSelectedBrush_ForEachFaceInstance(scene::Graph &graph, const Functor &functor) +{ + Scene_forEachSelectedBrush(BrushForEachFace(FaceInstanceVisitAll(functor))); + return functor; } template -class FaceVisitorWrapper -{ -const Functor& functor; +class FaceVisitorWrapper { + const Functor &functor; public: -FaceVisitorWrapper( const Functor& functor ) : functor( functor ){ -} - -void operator()( FaceInstance& faceInstance ) const { - functor( faceInstance.getFace() ); -} + FaceVisitorWrapper(const Functor &functor) : functor(functor) + { + } + + void operator()(FaceInstance &faceInstance) const + { + functor(faceInstance.getFace()); + } }; template -inline const Functor& Scene_ForEachSelectedBrushFace( scene::Graph& graph, const Functor& functor ){ - g_SelectedFaceInstances.foreach( FaceVisitorWrapper( functor ) ); - return functor; +inline const Functor &Scene_ForEachSelectedBrushFace(scene::Graph &graph, const Functor &functor) +{ + g_SelectedFaceInstances.foreach(FaceVisitorWrapper(functor)); + return functor; }