X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fnetradiant.git;a=blobdiff_plain;f=radiant%2Fbrush.h;h=7748327d089b520da17d63caa28ceffe2c5d4af7;hp=80e0bd0265ae99e0aec49e9905c1ccc6b8212343;hb=110313f1465df17a8dccd6429279ddb27e51ef2d;hpb=ce482631a36163e3ac6dc8ba21b3799eb4a0c10e diff --git a/radiant/brush.h b/radiant/brush.h index 80e0bd02..7748327d 100644 --- a/radiant/brush.h +++ b/radiant/brush.h @@ -1,25 +1,25 @@ /* -Copyright (C) 1999-2006 Id Software, Inc. and contributors. -For a list of contributors, see the accompanying CONTRIBUTORS file. + Copyright (C) 1999-2006 Id Software, Inc. and contributors. + For a list of contributors, see the accompanying CONTRIBUTORS file. -This file is part of GtkRadiant. + This file is part of GtkRadiant. -GtkRadiant is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + GtkRadiant is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -GtkRadiant is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + GtkRadiant is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with GtkRadiant; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + You should have received a copy of the GNU General Public License + along with GtkRadiant; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ -#if !defined(INCLUDED_BRUSH_H) +#if !defined( INCLUDED_BRUSH_H ) #define INCLUDED_BRUSH_H /// \file @@ -66,17 +66,17 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "brush_primit.h" const unsigned int BRUSH_DETAIL_FLAG = 27; -const unsigned int BRUSH_DETAIL_MASK = (1 << BRUSH_DETAIL_FLAG); +const unsigned int BRUSH_DETAIL_MASK = ( 1 << BRUSH_DETAIL_FLAG ); enum EBrushType { - eBrushTypeQuake, - eBrushTypeQuake2, - eBrushTypeQuake3, - eBrushTypeQuake3BP, - eBrushTypeDoom3, - eBrushTypeQuake4, - eBrushTypeHalfLife, + eBrushTypeQuake, + eBrushTypeQuake2, + eBrushTypeQuake3, + eBrushTypeQuake3BP, + eBrushTypeDoom3, + eBrushTypeQuake4, + eBrushTypeHalfLife, }; @@ -84,120 +84,109 @@ 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) - { - glDrawArrays(GL_TRIANGLE_FAN, 0, GLsizei(winding.numpoints)); - } - else - { - glDrawArrays(GL_LINE_LOOP, 0, GLsizei(winding.numpoints)); - } + if ( state & RENDER_FILL ) { + glDrawArrays( GL_TRIANGLE_FAN, 0, GLsizei( winding.numpoints ) ); + } + else + { + 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; - - if(state & RENDER_FILL) - { - glBegin(GL_POLYGON); - } - else - { - glBegin(GL_LINE_LOOP); - } - - if (state & RENDER_LIGHTING) - glNormal3fv(normal); - - for(int i = 0; i < winding.numpoints; ++i) - { - if (state & RENDER_TEXTURE) - glTexCoord2fv(&winding.points[i][3]); - glVertex3fv(winding.points[i]); - } - glEnd(); + const Winding& winding = winding; + + if ( state & RENDER_FILL ) { + glBegin( GL_POLYGON ); + } + else + { + glBegin( GL_LINE_LOOP ); + } + + if ( state & RENDER_LIGHTING ) { + glNormal3fv( normal ); + } + + for ( int i = 0; i < winding.numpoints; ++i ) + { + if ( state & RENDER_TEXTURE ) { + glTexCoord2fv( &winding.points[i][3] ); + } + glVertex3fv( winding.points[i] ); + } + glEnd(); #endif } @@ -206,152 +195,120 @@ inline void Winding_Draw(const Winding& winding, const Vector3& normal, RenderSt 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 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_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 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 float vector3_max_component( const Vector3& vec3 ){ + return std::max( fabsf( vec3[0] ), std::max( fabsf( vec3[1] ), fabsf( vec3[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 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_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 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_windingpoint(const Vector3& point, const Colour4b& colour) -{ - return PointVertex( - vertex3f_for_vector3(point), - colour - ); +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 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 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 -{ -public: - virtual void realiseShader() = 0; - virtual void unrealiseShader() = 0; -}; +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] ); +} -class FaceShaderObserverRealise -{ -public: - void operator()(FaceShaderObserver& observer) const - { - observer.realiseShader(); - } -}; +inline void brush_check_shader( const char* name ){ + 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; @@ -360,3867 +317,3516 @@ typedef ReferencePair FaceShaderObserverPair; 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 { public: - 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.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; - } +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(); +} + +// 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([](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 { - // not copyable - FaceTexdef(const FaceTexdef& other); - // not assignable - FaceTexdef& operator=(const FaceTexdef& other); +// not copyable +FaceTexdef( const FaceTexdef& other ); + +// not assignable +FaceTexdef& operator=( const FaceTexdef& other ); + +public: +class SavedState +{ 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()); - } +TextureProjection m_projection; + +SavedState( const FaceTexdef& faceTexdef ){ + m_projection = faceTexdef.m_projection; +} + +void exportState( FaceTexdef& faceTexdef ) const { + Texdef_Assign( faceTexdef.m_projection, m_projection ); +} }; -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] << " )"; +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() ); +} -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()); +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() ); } -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()); +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 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 { - PlanePoints m_planepts; - Plane3 m_planeCached; - Plane3 m_plane; +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: - 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()) - { +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 ) ) { + 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() ) { + #if 0 - if(check_plane_is_integer(m_planepts)) - { - globalErrorStream() << "non-integer planepts: "; - planepts_print(m_planepts, globalErrorStream()); - globalErrorStream() << "\n"; - } + bool off = check_plane_is_integer( planePoints() ); #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()) - { + + 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 0 - bool off = 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(); + } +} +}; + +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 quantiseFloating( double f ){ + return float_snapped( f, 1.f / ( 1 << 16 ) ); +} + +typedef double ( *QuantiseFunc )( double f ); + +class Face; + +class FaceFilter +{ +public: +virtual bool filter( const Face& face ) const = 0; +}; + +bool face_filtered( Face& face ); + +void add_face_filter( FaceFilter& filter, int mask, bool invert = false ); + +void Brush_addTextureChangedCallback( const SignalHandler& callback ); + +void Brush_textureChanged(); + + +extern bool g_brush_texturelock_enabled; + +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; + +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; + +PlanePoints m_move_planepts; +PlanePoints m_move_planeptsTransformed; +private: +FacePlane m_plane; +FacePlane m_planeTransformed; +FaceShader m_shader; +FaceTexdef m_texdef; +TextureProjection m_texdefTransformed; + +Winding m_winding; +Vector3 m_centroid; +bool m_filtered; + +FaceObserver* m_observer; +UndoObserver* m_undoable_observer; +MapFile* m_map; + +// assignment not supported +Face& operator=( const Face& other ); + +// copy-construction not supported +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 ); +} + +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 ); + } +} + +// 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(); +} + +/// \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() ) { +#if 0 + 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(); +#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; +} +}; + + +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; +} +}; + +typedef std::size_t faceIndex_t; + +struct EdgeRenderIndices +{ + RenderIndex first; + RenderIndex 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; + + 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 +{ +public: +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() ); +#else + glBegin( GL_LINES ); + for ( std::size_t i = 0; i < m_size; ++i ) + { + glVertex3fv( &m_vertices[m_faceVertex[i].first].vertex.x ); + glVertex3fv( &m_vertices[m_faceVertex[i].second].vertex.x ); + } + glEnd(); +#endif +} + +Array m_faceVertex; +std::size_t m_size; +const PointVertex* m_vertices; +}; + +class Brush; + +typedef std::vector brush_vector_t; + +class BrushFilter +{ +public: +virtual bool filter( const Brush& brush ) const = 0; +}; + +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; +} + +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() ); + + ASSERT_MESSAGE( adjacent_vertex != c_brush_maxFaces, "connectivity data invalid" ); + if ( adjacent_vertex == c_brush_maxFaces ) { + return faceVertex; + } + + 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 +{ +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 ); +} +}; + +class SelectableVertex +{ +Vector3 getVertex() const { + return getFace().getWinding()[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 ); +} +}; + +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 BrushVisitor +{ +public: +virtual void visit( Face& face ) const = 0; +}; + +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 +// ---- + +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 ); + } +} + +// nameable +const char* name() const { + return "brush"; +} + +void attach( const NameCallback& callback ){ +} + +void detach( const NameCallback& callback ){ +} + +// filterable +void updateFiltered(){ + if ( m_node != 0 ) { + if ( brush_filtered( *this ) ) { + m_node->enable( scene::Node::eFiltered ); + } + else + { + m_node->disable( scene::Node::eFiltered ); + } + } +} + +// observer +void planeChanged(){ + m_planeChanged = true; + aabbChanged(); + m_lightsChanged(); +} + +void shaderChanged(){ + updateFiltered(); + planeChanged(); +} + +void evaluateBRep() const { + if ( m_planeChanged ) { + m_planeChanged = false; + const_cast( 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(); + } +} + +/// \brief Returns the absolute index of the \p faceVertex. +std::size_t absoluteIndex( FaceVertexId faceVertex ){ + std::size_t index = 0; + for ( std::size_t i = 0; i < faceVertex.getFace(); ++i ) + { + index += m_faces[i]->getWinding().numpoints; + } + return index + faceVertex.getVertex(); +} + +void appendFaces( const Faces& other ){ + clear(); + for ( Faces::const_iterator i = other.begin(); i != other.end(); ++i ) + { + push_back( *i ); + } +} + +/// \brief The undo memento for a brush stores only the list of face references - the faces are not copied. +class BrushUndoMemento : public UndoMemento +{ +public: +BrushUndoMemento( const Faces& faces ) : m_faces( faces ){ +} + +void release(){ + delete this; +} + +Faces m_faces; +}; + +void undoSave(){ + if ( m_map != 0 ) { + m_map->changed(); + } + if ( m_undoable_observer != 0 ) { + m_undoable_observer->save( this ); + } +} + +UndoMemento* exportState() const { + return new BrushUndoMemento( m_faces ); +} + +void importState( const UndoMemento* state ){ + undoSave(); + appendFaces( static_cast( 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 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& 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; +} - matrix4_transform_point(matrix, m_planepts[0]); - matrix4_transform_point(matrix, m_planepts[1]); - matrix4_transform_point(matrix, m_planepts[2]); +void setSelected( bool select ){ + select_vertex( select ); +} - if(mirror) - { - reverse(); - } +bool isSelected() const { + return selected_vertex(); +} -#if 0 - 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(); - } - } +void testSelect( Selector& selector, SelectionTest& test ){ + SelectionIntersection best; + m_vertex->testSelect( test, best ); + if ( best.valid() ) { + Selector_add( selector, *this, best ); + } +} }; -inline void Winding_testSelect(Winding& winding, SelectionTest& test, SelectionIntersection& best) +class BrushInstanceVisitor { - test.TestPolygon(VertexPointer(reinterpret_cast(&winding.points.data()->vertex), sizeof(WindingVertex)), winding.numpoints, best); -} - -const double GRID_MIN = 0.125; +public: +virtual void visit( FaceInstance& face ) const = 0; +}; -inline double quantiseInteger(double f) -{ - return float_to_integer(f); +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 ); } -inline double quantiseFloating(double f) -{ - return float_snapped(f, 1.f / (1 << 16)); +InstanceTypeCastTable& get(){ + return m_casts; } +}; -typedef double (*QuantiseFunc)(double f); -class Face; +Brush& m_brush; -class FaceFilter -{ -public: - virtual bool filter(const Face& face) const = 0; -}; +FaceInstances m_faceInstances; -bool face_filtered(Face& face); -void add_face_filter(FaceFilter& filter, int mask, bool invert = false); +typedef std::vector EdgeInstances; +EdgeInstances m_edgeInstances; +typedef std::vector VertexInstances; +VertexInstances m_vertexInstances; -void Brush_addTextureChangedCallback(const SignalHandler& callback); -void Brush_textureChanged(); +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 -extern bool g_brush_texturelock_enabled; +BrushClipPlane m_clipPlane; -class FaceObserver -{ -public: - virtual void planeChanged() = 0; - virtual void connectivityChanged() = 0; - virtual void shaderChanged() = 0; - virtual void evaluateTransform() = 0; -}; +static Shader* m_state_selpoint; -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; - } - }; +const LightList* m_lightList; + +TransformModifier m_transform; +BrushInstance( const BrushInstance& other ); // NOT COPYABLE +BrushInstance& operator=( const BrushInstance& other ); // NOT ASSIGNABLE public: - static QuantiseFunc m_quantise; - static EBrushType m_type; +static Counter* m_counter; - PlanePoints m_move_planepts; - PlanePoints m_move_planeptsTransformed; -private: - FacePlane m_plane; - FacePlane m_planeTransformed; - FaceShader m_shader; - FaceTexdef m_texdef; - TextureProjection m_texdefTransformed; +typedef LazyStatic StaticTypeCasts; - Winding m_winding; - Vector3 m_centroid; - bool m_filtered; +void lightsChanged(){ + m_lightList->lightsChanged(); +} - FaceObserver* m_observer; - UndoObserver* m_undoable_observer; - MapFile* m_map; +typedef MemberCaller LightsChangedCaller; - // assignment not supported - Face& operator=(const Face& other); - // copy-construction not supported - Face(const Face& other); +STRING_CONSTANT( Name, "BrushInstance" ); -public: +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(); - 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); - } - } - - // 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); + m_lightList = &GlobalShaderCache().attach( *this ); + m_brush.m_lightsChanged = LightsChangedCaller( *this ); ///\todo Make this work with instancing. -#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(); - } - - /// \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()) - { -#if 0 - 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(); -#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; - } -}; + Instance::setTransformChangedCallback( LightsChangedCaller( *this ) ); +} +~BrushInstance(){ + Instance::setTransformChangedCallback( Callback() ); -class FaceVertexId -{ - std::size_t m_face; - std::size_t m_vertex; + m_brush.m_lightsChanged = Callback(); + GlobalShaderCache().detach( *this ); -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; - } -}; + m_counter->decrement(); + m_brush.detach( *this ); + m_brush.instanceDetach( Instance::path() ); +} -typedef std::size_t faceIndex_t; +Brush& getBrush(){ + return m_brush; +} +const Brush& getBrush() const { + return m_brush; +} -struct EdgeRenderIndices -{ - RenderIndex first; - RenderIndex second; - - EdgeRenderIndices() - : first(0), second(0) - { - } - EdgeRenderIndices(const RenderIndex _first, const RenderIndex _second) - : first(_first), second(_second) - { - } -}; +Bounded& get( NullType){ + return m_brush; +} -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) - { - } -}; +Cullable& get( NullType){ + return m_brush; +} -class RenderableWireframe : public OpenGLRenderable -{ -public: - 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()); -#else - glBegin(GL_LINES); - for(std::size_t i = 0; i < m_size; ++i) - { - glVertex3fv(&m_vertices[m_faceVertex[i].first].vertex.x); - glVertex3fv(&m_vertices[m_faceVertex[i].second].vertex.x); - } - glEnd(); -#endif - } +Transformable& get( NullType){ + return m_transform; +} - Array m_faceVertex; - std::size_t m_size; - const PointVertex* m_vertices; -}; +void selectedChanged( const Selectable& selectable ){ + GlobalSelectionSystem().getObserver ( SelectionSystem::ePrimitive )( selectable ); + GlobalSelectionSystem().onSelectedChanged( *this, selectable ); -class Brush; -typedef std::vector brush_vector_t; + Instance::selectedChanged(); +} +typedef MemberCaller SelectedChangedCaller; -class BrushFilter -{ -public: - virtual bool filter(const Brush& brush) const = 0; -}; +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; +} -bool brush_filtered(Brush& brush); -void add_brush_filter(BrushFilter& filter, int mask, bool invert = false); +static void constructStatic(){ + m_state_selpoint = GlobalShaderCache().capture( "$SELPOINT" ); +} +static void destroyStatic(){ + GlobalShaderCache().release( "$SELPOINT" ); +} -/// \brief Returns true if 'self' takes priority when building brush b-rep. -inline bool plane3_inside(const Plane3& self, const Plane3& other) -{ - if(vector3_equal_epsilon(self.normal(), other.normal(), 0.001)) - { - return self.dist() < other.dist(); - } - return true; +void clear(){ + m_faceInstances.clear(); } -typedef SmartPointer FaceSmartPointer; -typedef std::vector Faces; +void reserve( std::size_t size ){ + m_faceInstances.reserve( size ); +} -/// \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()); +void push_back( Face& face ){ + m_faceInstances.push_back( FaceInstance( face, SelectedChangedComponentCaller( *this ) ) ); +} - ASSERT_MESSAGE(adjacent_vertex != c_brush_maxFaces, "connectivity data invalid"); - if(adjacent_vertex == c_brush_maxFaces) - { - return faceVertex; - } +void pop_back(){ + ASSERT_MESSAGE( !m_faceInstances.empty(), "erasing invalid element" ); + m_faceInstances.pop_back(); +} - return FaceVertexId(adjacent_face, adjacent_vertex); +void erase( std::size_t index ){ + ASSERT_MESSAGE( index < m_faceInstances.size(), "erasing invalid element" ); + m_faceInstances.erase( m_faceInstances.begin() + index ); } -/// \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())); +void connectivityChanged(){ + for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) + { + ( *i ).connectivityChanged(); + } } -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); - } +void edge_clear(){ + m_edgeInstances.clear(); +} -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); - } -}; +void edge_push_back( SelectableEdge& edge ){ + m_edgeInstances.push_back( EdgeInstance( m_faceInstances, edge ) ); +} -class SelectableVertex -{ - Vector3 getVertex() const - { - return getFace().getWinding()[m_faceVertex.getVertex()].vertex; - } +void vertex_clear(){ + m_vertexInstances.clear(); +} -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); - } -}; +void vertex_push_back( SelectableVertex& vertex ){ + m_vertexInstances.push_back( VertexInstance( m_faceInstances, vertex ) ); +} -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; +void DEBUG_verify() const { + ASSERT_MESSAGE( m_faceInstances.size() == m_brush.DEBUG_size(), "FATAL: mismatch" ); +} - virtual void edge_clear() = 0; - virtual void edge_push_back(SelectableEdge& edge) = 0; +bool isSelected() const { + return m_selectable.isSelected(); +} - virtual void vertex_clear() = 0; - virtual void vertex_push_back(SelectableVertex& vertex) = 0; +void setSelected( bool select ){ + m_selectable.setSelected( select ); +} - virtual void DEBUG_verify() const = 0; -}; +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 ); + } + } +} -class BrushVisitor -{ -public: - virtual void visit(Face& face) const = 0; -}; +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 ); + } +} -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 - // ---- - -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); - } - } - - // nameable - const char* name() const - { - return "brush"; - } - void attach(const NameCallback& callback) - { - } - void detach(const NameCallback& callback) - { - } - - // filterable - void updateFiltered() - { - if(m_node != 0) - { - if(brush_filtered(*this)) - { - m_node->enable(scene::Node::eFiltered); - } - else - { - m_node->disable(scene::Node::eFiltered); - } - } - } - - // observer - void planeChanged() - { - m_planeChanged = true; - aabbChanged(); - m_lightsChanged(); - } - void shaderChanged() - { - updateFiltered(); - planeChanged(); - } - - void evaluateBRep() const - { - if(m_planeChanged) - { - m_planeChanged = false; - const_cast(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(); - } - } - - /// \brief Returns the absolute index of the \p faceVertex. - std::size_t absoluteIndex(FaceVertexId faceVertex) - { - std::size_t index = 0; - for(std::size_t i = 0; i < faceVertex.getFace(); ++i) - { - index += m_faces[i]->getWinding().numpoints; - } - return index + faceVertex.getVertex(); - } - - void appendFaces(const Faces& other) - { - clear(); - for(Faces::const_iterator i = other.begin(); i != other.end(); ++i) - { - push_back(*i); - } - } - - /// \brief The undo memento for a brush stores only the list of face references - the faces are not copied. - class BrushUndoMemento : public UndoMemento - { - public: - BrushUndoMemento(const Faces& faces) : m_faces(faces) - { - } - void release() - { - delete this; - } - - Faces m_faces; - }; - - void undoSave() - { - if(m_map != 0) - { - m_map->changed(); - } - if(m_undoable_observer != 0) - { - m_undoable_observer->save(this); - } - } - - UndoMemento* exportState() const - { - return new BrushUndoMemento(m_faces); - } - - void importState(const UndoMemento* state) - { - undoSave(); - appendFaces(static_cast(state)->m_faces); - planeChanged(); - - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) - { - (*i)->DEBUG_verify(); - } - } - - bool isDetail() - { - return !m_faces.empty() && m_faces.front()->isDetail(); - } - - /// \brief Appends a copy of \p face to the end of the face list. - Face* addFace(const Face& face) - { - if(m_faces.size() == c_brush_maxFaces) - { - return 0; - } - undoSave(); - push_back(FaceSmartPointer(new Face(face, this))); - m_faces.back()->setDetail(isDetail()); - planeChanged(); - return m_faces.back(); - } - - /// \brief Appends a new face constructed from the parameters to the end of the face list. - Face* addPlane(const Vector3& p0, const Vector3& p1, const Vector3& p2, const char* shader, const TextureProjection& projection) - { - if(m_faces.size() == c_brush_maxFaces) - { - return 0; - } - undoSave(); - push_back(FaceSmartPointer(new Face(p0, p1, p2, shader, projection, this))); - m_faces.back()->setDetail(isDetail()); - planeChanged(); - return m_faces.back(); - } - - static void constructStatic(EBrushType type) - { - m_type = type; - Face::m_type = type; - FacePlane::m_type = type; - - g_bp_globals.m_texdefTypeId = TEXDEFTYPEID_QUAKE; - if(m_type == eBrushTypeQuake3BP || m_type == eBrushTypeDoom3 || m_type == eBrushTypeQuake4) - { - g_bp_globals.m_texdefTypeId = TEXDEFTYPEID_BRUSHPRIMITIVES; - g_brush_texturelock_enabled = true; - } - else if(m_type == eBrushTypeHalfLife) - { - g_bp_globals.m_texdefTypeId = TEXDEFTYPEID_HALFLIFE; - g_brush_texturelock_enabled = true; - } - - Face::m_quantise = (m_type == eBrushTypeQuake) ? quantiseInteger : quantiseFloating; - - m_state_point = GlobalShaderCache().capture("$POINT"); - } - static void destroyStatic() - { - GlobalShaderCache().release("$POINT"); - } - - std::size_t DEBUG_size() - { - return m_faces.size(); - } - - typedef Faces::const_iterator const_iterator; - - const_iterator begin() const - { - return m_faces.begin(); - } - const_iterator end() const - { - return m_faces.end(); - } - - Face* back() - { - return m_faces.back(); - } - const Face* back() const - { - return m_faces.back(); - } - void reserve(std::size_t count) - { - m_faces.reserve(count); - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) - { - (*i)->reserve(count); - } - } - void push_back(Faces::value_type face) - { - m_faces.push_back(face); - if(m_instanceCounter.m_count != 0) - { - m_faces.back()->instanceAttach(m_map); - } - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) - { - (*i)->push_back(*face); - (*i)->DEBUG_verify(); - } - } - void pop_back() - { - if(m_instanceCounter.m_count != 0) - { - m_faces.back()->instanceDetach(m_map); - } - m_faces.pop_back(); - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) - { - (*i)->pop_back(); - (*i)->DEBUG_verify(); - } - } - void erase(std::size_t index) - { - if(m_instanceCounter.m_count != 0) - { - m_faces[index]->instanceDetach(m_map); - } - m_faces.erase(m_faces.begin() + index); - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) - { - (*i)->erase(index); - (*i)->DEBUG_verify(); - } - } - void connectivityChanged() - { - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) - { - (*i)->connectivityChanged(); - } - } - - - void clear() - { - undoSave(); - if(m_instanceCounter.m_count != 0) - { - forEachFace_instanceDetach(m_map); - } - m_faces.clear(); - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) - { - (*i)->clear(); - (*i)->DEBUG_verify(); - } - } - std::size_t size() const - { - return m_faces.size(); - } - bool empty() const - { - return m_faces.empty(); - } - - /// \brief Returns true if any face of the brush contributes to the final B-Rep. - bool hasContributingFaces() const - { - for(const_iterator i = begin(); i != end(); ++i) - { - if((*i)->contributes()) - { - return true; - } - } - return false; - } - - /// \brief Removes faces that do not contribute to the brush. This is useful for cleaning up after CSG operations on the brush. - /// Note: removal of empty faces is not performed during direct brush manipulations, because it would make a manipulation irreversible if it created an empty face. - void removeEmptyFaces() - { - evaluateBRep(); - - { - std::size_t i = 0; - while(i < m_faces.size()) - { - if(!m_faces[i]->contributes()) - { - erase(i); - planeChanged(); - } - else - { - ++i; - } - } - } - } - - /// \brief Constructs \p winding from the intersection of \p plane with the other planes of the brush. - void windingForClipPlane(Winding& winding, const Plane3& plane) const - { - FixedWinding buffer[2]; - bool swap = false; - - // get a poly that covers an effectively infinite area - Winding_createInfinite(buffer[swap], plane, m_maxWorldCoord + 1); - - // chop the poly by all of the other faces - { - for (std::size_t i = 0; i < m_faces.size(); ++i) - { - const Face& clip = *m_faces[i]; - - if(plane3_equal(clip.plane3(), plane) - || !plane3_valid(clip.plane3()) || !plane_unique(i) - || plane3_opposing(plane, clip.plane3())) - { - continue; - } - - buffer[!swap].clear(); +void renderComponentsSelected( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const { + m_brush.evaluateBRep(); -#if BRUSH_CONNECTIVITY_DEBUG - globalOutputStream() << "clip vs face: " << i << "\n"; -#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 ); + } +} - { - // 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]); - } +void renderComponents( Renderer& renderer, const VolumeTest& volume ) const { + m_brush.evaluateBRep(); -#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 + const Matrix4& localToWorld = Instance::localToWorld(); - //ASSERT_MESSAGE(buffer[!swap].numpoints != 1, "created single-point winding"); + renderer.SetState( m_brush.m_state_point, Renderer::eWireframeOnly ); + renderer.SetState( m_brush.m_state_point, Renderer::eFullMaterials ); - swap = !swap; - } - } + 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 ); + } +} - Winding_forFixedWinding(winding, buffer[swap]); +void renderClipPlane( Renderer& renderer, const VolumeTest& volume ) const { + if ( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eClip && isSelected() ) { + m_clipPlane.render( renderer, volume, localToWorld() ); + } +} -#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(); - } +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(); - } - } - - /// \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())) - { - 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(); -}; + 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(); -class FaceInstance; + for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) + { + renderer.setLights( ( *i ).m_lights ); + ( *i ).render( 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(); - } -}; + renderComponentsSelected( renderer, volume, localToWorld ); +} -extern FaceInstanceSet g_SelectedFaceInstances; +void renderWireframe( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const { + //renderCommon(renderer, volume); -typedef std::list VertexSelection; + evaluateViewDependent( volume, localToWorld ); -inline VertexSelection::iterator VertexSelection_find(VertexSelection& self, std::size_t value) -{ - return std::find(self.begin(), self.end(), value); + if ( m_render_wireframe.m_size != 0 ) { + renderer.addRenderable( m_render_wireframe, localToWorld ); + } + + renderComponentsSelected( renderer, volume, localToWorld ); } -inline VertexSelection::const_iterator VertexSelection_find(const VertexSelection& self, std::size_t value) -{ - return std::find(self.begin(), self.end(), value); +void renderSolid( Renderer& renderer, const VolumeTest& volume ) const { + m_brush.evaluateBRep(); + + renderClipPlane( renderer, volume ); + + renderSolid( renderer, volume, 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; -} -inline void VertexSelection_erase(VertexSelection& self, std::size_t value) -{ - VertexSelection::iterator i = VertexSelection_find(self, value); - if(i != self.end()) - { - self.erase(i); - } +void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const { + m_brush.evaluateBRep(); + + renderClipPlane( renderer, volume ); + + renderWireframe( renderer, volume, localToWorld() ); } -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 viewChanged() const { + m_viewChanged = true; } -template -inline Vector3 triangle_cross(const BasicVector3& x, const BasicVector3 y, const BasicVector3& z) -{ - return vector3_cross(y - x, z - x); + +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 ); + } } -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; + +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 ); + } +} -typedef const Plane3* PlanePointer; -typedef PlanePointer* PlanesIterator; +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; + } +} -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)); - } - } -}; +void selectPlanes( Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback ){ + test.BeginMesh( localToWorld() ); -class FaceInstance -{ - Face* m_face; - ObservedSelectable m_selectable; - ObservedSelectable m_selectableVertices; - ObservedSelectable m_selectableEdges; - SelectionChangeCallback m_selectionChanged; + PlanePointer brushPlanes[c_brush_maxFaces]; + PlanesIterator j = brushPlanes; - VertexSelection m_vertexSelection; - VertexSelection m_edgeSelection; + for ( Brush::const_iterator i = m_brush.begin(); i != m_brush.end(); ++i ) + { + *j++ = &( *i )->plane3(); + } -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); - } -}; + for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) + { + ( *i ).selectPlane( selector, Line( test.getNear(), test.getFar() ), brushPlanes, j, selectedPlaneCallback ); + } +} -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); - } -}; +void selectReversedPlanes( Selector& selector, const SelectedPlanes& selectedPlanes ){ + for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) + { + ( *i ).selectReversedPlane( selector, selectedPlanes ); + } +} -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 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 ); + } -typedef std::vector FaceInstances; + return m_aabb_component; +} -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; - } +void snapComponents( float snap ){ + for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i ) + { + ( *i ).snapComponents( snap ); + } +} -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); - } - } -}; +void evaluateTransform(){ + Matrix4 matrix( m_transform.calculateTransform() ); + //globalOutputStream() << "matrix: " << matrix << "\n"; + + if ( m_transform.getType() == TRANSFORM_PRIMITIVE ) { + m_brush.transform( matrix ); + } + else + { + transformComponents( matrix ); + } +} -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; - } +void applyTransform(){ + m_brush.revertTransform(); + evaluateTransform(); + m_brush.freezeTransform(); +} -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); - } - } -}; +typedef MemberCaller ApplyTransformCaller; -class BrushInstanceVisitor -{ -public: - virtual void visit(FaceInstance& face) const = 0; -}; +void setClipPlane( const Plane3& plane ){ + m_clipPlane.setPlane( m_brush, plane ); +} -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 MemberCaller1 SelectedChangedCaller; - - void selectedChangedComponent(const Selectable& selectable) - { - GlobalSelectionSystem().getObserver(SelectionSystem::eComponent)(selectable); - GlobalSelectionSystem().onComponentSelection(*this, selectable); - } - typedef MemberCaller1 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(); - } - } +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; +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; +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; +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; +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; +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; +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< InstanceApply >( functor ) ); + return functor; } template 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; +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; +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; }