X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=radiant%2Fbrush.h;h=6114d59af9ec5f3e80e1b532e72e47fc4bfaaf85;hb=408628d516d676ef436af6f2603182f15eba0be4;hp=03f76118c22ccbbf76f412437bb8c4def991d55d;hpb=90402b69785a250119233d2f1d91247e7b61b834;p=xonotic%2Fnetradiant.git diff --git a/radiant/brush.h b/radiant/brush.h index 03f76118..6114d59a 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 @@ -68,15 +68,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA const unsigned int BRUSH_DETAIL_FLAG = 27; const unsigned int BRUSH_DETAIL_MASK = (1 << BRUSH_DETAIL_FLAG); -enum EBrushType -{ - eBrushTypeQuake, - eBrushTypeQuake2, - eBrushTypeQuake3, - eBrushTypeQuake3BP, - eBrushTypeDoom3, - eBrushTypeQuake4, - eBrushTypeHalfLife, +enum EBrushType { + eBrushTypeQuake, + eBrushTypeQuake2, + eBrushTypeQuake3, + eBrushTypeQuake3BP, + eBrushTypeDoom3, + eBrushTypeQuake4, + eBrushTypeHalfLife, }; @@ -84,120 +83,112 @@ enum EBrushType #define BRUSH_DEGENERATE_DEBUG 0 template -inline TextOuputStreamType& ostream_write(TextOuputStreamType& ostream, const Matrix4& m) +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] << ")"; + 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) +inline void print_vector3(const Vector3 &v) { - globalOutputStream() << "( " << v.x() << " " << v.y() << " " << v.z() << " )\n"; + globalOutputStream() << "( " << v.x() << " " << v.y() << " " << v.z() << " )\n"; } -inline void print_3x3(const Matrix4& m) +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"; + 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) +inline bool texdef_sane(const texdef_t &texdef) { - return fabs(texdef.shift[0]) < (1 << 16) - && fabs(texdef.shift[1]) < (1 << 16); + return fabs(texdef.shift[0]) < (1 << 16) + && fabs(texdef.shift[1]) < (1 << 16); } -inline void Winding_DrawWireframe(const Winding& winding) +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)); + 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) +inline void Winding_Draw(const Winding &winding, const Vector3 &normal, RenderStateFlags state) { - glVertexPointer(3, GL_FLOAT, sizeof(WindingVertex), &winding.points.data()->vertex); + 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_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 (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 } @@ -208,4021 +199,3865 @@ 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]; + 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]; + 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); + vector3_snap(planepts[0], snap); + vector3_snap(planepts[1], snap); + vector3_snap(planepts[2], snap); } -inline float vector3_max_component(const Vector3& vec3) +inline float vector3_max_component(const Vector3 &vec3) { - return std::max(fabsf(vec3[0]), std::max(fabsf(vec3[1]), fabsf(vec3[2]))); + return std::max(fabsf(vec3[0]), std::max(fabsf(vec3[1]), fabsf(vec3[2]))); } -inline void edge_snap(Vector3& edge, double 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); + 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); + 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) +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 - ); + return PointVertex( + Vertex3f( + static_cast( point.x()), + static_cast( point.y()), + static_cast( point.z()) + ), + colour + ); } -inline PointVertex pointvertex_for_windingpoint(const Vector3& point, const Colour4b& colour) +inline PointVertex pointvertex_for_windingpoint(const Vector3 &point, const Colour4b &colour) { - return PointVertex( - vertex3f_for_vector3(point), - colour - ); + return PointVertex( + vertex3f_for_vector3(point), + 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 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) +inline void brush_check_shader(const char *name) { - if(!shader_valid(name)) - { - globalErrorStream() << "brush face has invalid texture name: '" << name << "'\n"; - } + if (!shader_valid(name)) { + globalErrorStream() << "brush face has invalid texture name: '" << name << "'\n"; + } } -class FaceShaderObserver -{ -public: - virtual void realiseShader() = 0; - virtual void unrealiseShader() = 0; -}; - -class FaceShaderObserverRealise -{ +class FaceShaderObserver { public: - void operator()(FaceShaderObserver& observer) const - { - observer.realiseShader(); - } -}; + virtual void realiseShader() = 0; -class FaceShaderObserverUnrealise -{ -public: - void operator()(FaceShaderObserver& observer) const - { - observer.unrealiseShader(); - } + virtual void unrealiseShader() = 0; }; typedef ReferencePair FaceShaderObserverPair; -class ContentsFlagsValue -{ +class ContentsFlagsValue { public: - ContentsFlagsValue() - { - } - ContentsFlagsValue(int surfaceFlags, int contentFlags, int value, bool specified) : - m_surfaceFlags(surfaceFlags), - m_contentFlags(contentFlags), - m_value(value), - m_specified(specified) - { - } - int m_surfaceFlags; - int m_contentFlags; - int m_value; - bool m_specified; + ContentsFlagsValue() + { + } + + ContentsFlagsValue(int surfaceFlags, int contentFlags, int value, bool specified) : + m_surfaceFlags(surfaceFlags), + m_contentFlags(contentFlags), + m_value(value), + m_specified(specified) + { + } + + int m_surfaceFlags; + int m_contentFlags; + int m_value; + bool m_specified; }; -inline void ContentsFlagsValue_assignMasked(ContentsFlagsValue& flags, const ContentsFlagsValue& other) +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); - } + bool detail = bitfield_enabled(flags.m_contentFlags, BRUSH_DETAIL_MASK); + flags = other; + if (detail) { + flags.m_contentFlags = bitfield_enable(flags.m_contentFlags, BRUSH_DETAIL_MASK); + } else { + flags.m_contentFlags = bitfield_disable(flags.m_contentFlags, BRUSH_DETAIL_MASK); + } } -class FaceShader : public ModuleObserver -{ +class FaceShader : public ModuleObserver { public: - class SavedState - { - public: - CopiedString m_shader; - ContentsFlagsValue m_flags; + 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; - } -}; + 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(); + } -class FaceTexdef : public FaceShaderObserver -{ - // not copyable - FaceTexdef(const FaceTexdef& other); - // not assignable - FaceTexdef& operator=(const FaceTexdef& other); -public: - class SavedState - { - public: - TextureProjection m_projection; + ~FaceShader() + { + releaseShader(); + } - 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()); - } -}; +// copy-construction not supported + FaceShader(const FaceShader &other); -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] << " )"; -} + void instanceAttach() + { + m_instanced = true; + m_state->incrementUsed(); + } + void instanceDetach() + { + m_state->decrementUsed(); + m_instanced = false; + } -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 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); + } -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 releaseShader() + { + ASSERT_MESSAGE(m_state != 0, "shader cannot be released"); + m_state->detach(*this); + GlobalShaderCache().release(m_shader.c_str()); + m_state = 0; + } -class FacePlane -{ - PlanePoints m_planepts; - Plane3 m_planeCached; - Plane3 m_plane; -public: - Vector3 m_funcStaticOrigin; + void realise() + { + ASSERT_MESSAGE(!m_realised, "FaceTexdef::realise: already realised"); + m_realised = true; + m_observers.forEach([](FaceShaderObserver &observer) { + observer.realiseShader(); + }); + } - static EBrushType m_type; + void unrealise() + { + ASSERT_MESSAGE(m_realised, "FaceTexdef::unrealise: already unrealised"); + m_observers.forEach([](FaceShaderObserver &observer) { + observer.unrealiseShader(); + }); + m_realised = false; + } - static bool isDoom3Plane() - { - return FacePlane::m_type == eBrushTypeDoom3 || FacePlane::m_type == eBrushTypeQuake4; - } + void attach(FaceShaderObserver &observer) + { + m_observers.attach(observer); + if (m_realised) { + observer.realiseShader(); + } + } - class SavedState - { - public: - PlanePoints m_planepts; - Plane3 m_plane; + void detach(FaceShaderObserver &observer) + { + if (m_realised) { + observer.unrealiseShader(); + } + m_observers.detach(observer); + } - SavedState(const FacePlane& facePlane) + const char *getShader() const { - if(facePlane.isDoom3Plane()) - { - m_plane = facePlane.m_plane; - } - else - { - planepts_assign(m_planepts, facePlane.planePoints()); - } + return m_shader.c_str(); } - void exportState(FacePlane& facePlane) const + void setShader(const char *name) { - if(facePlane.isDoom3Plane()) - { - facePlane.m_plane = m_plane; - facePlane.updateTranslated(); - } - else - { - planepts_assign(facePlane.planePoints(), m_planepts); - facePlane.MakePlane(); - } + if (m_instanced) { + m_state->decrementUsed(); + } + releaseShader(); + m_shader = name; + captureShader(); + if (m_instanced) { + m_state->incrementUsed(); + } } - }; - FacePlane() : m_funcStaticOrigin(0, 0, 0) - { - } - FacePlane(const FacePlane& other) : m_funcStaticOrigin(0, 0, 0) - { - if(!isDoom3Plane()) + ContentsFlagsValue getFlags() const { - planepts_assign(m_planepts, other.m_planepts); - MakePlane(); + 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; } - else + + void setFlags(const ContentsFlagsValue &flags) { - m_plane = other.m_plane; - updateTranslated(); + ASSERT_MESSAGE(m_realised, "FaceShader::setFlags: flags not valid when unrealised"); + ContentsFlagsValue_assignMasked(m_flags, flags); } - } - void MakePlane() - { - if(!isDoom3Plane()) + Shader *state() const { -#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); + return m_state; } - } - void reverse() - { - if(!isDoom3Plane()) + std::size_t width() const { - vector3_swap(m_planepts[0], m_planepts[2]); - MakePlane(); + if (m_realised) { + return m_state->getTexture().width; + } + return 1; } - else + + std::size_t height() const { - m_planeCached = plane3_flipped(m_plane); - updateSource(); + if (m_realised) { + return m_state->getTexture().height; + } + return 1; } - } - void transform(const Matrix4& matrix, bool mirror) - { - if(!isDoom3Plane()) + + unsigned int shaderFlags() const { + if (m_realised) { + return m_state->getFlags(); + } + return 0; + } +}; -#if 0 - bool off = check_plane_is_integer(planePoints()); -#endif - matrix4_transform_point(matrix, m_planepts[0]); - matrix4_transform_point(matrix, m_planepts[1]); - matrix4_transform_point(matrix, m_planepts[2]); +class FaceTexdef : public FaceShaderObserver { +// not copyable + FaceTexdef(const FaceTexdef &other); - if(mirror) - { - reverse(); - } +// not assignable + FaceTexdef &operator=(const FaceTexdef &other); -#if 0 - if(check_plane_is_integer(planePoints())) - { - if(!off) +public: + class SavedState { + public: + TextureProjection m_projection; + + SavedState(const FaceTexdef &faceTexdef) { - globalErrorStream() << "caused by transform\n"; + m_projection = faceTexdef.m_projection; } - } -#endif - MakePlane(); + + 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()); } - else + + void removeScale() { - m_planeCached = Plane3_applyTransform(m_planeCached, matrix); - updateSource(); + ASSERT_MESSAGE(m_scaleApplied, "texture scale aready removed"); + m_scaleApplied = false; + m_projection.m_brushprimit_texdef.removeScale(m_shader.width(), m_shader.height()); } - } - void offset(float offset) - { - if(!isDoom3Plane()) + + void realiseShader() { - Vector3 move(vector3_scaled(m_planeCached.normal(), -offset)); + if (m_projectionInitialised && !m_scaleApplied) { + addScale(); + } + } - vector3_subtract(m_planepts[0], move); - vector3_subtract(m_planepts[1], move); - vector3_subtract(m_planepts[2], move); + void unrealiseShader() + { + if (m_projectionInitialised && m_scaleApplied) { + removeScale(); + } + } - MakePlane(); + void setTexdef(const TextureProjection &projection) + { + removeScale(); + Texdef_Assign(m_projection, projection); + addScale(); } - else + + void shift(float s, float t) { - m_planeCached.d += offset; - updateSource(); + ASSERT_MESSAGE(texdef_sane(m_projection.m_texdef), "FaceTexdef::shift: bad texdef"); + removeScale(); + Texdef_Shift(m_projection, s, t); + addScale(); } - } - void updateTranslated() - { - m_planeCached = Plane3_applyTranslation(m_plane, m_funcStaticOrigin); - } - void updateSource() - { - m_plane = Plane3_applyTranslation(m_planeCached, vector3_negated(m_funcStaticOrigin)); - } + 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(); + } - 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 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 copy(const FacePlane& other) - { - if(!isDoom3Plane()) + void emitTextureCoordinates(Winding &winding, const Vector3 &normal, const Matrix4 &localToWorld) { - planepts_assign(m_planepts, other.m_planepts); - MakePlane(); + Texdef_EmitTextureCoordinates(m_projection, m_shader.width(), m_shader.height(), winding, normal, localToWorld); } - else + + void transform(const Plane3 &plane, const Matrix4 &matrix) { - m_planeCached = other.m_plane; - updateSource(); + removeScale(); + Texdef_transformLocked(m_projection, m_shader.width(), m_shader.height(), plane, matrix); + addScale(); } - } - void copy(const Vector3& p0, const Vector3& p1, const Vector3& p2) - { - if(!isDoom3Plane()) + + TextureProjection normalised() const { - m_planepts[0] = p0; - m_planepts[1] = p1; - m_planepts[2] = p2; - MakePlane(); + 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); } - else + + void setBasis(const Vector3 &normal) { - m_planeCached = plane3_for_points(p2, p1, p0); - updateSource(); + 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 Winding_testSelect(Winding& winding, SelectionTest& test, SelectionIntersection& best) +inline void planepts_print(const PlanePoints &planePoints, TextOutputStream &ostream) { - test.TestPolygon(VertexPointer(reinterpret_cast(&winding.points.data()->vertex), sizeof(WindingVertex)), winding.numpoints, best); + 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] << " )"; } -const double GRID_MIN = 0.125; -inline double quantiseInteger(double f) +inline Plane3 Plane3_applyTranslation(const Plane3 &plane, const Vector3 &translation) { - return float_to_integer(f); + Plane3 tmp(plane3_translated(Plane3(plane.normal(), -plane.dist()), translation)); + return Plane3(tmp.normal(), -tmp.dist()); } -inline double quantiseFloating(double f) +inline Plane3 Plane3_applyTransform(const Plane3 &plane, const Matrix4 &matrix) { - return float_snapped(f, 1.f / (1 << 16)); + Plane3 tmp(plane3_transformed(Plane3(plane.normal(), -plane.dist()), matrix)); + return Plane3(tmp.normal(), -tmp.dist()); } -typedef double (*QuantiseFunc)(double f); - -class Face; - -class FaceFilter -{ +class FacePlane { + PlanePoints m_planepts; + Plane3 m_planeCached; + Plane3 m_plane; public: - virtual bool filter(const Face& face) const = 0; -}; + Vector3 m_funcStaticOrigin; -bool face_filtered(Face& face); -void add_face_filter(FaceFilter& filter, int mask, bool invert = false); + static EBrushType m_type; -void Brush_addTextureChangedCallback(const SignalHandler& callback); -void Brush_textureChanged(); - - -extern bool g_brush_texturelock_enabled; + static bool isDoom3Plane() + { + return FacePlane::m_type == eBrushTypeDoom3 || FacePlane::m_type == eBrushTypeQuake4; + } -class FaceObserver -{ -public: - virtual void planeChanged() = 0; - virtual void connectivityChanged() = 0; - virtual void shaderChanged() = 0; - virtual void evaluateTransform() = 0; -}; + class SavedState { + public: + PlanePoints m_planepts; + Plane3 m_plane; -class Face : -public OpenGLRenderable, -public Filterable, -public Undoable, -public FaceShaderObserver -{ - std::size_t m_refcount; + SavedState(const FacePlane &facePlane) + { + if (facePlane.isDoom3Plane()) { + m_plane = facePlane.m_plane; + } else { + planepts_assign(m_planepts, facePlane.planePoints()); + } + } - class SavedState : public UndoMemento - { - public: - FacePlane::SavedState m_planeState; - FaceTexdef::SavedState m_texdefState; - FaceShader::SavedState m_shaderState; + void exportState(FacePlane &facePlane) const + { + if (facePlane.isDoom3Plane()) { + facePlane.m_plane = m_plane; + facePlane.updateTranslated(); + } else { + planepts_assign(facePlane.planePoints(), m_planepts); + facePlane.MakePlane(); + } + } + }; - SavedState(const Face& face) : m_planeState(face.getPlane()), m_texdefState(face.getTexdef()), m_shaderState(face.getShader()) + FacePlane() : m_funcStaticOrigin(0, 0, 0) { } - void exportState(Face& face) const + FacePlane(const FacePlane &other) : m_funcStaticOrigin(0, 0, 0) { - m_planeState.exportState(face.getPlane()); - m_shaderState.exportState(face.getShader()); - m_texdefState.exportState(face.getTexdef()); + if (!isDoom3Plane()) { + planepts_assign(m_planepts, other.m_planepts); + MakePlane(); + } else { + m_plane = other.m_plane; + updateTranslated(); + } } - void release() + void MakePlane() { - delete this; + 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); + } } - }; - -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; + void reverse() + { + if (!isDoom3Plane()) { + vector3_swap(m_planepts[0], m_planepts[2]); + MakePlane(); + } else { + m_planeCached = plane3_flipped(m_plane); + updateSource(); + } + } - FaceObserver* m_observer; - UndoObserver* m_undoable_observer; - MapFile* m_map; + void transform(const Matrix4 &matrix, bool mirror) + { + if (!isDoom3Plane()) { - // assignment not supported - Face& operator=(const Face& other); - // copy-construction not supported - Face(const Face& other); +#if 0 + bool off = check_plane_is_integer( planePoints() ); +#endif -public: + matrix4_transform_point(matrix, m_planepts[0]); + matrix4_transform_point(matrix, m_planepts[1]); + matrix4_transform_point(matrix, m_planepts[2]); - 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 (mirror) { + reverse(); + } #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(); + if ( check_plane_is_integer( planePoints() ) ) { + if ( !off ) { + globalErrorStream() << "caused by transform\n"; + } + } #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; - } + MakePlane(); + } else { + m_planeCached = Plane3_applyTransform(m_planeCached, matrix); + updateSource(); + } } - return true; - } -}; + void offset(float offset) + { + if (!isDoom3Plane()) { + Vector3 move(vector3_scaled(m_planeCached.normal(), -offset)); -class FaceVertexId -{ - std::size_t m_face; - std::size_t m_vertex; + vector3_subtract(m_planepts[0], move); + vector3_subtract(m_planepts[1], move); + vector3_subtract(m_planepts[2], move); -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; - } -}; + MakePlane(); + } else { + m_planeCached.d += offset; + updateSource(); + } + } -typedef std::size_t faceIndex_t; + void updateTranslated() + { + m_planeCached = Plane3_applyTranslation(m_plane, m_funcStaticOrigin); + } -struct EdgeRenderIndices -{ - RenderIndex first; - RenderIndex second; - - EdgeRenderIndices() - : first(0), second(0) - { - } - EdgeRenderIndices(const RenderIndex _first, const RenderIndex _second) - : first(_first), second(_second) - { - } -}; + void updateSource() + { + m_plane = Plane3_applyTranslation(m_planeCached, vector3_negated(m_funcStaticOrigin)); + } -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) + PlanePoints &planePoints() { - glVertex3fv(&m_vertices[m_faceVertex[i].first].vertex.x); - glVertex3fv(&m_vertices[m_faceVertex[i].second].vertex.x); + return m_planepts; } - glEnd(); -#endif - } - Array m_faceVertex; - std::size_t m_size; - const PointVertex* m_vertices; -}; + const PlanePoints &planePoints() const + { + return m_planepts; + } -class Brush; -typedef std::vector brush_vector_t; + const Plane3 &plane3() const + { + return m_planeCached; + } -class BrushFilter -{ -public: - virtual bool filter(const Brush& brush) const = 0; -}; + void setDoom3Plane(const Plane3 &plane) + { + m_plane = plane; + updateTranslated(); + } -bool brush_filtered(Brush& brush); -void add_brush_filter(BrushFilter& filter, int mask, bool invert = false); + 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(); + } + } -/// \brief Returns true if 'self' takes priority when building brush b-rep. -inline bool plane3_inside(const Plane3& self, const Plane3& other) + 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) { - if(vector3_equal_epsilon(self.normal(), other.normal(), 0.001)) - { - return self.dist() < other.dist(); - } - return true; + test.TestPolygon(VertexPointer(reinterpret_cast( &winding.points.data()->vertex ), + sizeof(WindingVertex)), winding.numpoints, best); } -typedef SmartPointer FaceSmartPointer; -typedef std::vector Faces; +const double GRID_MIN = 0.125; -/// \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) +inline double quantiseInteger(double f) { - 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); + return float_to_integer(f); } -/// \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) +inline double quantiseFloating(double f) { - FaceVertexId nextEdge = next_edge(faces, faceVertex); - return FaceVertexId(nextEdge.getFace(), Winding_next(faces[nextEdge.getFace()]->getWinding(), nextEdge.getVertex())); + return float_snapped(f, 1.f / (1 << 16)); } -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); - } +typedef double ( *QuantiseFunc )(double f); + +class Face; +class FaceFilter { 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); - } + virtual bool filter(const Face &face) const = 0; }; -class SelectableVertex -{ - Vector3 getVertex() const - { - return getFace().getWinding()[m_faceVertex.getVertex()].vertex; - } +bool face_filtered(Face &face); -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 add_face_filter(FaceFilter &filter, int mask, bool invert = false); -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 Brush_addTextureChangedCallback(const SignalHandler &callback); - virtual void edge_clear() = 0; - virtual void edge_push_back(SelectableEdge& edge) = 0; +void Brush_textureChanged(); - virtual void vertex_clear() = 0; - virtual void vertex_push_back(SelectableVertex& vertex) = 0; - virtual void DEBUG_verify() const = 0; -}; +extern bool g_brush_texturelock_enabled; -class BrushVisitor -{ +class FaceObserver { public: - virtual void visit(Face& face) const = 0; + virtual void planeChanged() = 0; + + virtual void connectivityChanged() = 0; + + virtual void shaderChanged() = 0; + + virtual void evaluateTransform() = 0; }; -class Brush : - public TransformNode, - public Bounded, - public Cullable, - public Snappable, - public Undoable, - public FaceObserver, - public Filterable, - public Nameable, - public BrushDoom3 -{ +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: - 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(); - } - } + 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); - /// \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) +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) { - index += m_faces[i]->getWinding().numpoints; + 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(); } - return index + faceVertex.getVertex(); - } - void appendFaces(const Faces& other) - { - clear(); - for(Faces::const_iterator i = other.begin(); i != other.end(); ++i) + 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) { - push_back(*i); + m_shader.attach(*this); + m_plane.copy(p0, p1, p2); + m_texdef.setBasis(m_plane.plane3().normal()); + planeChanged(); + updateFiltered(); } - } - /// \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) + 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(); } - void release() + + ~Face() { - delete this; + m_shader.detach(*this); } - Faces m_faces; - }; - - void undoSave() - { - if(m_map != 0) + void planeChanged() { - m_map->changed(); + revertTransform(); + m_observer->planeChanged(); } - if(m_undoable_observer != 0) + + void realiseShader() { - m_undoable_observer->save(this); + m_observer->shaderChanged(); } - } - UndoMemento* exportState() const - { - return new BrushUndoMemento(m_faces); - } - - void importState(const UndoMemento* state) - { - undoSave(); - appendFaces(static_cast(state)->m_faces); - planeChanged(); + void unrealiseShader() + { + } - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + void instanceAttach(MapFile *map) { - (*i)->DEBUG_verify(); + m_shader.instanceAttach(); + m_map = map; + m_undoable_observer = GlobalUndoSystem().observer(this); + GlobalFilterSystem().registerFilterable(*this); } - } - bool isDetail() - { - return !m_faces.empty() && m_faces.front()->isDetail(); - } + void instanceDetach(MapFile *map) + { + GlobalFilterSystem().unregisterFilterable(*this); + m_undoable_observer = 0; + GlobalUndoSystem().release(this); + m_map = 0; + m_shader.instanceDetach(); + } - /// \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) + void render(RenderStateFlags state) const { - return 0; + Winding_Draw(m_winding, m_planeTransformed.plane3().normal(), state); } - 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) + void updateFiltered() { - return 0; + m_filtered = face_filtered(*this); } - 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; + bool isFiltered() const + { + return m_filtered; + } - g_bp_globals.m_texdefTypeId = TEXDEFTYPEID_QUAKE; - if(m_type == eBrushTypeQuake3BP || m_type == eBrushTypeDoom3 || m_type == eBrushTypeQuake4) + void undoSave() { - g_bp_globals.m_texdefTypeId = TEXDEFTYPEID_BRUSHPRIMITIVES; - g_brush_texturelock_enabled = true; + if (m_map != 0) { + m_map->changed(); + } + if (m_undoable_observer != 0) { + m_undoable_observer->save(this); + } } - else if(m_type == eBrushTypeHalfLife) + +// undoable + UndoMemento *exportState() const { - g_bp_globals.m_texdefTypeId = TEXDEFTYPEID_HALFLIFE; - g_brush_texturelock_enabled = true; + return new SavedState(*this); } - Face::m_quantise = (m_type == eBrushTypeQuake) ? quantiseInteger : quantiseFloating; + void importState(const UndoMemento *data) + { + undoSave(); - m_state_point = GlobalShaderCache().capture("$POINT"); - } - static void destroyStatic() - { - GlobalShaderCache().release("$POINT"); - } + static_cast( data )->exportState(*this); - std::size_t DEBUG_size() - { - return m_faces.size(); - } + planeChanged(); + m_observer->connectivityChanged(); + texdefChanged(); + m_observer->shaderChanged(); + updateFiltered(); + } - typedef Faces::const_iterator const_iterator; + void IncRef() + { + ++m_refcount; + } - const_iterator begin() const - { - return m_faces.begin(); - } - const_iterator end() const - { - return m_faces.end(); - } + void DecRef() + { + if (--m_refcount == 0) { + delete this; + } + } - 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) + void flipWinding() { - (*i)->reserve(count); + m_plane.reverse(); + planeChanged(); } - } - void push_back(Faces::value_type face) - { - m_faces.push_back(face); - if(m_instanceCounter.m_count != 0) + + bool intersectVolume(const VolumeTest &volume, const Matrix4 &localToWorld) const { - m_faces.back()->instanceAttach(m_map); + return volume.TestPlane(Plane3(plane3().normal(), -plane3().dist()), localToWorld); } - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + + void render(Renderer &renderer, const Matrix4 &localToWorld) const { - (*i)->push_back(*face); - (*i)->DEBUG_verify(); + renderer.SetState(m_shader.state(), Renderer::eFullMaterials); + renderer.addRenderable(*this, localToWorld); } - } - void pop_back() - { - if(m_instanceCounter.m_count != 0) + + void transform(const Matrix4 &matrix, bool mirror) { - m_faces.back()->instanceDetach(m_map); + 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(); + } } - m_faces.pop_back(); - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + + void assign_planepts(const PlanePoints planepts) { - (*i)->pop_back(); - (*i)->DEBUG_verify(); + m_planeTransformed.copy(planepts[0], planepts[1], planepts[2]); + m_observer->planeChanged(); } - } - void erase(std::size_t index) - { - if(m_instanceCounter.m_count != 0) + +/// \brief Reverts the transformable state of the brush to identity. + void revertTransform() { - m_faces[index]->instanceDetach(m_map); + m_planeTransformed = m_plane; + planepts_assign(m_move_planeptsTransformed, m_move_planepts); + m_texdefTransformed = m_texdef.m_projection; } - m_faces.erase(m_faces.begin() + index); - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + + void freezeTransform() { - (*i)->erase(index); - (*i)->DEBUG_verify(); + undoSave(); + m_plane = m_planeTransformed; + planepts_assign(m_move_planepts, m_move_planeptsTransformed); + m_texdef.m_projection = m_texdefTransformed; } - } - void connectivityChanged() - { - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + + void update_move_planepts_vertex(std::size_t index, PlanePoints planePoints) { - (*i)->connectivityChanged(); + 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 clear() - { - undoSave(); - if(m_instanceCounter.m_count != 0) + void testSelect(SelectionTest &test, SelectionIntersection &best) { - forEachFace_instanceDetach(m_map); + Winding_testSelect(m_winding, test, best); } - m_faces.clear(); - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + + void testSelect_centroid(SelectionTest &test, SelectionIntersection &best) { - (*i)->clear(); - (*i)->DEBUG_verify(); + test.TestPoint(m_centroid, best); } - } - 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) + void shaderChanged() { - if((*i)->contributes()) - { - return true; - } + EmitTextureCoordinates(); + Brush_textureChanged(); + m_observer->shaderChanged(); + updateFiltered(); + planeChanged(); + SceneChangeNotify(); } - 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(); + const char *GetShader() const + { + return m_shader.getShader(); + } + void SetShader(const char *name) { - std::size_t i = 0; - while(i < m_faces.size()) - { - if(!m_faces[i]->contributes()) - { - erase(i); - planeChanged(); - } - else - { - ++i; - } - } + undoSave(); + m_shader.setShader(name); + shaderChanged(); } - } - /// \brief Constructs \p winding from the intersection of \p plane with the other planes of the brush. - void windingForClipPlane(Winding& winding, const Plane3& plane) const - { - FixedWinding buffer[2]; - bool swap = false; + void revertTexdef() + { + m_texdefTransformed = m_texdef.m_projection; + } - // get a poly that covers an effectively infinite area - Winding_createInfinite(buffer[swap], plane, m_maxWorldCoord + 1); + void texdefChanged() + { + revertTexdef(); + EmitTextureCoordinates(); + Brush_textureChanged(); + } - // chop the poly by all of the other faces + void GetTexdef(TextureProjection &projection) const { - for (std::size_t i = 0; i < m_faces.size(); ++i) - { - const Face& clip = *m_faces[i]; + projection = m_texdef.normalised(); + } - if(plane3_equal(clip.plane3(), plane) - || !plane3_valid(clip.plane3()) || !plane_unique(i) - || plane3_opposing(plane, clip.plane3())) - { - continue; - } + void SetTexdef(const TextureProjection &projection) + { + undoSave(); + m_texdef.setTexdef(projection); + texdefChanged(); + } - buffer[!swap].clear(); + void GetFlags(ContentsFlagsValue &flags) const + { + flags = m_shader.getFlags(); + } -#if BRUSH_CONNECTIVITY_DEBUG - globalOutputStream() << "clip vs face: " << i << "\n"; -#endif + void SetFlags(const ContentsFlagsValue &flags) + { + undoSave(); + m_shader.setFlags(flags); + m_observer->shaderChanged(); + updateFiltered(); + } - { - // 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 ShiftTexdef(float s, float t) + { + undoSave(); + m_texdef.shift(s, t); + texdefChanged(); + } -#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 + void ScaleTexdef(float s, float t) + { + undoSave(); + m_texdef.scale(s, t); + texdefChanged(); + } - //ASSERT_MESSAGE(buffer[!swap].numpoints != 1, "created single-point winding"); + void RotateTexdef(float angle) + { + undoSave(); + m_texdef.rotate(angle); + texdefChanged(); + } - swap = !swap; - } + void FitTexture(float s_repeat, float t_repeat) + { + undoSave(); + m_texdef.fit(m_plane.plane3().normal(), m_winding, s_repeat, t_repeat); + texdefChanged(); } - Winding_forFixedWinding(winding, buffer[swap]); + void EmitTextureCoordinates() + { + Texdef_EmitTextureCoordinates(m_texdefTransformed, m_shader.width(), m_shader.height(), m_winding, + plane3().normal(), g_matrix4_identity); + } -#if BRUSH_CONNECTIVITY_DEBUG - Winding_printConnectivity(winding); - for(Winding::iterator i = winding.begin(), j = winding.end() - 1; i != winding.end(); j = i, ++i) + const Vector3 ¢roid() const { - 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"; - } + return m_centroid; } -#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) + void construct_centroid() { - 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]; - } + Winding_Centroid(m_winding, plane3(), m_centroid); } - } - - 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) + const Winding &getWinding() const { - if(faces_visible[i]) - { - ++count; - } + return m_winding; } - wire.resize(count); - Array::iterator p = wire.begin(); - for(std::size_t i = 0; i < m_faceCentroidPoints.size(); ++i) + Winding &getWinding() { - if(faces_visible[i]) - { - *p++ = m_faceCentroidPoints[i]; - } + return m_winding; } - } - /// \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) + const Plane3 &plane3() const { - addFace(*(*i)); + m_observer->evaluateTransform(); + return m_planeTransformed.plane3(); } - 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) + FacePlane &getPlane() { - (*i)->edge_push_back(m_select_edges.back()); + return m_plane; } - } - void edge_clear() - { - m_select_edges.clear(); - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + + const FacePlane &getPlane() const { - (*i)->edge_clear(); + return m_plane; } - } - 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) + + FaceTexdef &getTexdef() { - (*i)->vertex_push_back(m_select_vertices.back()); + return m_texdef; } - } - void vertex_clear() - { - m_select_vertices.clear(); - for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + + const FaceTexdef &getTexdef() const { - (*i)->vertex_clear(); + return m_texdef; } - } - /// \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) + FaceShader &getShader() { - if(index != i && !plane3_inside(m_faces[index]->plane3(), m_faces[i]->plane3())) - { - return false; - } + return m_shader; } - 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; - } - } + + const FaceShader &getShader() const + { + return m_shader; } - } - /// \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) + bool isDetail() const { - 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; - } - } + return (m_shader.m_flags.m_contentFlags & BRUSH_DETAIL_MASK) != 0; + } - { - Winding& winding = m_faces[degen[1].adjacent]->getWinding(); - std::size_t index = Winding_FindAdjacent(winding, i); - if(index != c_brush_maxFaces) - { -#if BRUSH_DEGENERATE_DEBUG - globalOutputStream() << "Brush::buildWindings: face " << degen[1].adjacent << ": remapping adjacent " << winding[index].adjacent << " to " << degen[0].adjacent << "\n"; -#endif - winding[index].adjacent = degen[0].adjacent; - } + void 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(); + } - degen.resize(0); - } + bool contributes() const + { + return m_winding.numpoints > 2; } - } - /// \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) + bool is_bounded() const { - //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; - } + for (Winding::const_iterator i = m_winding.begin(); i != m_winding.end(); ++i) { + if ((*i).adjacent == c_brush_maxFaces) { + return false; + } } - } + return true; } - } +}; + - /// \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) +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) { - 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() - { + std::size_t getFace() const + { + return m_face; + } + std::size_t getVertex() const { - m_aabb_local = AABB(); + return m_vertex; + } +}; - for (std::size_t i = 0; i < m_faces.size(); ++i) - { - Face& f = *m_faces[i]; +typedef std::size_t faceIndex_t; - 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()); +struct EdgeRenderIndices { + RenderIndex first; + RenderIndex second; - // 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); - } + EdgeRenderIndices() + : first(0), second(0) + { + } - // update texture coordinates - f.EmitTextureCoordinates(); - } - } + EdgeRenderIndices(const RenderIndex _first, const RenderIndex _second) + : first(_first), second(_second) + { } +}; - bool degenerate = !isBounded(); +struct EdgeFaces { + faceIndex_t first; + faceIndex_t second; - if(!degenerate) + EdgeFaces() + : first(c_brush_maxFaces), second(c_brush_maxFaces) { - // 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(); + 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 FaceInstance; +class Brush; -class FaceInstanceSet -{ - typedef SelectionList FaceInstances; - FaceInstances m_faceInstances; +typedef std::vector brush_vector_t; + +class BrushFilter { 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(); - } + virtual bool filter(const Brush &brush) const = 0; }; -extern FaceInstanceSet g_SelectedFaceInstances; +bool brush_filtered(Brush &brush); -typedef std::list VertexSelection; +void add_brush_filter(BrushFilter &filter, int mask, bool invert = false); -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) +/// \brief Returns true if 'self' takes priority when building brush b-rep. +inline bool plane3_inside(const Plane3 &self, const Plane3 &other, bool selfIsLater) { - return std::find(self.begin(), self.end(), value); + if (vector3_equal_epsilon(self.normal(), other.normal(), 0.001)) { + // same plane? prefer the one with smaller index + if (self.dist() == other.dist()) { + return selfIsLater; + } + return self.dist() < other.dist(); + } + return true; } -inline 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); - } -} +typedef SmartPointer FaceSmartPointer; +typedef std::vector Faces; -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) +/// \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) { - return vector3_cross(y - x, z - x); + 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); } -template -inline bool triangles_same_winding(const BasicVector3& x1, const BasicVector3 y1, const BasicVector3& z1, const BasicVector3& x2, const BasicVector3 y2, const BasicVector3& z2) + +/// \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) { - return vector3_dot(triangle_cross(x1, y1, z1), triangle_cross(x2, y2, z2)) > 0; + 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); + } -typedef const Plane3* PlanePointer; -typedef PlanePointer* PlanesIterator; +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; + } -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)); - } - } + 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 FaceInstance -{ - Face* m_face; - ObservedSelectable m_selectable; - ObservedSelectable m_selectableVertices; - ObservedSelectable m_selectableEdges; - SelectionChangeCallback m_selectionChanged; +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 °en = m_faces[i]->getWinding(); + + if (degen.numpoints == 2) { +#if BRUSH_DEGENERATE_DEBUG + globalOutputStream() << "Brush::buildWindings: face " << i << ": degenerate winding adjacent to " << degen[0].adjacent << ", " << degen[1].adjacent << "\n"; +#endif + // this is an "edge" face, where the plane touches the edge of the brush + { + Winding &winding = m_faces[degen[0].adjacent]->getWinding(); + std::size_t index = Winding_FindAdjacent(winding, i); + if (index != c_brush_maxFaces) { +#if BRUSH_DEGENERATE_DEBUG + globalOutputStream() << "Brush::buildWindings: face " << degen[0].adjacent << ": remapping adjacent " << winding[index].adjacent << " to " << degen[1].adjacent << "\n"; +#endif + winding[index].adjacent = degen[1].adjacent; + } + } + + { + Winding &winding = m_faces[degen[1].adjacent]->getWinding(); + std::size_t index = Winding_FindAdjacent(winding, i); + if (index != c_brush_maxFaces) { +#if BRUSH_DEGENERATE_DEBUG + globalOutputStream() << "Brush::buildWindings: face " << degen[1].adjacent << ": remapping adjacent " << winding[index].adjacent << " to " << degen[0].adjacent << "\n"; +#endif + winding[index].adjacent = degen[0].adjacent; + } + } + + 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 ¢roid() const + { + return m_face->centroid(); + } - VertexSelection m_vertexSelection; - VertexSelection m_edgeSelection; + 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: - 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" - ); + static void constructStatic() + { + m_state = GlobalShaderCache().capture("$CLIPPER_OVERLAY"); + } - 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) + static void destroyStatic() { - m_selectableVertices.setSelected(false); + GlobalShaderCache().release("$CLIPPER_OVERLAY"); } - else + + void setPlane(const Brush &brush, const Plane3 &plane) { - m_selectableVertices.setSelected(true); + m_plane = plane; + if (plane3_valid(m_plane)) { + brush.windingForClipPlane(m_winding, m_plane); + } else { + m_winding.resize(0); + } + } - if(m_vertexSelection.size() == 1) - { - std::size_t index = Winding_FindAdjacent(getFace().getWinding(), *m_vertexSelection.begin()); + void render(RenderStateFlags state) const + { + if ((state & RENDER_FILL) != 0) { + Winding_Draw(m_winding, m_plane.normal(), state); + } else { + Winding_DrawWireframe(m_winding); - 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())); + // 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)); - if(index != c_brush_maxFaces - && other != c_brush_maxFaces) - { - update_move_planepts_vertex2(index, other); + glVertexPointer(3, GL_FLOAT, sizeof(Vector3), &lineverts[0]); + glDrawArrays(GL_LINES, 0, GLsizei(2)); } - } } - } - void select_vertex(std::size_t index, bool select) - { - if(select) + + void render(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld) const { - VertexSelection_insert(m_vertexSelection, getFace().getWinding()[index].adjacent); + renderer.SetState(m_state, Renderer::eWireframeOnly); + renderer.SetState(m_state, Renderer::eFullMaterials); + renderer.addRenderable(*this, localToWorld); } - else - { - VertexSelection_erase(m_vertexSelection, getFace().getWinding()[index].adjacent); +}; + +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); } +} - SceneChangeNotify(); - update_selection_vertex(); - } - bool selected_vertex(std::size_t index) const - { - return VertexSelection_find(m_vertexSelection, getFace().getWinding()[index].adjacent) != m_vertexSelection.end(); - } +typedef std::vector FaceInstances; - 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"); +class EdgeInstance : public Selectable { + FaceInstances &m_faceInstances; + SelectableEdge *m_edge; - 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) + void select_edge(bool select) { - m_selectableEdges.setSelected(false); + 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); } - 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); + 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 select_edge(std::size_t index, bool select) - { - if(select) + +public: + EdgeInstance(FaceInstances &faceInstances, SelectableEdge &edge) + : m_faceInstances(faceInstances), m_edge(&edge) { - VertexSelection_insert(m_edgeSelection, getFace().getWinding()[index].adjacent); } - else + + EdgeInstance &operator=(const EdgeInstance &other) { - VertexSelection_erase(m_edgeSelection, getFace().getWinding()[index].adjacent); + m_edge = other.m_edge; + return *this; } - SceneChangeNotify(); - update_selection_edge(); - } + void setSelected(bool select) + { + select_edge(select); + } - bool selected_edge(std::size_t index) const - { - return VertexSelection_find(m_edgeSelection, getFace().getWinding()[index].adjacent) != m_edgeSelection.end(); - } + bool isSelected() const + { + return selected_edge(); + } - 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); - } + void testSelect(Selector &selector, SelectionTest &test) + { + SelectionIntersection best; + m_edge->testSelect(test, best); + if (best.valid()) { + Selector_add(selector, *this, best); + } + } }; -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"); - } +class VertexInstance : public Selectable { + FaceInstances &m_faceInstances; + SelectableVertex *m_vertex; - void setPlane(const Brush& brush, const Plane3& plane) - { - m_plane = plane; - if(plane3_valid(m_plane)) + void select_vertex(bool select) { - brush.windingForClipPlane(m_winding, m_plane); + 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()); } - else + + 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) { - m_winding.resize(0); } - } - void render(RenderStateFlags state) const - { - if((state & RENDER_FILL) != 0) + VertexInstance &operator=(const VertexInstance &other) { - Winding_Draw(m_winding, m_plane.normal(), state); + m_vertex = other.m_vertex; + return *this; } - else + + void setSelected(bool select) { - Winding_DrawWireframe(m_winding); + select_vertex(select); + } - // 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)); + bool isSelected() const + { + return selected_vertex(); + } - glVertexPointer(3, GL_FLOAT, sizeof(Vector3), &lineverts[0]); - glDrawArrays(GL_LINES, 0, GLsizei(2)); + void testSelect(Selector &selector, SelectionTest &test) + { + SelectionIntersection best; + m_vertex->testSelect(test, best); + if (best.valid()) { + Selector_add(selector, *this, best); + } } - } +}; - 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); - } +class BrushInstanceVisitor { +public: + virtual void visit(FaceInstance &face) const = 0; }; -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); - } -} +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; -typedef std::vector FaceInstances; + FaceInstances m_faceInstances; -class EdgeInstance : public Selectable -{ - FaceInstances& m_faceInstances; - SelectableEdge* m_edge; + typedef std::vector EdgeInstances; + EdgeInstances m_edgeInstances; + typedef std::vector VertexInstances; + VertexInstances m_vertexInstances; - 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())) + 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() { - return false; + m_lightList->lightsChanged(); } - faceVertex = next_edge(m_edge->m_faces, faceVertex); - if(!m_faceInstances[faceVertex.getFace()].selected_edge(faceVertex.getVertex())) + + 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)) { - return false; + 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)); } - return true; - } + ~BrushInstance() + { + Instance::setTransformChangedCallback(Callback()); -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); - } - } -}; + m_brush.m_lightsChanged = Callback(); + GlobalShaderCache().detach(*this); -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); + m_counter->decrement(); + m_brush.detach(*this); + m_brush.instanceDetach(Instance::path()); } - while(faceVertex.getFace() != m_vertex->m_faceVertex.getFace()); - return true; - } -public: - VertexInstance(FaceInstances& faceInstances, SelectableVertex& vertex) - : m_faceInstances(faceInstances), m_vertex(&vertex) - { - } - VertexInstance& operator=(const VertexInstance& other) - { - m_vertex = other.m_vertex; - return *this; - } - - void setSelected(bool select) - { - select_vertex(select); - } - bool isSelected() const - { - return selected_vertex(); - } - - void testSelect(Selector& selector, SelectionTest& test) - { - SelectionIntersection best; - m_vertex->testSelect(test, best); - if(best.valid()) - { - Selector_add(selector, *this, best); - } - } -}; + Brush &getBrush() + { + return m_brush; + } -class BrushInstanceVisitor -{ -public: - virtual void visit(FaceInstance& face) const = 0; -}; + const Brush &getBrush() const + { + return m_brush; + } -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() + Bounded &get(NullType) { - 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); + return m_brush; } - InstanceTypeCastTable& get() + + Cullable &get(NullType) + { + return m_brush; + } + + Transformable &get(NullType) { - return m_casts; + return m_transform; } - }; + void selectedChanged(const Selectable &selectable) + { + GlobalSelectionSystem().getObserver(SelectionSystem::ePrimitive)(selectable); + GlobalSelectionSystem().onSelectedChanged(*this, selectable); - Brush& m_brush; + Instance::selectedChanged(); + } - FaceInstances m_faceInstances; + typedef MemberCaller SelectedChangedCaller; - typedef std::vector EdgeInstances; - EdgeInstances m_edgeInstances; - typedef std::vector VertexInstances; - VertexInstances m_vertexInstances; + void selectedChangedComponent(const Selectable &selectable) + { + GlobalSelectionSystem().getObserver(SelectionSystem::eComponent)(selectable); + GlobalSelectionSystem().onComponentSelection(*this, selectable); + } - ObservedSelectable m_selectable; + typedef MemberCaller SelectedChangedComponentCaller; - 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 + const BrushInstanceVisitor &forEachFaceInstance(const BrushInstanceVisitor &visitor) + { + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + visitor.visit(*i); + } + return visitor; + } - BrushClipPlane m_clipPlane; + static void constructStatic() + { + m_state_selpoint = GlobalShaderCache().capture("$SELPOINT"); + } - static Shader* m_state_selpoint; + static void destroyStatic() + { + GlobalShaderCache().release("$SELPOINT"); + } - const LightList* m_lightList; + void clear() + { + m_faceInstances.clear(); + } - TransformModifier m_transform; + void reserve(std::size_t size) + { + m_faceInstances.reserve(size); + } - 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); - } - } + void push_back(Face &face) + { + m_faceInstances.push_back(FaceInstance(face, SelectedChangedComponentCaller(*this))); + } - m_brush.update_wireframe(m_render_wireframe, faces_visible); - m_brush.update_faces_wireframe(m_faceCentroidPointsCulled, faces_visible); + void pop_back() + { + ASSERT_MESSAGE(!m_faceInstances.empty(), "erasing invalid element"); + m_faceInstances.pop_back(); } - } - void renderComponentsSelected(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const - { - m_brush.evaluateBRep(); + void erase(std::size_t index) + { + ASSERT_MESSAGE(index < m_faceInstances.size(), "erasing invalid element"); + m_faceInstances.erase(m_faceInstances.begin() + index); + } - update_selected(); - if(!m_render_selected.empty()) + void connectivityChanged() { - 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); + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).connectivityChanged(); + } } - } - void renderComponents(Renderer& renderer, const VolumeTest& volume) const - { - m_brush.evaluateBRep(); + void edge_clear() + { + m_edgeInstances.clear(); + } - const Matrix4& localToWorld = Instance::localToWorld(); + void edge_push_back(SelectableEdge &edge) + { + m_edgeInstances.push_back(EdgeInstance(m_faceInstances, edge)); + } - renderer.SetState(m_brush.m_state_point, Renderer::eWireframeOnly); - renderer.SetState(m_brush.m_state_point, Renderer::eFullMaterials); + void vertex_clear() + { + m_vertexInstances.clear(); + } - if(volume.fill() && GlobalSelectionSystem().ComponentMode() == SelectionSystem::eFace) + void vertex_push_back(SelectableVertex &vertex) { - evaluateViewDependent(volume, localToWorld); - renderer.addRenderable(m_render_faces_wireframe, localToWorld); + m_vertexInstances.push_back(VertexInstance(m_faceInstances, vertex)); } - else + + void DEBUG_verify() const { - m_brush.renderComponents(GlobalSelectionSystem().ComponentMode(), renderer, volume, localToWorld); + ASSERT_MESSAGE(m_faceInstances.size() == m_brush.DEBUG_size(), "FATAL: mismatch"); } - } - void renderClipPlane(Renderer& renderer, const VolumeTest& volume) const - { - if(GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eClip && isSelected()) + bool isSelected() const { - m_clipPlane.render(renderer, volume, localToWorld()); + return m_selectable.isSelected(); } - } - void renderCommon(Renderer& renderer, const VolumeTest& volume) const - { - bool componentMode = GlobalSelectionSystem().Mode() == SelectionSystem::eComponent; - - if(componentMode && isSelected()) + void setSelected(bool select) { - renderComponents(renderer, volume); + m_selectable.setSelected(select); } - - if(parentSelected()) + + void update_selected() const { - if(!componentMode) - { - renderer.Highlight(Renderer::eFace); - } - renderer.Highlight(Renderer::ePrimitive); + 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 renderSolid(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const - { - //renderCommon(renderer, volume); + 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_lightList->evaluateLights(); + m_brush.update_wireframe(m_render_wireframe, faces_visible); + m_brush.update_faces_wireframe(m_faceCentroidPointsCulled, faces_visible); + } + } - for(FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) + void renderComponentsSelected(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld) const { - renderer.setLights((*i).m_lights); - (*i).render(renderer, volume, localToWorld); + 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); + } } - renderComponentsSelected(renderer, volume, localToWorld); - } + void renderComponents(Renderer &renderer, const VolumeTest &volume) const + { + m_brush.evaluateBRep(); + + const Matrix4 &localToWorld = Instance::localToWorld(); - void renderWireframe(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const - { - //renderCommon(renderer, volume); + renderer.SetState(m_brush.m_state_point, Renderer::eWireframeOnly); + renderer.SetState(m_brush.m_state_point, Renderer::eFullMaterials); - evaluateViewDependent(volume, localToWorld); + 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); + } + } - if(m_render_wireframe.m_size != 0) + void renderClipPlane(Renderer &renderer, const VolumeTest &volume) const { - renderer.addRenderable(m_render_wireframe, localToWorld); + if (GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eClip && isSelected()) { + m_clipPlane.render(renderer, volume, localToWorld()); + } } - renderComponentsSelected(renderer, volume, localToWorld); - } + void renderCommon(Renderer &renderer, const VolumeTest &volume) const + { + bool componentMode = GlobalSelectionSystem().Mode() == SelectionSystem::eComponent; + + if (componentMode && isSelected()) { + renderComponents(renderer, volume); + } - void renderSolid(Renderer& renderer, const VolumeTest& volume) const - { - m_brush.evaluateBRep(); + if (parentSelected()) { + if (!componentMode) { + renderer.Highlight(Renderer::eFace); + } + renderer.Highlight(Renderer::ePrimitive); + } + } - renderClipPlane(renderer, volume); + void renderSolid(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld) const + { + //renderCommon(renderer, volume); - renderSolid(renderer, volume, localToWorld()); - } + m_lightList->evaluateLights(); - void renderWireframe(Renderer& renderer, const VolumeTest& volume) const - { - m_brush.evaluateBRep(); + for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + renderer.setLights((*i).m_lights); + (*i).render(renderer, volume, localToWorld); + } - renderClipPlane(renderer, volume); + renderComponentsSelected(renderer, volume, localToWorld); + } - renderWireframe(renderer, volume, localToWorld()); - } + void renderWireframe(Renderer &renderer, const VolumeTest &volume, const Matrix4 &localToWorld) const + { + //renderCommon(renderer, volume); - void viewChanged() const - { - m_viewChanged = true; - } + evaluateViewDependent(volume, localToWorld); - void testSelect(Selector& selector, SelectionTest& test) - { - test.BeginMesh(localToWorld()); + if (m_render_wireframe.m_size != 0) { + renderer.addRenderable(m_render_wireframe, localToWorld); + } - SelectionIntersection best; - for(FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) - { - (*i).testSelect(test, best); + renderComponentsSelected(renderer, volume, localToWorld); } - if(best.valid()) + + void renderSolid(Renderer &renderer, const VolumeTest &volume) const { - selector.addIntersection(best); + m_brush.evaluateBRep(); + + renderClipPlane(renderer, volume); + + renderSolid(renderer, volume, localToWorld()); } - } - bool isSelectedComponents() const - { - for(FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) + void renderWireframe(Renderer &renderer, const VolumeTest &volume) const { - if((*i).selectedComponents()) - { - return true; - } + m_brush.evaluateBRep(); + + renderClipPlane(renderer, volume); + + renderWireframe(renderer, volume, localToWorld()); } - return false; - } - void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode) - { - for(FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) + + void viewChanged() const { - (*i).setSelected(mode, select); + m_viewChanged = true; } - } - void testSelectComponents(Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode) - { - test.BeginMesh(localToWorld()); - switch(mode) + void testSelect(Selector &selector, SelectionTest &test) { - 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); - } + test.BeginMesh(localToWorld()); + + SelectionIntersection best; + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).testSelect(test, best); } - else - { - for(FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) - { - (*i).testSelect_centroid(selector, test); - } + if (best.valid()) { + selector.addIntersection(best); } - } - break; - default: - break; } - } - - void selectPlanes(Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback) - { - test.BeginMesh(localToWorld()); - PlanePointer brushPlanes[c_brush_maxFaces]; - PlanesIterator j = brushPlanes; + bool isSelectedComponents() const + { + for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + if ((*i).selectedComponents()) { + return true; + } + } + return false; + } - for(Brush::const_iterator i = m_brush.begin(); i != m_brush.end(); ++i) + void setSelectedComponents(bool select, SelectionSystem::EComponentMode mode) { - *j++ = &(*i)->plane3(); + 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; + } } - for(FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) + void selectPlanes(Selector &selector, SelectionTest &test, const PlaneCallback &selectedPlaneCallback) { - (*i).selectPlane(selector, Line(test.getNear(), test.getFar()), brushPlanes, j, 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) + + void selectReversedPlanes(Selector &selector, const SelectedPlanes &selectedPlanes) { - (*i).selectReversedPlane(selector, 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) + void transformComponents(const Matrix4 &matrix) { - (*i).transformComponents(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) + const AABB &getSelectedComponentsBounds() const { - (*i).iterate_selected(m_aabb_component); + 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; } - return m_aabb_component; - } + void snapComponents(float snap) + { + for (FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).snapComponents(snap); + } + } - void snapComponents(float snap) - { - for(FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) + void evaluateTransform() { - (*i).snapComponents(snap); + Matrix4 matrix(m_transform.calculateTransform()); + //globalOutputStream() << "matrix: " << matrix << "\n"; + + if (m_transform.getType() == TRANSFORM_PRIMITIVE) { + m_brush.transform(matrix); + } else { + transformComponents(matrix); + } } - } - void evaluateTransform() - { - Matrix4 matrix(m_transform.calculateTransform()); - //globalOutputStream() << "matrix: " << matrix << "\n"; - if(m_transform.getType() == TRANSFORM_PRIMITIVE) + void applyTransform() { - m_brush.transform(matrix); + m_brush.revertTransform(); + evaluateTransform(); + m_brush.freezeTransform(); } - else + + typedef MemberCaller ApplyTransformCaller; + + void setClipPlane(const Plane3 &plane) { - transformComponents(matrix); + m_clipPlane.setPlane(m_brush, plane); } - } - 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()); + } - 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) + void insertLight(const RendererLight &light) { - Face_addLight(*i, localToWorld, 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) + + void clearLights() { - (*i).m_lights.clear(); + for (FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i) { + (*i).m_lights.clear(); + } } - } }; -inline BrushInstance* Instance_getBrush(scene::Instance& instance) +inline BrushInstance *Instance_getBrush(scene::Instance &instance) { - return InstanceTypeCast::cast(instance); + return InstanceTypeCast::cast(instance); } template -class BrushSelectedVisitor : public SelectionSystem::Visitor -{ - const Functor& m_functor; +class BrushSelectedVisitor : public SelectionSystem::Visitor { + const Functor &m_functor; public: - BrushSelectedVisitor(const Functor& functor) : m_functor(functor) - { - } - void visit(scene::Instance& instance) const - { - BrushInstance* brush = Instance_getBrush(instance); - if(brush != 0) + BrushSelectedVisitor(const Functor &functor) : m_functor(functor) + { + } + + void visit(scene::Instance &instance) const { - m_functor(*brush); + BrushInstance *brush = Instance_getBrush(instance); + if (brush != 0) { + m_functor(*brush); + } } - } }; template -inline const Functor& Scene_forEachSelectedBrush(const Functor& functor) +inline const Functor &Scene_forEachSelectedBrush(const Functor &functor) { - GlobalSelectionSystem().foreachSelected(BrushSelectedVisitor(functor)); - return functor; + GlobalSelectionSystem().foreachSelected(BrushSelectedVisitor(functor)); + return functor; } template -class BrushVisibleSelectedVisitor : public SelectionSystem::Visitor -{ - const Functor& m_functor; +class BrushVisibleSelectedVisitor : public SelectionSystem::Visitor { + const Functor &m_functor; public: - BrushVisibleSelectedVisitor(const Functor& functor) : m_functor(functor) - { - } - void visit(scene::Instance& instance) const - { - BrushInstance* brush = Instance_getBrush(instance); - if(brush != 0 - && instance.path().top().get().visible()) - { - m_functor(*brush); - } - } + BrushVisibleSelectedVisitor(const Functor &functor) : m_functor(functor) + { + } + + void visit(scene::Instance &instance) const + { + BrushInstance *brush = Instance_getBrush(instance); + if (brush != 0 + && instance.path().top().get().visible()) { + m_functor(*brush); + } + } }; template -inline const Functor& Scene_forEachVisibleSelectedBrush(const Functor& functor) +inline const Functor &Scene_forEachVisibleSelectedBrush(const Functor &functor) { - GlobalSelectionSystem().foreachSelected(BrushVisibleSelectedVisitor(functor)); - return functor; + GlobalSelectionSystem().foreachSelected(BrushVisibleSelectedVisitor(functor)); + return functor; } -class BrushForEachFace -{ - const BrushInstanceVisitor& m_visitor; +class BrushForEachFace { + const BrushInstanceVisitor &m_visitor; public: - BrushForEachFace(const BrushInstanceVisitor& visitor) : m_visitor(visitor) - { - } - void operator()(BrushInstance& brush) const - { - brush.forEachFaceInstance(m_visitor); - } + BrushForEachFace(const BrushInstanceVisitor &visitor) : m_visitor(visitor) + { + } + + void operator()(BrushInstance &brush) const + { + brush.forEachFaceInstance(m_visitor); + } }; template -class FaceInstanceVisitFace : public BrushInstanceVisitor -{ - const Functor& functor; +class FaceInstanceVisitFace : public BrushInstanceVisitor { + const Functor &functor; public: - FaceInstanceVisitFace(const Functor& functor) - : functor(functor) - { - } - void visit(FaceInstance& face) const - { - functor(face.getFace()); - } + FaceInstanceVisitFace(const Functor &functor) + : functor(functor) + { + } + + void visit(FaceInstance &face) const + { + functor(face.getFace()); + } }; template -inline const Functor& Brush_forEachFace(BrushInstance& brush, const Functor& functor) +inline const Functor &Brush_forEachFace(BrushInstance &brush, const Functor &functor) { - brush.forEachFaceInstance(FaceInstanceVisitFace(functor)); - return functor; + brush.forEachFaceInstance(FaceInstanceVisitFace(functor)); + return functor; } template -class FaceVisitAll : public BrushVisitor -{ - const Functor& functor; +class FaceVisitAll : public BrushVisitor { + const Functor &functor; public: - FaceVisitAll(const Functor& functor) - : functor(functor) - { - } - void visit(Face& face) const - { - functor(face); - } + FaceVisitAll(const Functor &functor) + : functor(functor) + { + } + + void visit(Face &face) const + { + functor(face); + } }; template -inline const Functor& Brush_forEachFace(const Brush& brush, const Functor& functor) +inline const Functor &Brush_forEachFace(const Brush &brush, const Functor &functor) { - brush.forEachFace(FaceVisitAll(functor)); - return functor; + brush.forEachFace(FaceVisitAll(functor)); + return functor; } template -inline const Functor& Brush_forEachFace(Brush& brush, const Functor& functor) +inline const Functor &Brush_forEachFace(Brush &brush, const Functor &functor) { - brush.forEachFace(FaceVisitAll(functor)); - return functor; + brush.forEachFace(FaceVisitAll(functor)); + return functor; } template -class FaceInstanceVisitAll : public BrushInstanceVisitor -{ - const Functor& functor; +class FaceInstanceVisitAll : public BrushInstanceVisitor { + const Functor &functor; public: - FaceInstanceVisitAll(const Functor& functor) - : functor(functor) - { - } - void visit(FaceInstance& face) const - { - functor(face); - } + FaceInstanceVisitAll(const Functor &functor) + : functor(functor) + { + } + + void visit(FaceInstance &face) const + { + functor(face); + } }; template -inline const Functor& Brush_ForEachFaceInstance(BrushInstance& brush, const Functor& functor) +inline const Functor &Brush_ForEachFaceInstance(BrushInstance &brush, const Functor &functor) { - brush.forEachFaceInstance(FaceInstanceVisitAll(functor)); - return functor; + brush.forEachFaceInstance(FaceInstanceVisitAll(functor)); + return functor; } template -inline const Functor& Scene_forEachBrush(scene::Graph& graph, const Functor& functor) +inline const Functor &Scene_forEachBrush(scene::Graph &graph, const Functor &functor) { - graph.traverse(InstanceWalker< InstanceApply >(functor)); - return functor; + graph.traverse(InstanceWalker >(functor)); + return functor; } template -class InstanceIfVisible : public Functor -{ +class InstanceIfVisible : public Functor { public: - InstanceIfVisible(const Functor& functor) : Functor(functor) - { - } - void operator()(scene::Instance& instance) - { - if(instance.path().top().get().visible()) + InstanceIfVisible(const Functor &functor) : Functor(functor) + { + } + + void operator()(scene::Instance &instance) { - Functor::operator()(instance); + if (instance.path().top().get().visible()) { + Functor::operator()(instance); + } } - } }; template -class BrushVisibleWalker : public scene::Graph::Walker -{ - const Functor& m_functor; +class BrushVisibleWalker : public scene::Graph::Walker { + const Functor &m_functor; public: - BrushVisibleWalker(const Functor& functor) : m_functor(functor) - { - } - bool pre(const scene::Path& path, scene::Instance& instance) const - { - if(path.top().get().visible()) - { - BrushInstance* brush = Instance_getBrush(instance); - if(brush != 0) - { - m_functor(*brush); - } + 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; } - return true; - } }; template -inline const Functor& Scene_forEachVisibleBrush(scene::Graph& graph, const Functor& functor) +inline const Functor &Scene_forEachVisibleBrush(scene::Graph &graph, const Functor &functor) { - graph.traverse(BrushVisibleWalker(functor)); - return functor; + graph.traverse(BrushVisibleWalker(functor)); + return functor; } template -inline const Functor& Scene_ForEachBrush_ForEachFace(scene::Graph& graph, const Functor& functor) +inline const Functor &Scene_ForEachBrush_ForEachFace(scene::Graph &graph, const Functor &functor) { - Scene_forEachBrush(graph, BrushForEachFace(FaceInstanceVisitFace(functor))); - return functor; + Scene_forEachBrush(graph, BrushForEachFace(FaceInstanceVisitFace(functor))); + return functor; } // d1223m template -inline const Functor& Scene_ForEachBrush_ForEachFaceInstance(scene::Graph& graph, const Functor& functor) +inline const Functor &Scene_ForEachBrush_ForEachFaceInstance(scene::Graph &graph, const Functor &functor) { - Scene_forEachBrush(graph, BrushForEachFace(FaceInstanceVisitAll(functor))); - return functor; + Scene_forEachBrush(graph, BrushForEachFace(FaceInstanceVisitAll(functor))); + return functor; } template -inline const Functor& Scene_ForEachSelectedBrush_ForEachFace(scene::Graph& graph, const Functor& functor) +inline const Functor &Scene_ForEachSelectedBrush_ForEachFace(scene::Graph &graph, const Functor &functor) { - Scene_forEachSelectedBrush(BrushForEachFace(FaceInstanceVisitFace(functor))); - return functor; + Scene_forEachSelectedBrush(BrushForEachFace(FaceInstanceVisitFace(functor))); + return functor; } template -inline const Functor& Scene_ForEachSelectedBrush_ForEachFaceInstance(scene::Graph& graph, const Functor& functor) +inline const Functor &Scene_ForEachSelectedBrush_ForEachFaceInstance(scene::Graph &graph, const Functor &functor) { - Scene_forEachSelectedBrush(BrushForEachFace(FaceInstanceVisitAll(functor))); - return functor; + Scene_forEachSelectedBrush(BrushForEachFace(FaceInstanceVisitAll(functor))); + return functor; } template -class FaceVisitorWrapper -{ - const Functor& functor; +class FaceVisitorWrapper { + const Functor &functor; public: - FaceVisitorWrapper(const Functor& functor) : functor(functor) - { - } - - void operator()(FaceInstance& faceInstance) const - { - functor(faceInstance.getFace()); - } + FaceVisitorWrapper(const Functor &functor) : functor(functor) + { + } + + void operator()(FaceInstance &faceInstance) const + { + functor(faceInstance.getFace()); + } }; template -inline const Functor& Scene_ForEachSelectedBrushFace(scene::Graph& graph, const Functor& functor) +inline const Functor &Scene_ForEachSelectedBrushFace(scene::Graph &graph, const Functor &functor) { - g_SelectedFaceInstances.foreach(FaceVisitorWrapper(functor)); - return functor; + g_SelectedFaceInstances.foreach(FaceVisitorWrapper(functor)); + return functor; }