netradiant-custom/radiant/brush.h
Garux df02774ff5 tweak StringOutputStream use
auto str = StringOutputStream()(bla) use form was not doing copy elision or move, but copy
2024-01-29 16:54:08 +06:00

4325 lines
123 KiB
C++

/*
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.
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.
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
*/
#pragma once
/// \file
/// \brief The brush primitive.
///
/// A collection of planes that define a convex polyhedron.
/// The Boundary-Representation of this primitive is a manifold polygonal mesh.
/// Each face polygon is represented by a list of vertices in a \c Winding.
/// Each vertex is associated with another face that is adjacent to the edge
/// formed by itself and the next vertex in the winding. This information can
/// be used to find edge-pairs and vertex-rings.
#include "debugging/debugging.h"
#include "itexdef.h"
#include "iundo.h"
#include "iselection.h"
#include "irender.h"
#include "imap.h"
#include "ibrush.h"
#include "igl.h"
#include "ifilter.h"
#include "nameable.h"
#include "moduleobserver.h"
#include <set>
#include "cullable.h"
#include "renderable.h"
#include "selectable.h"
#include "editable.h"
#include "mapfile.h"
#include "math/frustum.h"
#include "selectionlib.h"
#include "render.h"
#include "texturelib.h"
#include "container/container.h"
#include "generic/bitfield.h"
#include "signal/signalfwd.h"
#include "winding.h"
#include "brush_primit.h"
const unsigned int BRUSH_DETAIL_FLAG = 27;
const unsigned int BRUSH_DETAIL_MASK = ( 1 << BRUSH_DETAIL_FLAG );
#define BRUSH_CONNECTIVITY_DEBUG 0
#define BRUSH_DEGENERATE_DEBUG 0
#define Update_move_planepts_vertex 0
inline bool texdef_sane( const texdef_t& texdef ){
return fabs( texdef.shift[0] ) < ( 1 << 16 )
&& fabs( texdef.shift[1] ) < ( 1 << 16 );
}
inline void Winding_DrawWireframe( const Winding& winding ){
gl().glVertexPointer( 3, GL_DOUBLE, sizeof( WindingVertex ), &winding.points.data()->vertex );
gl().glDrawArrays( GL_LINE_LOOP, 0, GLsizei( winding.numpoints ) );
}
inline void Winding_Draw( const Winding& winding, const Vector3& normal, RenderStateFlags state ){
gl().glVertexPointer( 3, GL_DOUBLE, sizeof( WindingVertex ), &winding.points.data()->vertex );
Vector3 normals[c_brush_maxFaces];
if ( ( state & RENDER_BUMP ) != 0 ) {
typedef Vector3* Vector3Iter;
for ( Vector3Iter i = normals, end = normals + winding.numpoints; i != end; ++i )
{
*i = normal;
}
gl().glNormalPointer( GL_FLOAT, sizeof( Vector3 ), normals );
gl().glVertexAttribPointer( c_attr_TexCoord0, 2, GL_FLOAT, 0, sizeof( WindingVertex ), &winding.points.data()->texcoord );
gl().glVertexAttribPointer( c_attr_Tangent, 3, GL_FLOAT, 0, sizeof( WindingVertex ), &winding.points.data()->tangent );
gl().glVertexAttribPointer( c_attr_Binormal, 3, GL_FLOAT, 0, sizeof( WindingVertex ), &winding.points.data()->bitangent );
}
else
{
if ( state & RENDER_LIGHTING ) {
typedef Vector3* Vector3Iter;
for ( Vector3Iter i = normals, last = normals + winding.numpoints; i != last; ++i )
{
*i = normal;
}
gl().glNormalPointer( GL_FLOAT, sizeof( Vector3 ), normals );
}
if ( state & RENDER_TEXTURE ) {
gl().glTexCoordPointer( 2, GL_FLOAT, sizeof( WindingVertex ), &winding.points.data()->texcoord );
}
}
#if 0
if ( state & RENDER_FILL ) {
gl().glDrawArrays( GL_TRIANGLE_FAN, 0, GLsizei( winding.numpoints ) );
}
else
{
gl().glDrawArrays( GL_LINE_LOOP, 0, GLsizei( winding.numpoints ) );
}
#else
gl().glDrawArrays( GL_POLYGON, 0, GLsizei( winding.numpoints ) );
#endif
#if 0
const Winding& winding = winding;
if ( state & RENDER_FILL ) {
gl().glBegin( GL_POLYGON );
}
else
{
gl().glBegin( GL_LINE_LOOP );
}
if ( state & RENDER_LIGHTING ) {
gl().glNormal3fv( normal );
}
for ( int i = 0; i < winding.numpoints; ++i )
{
if ( state & RENDER_TEXTURE ) {
gl().glTexCoord2fv( &winding.points[i][3] );
}
gl().glVertex3fv( winding.points[i] );
}
gl().glEnd();
#endif
}
#include "shaderlib.h"
typedef DoubleVector3 PlanePoints[3];
inline bool planepts_equal( const PlanePoints planepts, const PlanePoints other ){
return planepts[0] == other[0] && planepts[1] == other[1] && planepts[2] == other[2];
}
inline void planepts_assign( PlanePoints planepts, const PlanePoints other ){
planepts[0] = other[0];
planepts[1] = other[1];
planepts[2] = other[2];
}
inline void planepts_quantise( PlanePoints planepts, double snap ){
vector3_snap( planepts[0], snap );
vector3_snap( planepts[1], snap );
vector3_snap( planepts[2], snap );
}
inline float vector3_max_component( const Vector3& vec3 ){
return std::max( fabsf( vec3[0] ), std::max( fabsf( vec3[1] ), fabsf( vec3[2] ) ) );
}
inline void edge_snap( Vector3& edge, double snap ){
float scale = static_cast<float>( ceil( fabs( snap / vector3_max_component( edge ) ) ) );
if ( scale > 0.0f ) {
vector3_scale( edge, scale );
}
vector3_snap( edge, snap );
}
inline void planepts_snap( PlanePoints planepts, double snap ){
Vector3 edge01( vector3_subtracted( planepts[1], planepts[0] ) );
Vector3 edge12( vector3_subtracted( planepts[2], planepts[1] ) );
Vector3 edge20( vector3_subtracted( planepts[0], planepts[2] ) );
double length_squared_01 = vector3_dot( edge01, edge01 );
double length_squared_12 = vector3_dot( edge12, edge12 );
double length_squared_20 = vector3_dot( edge20, edge20 );
vector3_snap( planepts[0], snap );
if ( length_squared_01 < length_squared_12 ) {
if ( length_squared_12 < length_squared_20 ) {
edge_snap( edge01, snap );
edge_snap( edge12, snap );
planepts[1] = vector3_added( planepts[0], edge01 );
planepts[2] = vector3_added( planepts[1], edge12 );
}
else
{
edge_snap( edge20, snap );
edge_snap( edge01, snap );
planepts[1] = vector3_added( planepts[0], edge20 );
planepts[2] = vector3_added( planepts[1], edge01 );
}
}
else
{
if ( length_squared_01 < length_squared_20 ) {
edge_snap( edge01, snap );
edge_snap( edge12, snap );
planepts[1] = vector3_added( planepts[0], edge01 );
planepts[2] = vector3_added( planepts[1], edge12 );
}
else
{
edge_snap( edge12, snap );
edge_snap( edge20, snap );
planepts[1] = vector3_added( planepts[0], edge12 );
planepts[2] = vector3_added( planepts[1], edge20 );
}
}
}
inline PointVertex pointvertex_for_planept( const DoubleVector3& point, const Colour4b& colour ){
return PointVertex(
Vertex3f(
static_cast<float>( point.x() ),
static_cast<float>( point.y() ),
static_cast<float>( point.z() )
),
colour
);
}
inline PointVertex pointvertex_for_windingpoint( const Vector3& point, const Colour4b& colour ){
return PointVertex(
vertex3f_for_vector3( point ),
colour
);
}
inline DepthTestedPointVertex depthtested_pointvertex_for_windingpoint( const Vector3& point, const Colour4b& colour ){
return DepthTestedPointVertex(
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 void brush_check_shader( const char* name ){
if ( !texdef_name_valid( name ) ) {
globalErrorStream() << "brush face has invalid texture name: '" << name << "'\n";
}
}
class FaceShaderObserver
{
public:
virtual void realiseShader() = 0;
virtual void unrealiseShader() = 0;
};
typedef ReferencePair<FaceShaderObserver> FaceShaderObserverPair;
class ContentsFlagsValue
{
public:
ContentsFlagsValue(){
}
ContentsFlagsValue( int surfaceFlags, int contentFlags, int value, bool specified ) :
m_surfaceFlags( surfaceFlags ),
m_contentFlags( contentFlags ),
m_value( value ),
m_specified( specified ){
}
int m_surfaceFlags;
int m_contentFlags;
int m_value;
bool m_specified;
};
inline void ContentsFlagsValue_assignMasked( ContentsFlagsValue& flags, const ContentsFlagsValue& other ){
bool detail = bitfield_enabled( flags.m_contentFlags, BRUSH_DETAIL_MASK );
flags = other;
if ( detail ) {
flags.m_contentFlags = bitfield_enable( flags.m_contentFlags, BRUSH_DETAIL_MASK );
}
else
{
flags.m_contentFlags = bitfield_disable( flags.m_contentFlags, BRUSH_DETAIL_MASK );
}
}
class FaceShader : public ModuleObserver
{
public:
class SavedState
{
public:
CopiedString m_shader;
ContentsFlagsValue m_flags;
SavedState( const FaceShader& faceShader ){
m_shader = faceShader.getShader();
m_flags = faceShader.m_flags;
}
void exportState( FaceShader& faceShader ) const {
faceShader.setShader( m_shader.c_str() );
//faceShader.setFlags( m_flags ); //detail, structural flags aren't undoable with this
faceShader.m_flags = m_flags;
}
};
CopiedString m_shader;
Shader* m_state;
ContentsFlagsValue m_flags;
FaceShaderObserverPair m_observers;
bool m_instanced;
bool m_realised;
FaceShader( const char* shader, const ContentsFlagsValue& flags = ContentsFlagsValue( 0, 0, 0, false ) ) :
m_shader( shader ),
m_state( 0 ),
m_flags( flags ),
m_instanced( false ),
m_realised( false ){
captureShader();
}
~FaceShader(){
releaseShader();
}
// copy-construction not supported
FaceShader( const FaceShader& other ) = delete;
void instanceAttach(){
m_instanced = true;
m_state->incrementUsed();
}
void instanceDetach(){
m_state->decrementUsed();
m_instanced = false;
}
void captureShader(){
ASSERT_MESSAGE( m_state == 0, "shader cannot be captured" );
brush_check_shader( m_shader.c_str() );
m_state = GlobalShaderCache().capture( m_shader.c_str() );
m_state->attach( *this );
}
void releaseShader(){
ASSERT_MESSAGE( m_state != 0, "shader cannot be released" );
m_state->detach( *this );
GlobalShaderCache().release( m_shader.c_str() );
m_state = 0;
}
void realise(){
ASSERT_MESSAGE( !m_realised, "FaceTexdef::realise: already realised" );
m_realised = true;
m_observers.forEach( []( FaceShaderObserver& observer ){ observer.realiseShader(); } );
}
void unrealise(){
ASSERT_MESSAGE( m_realised, "FaceTexdef::unrealise: already unrealised" );
m_observers.forEach( []( FaceShaderObserver& observer ){ observer.unrealiseShader(); } );
m_realised = false;
}
void attach( FaceShaderObserver& observer ){
m_observers.attach( observer );
if ( m_realised ) {
observer.realiseShader();
}
}
void detach( FaceShaderObserver& observer ){
if ( m_realised ) {
observer.unrealiseShader();
}
m_observers.detach( observer );
}
const char* getShader() const {
return m_shader.c_str();
}
void setShader( const char* name ){
if ( m_instanced ) {
m_state->decrementUsed();
}
releaseShader();
m_shader = name;
captureShader();
if ( m_instanced ) {
m_state->incrementUsed();
}
}
ContentsFlagsValue getFlags() const {
ASSERT_MESSAGE( m_realised, "FaceShader::getFlags: flags not valid when unrealised" );
if ( !m_flags.m_specified ) {
return ContentsFlagsValue(
m_state->getTexture().surfaceFlags,
m_state->getTexture().contentFlags,
m_state->getTexture().value,
true
);
}
return m_flags;
}
void setFlags( const ContentsFlagsValue& flags ){
ASSERT_MESSAGE( m_realised, "FaceShader::setFlags: flags not valid when unrealised" );
ContentsFlagsValue_assignMasked( m_flags, flags );
}
Shader* state() const {
return m_state;
}
std::size_t width() const {
if ( m_realised ) {
return m_state->getTexture().width;
}
return 1;
}
std::size_t height() const {
if ( m_realised ) {
return m_state->getTexture().height;
}
return 1;
}
unsigned int shaderFlags() const {
if ( m_realised ) {
return m_state->getFlags();
}
return 0;
}
};
class FaceTexdef : public FaceShaderObserver
{
public:
class SavedState
{
public:
TextureProjection m_projection;
SavedState( const FaceTexdef& faceTexdef ){
m_projection = faceTexdef.m_projection;
}
void exportState( FaceTexdef& faceTexdef ) const {
Texdef_Assign( faceTexdef.m_projection, m_projection );
}
};
FaceShader& m_shader;
TextureProjection m_projection;
bool m_projectionInitialised;
bool m_scaleApplied;
// not copyable
FaceTexdef( const FaceTexdef& other ) = delete;
// not assignable
FaceTexdef& operator=( const FaceTexdef& other ) = delete;
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 already added" );
m_scaleApplied = true;
m_projection.m_brushprimit_texdef.addScale( m_shader.width(), m_shader.height() );
}
void removeScale(){
ASSERT_MESSAGE( m_scaleApplied, "texture scale already 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, bool setBasis ){
removeScale();
Texdef_Assign( m_projection, projection, setBasis );
addScale();
}
void setTexdef( const float* hShift, const float* vShift, const float* hScale, const float* vScale, const float* rotation ){
removeScale();
Texdef_Assign( m_projection, hShift, vShift, hScale, vScale, rotation );
// if( hShift || vShift ){
// Texdef_normalise( m_projection, m_shader.width(), m_shader.height() );
// }
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 );
// Texdef_normalise( m_projection, m_shader.width(), m_shader.height() );
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();
}
///ProjectTexture along 'direction' with parameters, defined by texdef_t
void ProjectTexture( const Plane3& plane, const texdef_t& texdef, const Vector3* direction ){
Texdef_ProjectTexture( m_projection, m_shader.width(), m_shader.height(), plane, texdef, direction );
}
///ProjectTexture along 'normal' with parameters, defined by TextureProjection
void ProjectTexture( const Plane3& plane, const TextureProjection& projection, const Vector3& normal ){
Texdef_ProjectTexture( m_projection, m_shader.width(), m_shader.height(), plane, projection, normal );
}
void Convert( TexdefTypeId in, TexdefTypeId out, const Plane3& plane ){
Texdef_Convert( in, out, plane, m_projection, m_shader.width(), m_shader.height() );
m_scaleApplied = true;
}
void fit( const Vector3& normal, const Winding& winding, float s_repeat, float t_repeat, bool only_dimension ){
Texdef_FitTexture( m_projection, m_shader.width(), m_shader.height(), normal, winding, s_repeat, t_repeat, only_dimension );
}
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 );
}
#if 0 /* axial projection */
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() );
}
#else /* face projection */
void setBasis( const Vector3& normal ){
ComputeAxisBase( normal, m_projection.m_basis_s, m_projection.m_basis_t );
}
#endif
};
inline void planepts_print( const PlanePoints& planePoints, TextOutputStream& ostream ){
ostream << "( " << planePoints[0][0] << ' ' << planePoints[0][1] << ' ' << planePoints[0][2] << " ) "
<< "( " << planePoints[1][0] << ' ' << planePoints[1][1] << ' ' << planePoints[1][2] << " ) "
<< "( " << planePoints[2][0] << ' ' << planePoints[2][1] << ' ' << planePoints[2][2] << " )";
}
inline Plane3 Plane3_applyTranslation( const Plane3& plane, const Vector3& translation ){
Plane3 tmp( plane3_translated( Plane3( plane.normal(), -plane.dist() ), translation ) );
return Plane3( tmp.normal(), -tmp.dist() );
}
inline Plane3 Plane3_applyTransform( const Plane3& plane, const Matrix4& matrix ){
/* fails for scaling */
//Plane3 tmp( plane3_transformed( Plane3( plane.normal(), -plane.dist() ), matrix ) );
//return Plane3( tmp.normal(), -tmp.dist() );
/* ok */
return plane3_transformed_affine_full( plane, matrix );
}
class FacePlane
{
PlanePoints m_planepts;
Plane3 m_planeCached;
Plane3 m_plane;
public:
Vector3 m_funcStaticOrigin;
static EBrushType m_type;
static bool isDoom3Plane(){
return FacePlane::m_type == eBrushTypeDoom3 || FacePlane::m_type == eBrushTypeQuake4;
}
class SavedState
{
public:
PlanePoints m_planepts;
Plane3 m_plane;
SavedState( const FacePlane& facePlane ){
if ( facePlane.isDoom3Plane() ) {
m_plane = facePlane.m_plane;
}
else
{
planepts_assign( m_planepts, facePlane.planePoints() );
}
}
void exportState( FacePlane& facePlane ) const {
if ( facePlane.isDoom3Plane() ) {
facePlane.m_plane = m_plane;
facePlane.updateTranslated();
}
else
{
planepts_assign( facePlane.planePoints(), m_planepts );
facePlane.MakePlane();
}
}
};
FacePlane() : m_funcStaticOrigin( 0, 0, 0 ){
}
FacePlane( const FacePlane& other ) : m_funcStaticOrigin( 0, 0, 0 ){
if ( !isDoom3Plane() ) {
planepts_assign( m_planepts, other.m_planepts );
MakePlane();
}
else
{
m_plane = other.m_plane;
updateTranslated();
}
}
FacePlane& operator=( const FacePlane& ) = default;
void MakePlane(){
if ( !isDoom3Plane() ) {
#if 0
if ( check_plane_is_integer( m_planepts ) ) {
globalErrorStream() << "non-integer planepts: ";
planepts_print( m_planepts, globalErrorStream() );
globalErrorStream() << '\n';
}
#endif
m_planeCached = plane3_for_points( m_planepts );
}
}
void reverse(){
if ( !isDoom3Plane() ) {
vector3_swap( m_planepts[0], m_planepts[2] );
MakePlane();
}
else
{
m_planeCached = plane3_flipped( m_plane );
updateSource();
}
}
void transform( const Matrix4& matrix, bool mirror ){
if ( !isDoom3Plane() ) {
#if 0
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] );
if ( mirror ) {
reverse();
}
#if 0
if ( check_plane_is_integer( planePoints() ) ) {
if ( !off ) {
globalErrorStream() << "caused by transform\n";
}
}
#endif
MakePlane();
}
else
{
m_planeCached = Plane3_applyTransform( m_planeCached, matrix );
updateSource();
}
}
void offset( float offset ){
if ( !isDoom3Plane() ) {
Vector3 move( vector3_scaled( m_planeCached.normal(), -offset ) );
vector3_subtract( m_planepts[0], move );
vector3_subtract( m_planepts[1], move );
vector3_subtract( m_planepts[2], move );
MakePlane();
}
else
{
m_planeCached.d += offset;
updateSource();
}
}
void updateTranslated(){
m_planeCached = Plane3_applyTranslation( m_plane, m_funcStaticOrigin );
}
void updateSource(){
m_plane = Plane3_applyTranslation( m_planeCached, vector3_negated( m_funcStaticOrigin ) );
}
PlanePoints& planePoints(){
return m_planepts;
}
const PlanePoints& planePoints() const {
return m_planepts;
}
const PlanePoints& getPlanePoints(){
if( isDoom3Plane() ){
m_planepts[0] = m_planeCached.normal() * m_planeCached.dist();
ComputeAxisBase( m_planeCached.normal(), m_planepts[1], m_planepts[2] );
m_planepts[1] = m_planepts[1] + m_planepts[0];
m_planepts[2] = m_planepts[2] + m_planepts[0];
}
return m_planepts;
}
const Plane3& plane3() const {
return m_planeCached;
}
void setDoom3Plane( const Plane3& plane ){
m_plane = plane;
updateTranslated();
}
const Plane3& getDoom3Plane() const {
return m_plane;
}
void copy( const FacePlane& other ){
if ( !isDoom3Plane() ) {
planepts_assign( m_planepts, other.m_planepts );
MakePlane();
}
else
{
m_planeCached = other.m_plane;
updateSource();
}
}
void copy( const DoubleVector3& p0, const DoubleVector3& p1, const DoubleVector3& p2 ){
if ( !isDoom3Plane() ) {
m_planepts[0] = p0;
m_planepts[1] = p1;
m_planepts[2] = p2;
MakePlane();
}
else
{
m_planeCached = plane3_for_points( p0, p1, p2 );
updateSource();
}
}
void copy( const PlanePoints planepoints ){
if ( !isDoom3Plane() ) {
planepts_assign( m_planepts, planepoints );
MakePlane();
}
else
{
m_planeCached = plane3_for_points( planepoints );
updateSource();
}
}
};
inline void Winding_testSelect( Winding& winding, SelectionTest& test, SelectionIntersection& best, const DoubleVector3 planepoints[3] ){
test.TestPolygon( VertexPointer( reinterpret_cast<VertexPointer::pointer>( &winding.points.data()->vertex ), sizeof( WindingVertex ) ), winding.numpoints, best, planepoints );
}
const double GRID_MIN = 0.125;
inline double quantiseInteger( double f ){
return float_to_integer( f );
}
inline double quantiseFloating( double f ){
#if 0
return float_snapped( f, 1.f / ( 1 << 16 ) );
#else
return f;
#endif
}
typedef double ( *QuantiseFunc )( double f );
class Face;
class FaceFilter
{
public:
virtual bool filter( const Face& face ) const = 0;
};
bool face_filtered( Face& face );
void add_face_filter( FaceFilter& filter, int mask, bool invert = false );
void Brush_addTextureChangedCallback( const SignalHandler& callback );
void Brush_textureChanged();
extern bool g_brush_texturelock_enabled;
extern bool g_brush_textureVertexlock_enabled;
inline TexdefTypeId BrushType_getTexdefType( EBrushType type ){
switch ( type )
{
case eBrushTypeQuake3BP:
case eBrushTypeQuake2BP:
case eBrushTypeDoom3:
case eBrushTypeQuake4:
return TEXDEFTYPEID_BRUSHPRIMITIVES;
case eBrushTypeValve220:
case eBrushTypeQuake2Valve220:
case eBrushTypeQuake3Valve220:
return TEXDEFTYPEID_VALVE;
default:
return TEXDEFTYPEID_QUAKE;
}
}
class FaceObserver
{
public:
virtual void planeChanged() = 0;
virtual void connectivityChanged() = 0;
virtual void shaderChanged() = 0;
virtual void evaluateTransform() = 0;
};
class Face :
public OpenGLRenderable,
public Filterable,
public Undoable,
public FaceShaderObserver
{
std::size_t m_refcount;
class SavedState : public UndoMemento
{
public:
FacePlane::SavedState m_planeState;
FaceTexdef::SavedState m_texdefState;
FaceShader::SavedState m_shaderState;
SavedState( const Face& face ) : m_planeState( face.getPlane() ), m_texdefState( face.getTexdef() ), m_shaderState( face.getShader() ){
}
void exportState( Face& face ) const {
m_planeState.exportState( face.getPlane() );
m_shaderState.exportState( face.getShader() );
m_texdefState.exportState( face.getTexdef() );
}
void release(){
delete this;
}
};
public:
static QuantiseFunc m_quantise;
static EBrushType m_type;
PlanePoints m_move_planepts;
PlanePoints m_move_planeptsTransformed;
private:
FacePlane m_plane;
FacePlane m_planeTransformed;
FaceShader m_shader;
FaceTexdef m_texdef;
TextureProjection m_texdefTransformed;
Winding m_winding;
Vector3 m_centroid;
Vector3 m_centroid_cached; //this is far not pretty hack! (invariant point for texlock in AP)
bool m_filtered;
FaceObserver* m_observer;
UndoObserver* m_undoable_observer;
MapFile* m_map;
public:
// assignment not supported
Face& operator=( const Face& other ) = delete;
// copy-construction not supported
Face( const Face& other ) = delete;
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( DoubleVector3( 0, 0, 0 ), DoubleVector3( 64, 0, 0 ), DoubleVector3( 0, 64, 0 ) );
m_texdef.setBasis( m_plane.plane3().normal() );
planeChanged();
}
Face(
const DoubleVector3& p0,
const DoubleVector3& p1,
const DoubleVector3& 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() ); //don't reset basis on face clone
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<const SavedState*>( 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 texdef_from_points(){
Matrix4 local2tex;
Texdef_Construct_local2tex( m_texdef.m_projection, m_shader.width(), m_shader.height(), m_plane.plane3().normal(), local2tex );
DoubleVector3 st[3];
for ( std::size_t i = 0; i < 3; ++i )
st[i] = matrix4_transformed_point( local2tex, m_move_planepts[i] );
Texdef_from_ST( m_texdefTransformed, m_move_planeptsTransformed, st, m_shader.width(), m_shader.height() );
Brush_textureChanged();
}
void transform_texdef( const Matrix4& matrix, const Vector3& invariant = g_vector3_identity ){
revertTexdef();
// Texdef_transformLocked( m_texdefTransformed, m_shader.width(), m_shader.height(), m_plane.plane3(), matrix, static_cast<Vector3>( m_plane.plane3().normal() * m_plane.plane3().dist() ) );
Texdef_transform( m_texdefTransformed, m_shader.width(), m_shader.height(), m_plane.plane3(), matrix, invariant );
EmitTextureCoordinates();
Brush_textureChanged();
}
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,
contributes() ? m_centroid_cached
: static_cast<Vector3>( m_plane.plane3().normal() * m_plane.plane3().dist() ) );
Brush_textureChanged();
}
else if( g_bp_globals.m_texdefTypeId == TEXDEFTYPEID_VALVE ){
const DoubleVector3 from = vector3_normalised( vector3_cross( m_texdefTransformed.m_basis_s, m_texdefTransformed.m_basis_t ) );
const DoubleVector3 to = matrix4_transformed_normal( matrix, from );
Quaternion quat = quaternion_for_unit_vectors( from, to );
if( quat.w() != quat.w() ){ //handle 180` cases
if( vector3_max_abs_component_index( from ) == 2 )
quat = Quaternion( g_vector3_axis_y, 0 );
else
quat = Quaternion( g_vector3_axis_z, 0 );
}
const Matrix4 mat = matrix4_rotation_for_quaternion( quat );
m_texdefTransformed.m_basis_s = vector3_normalised( matrix4_transformed_direction( mat, m_texdefTransformed.m_basis_s ) );
m_texdefTransformed.m_basis_t = vector3_normalised( matrix4_transformed_direction( mat, m_texdefTransformed.m_basis_t ) );
}
m_planeTransformed.transform( matrix, mirror );
#if 0
ASSERT_MESSAGE( projectionaxis_for_normal( normal ) == projectionaxis_for_normal( plane3().normal() ), "bleh" );
#endif
m_observer->planeChanged();
}
void assign_planepts( const PlanePoints planepts ){
m_planeTransformed.copy( planepts );
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 ){
if( contributes() ){
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() ) ) {
globalWarningStream() << "WARNING: invalid plane after snap to grid\n";
}
}
}
void testSelect( SelectionTest& test, SelectionIntersection& best ){
Winding_testSelect( m_winding, test, best, m_plane.getPlanePoints() );
}
void testSelect_centroid( SelectionTest& test, SelectionIntersection& best ) const {
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, bool setBasis = true, bool resetBasis = false ){
undoSave();
m_texdef.setTexdef( projection, setBasis );
if( resetBasis ){
m_texdef.setBasis( m_plane.plane3().normal() );
}
texdefChanged();
}
void SetTexdef( const float* hShift, const float* vShift, const float* hScale, const float* vScale, const float* rotation ){
undoSave();
m_texdef.setTexdef( hShift, vShift, hScale, vScale, rotation );
texdefChanged();
}
void GetFlags( ContentsFlagsValue& flags ) const {
flags = m_shader.getFlags();
}
void SetFlags( const ContentsFlagsValue& flags ){
undoSave();
m_shader.setFlags( flags );
m_observer->shaderChanged();
Brush_textureChanged();
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 ProjectTexture( const texdef_t& texdef, const Vector3* direction ){
undoSave();
m_texdef.ProjectTexture( m_plane.plane3(), texdef, direction );
texdefChanged();
}
void ProjectTexture( const TextureProjection& projection, const Vector3& normal ){
undoSave();
m_texdef.ProjectTexture( m_plane.plane3(), projection, normal );
texdefChanged();
}
void Convert( TexdefTypeId in, TexdefTypeId out ){
m_texdef.Convert( in, out, m_plane.plane3() );
texdefChanged();
}
void FitTexture( float s_repeat, float t_repeat, bool only_dimension ){
undoSave();
m_texdef.fit( m_plane.plane3().normal(), m_winding, s_repeat, t_repeat, only_dimension );
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;
}
void cacheCentroid() {
m_centroid_cached = m_centroid;
}
const Plane3& plane3() const {
m_observer->evaluateTransform();
return m_planeTransformed.plane3();
}
const Plane3& plane3_() const {
return m_planeTransformed.plane3();
}
FacePlane& getPlane(){
return m_plane;
}
const FacePlane& getPlane() const {
return m_plane;
}
FaceTexdef& getTexdef(){
return m_texdef;
}
const FaceTexdef& getTexdef() const {
return m_texdef;
}
FaceShader& getShader(){
return m_shader;
}
const FaceShader& getShader() const {
return m_shader;
}
bool isDetail() const {
return ( m_shader.m_flags.m_contentFlags & BRUSH_DETAIL_MASK ) != 0;
}
void setDetail( bool detail ){
undoSave();
if ( detail && !isDetail() ) {
m_shader.m_flags.m_contentFlags |= BRUSH_DETAIL_MASK;
}
else if ( !detail && isDetail() ) {
m_shader.m_flags.m_contentFlags &= ~BRUSH_DETAIL_MASK;
}
m_observer->shaderChanged();
}
bool contributes() const {
return m_winding.numpoints > 2;
}
bool is_bounded() const {
for ( Winding::const_iterator i = m_winding.begin(); i != m_winding.end(); ++i )
{
if ( ( *i ).adjacent == c_brush_maxFaces ) {
return false;
}
}
return true;
}
};
class FaceVertexId
{
std::size_t m_face;
std::size_t m_vertex;
public:
FaceVertexId( std::size_t face, std::size_t vertex )
: m_face( face ), m_vertex( vertex ){
}
std::size_t getFace() const {
return m_face;
}
std::size_t getVertex() const {
return m_vertex;
}
};
typedef std::size_t faceIndex_t;
struct EdgeRenderIndices
{
RenderIndex first;
RenderIndex second;
EdgeRenderIndices()
: first( 0 ), second( 0 ){
}
EdgeRenderIndices( const RenderIndex _first, const RenderIndex _second )
: first( _first ), second( _second ){
}
};
struct EdgeFaces
{
faceIndex_t first;
faceIndex_t second;
EdgeFaces()
: first( c_brush_maxFaces ), second( c_brush_maxFaces ){
}
EdgeFaces( const faceIndex_t _first, const faceIndex_t _second )
: first( _first ), second( _second ){
}
};
class RenderableWireframe : public OpenGLRenderable
{
public:
void render( RenderStateFlags state ) const {
#if 1
gl().glVertexPointer( 3, GL_FLOAT, sizeof( DepthTestedPointVertex ), &m_vertices->vertex );
gl().glDrawElements( GL_LINES, GLsizei( m_size << 1 ), RenderIndexTypeID, m_faceVertex.data() );
#else
gl().glBegin( GL_LINES );
for ( std::size_t i = 0; i < m_size; ++i )
{
gl().glVertex3fv( &m_vertices[m_faceVertex[i].first].vertex.x );
gl().glVertex3fv( &m_vertices[m_faceVertex[i].second].vertex.x );
}
gl().glEnd();
#endif
}
Array<EdgeRenderIndices> m_faceVertex;
std::size_t m_size;
const DepthTestedPointVertex* m_vertices;
};
class Brush;
typedef std::vector<Brush*> brush_vector_t;
class BrushFilter
{
public:
virtual bool filter( const Brush& brush ) const = 0;
};
bool brush_filtered( Brush& brush );
void add_brush_filter( BrushFilter& filter, int mask, bool invert = false );
/// \brief Returns true if 'self' takes priority when building brush b-rep.
inline bool plane3_inside( const Plane3& self, const Plane3& other, bool selfIsLater ){
if ( vector3_equal_epsilon( self.normal(), other.normal(), 0.001 ) ) {
// same plane? prefer the one with smaller index
if ( self.dist() == other.dist() ) {
return selfIsLater;
}
return self.dist() < other.dist();
}
return true;
}
typedef SmartPointer<Face> FaceSmartPointer;
typedef std::vector<FaceSmartPointer> Faces;
/// \brief Returns the unique-id of the edge adjacent to \p faceVertex in the edge-pair for the set of \p faces.
inline FaceVertexId next_edge( const Faces& faces, FaceVertexId faceVertex ){
std::size_t adjacent_face = faces[faceVertex.getFace()]->getWinding()[faceVertex.getVertex()].adjacent;
std::size_t adjacent_vertex = Winding_FindAdjacent( faces[adjacent_face]->getWinding(), faceVertex.getFace() );
ASSERT_MESSAGE( adjacent_vertex != c_brush_maxFaces, "connectivity data invalid" );
if ( adjacent_vertex == c_brush_maxFaces ) {
return faceVertex;
}
return FaceVertexId( adjacent_face, adjacent_vertex );
}
/// \brief Returns the unique-id of the vertex adjacent to \p faceVertex in the vertex-ring for the set of \p faces.
inline FaceVertexId next_vertex( const Faces& faces, FaceVertexId faceVertex ){
FaceVertexId nextEdge = next_edge( faces, faceVertex );
return FaceVertexId( nextEdge.getFace(), Winding_next( faces[nextEdge.getFace()]->getWinding(), nextEdge.getVertex() ) );
}
class SelectableEdge
{
public:
Vector3 getEdge() const {
const Winding& winding = getFace().getWinding();
return vector3_mid( winding[m_faceVertex.getVertex()].vertex, winding[Winding_next( winding, m_faceVertex.getVertex() )].vertex );
}
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
{
public:
DoubleVector3 getVertex() const {
return getFace().getWinding()[m_faceVertex.getVertex()].vertex;
}
Faces& m_faces;
FaceVertexId m_faceVertex;
SelectableVertex( Faces& faces, FaceVertexId faceVertex )
: m_faces( faces ), m_faceVertex( faceVertex ){
}
SelectableVertex& operator=( const SelectableVertex& other ){
m_faceVertex = other.m_faceVertex;
return *this;
}
Face& getFace() const {
return *m_faces[m_faceVertex.getFace()];
}
void testSelect( SelectionTest& test, SelectionIntersection& best ){
test.TestPoint( getVertex(), best );
}
};
class BrushObserver
{
public:
virtual void reserve( std::size_t size ) = 0;
virtual void clear() = 0;
virtual void push_back( Face& face ) = 0;
virtual void pop_back() = 0;
virtual void erase( std::size_t index ) = 0;
virtual void connectivityChanged() = 0;
virtual void edge_clear() = 0;
virtual void edge_push_back( SelectableEdge& edge ) = 0;
virtual void vertex_clear() = 0;
virtual void vertex_push_back( SelectableVertex& vertex ) = 0;
virtual void vertex_select() = 0;
virtual void vertex_snap( const float snap ) = 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<BrushObserver*> Observers;
Observers m_observers;
UndoObserver* m_undoable_observer;
MapFile* m_map;
// state
Faces m_faces;
// ----
// cached data compiled from state
Array<PointVertex> m_faceCentroidPoints;
RenderablePointArray<PointVertex> m_render_faces;
mutable Array<DepthTestedPointVertex> m_uniqueVertexPoints;
typedef std::vector<SelectableVertex> SelectableVertices;
SelectableVertices m_select_vertices;
RenderablePointArray<DepthTestedPointVertex> m_render_vertices;
RenderableDepthTestedPointArray m_render_deepvertices;
Array<PointVertex> m_uniqueEdgePoints;
typedef std::vector<SelectableEdge> SelectableEdges;
SelectableEdges m_select_edges;
RenderablePointArray<PointVertex> m_render_edges;
Array<EdgeRenderIndices> m_edge_indices;
Array<EdgeFaces> m_edge_faces;
AABB m_aabb_local;
// ----
Callback m_evaluateTransform;
Callback m_boundsChanged;
mutable bool m_planeChanged; // b-rep evaluation required
mutable bool m_transformChanged; // transform evaluation required
bool m_BRep_evaluation = false; //mutex for invalidation
// ----
public:
STRING_CONSTANT( Name, "Brush" );
Callback m_lightsChanged;
// static data
static Shader* m_state_point;
static Shader* m_state_deeppoint;
// ----
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_deepvertices( 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_deepvertices( 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_deepvertices( 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 ) = delete;
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_BRep_evaluation mutex prevents cyclic dependency:
transformModifier.set ; transformChanged() ; planeChanged() ; pivotChanged() ; sceneChangeNotify() ;
sceneRender() ; localAABB ; evaluateBRep ; buildBRep() ; evaluateTransform ; !!!problem starts here!!!! planeChanged() ; pivotChanged() ; sceneChangeNotify() ;
sceneRender() ; localAABB ; evaluateBRep ; buildBRep() ; */
if( !m_BRep_evaluation ){
m_planeChanged = true;
aabbChanged();
m_lightsChanged();
}
}
void shaderChanged(){
updateFiltered();
planeChanged(); ///isn't too much for shader changed only?
}
void evaluateBRep() const {
if ( m_planeChanged ) {
m_planeChanged = false;
const_cast<Brush*>( this )->buildBRep();
}
}
void transformChanged(){
planeChanged();
m_transformChanged = true;
}
typedef MemberCaller<Brush, &Brush::transformChanged> TransformChangedCaller;
void evaluateTransform(){
if ( m_transformChanged ) {
revertTransform();
m_evaluateTransform();
m_transformChanged = false;
}
}
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:
{
if( volume.fill() ){
renderer.SetState( m_state_deeppoint, Renderer::eFullMaterials );
renderer.addRenderable( m_render_deepvertices, localToWorld );
}
else{
for( auto& p : m_uniqueVertexPoints )
p.colour = colour_vertex;
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 ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
{
( *i )->vertex_snap( 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();
}
m_transformChanged = false;
}
class VertexModeVertex
{
public:
const DoubleVector3 m_vertex;
DoubleVector3 m_vertexTransformed;
const bool m_selected;
std::vector<const Face*> m_faces;
VertexModeVertex( const DoubleVector3& vertex, const bool selected ) : m_vertex( vertex ), m_vertexTransformed( vertex ), m_selected( selected ) {
}
};
typedef std::vector<VertexModeVertex> VertexModeVertices;
VertexModeVertices m_vertexModeVertices;
bool m_vertexModeOn{false};
void vertexModeInit(){
m_vertexModeOn = true;
m_vertexModeVertices.clear();
undoSave();
}
void vertexModeFree(){
m_vertexModeOn = false;
// m_vertexModeVertices.clear(); //keep, as it may be required by buildBRep() after this call
}
void vertexModeTransform( const Matrix4& matrix );
void vertexModeBuildHull( bool allTransformed = false );
void vertexModeSnap( const float snap, bool all );
/// \brief Returns the absolute index of the \p faceVertex.
std::size_t absoluteIndex( FaceVertexId faceVertex ){
std::size_t index = 0;
for ( std::size_t i = 0; i < faceVertex.getFace(); ++i )
{
index += m_faces[i]->getWinding().numpoints;
}
return index + faceVertex.getVertex();
}
void appendFaces( const Faces& other ){
clear();
for ( Faces::const_iterator i = other.begin(); i != other.end(); ++i )
{
push_back( *i );
}
}
/// \brief The undo memento for a brush stores only the list of face references - the faces are not copied.
class BrushUndoMemento : public UndoMemento
{
public:
BrushUndoMemento( const Faces& faces ) : m_faces( faces ){
}
void release(){
delete this;
}
Faces m_faces;
};
void undoSave(){
if ( m_map != 0 ) {
m_map->changed();
}
if ( m_undoable_observer != 0 ) {
m_undoable_observer->save( this );
}
}
UndoMemento* exportState() const {
return new BrushUndoMemento( m_faces );
}
void importState( const UndoMemento* state ){
undoSave();
appendFaces( static_cast<const BrushUndoMemento*>( state )->m_faces );
planeChanged();
for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
{
( *i )->DEBUG_verify();
}
}
bool isDetail(){
return !m_faces.empty() && m_faces.front()->isDetail();
}
/// \brief Appends a copy of \p face to the end of the face list.
Face* addFace( const Face& face ){
if ( m_faces.size() == c_brush_maxFaces ) {
return 0;
}
undoSave();
push_back( FaceSmartPointer( new Face( face, this ) ) );
m_faces.back()->setDetail( isDetail() );
planeChanged();
return m_faces.back();
}
/// \brief Appends a new face constructed from the parameters to the end of the face list.
Face* addPlane( const DoubleVector3& p0, const DoubleVector3& p1, const DoubleVector3& 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 = BrushType_getTexdefType( type );
m_state_point = GlobalShaderCache().capture( "$POINT" );
m_state_deeppoint = GlobalShaderCache().capture( "$DEEPPOINT" );
}
static void destroyStatic(){
GlobalShaderCache().release( "$POINT" );
GlobalShaderCache().release( "$DEEPPOINT" );
}
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 );
// 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;
}
if( buffer[swap].points.empty() ){
//globalErrorStream() << "windingForClipPlane: about to feed empty winding to Winding_Clip\n";
break;
}
buffer[!swap].clear();
#if BRUSH_CONNECTIVITY_DEBUG
globalOutputStream() << "clip vs face: " << i << '\n';
#endif
{
// flip the plane, because we want to keep the back side
Plane3 clipPlane( vector3_negated( clip.plane3().normal() ), -clip.plane3().dist() );
Winding_Clip( buffer[swap], plane, clipPlane, i, buffer[!swap] );
}
#if BRUSH_CONNECTIVITY_DEBUG
for ( FixedWinding::Points::iterator k = buffer[!swap].points.begin(), j = buffer[!swap].points.end() - 1; k != buffer[!swap].points.end(); j = k, ++k )
{
if ( vector3_length_squared( vector3_subtracted( ( *k ).vertex, ( *j ).vertex ) ) < 1 ) {
globalOutputStream() << "v: " << std::distance( buffer[!swap].points.begin(), j ) << " tiny edge adjacent to face " << ( *j ).adjacent << '\n';
}
}
#endif
//ASSERT_MESSAGE(buffer[!swap].numpoints != 1, "created single-point winding");
swap = !swap;
}
}
Winding_forFixedWinding( winding, buffer[swap] );
#if BRUSH_CONNECTIVITY_DEBUG
Winding_printConnectivity( winding );
for ( Winding::iterator i = winding.begin(), j = winding.end() - 1; i != winding.end(); j = i, ++i )
{
if ( vector3_length_squared( vector3_subtracted( ( *i ).vertex, ( *j ).vertex ) ) < 1 ) {
globalOutputStream() << "v: " << std::distance( winding.begin(), j ) << " tiny edge adjacent to face " << ( *j ).adjacent << '\n';
}
}
#endif
}
void update_wireframe( RenderableWireframe& wire, const bool* faces_visible ) const {
wire.m_faceVertex.resize( m_edge_indices.size() );
wire.m_vertices = m_uniqueVertexPoints.data();
wire.m_size = 0;
for ( std::size_t i = 0; i < m_edge_faces.size(); ++i )
{
if ( faces_visible[m_edge_faces[i].first]
|| faces_visible[m_edge_faces[i].second] ) {
wire.m_faceVertex[wire.m_size++] = m_edge_indices[i];
}
}
}
void update_faces_wireframe( Array<PointVertex>& wire, const bool* faces_visible ) const {
std::size_t count = 0;
for ( std::size_t i = 0; i < m_faceCentroidPoints.size(); ++i )
{
if ( faces_visible[i] ) {
++count;
}
}
wire.resize( count );
Array<PointVertex>::iterator p = wire.begin();
for ( std::size_t i = 0; i < m_faceCentroidPoints.size(); ++i )
{
if ( faces_visible[i] ) {
*p++ = m_faceCentroidPoints[i];
}
}
}
/// \brief Makes this brush a deep-copy of the \p other.
void copy( const Brush& other ){
for ( Faces::const_iterator i = other.m_faces.begin(); i != other.m_faces.end(); ++i )
{
addFace( *( *i ) );
}
planeChanged();
}
/// for the only use to quickly check, if about to be transformed brush makes sense
bool contributes() const {
/* plane_unique() ripoff, calling no evaluation */
auto plane_unique_ = [this]( std::size_t index ) -> bool {
// 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;
};
/* windingForClipPlane() ripoff, calling no evaluation */
auto windingForClipPlane_ = [this, &plane_unique_]( const Plane3& plane ) -> bool {
FixedWinding buffer[2];
bool swap = false;
// get a poly that covers an effectively infinite area
Winding_createInfinite( buffer[swap], plane, m_maxWorldCoord );
// 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;
}
if( buffer[swap].points.empty() ){
break;
}
buffer[!swap].clear();
{
// 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] );
}
swap = !swap;
}
return buffer[swap].size() > 2;
};
std::size_t contributing = 0;
for ( std::size_t i = 0; i < m_faces.size(); ++i )
{
Face& f = *m_faces[i];
if ( plane3_valid( f.plane3_() ) && plane_unique_( i ) ) {
if( windingForClipPlane_( f.plane3_() ) ){
++contributing;
if( contributing > 3 )
return true;
}
}
}
return false;
}
private:
void edge_push_back( FaceVertexId faceVertex ){
m_select_edges.push_back( SelectableEdge( m_faces, faceVertex ) );
for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
{
( *i )->edge_push_back( m_select_edges.back() );
}
}
void edge_clear(){
m_select_edges.clear();
for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
{
( *i )->edge_clear();
}
}
void vertex_push_back( FaceVertexId faceVertex ){
m_select_vertices.push_back( SelectableVertex( m_faces, faceVertex ) );
for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
{
( *i )->vertex_push_back( m_select_vertices.back() );
}
}
void vertex_clear(){
m_select_vertices.clear();
for ( Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i )
{
( *i )->vertex_clear();
}
}
/// \brief Returns true if the face identified by \p index is preceded by another plane that takes priority over it.
bool plane_unique( std::size_t index ) const {
// duplicate plane
for ( std::size_t i = 0; i < m_faces.size(); ++i )
{
if ( index != i && !plane3_inside( m_faces[index]->plane3(), m_faces[i]->plane3(), index < i ) ) {
return false;
}
}
return true;
}
/// \brief Removes edges that are smaller than the tolerance used when generating brush windings.
void removeDegenerateEdges(){
for ( std::size_t i = 0; i < m_faces.size(); ++i )
{
Winding& winding = m_faces[i]->getWinding();
for ( Winding::iterator j = winding.begin(); j != winding.end(); )
{
std::size_t index = std::distance( winding.begin(), j );
std::size_t next = Winding_next( winding, index );
if ( Edge_isDegenerate( winding[index].vertex, winding[next].vertex ) ) {
#if BRUSH_DEGENERATE_DEBUG
globalOutputStream() << "Brush::buildWindings: face " << i << ": degenerate edge adjacent to " << winding[index].adjacent << '\n';
#endif
Winding& other = m_faces[winding[index].adjacent]->getWinding();
std::size_t adjacent = Winding_FindAdjacent( other, i );
if ( adjacent != c_brush_maxFaces ) {
other.erase( other.begin() + adjacent );
}
winding.erase( j );
}
else
{
++j;
}
}
}
}
/// \brief Invalidates faces that have only two vertices in their winding, while preserving edge-connectivity information.
void removeDegenerateFaces(){
// save adjacency info for degenerate faces
for ( std::size_t i = 0; i < m_faces.size(); ++i )
{
Winding& degen = m_faces[i]->getWinding();
if ( degen.numpoints == 2 ) {
#if BRUSH_DEGENERATE_DEBUG
globalOutputStream() << "Brush::buildWindings: face " << i << ": degenerate winding adjacent to " << degen[0].adjacent << ", " << degen[1].adjacent << '\n';
#endif
// this is an "edge" face, where the plane touches the edge of the brush
{
Winding& winding = m_faces[degen[0].adjacent]->getWinding();
std::size_t index = Winding_FindAdjacent( winding, i );
if ( index != c_brush_maxFaces ) {
#if BRUSH_DEGENERATE_DEBUG
globalOutputStream() << "Brush::buildWindings: face " << degen[0].adjacent << ": remapping adjacent " << winding[index].adjacent << " to " << degen[1].adjacent << '\n';
#endif
winding[index].adjacent = degen[1].adjacent;
}
}
{
Winding& winding = m_faces[degen[1].adjacent]->getWinding();
std::size_t index = Winding_FindAdjacent( winding, i );
if ( index != c_brush_maxFaces ) {
#if BRUSH_DEGENERATE_DEBUG
globalOutputStream() << "Brush::buildWindings: face " << degen[1].adjacent << ": remapping adjacent " << winding[index].adjacent << " to " << degen[0].adjacent << '\n';
#endif
winding[index].adjacent = degen[0].adjacent;
}
}
degen.resize( 0 );
}
}
}
/// \brief Removes edges that have the same adjacent-face as their immediate neighbour.
void removeDuplicateEdges(){
// verify face connectivity graph
for ( std::size_t i = 0; i < m_faces.size(); ++i )
{
//if(m_faces[i]->contributes())
{
Winding& winding = m_faces[i]->getWinding();
for ( std::size_t j = 0; j != winding.numpoints; )
{
std::size_t next = Winding_next( winding, j );
if ( winding[j].adjacent == winding[next].adjacent ) {
#if BRUSH_DEGENERATE_DEBUG
globalOutputStream() << "Brush::buildWindings: face " << i << ": removed duplicate edge adjacent to face " << winding[j].adjacent << '\n';
#endif
winding.erase( winding.begin() + next );
}
else
{
++j;
}
}
}
}
}
/// \brief Removes edges that do not have a matching pair in their adjacent-face.
void verifyConnectivityGraph(){
// verify face connectivity graph
for ( std::size_t i = 0; i < m_faces.size(); ++i )
{
//if(m_faces[i]->contributes())
{
Winding& winding = m_faces[i]->getWinding();
for ( Winding::iterator j = winding.begin(); j != winding.end(); )
{
#if BRUSH_CONNECTIVITY_DEBUG
globalOutputStream() << "Brush::buildWindings: face " << i << ": adjacent to face " << ( *j ).adjacent << '\n';
#endif
// remove unidirectional graph edges
if ( ( *j ).adjacent == c_brush_maxFaces
|| Winding_FindAdjacent( m_faces[( *j ).adjacent]->getWinding(), i ) == c_brush_maxFaces ) {
#if BRUSH_CONNECTIVITY_DEBUG
globalOutputStream() << "Brush::buildWindings: face " << i << ": removing unidirectional connectivity graph edge adjacent to face " << ( *j ).adjacent << '\n';
#endif
winding.erase( j );
}
else
{
++j;
}
}
}
}
}
/// \brief Returns true if the brush is a finite volume. A brush without a finite volume extends past the maximum world bounds and is not valid.
bool isBounded(){
for ( const_iterator i = begin(); i != end(); ++i )
{
if ( !( *i )->is_bounded() ) {
return false;
}
}
return true;
}
/// \brief Constructs the polygon windings for each face of the brush. Also updates the brush bounding-box and face texture-coordinates.
bool buildWindings(){
{
m_aabb_local = AABB();
if( m_faces.size() )
m_faces[0]->plane3(); //force evaluateTransform() first, as m_faces is changed during vertexModeTransform
for ( std::size_t i = 0; i < m_faces.size(); ++i )
{
Face& f = *m_faces[i];
if ( !plane3_valid( f.plane3() ) || !plane_unique( i ) ) {
f.getWinding().resize( 0 );
}
else
{
#if BRUSH_CONNECTIVITY_DEBUG
globalOutputStream() << "face: " << i << '\n';
#endif
windingForClipPlane( f.getWinding(), f.plane3() );
// update brush bounds
const Winding& winding = f.getWinding();
for ( Winding::const_iterator i = winding.begin(); i != winding.end(); ++i )
{
aabb_extend_by_point_safe( m_aabb_local, ( *i ).vertex );
}
// update texture coordinates
f.EmitTextureCoordinates();
}
}
}
bool degenerate = !isBounded();
if ( !degenerate ) {
// clean up connectivity information.
// these cleanups must be applied in a specific order.
removeDegenerateEdges();
removeDegenerateFaces();
removeDuplicateEdges();
verifyConnectivityGraph();
}
return degenerate;
}
/// \brief Constructs the face windings and updates anything that depends on them.
void buildBRep();
};
class FaceInstance;
class FaceInstanceSet
{
typedef SelectionList<FaceInstance> FaceInstances;
FaceInstances m_faceInstances;
public:
void insert( FaceInstance& faceInstance ){
m_faceInstances.append( faceInstance );
}
void erase( FaceInstance& faceInstance ){
m_faceInstances.erase( faceInstance );
}
template<typename Functor>
void foreach( Functor functor ){
for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
functor( *( *i ) );
}
}
bool empty() const {
return m_faceInstances.empty();
}
FaceInstance& last() const {
return m_faceInstances.back();
}
};
extern FaceInstanceSet g_SelectedFaceInstances;
typedef std::list<std::size_t> VertexSelection;
inline VertexSelection::iterator VertexSelection_find( VertexSelection& self, std::size_t value ){
return std::find( self.begin(), self.end(), value );
}
inline VertexSelection::const_iterator VertexSelection_find( const VertexSelection& self, std::size_t value ){
return std::find( self.begin(), self.end(), value );
}
inline VertexSelection::iterator VertexSelection_insert( VertexSelection& self, std::size_t value ){
VertexSelection::iterator i = VertexSelection_find( self, value );
if ( i == self.end() ) {
self.push_back( value );
return --self.end();
}
return i;
}
inline void VertexSelection_erase( VertexSelection& self, std::size_t value ){
VertexSelection::iterator i = VertexSelection_find( self, value );
if ( i != self.end() ) {
self.erase( i );
}
}
inline bool triangle_reversed( std::size_t x, std::size_t y, std::size_t z ){
return !( ( x < y && y < z ) || ( z < x && x < y ) || ( y < z && z < x ) );
}
template<typename Element>
inline Vector3 triangle_cross( const BasicVector3<Element>& x, const BasicVector3<Element> y, const BasicVector3<Element>& z ){
return vector3_cross( y - x, z - x );
}
template<typename Element>
inline bool triangles_same_winding( const BasicVector3<Element>& x1, const BasicVector3<Element> y1, const BasicVector3<Element>& z1, const BasicVector3<Element>& x2, const BasicVector3<Element> y2, const BasicVector3<Element>& z2 ){
return vector3_dot( triangle_cross( x1, y1, z1 ), triangle_cross( x2, y2, z2 ) ) > 0;
}
class VectorLightList : public LightList
{
typedef std::vector<const RendererLight*> Lights;
Lights m_lights;
public:
void addLight( const RendererLight& light ){
m_lights.push_back( &light );
}
void clear(){
m_lights.clear();
}
void evaluateLights() const {
}
void lightsChanged() const {
}
void forEachLight( const RendererLightCallback& callback ) const {
for ( Lights::const_iterator i = m_lights.begin(); i != m_lights.end(); ++i )
{
callback( *( *i ) );
}
}
};
class FaceInstance
{
Face* m_face;
ObservedSelectable m_selectable;
ObservedSelectable m_selectableVertices;
ObservedSelectable m_selectableEdges;
SelectionChangeCallback m_selectionChanged;
VertexSelection m_vertexSelection;
VertexSelection m_edgeSelection;
public:
mutable VectorLightList m_lights;
FaceInstance( Face& face, const SelectionChangeCallback& observer ) :
m_face( &face ),
m_selectable( SelectedChangedCaller( *this ) ),
m_selectableVertices( observer ),
m_selectableEdges( observer ),
m_selectionChanged( observer ){
}
FaceInstance( const FaceInstance& other ) :
m_face( other.m_face ),
m_selectable( SelectedChangedCaller( *this ) ),
m_selectableVertices( other.m_selectableVertices ),
m_selectableEdges( other.m_selectableEdges ),
m_selectionChanged( other.m_selectionChanged ){
}
FaceInstance& operator=( const FaceInstance& other ){
m_face = other.m_face;
return *this;
}
Face& getFace(){
return *m_face;
}
const Face& getFace() const {
return *m_face;
}
void selectedChanged( const Selectable& selectable ){
if ( selectable.isSelected() ) {
g_SelectedFaceInstances.insert( *this );
}
else
{
g_SelectedFaceInstances.erase( *this );
}
m_selectionChanged( selectable );
}
typedef MemberCaller1<FaceInstance, const Selectable&, &FaceInstance::selectedChanged> SelectedChangedCaller;
bool selectedVertices() const {
return !m_vertexSelection.empty();
}
bool selectedEdges() const {
return !m_edgeSelection.empty();
}
bool isSelected() const {
return m_selectable.isSelected();
}
bool selectedComponents() const {
return selectedVertices() || selectedEdges() || isSelected();
}
bool selectedComponents( SelectionSystem::EComponentMode mode ) const {
switch ( mode )
{
case SelectionSystem::eVertex:
return selectedVertices();
case SelectionSystem::eEdge:
return selectedEdges();
case SelectionSystem::eFace:
return isSelected();
default:
return false;
}
}
void setSelected( SelectionSystem::EComponentMode mode, bool select ){
switch ( mode )
{
case SelectionSystem::eFace:
m_selectable.setSelected( select );
break;
case SelectionSystem::eVertex:
ASSERT_MESSAGE( !select, "select-all not supported" );
m_vertexSelection.clear();
m_selectableVertices.setSelected( false );
break;
case SelectionSystem::eEdge:
ASSERT_MESSAGE( !select, "select-all not supported" );
m_edgeSelection.clear();
m_selectableEdges.setSelected( false );
break;
default:
break;
}
}
template<typename Functor>
void SelectedVertices_foreach( Functor functor ) const {
for ( VertexSelection::const_iterator i = m_vertexSelection.begin(); i != m_vertexSelection.end(); ++i )
{
std::size_t index = Winding_FindAdjacent( getFace().getWinding(), *i );
if ( index != c_brush_maxFaces ) {
functor( getFace().getWinding()[index].vertex );
}
}
}
template<typename Functor>
void SelectedEdges_foreach( Functor functor ) const {
for ( VertexSelection::const_iterator i = m_edgeSelection.begin(); i != m_edgeSelection.end(); ++i )
{
std::size_t index = Winding_FindAdjacent( getFace().getWinding(), *i );
if ( index != c_brush_maxFaces ) {
const Winding& winding = getFace().getWinding();
std::size_t adjacent = Winding_next( winding, index );
functor( vector3_mid( winding[index].vertex, winding[adjacent].vertex ) );
}
}
}
template<typename Functor>
void SelectedFaces_foreach( Functor functor ) const {
if ( isSelected() ) {
functor( centroid() );
}
}
template<typename Functor>
void SelectedComponents_foreach( Functor functor ) const {
SelectedVertices_foreach( functor );
SelectedEdges_foreach( functor );
SelectedFaces_foreach( functor );
}
void iterate_selected( AABB& aabb ) const {
SelectedComponents_foreach( AABBExtendByPoint( aabb ) );
}
void gatherSelectedComponents( const Vector3Callback& callback ) const {
const Winding& winding = getFace().getWinding();
if( isSelected() )
for ( std::size_t i = 0; i != winding.numpoints; ++i )
callback( winding[i].vertex );
for ( VertexSelection::const_iterator i = m_vertexSelection.begin(); i != m_vertexSelection.end(); ++i ){
std::size_t index = Winding_FindAdjacent( winding, *i );
if ( index != c_brush_maxFaces ) {
callback( winding[index].vertex );
}
}
for ( VertexSelection::const_iterator i = m_edgeSelection.begin(); i != m_edgeSelection.end(); ++i ){
std::size_t index = Winding_FindAdjacent( winding, *i );
if ( index != c_brush_maxFaces ) {
std::size_t adjacent = Winding_next( winding, index );
callback( winding[index].vertex );
callback( winding[adjacent].vertex );
}
}
}
class RenderablePointVectorPushBack
{
RenderablePointVector& m_points;
public:
RenderablePointVectorPushBack( RenderablePointVector& points ) : m_points( points ){
}
void operator()( const Vector3& point ) const {
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();
renderer.Highlight( Renderer::ePrimitiveWire );
if ( selectedComponents() ) {
renderer.Highlight( Renderer::EHighlightMode( Renderer::eFace | Renderer::eFaceWire ) );
}
m_face->render( renderer, localToWorld );
renderer.PopState();
}
}
void testSelect( SelectionTest& test, SelectionIntersection& best ) const {
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 addSelectable( Selector& selector ){
Selector_add( selector, m_selectable );
}
void selectReversedPlane( Selector& selector, const SelectedPlanes& selectedPlanes ){
if ( selectedPlanes.contains( plane3_flipped( getFace().plane3() ) ) ) {
Selector_add( selector, m_selectable );
}
}
bool trySelectPlane( const SelectionTest& test ){
const Vector3 projected = vector4_projected( matrix4_transformed_vector4( test.getVolume().GetViewMatrix(), Vector4( getFace().centroid(), 1 ) ) );
const Vector3 closest_point = vector4_projected( matrix4_transformed_vector4( test.getScreen2world(), Vector4( 0, 0, projected[2], 1 ) ) );
for ( Winding::const_iterator i = getFace().getWinding().begin(); i != getFace().getWinding().end(); ++i ){
if ( vector3_dot( getFace().plane3().normal(), closest_point - ( *i ).vertex ) < 0.005 ) /* epsilon to prevent almost perpendicular faces pickup */
return false;
}
return true;
}
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 );
}
}
if ( g_brush_textureVertexlock_enabled && ( selectedVertices() || selectedEdges() ) ) {
m_face->texdef_from_points();
}
}
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();
}
}
#if Update_move_planepts_vertex
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 ){
if( m_face->contributes() ){
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 );
}
///! this actually happens with ON_EPSILON 1.0 / ( 1 << 8 )
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
}
}
#endif
void update_selection_vertex(){
if ( m_vertexSelection.size() == 0 ) {
m_selectableVertices.setSelected( false );
}
else
{
m_selectableVertices.setSelected( true );
#if Update_move_planepts_vertex
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 );
}
}
#endif
}
}
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 ){
if( m_face->contributes() ){
std::size_t numpoints = m_face->getWinding().numpoints;
ASSERT_MESSAGE( index < numpoints, "select_edge: invalid index" );
std::size_t adjacent = Winding_next( m_face->getWinding(), index );
std::size_t opposite = Winding_Opposite( m_face->getWinding(), index );
m_face->m_move_planepts[0] = m_face->getWinding()[index].vertex;
m_face->m_move_planepts[1] = m_face->getWinding()[adjacent].vertex;
m_face->m_move_planepts[2] = m_face->getWinding()[opposite].vertex;
// planepts_quantise( m_face->m_move_planepts, GRID_MIN ); // winding points are very inaccurate
}
}
void update_selection_edge(){
if ( m_edgeSelection.size() == 0 ) {
m_selectableEdges.setSelected( false );
}
else
{
m_selectableEdges.setSelected( true );
if ( m_edgeSelection.size() == 1 ) {
std::size_t index = Winding_FindAdjacent( getFace().getWinding(), *m_edgeSelection.begin() );
if ( index != c_brush_maxFaces ) {
update_move_planepts_edge( index );
}
}
}
}
void select_edge( std::size_t index, bool select ){
if ( select ) {
VertexSelection_insert( m_edgeSelection, getFace().getWinding()[index].adjacent );
}
else
{
VertexSelection_erase( m_edgeSelection, getFace().getWinding()[index].adjacent );
}
SceneChangeNotify();
update_selection_edge();
}
bool selected_edge( std::size_t index ) const {
return VertexSelection_find( m_edgeSelection, getFace().getWinding()[index].adjacent ) != m_edgeSelection.end();
}
const Vector3& centroid() const {
return m_face->centroid();
}
void connectivityChanged(){
// This occurs when a face is added or removed.
// The current vertex and edge selections no longer valid and must be cleared.
m_vertexSelection.clear();
m_selectableVertices.setSelected( false );
m_edgeSelection.clear();
m_selectableEdges.setSelected( false );
}
};
class BrushClipPlane : public OpenGLRenderable
{
Plane3 m_plane;
Winding m_winding;
static Shader* m_state;
public:
static void constructStatic(){
m_state = GlobalShaderCache().capture( "$CLIPPER_OVERLAY" );
}
static void destroyStatic(){
GlobalShaderCache().release( "$CLIPPER_OVERLAY" );
}
void setPlane( const Brush& brush, const Plane3& plane ){
m_plane = plane;
if ( plane3_valid( m_plane ) ) {
brush.windingForClipPlane( m_winding, m_plane );
}
else
{
m_winding.resize( 0 );
}
}
void render( RenderStateFlags state ) const {
if ( ( state & RENDER_FILL ) != 0 ) {
Winding_Draw( m_winding, m_plane.normal(), state );
}
else
{
Winding_DrawWireframe( m_winding );
// also draw a line indicating the direction of the cut
Vector3 lineverts[2];
Winding_Centroid( m_winding, m_plane, lineverts[0] );
lineverts[1] = vector3_added( lineverts[0], vector3_scaled( m_plane.normal(), Brush::m_maxWorldCoord * 4 ) );
gl().glVertexPointer( 3, GL_FLOAT, sizeof( Vector3 ), &lineverts[0] );
gl().glDrawArrays( GL_LINES, 0, GLsizei( 2 ) );
}
}
void render( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
renderer.SetState( m_state, Renderer::eWireframeOnly );
renderer.SetState( m_state, Renderer::eFullMaterials );
renderer.addRenderable( *this, localToWorld );
}
};
inline void Face_addLight( const FaceInstance& face, const Matrix4& localToWorld, const RendererLight& light ){
const Plane3& facePlane = face.getFace().plane3();
const Vector3& origin = light.aabb().origin;
Plane3 tmp( plane3_transformed( Plane3( facePlane.normal(), -facePlane.dist() ), localToWorld ) );
if ( !plane3_test_point( tmp, origin )
|| !plane3_test_point( tmp, vector3_added( origin, light.offset() ) ) ) {
face.m_lights.addLight( light );
}
}
typedef std::vector<FaceInstance> FaceInstances;
typedef std::vector<FaceInstance*> FaceInstances_ptrs;
class EdgeInstance : public Selectable
{
FaceInstances& m_faceInstances;
SelectableEdge* m_edge;
void select_edge( bool select ){
FaceVertexId faceVertex = m_edge->m_faceVertex;
m_faceInstances[faceVertex.getFace()].select_edge( faceVertex.getVertex(), select );
faceVertex = next_edge( m_edge->m_faces, faceVertex );
m_faceInstances[faceVertex.getFace()].select_edge( faceVertex.getVertex(), select );
}
bool selected_edge() const {
FaceVertexId faceVertex = m_edge->m_faceVertex;
if ( !m_faceInstances[faceVertex.getFace()].selected_edge( faceVertex.getVertex() ) ) {
return false;
}
faceVertex = next_edge( m_edge->m_faces, faceVertex );
if ( !m_faceInstances[faceVertex.getFace()].selected_edge( faceVertex.getVertex() ) ) {
return false;
}
return true;
}
public:
EdgeInstance( FaceInstances& faceInstances, SelectableEdge& edge )
: m_faceInstances( faceInstances ), m_edge( &edge ){
}
EdgeInstance& operator=( const EdgeInstance& other ){
m_edge = other.m_edge;
return *this;
}
void setSelected( bool select ){
select_edge( select );
}
bool isSelected() const {
return selected_edge();
}
void testSelect( Selector& selector, SelectionTest& test ){
SelectionIntersection best;
m_edge->testSelect( test, best );
if ( best.valid() ) {
Selector_add( selector, *this, best );
}
}
void gatherComponentsHighlight( std::vector<std::vector<Vector3>>& polygons, SelectionIntersection& intersection, SelectionTest& test ) const {
SelectionIntersection best;
m_edge->testSelect( test, best );
if ( SelectionIntersection_closer( best, intersection ) ) {
intersection = best;
polygons.clear();
polygons.emplace_back( std::initializer_list<Vector3>( { m_edge->getEdge() } ) );
}
}
void bestPlaneIndirect( const SelectionTest& test, Plane3& plane, Vector3& intersection, float& dist, float& dot ) const {
const Winding& winding = m_edge->getFace().getWinding();
FaceVertexId faceVertex = m_edge->m_faceVertex;
Line line( winding[faceVertex.getVertex()].vertex, winding[Winding_next( winding, faceVertex.getVertex() )].vertex );
if( matrix4_clip_line_by_nearplane( test.getVolume().GetViewMatrix(), line ) == 2 ){
const Vector3 intersection_new = line_closest_point( line, g_vector3_identity );
const float dist_new = vector3_length_squared( intersection_new );
const float dot_new = fabs( vector3_dot( vector3_normalised( intersection_new ), vector3_normalised( line.end - line.start ) ) );
if( dist - dist_new > 1e-6f // new dist noticeably smaller
|| ( float_equal_epsilon( dist_new, dist, 1e-6f ) && dot_new < dot ) ){ // or ambiguous case. Resolve it by dot comparison
const Plane3& plane1 = m_faceInstances[faceVertex.getFace()].getFace().plane3();
faceVertex = next_edge( m_edge->m_faces, faceVertex );
const Plane3& plane2 = m_faceInstances[faceVertex.getFace()].getFace().plane3();
auto assign_plane = [&plane, &intersection, intersection_new, &dist, dist_new, &dot, dot_new]( const Plane3& plane_new ){
plane = plane_new;
intersection = intersection_new;
dist = dist_new;
dot = dot_new;
};
if( test.getVolume().fill() ){
if( plane3_distance_to_point( plane1, test.getVolume().getViewer() ) <= 0 )
assign_plane( plane1 );
else if( plane3_distance_to_point( plane2, test.getVolume().getViewer() ) <= 0 )
assign_plane( plane2 );
}
else if( fabs( vector3_length_squared( line.end - line.start ) ) > 1e-3 ){
if( fabs( vector3_dot( plane1.normal(), test.getVolume().getViewDir() ) ) < fabs( vector3_dot( plane2.normal(), test.getVolume().getViewDir() ) ) )
assign_plane( plane1 );
else
assign_plane( plane2 );
}
}
}
}
};
class VertexInstance : public Selectable
{
FaceInstances& m_faceInstances;
SelectableVertex* m_vertex;
void select_vertex( bool select ){
FaceVertexId faceVertex = m_vertex->m_faceVertex;
do
{
m_faceInstances[faceVertex.getFace()].select_vertex( faceVertex.getVertex(), select );
faceVertex = next_vertex( m_vertex->m_faces, faceVertex );
}
while ( faceVertex.getFace() != m_vertex->m_faceVertex.getFace() );
}
bool selected_vertex() const {
FaceVertexId faceVertex = m_vertex->m_faceVertex;
do
{
if ( !m_faceInstances[faceVertex.getFace()].selected_vertex( faceVertex.getVertex() ) ) {
return false;
}
faceVertex = next_vertex( m_vertex->m_faces, faceVertex );
}
while ( faceVertex.getFace() != m_vertex->m_faceVertex.getFace() );
return true;
}
public:
VertexInstance( FaceInstances& faceInstances, SelectableVertex& vertex )
: m_faceInstances( faceInstances ), m_vertex( &vertex ){
}
VertexInstance& operator=( const VertexInstance& other ){
m_vertex = other.m_vertex;
return *this;
}
void setSelected( bool select ){
select_vertex( select );
}
bool isSelected() const {
return selected_vertex();
}
void testSelect( Selector& selector, SelectionTest& test ){
SelectionIntersection best;
m_vertex->testSelect( test, best );
if ( best.valid() ) {
Selector_add( selector, *this, best );
}
}
void gatherComponentsHighlight( std::vector<std::vector<Vector3>>& polygons, SelectionIntersection& intersection, SelectionTest& test ) const {
SelectionIntersection best;
m_vertex->testSelect( test, best );
if ( SelectionIntersection_closer( best, intersection ) ) {
intersection = best;
polygons.clear();
polygons.emplace_back( std::initializer_list<Vector3>( { m_vertex->getVertex() } ) );
}
}
void selectVerticesOfFace( const FaceInstance& faceinstance ){
FaceVertexId faceVertex = m_vertex->m_faceVertex;
do
{
if( &faceinstance == &m_faceInstances[faceVertex.getFace()] ){
setSelected( true );
return;
}
faceVertex = next_vertex( m_vertex->m_faces, faceVertex );
}
while ( faceVertex.getFace() != m_vertex->m_faceVertex.getFace() );
}
void gather( Brush::VertexModeVertices& vertexModeVertices ) const {
vertexModeVertices.emplace_back( m_vertex->getVertex(), isSelected() );
FaceVertexId faceVertex = m_vertex->m_faceVertex;
do
{
vertexModeVertices.back().m_faces.push_back( &m_faceInstances[faceVertex.getFace()].getFace() );
faceVertex = next_vertex( m_vertex->m_faces, faceVertex );
}
while ( faceVertex.getFace() != m_vertex->m_faceVertex.getFace() );
}
bool vertex_select( const DoubleVector3& vertex ){
if( vector3_length_squared( vertex - m_vertex->getVertex() ) < ( 0.1 * 0.1 ) ){
setSelected( true );
return true;
}
return false;
}
};
class BrushInstanceVisitor
{
public:
virtual void visit( FaceInstance& face ) const = 0;
};
class BrushInstance :
public BrushObserver,
public scene::Instance,
public Selectable,
public Renderable,
public SelectionTestable,
public ComponentSelectionTestable,
public ComponentEditable,
public ComponentSnappable,
public PlaneSelectable,
public LightCullable
{
class TypeCasts
{
InstanceTypeCastTable m_casts;
public:
TypeCasts(){
InstanceStaticCast<BrushInstance, Selectable>::install( m_casts );
InstanceContainedCast<BrushInstance, Bounded>::install( m_casts );
InstanceContainedCast<BrushInstance, Cullable>::install( m_casts );
InstanceStaticCast<BrushInstance, Renderable>::install( m_casts );
InstanceStaticCast<BrushInstance, SelectionTestable>::install( m_casts );
InstanceStaticCast<BrushInstance, ComponentSelectionTestable>::install( m_casts );
InstanceStaticCast<BrushInstance, ComponentEditable>::install( m_casts );
InstanceStaticCast<BrushInstance, ComponentSnappable>::install( m_casts );
InstanceStaticCast<BrushInstance, PlaneSelectable>::install( m_casts );
InstanceIdentityCast<BrushInstance>::install( m_casts );
InstanceContainedCast<BrushInstance, Transformable>::install( m_casts );
}
InstanceTypeCastTable& get(){
return m_casts;
}
};
Brush& m_brush;
FaceInstances m_faceInstances;
typedef std::vector<EdgeInstance> EdgeInstances;
EdgeInstances m_edgeInstances;
typedef std::vector<VertexInstance> VertexInstances;
VertexInstances m_vertexInstances;
ObservedSelectable m_selectable;
mutable RenderableWireframe m_render_wireframe;
mutable RenderablePointVector m_render_selected;
mutable AABB m_aabb_component;
mutable Array<PointVertex> m_faceCentroidPointsCulled;
RenderablePointArray<PointVertex> 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;
BrushTransformModifier m_transform;
public:
static Counter* m_counter;
typedef LazyStatic<TypeCasts> StaticTypeCasts;
void lightsChanged(){
m_lightList->lightsChanged();
}
typedef MemberCaller<BrushInstance, &BrushInstance::lightsChanged> LightsChangedCaller;
STRING_CONSTANT( Name, "BrushInstance" );
BrushInstance( const BrushInstance& other ) = delete; // NOT COPYABLE
BrushInstance& operator=( const BrushInstance& other ) = delete; // NOT ASSIGNABLE
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<Bounded>){
return m_brush;
}
Cullable& get( NullType<Cullable>){
return m_brush;
}
Transformable& get( NullType<Transformable>){
return m_transform;
}
void selectedChanged( const Selectable& selectable ){
GlobalSelectionSystem().getObserver ( SelectionSystem::ePrimitive )( selectable );
GlobalSelectionSystem().onSelectedChanged( *this, selectable );
Instance::selectedChanged();
}
typedef MemberCaller1<BrushInstance, const Selectable&, &BrushInstance::selectedChanged> SelectedChangedCaller;
void selectedChangedComponent( const Selectable& selectable ){
GlobalSelectionSystem().getObserver ( SelectionSystem::eComponent )( selectable );
GlobalSelectionSystem().onComponentSelection( *this, selectable );
}
typedef MemberCaller1<BrushInstance, const Selectable&, &BrushInstance::selectedChangedComponent> 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 vertex_select(){
bool src_selected = false;
bool dst_selected = false;
for( const auto& v : m_brush.m_vertexModeVertices )
if( v.m_selected ){
src_selected = true;
for( auto& i : m_vertexInstances )
dst_selected |= i.vertex_select( v.m_vertexTransformed );
}
if( src_selected && !dst_selected && !m_vertexInstances.empty() )
m_vertexInstances[0].setSelected( true ); //select at least something to prevent transform interruption after removing all selected vertices during vertexModeTransform
}
void vertex_snap( const float snap, bool all ){
m_brush.vertexModeInit();
m_brush.m_vertexModeVertices.reserve( m_vertexInstances.size() );
for ( const auto& i : m_vertexInstances ){
i.gather( m_brush.m_vertexModeVertices );
}
m_brush.vertexModeSnap( snap, all );
m_brush.evaluateBRep();
m_brush.vertexModeFree();
m_brush.freezeTransform();
}
void vertex_snap( const float snap ){
vertex_snap( snap, true );
}
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 );
if ( !select && parent() ){
Selectable* sel_parent = Instance_getSelectable( *parent() );
if ( sel_parent && sel_parent->isSelected() )
sel_parent->setSelected( false );
}
}
void update_selected() const {
m_render_selected.clear();
for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
if ( ( *i ).getFace().contributes() ) {
( *i ).iterate_selected( m_render_selected );
}
}
}
void evaluateViewDependent( const VolumeTest& volume, const Matrix4& localToWorld ) const {
if ( m_viewChanged ) {
m_viewChanged = false;
bool faces_visible[c_brush_maxFaces];
{
bool* j = faces_visible;
for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i, ++j )
{
*j = ( *i ).intersectVolume( volume, localToWorld );
}
}
m_brush.update_wireframe( m_render_wireframe, faces_visible );
m_brush.update_faces_wireframe( m_faceCentroidPointsCulled, faces_visible );
}
}
void renderComponentsSelected( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
m_brush.evaluateBRep();
update_selected();
if ( !m_render_selected.empty() ) {
renderer.Highlight( Renderer::ePrimitive, false );
renderer.SetState( m_state_selpoint, Renderer::eWireframeOnly );
renderer.SetState( m_state_selpoint, Renderer::eFullMaterials );
renderer.addRenderable( m_render_selected, localToWorld );
}
}
void renderComponents( Renderer& renderer, const VolumeTest& volume ) const {
m_brush.evaluateBRep();
const Matrix4& localToWorld = Instance::localToWorld();
renderer.SetState( m_brush.m_state_point, Renderer::eWireframeOnly );
renderer.SetState( m_brush.m_state_point, Renderer::eFullMaterials );
if ( volume.fill() && GlobalSelectionSystem().ComponentMode() == SelectionSystem::eFace ) {
evaluateViewDependent( volume, localToWorld );
renderer.addRenderable( m_render_faces_wireframe, localToWorld );
}
else
{
m_brush.renderComponents( GlobalSelectionSystem().ComponentMode(), renderer, volume, localToWorld );
}
}
void renderClipPlane( Renderer& renderer, const VolumeTest& volume ) const {
if ( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eClip && isSelected() ) {
m_clipPlane.render( renderer, volume, localToWorld() );
}
}
void renderCommon( Renderer& renderer, const VolumeTest& volume ) const {
bool componentMode = GlobalSelectionSystem().Mode() == SelectionSystem::eComponent;
if ( componentMode && isSelected() ) {
renderComponents( renderer, volume );
}
if ( parentSelected() ) {
if ( !componentMode ) {
renderer.Highlight( Renderer::eFace );
}
renderer.Highlight( Renderer::ePrimitive );
}
}
void renderSolid( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
//renderCommon(renderer, volume);
m_lightList->evaluateLights();
for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
renderer.setLights( ( *i ).m_lights );
( *i ).render( renderer, volume, localToWorld );
}
renderComponentsSelected( renderer, volume, localToWorld );
}
void renderWireframe( Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld ) const {
//renderCommon(renderer, volume);
evaluateViewDependent( volume, localToWorld );
if ( m_render_wireframe.m_size != 0 ) {
renderer.addRenderable( m_render_wireframe, localToWorld );
}
renderComponentsSelected( renderer, volume, localToWorld );
}
void renderSolid( Renderer& renderer, const VolumeTest& volume ) const {
m_brush.evaluateBRep();
renderClipPlane( renderer, volume );
renderSolid( renderer, volume, localToWorld() );
}
void renderWireframe( Renderer& renderer, const VolumeTest& volume ) const {
m_brush.evaluateBRep();
renderClipPlane( renderer, volume );
renderWireframe( renderer, volume, localToWorld() );
}
void viewChanged() const {
m_viewChanged = true;
}
void testSelect( Selector& selector, SelectionTest& test ){
test.BeginMesh( localToWorld() );
SelectionIntersection best;
for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
( *i ).testSelect( test, best );
}
if ( best.valid() ) {
selector.addIntersection( best );
}
}
bool isSelectedComponents() const {
for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
if ( ( *i ).selectedComponents() ) {
return true;
}
}
return false;
}
void setSelectedComponents( bool select, SelectionSystem::EComponentMode mode ){
for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
( *i ).setSelected( mode, select );
}
}
void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ){
test.BeginMesh( localToWorld() );
switch ( mode )
{
case SelectionSystem::eVertex:
{
for ( VertexInstances::iterator i = m_vertexInstances.begin(); i != m_vertexInstances.end(); ++i )
{
( *i ).testSelect( selector, test );
}
}
break;
case SelectionSystem::eEdge:
{
for ( EdgeInstances::iterator i = m_edgeInstances.begin(); i != m_edgeInstances.end(); ++i )
{
( *i ).testSelect( selector, test );
}
}
break;
case SelectionSystem::eFace:
{
if ( test.getVolume().fill() ) {
for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
( *i ).testSelect( selector, test );
}
}
else
{
for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
( *i ).testSelect_centroid( selector, test );
}
}
}
break;
default:
break;
}
}
void gatherComponentsHighlight( std::vector<std::vector<Vector3>>& polygons, SelectionIntersection& intersection, SelectionTest& test, SelectionSystem::EComponentMode mode ) const {
test.BeginMesh( localToWorld() );
switch ( mode )
{
case SelectionSystem::eVertex:
{
for ( const VertexInstance& i : m_vertexInstances )
{
i.gatherComponentsHighlight( polygons, intersection, test );
}
}
break;
case SelectionSystem::eEdge:
{
for ( const EdgeInstance& i : m_edgeInstances )
{
i.gatherComponentsHighlight( polygons, intersection, test );
}
}
break;
case SelectionSystem::eFace:
{
if ( test.getVolume().fill() ) {
const Plane3* plane = nullptr;
bool newint = false;
for ( const FaceInstance& i : m_faceInstances )
{
SelectionIntersection best;
i.testSelect( test, best );
if( best.valid() && intersection.equalEpsilon( best, 0.25f, 2e-6f ) ){
plane = &i.getFace().plane3();
}
else if ( SelectionIntersection_closer( best, intersection ) ) {
intersection = best;
newint = true;
plane = &i.getFace().plane3();
}
}
if( plane ){
if( newint || ( !polygons.empty() && polygons.back().size() >= 3 && !plane3_equal( *plane, plane3_for_points( polygons.back().data() ) ) ) ){
polygons.clear();
}
gatherPolygonsByPlane( *plane, polygons, false );
}
}
else
{
for ( const FaceInstance& i : m_faceInstances )
{
SelectionIntersection best;
i.getFace().testSelect_centroid( test, best );
if ( SelectionIntersection_closer( best, intersection ) ) {
intersection = best;
polygons.clear();
polygons.emplace_back( std::initializer_list<Vector3>( { i.getFace().centroid() } ) );
}
}
}
}
break;
default:
break;
}
}
void invertComponentSelection( SelectionSystem::EComponentMode mode ){
switch ( mode )
{
case SelectionSystem::eVertex:
{
for ( VertexInstances::iterator i = m_vertexInstances.begin(); i != m_vertexInstances.end(); ++i )
{
( *i ).setSelected( !( *i ).isSelected() );
}
}
break;
case SelectionSystem::eEdge:
{
for ( EdgeInstances::iterator i = m_edgeInstances.begin(); i != m_edgeInstances.end(); ++i )
{
( *i ).setSelected( !( *i ).isSelected() );
}
}
break;
case SelectionSystem::eFace:
{
for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
if( !( *i ).getFace().isFiltered() )
( *i ).setSelected( mode, !( *i ).isSelected() );
}
}
break;
default:
break;
}
}
void selectPlanes( SelectionTest& test, FaceInstances_ptrs& bestInstances ){
test.BeginMesh( localToWorld() );
const Vector3 viewdir( test.getVolume().getViewDir() );
double bestDot = 1;
for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
if( !( *i ).trySelectPlane( test ) ){
continue;
}
const double dot = fabs( vector3_dot( ( *i ).getFace().plane3().normal(), viewdir ) );
const double diff = bestDot - dot;
if( diff > 0.03 ){
bestDot = dot;
bestInstances.clear();
bestInstances.push_back( &( *i ) );
}
else if( fabs( diff ) <= 0.03 ){
bestInstances.push_back( &( *i ) );
}
}
}
void selectPlanes( Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback ){
FaceInstances_ptrs bestInstances;
selectPlanes( test, bestInstances );
for ( FaceInstances_ptrs::iterator i = bestInstances.begin(); i != bestInstances.end(); ++i ){
( *i )->addSelectable( selector );
selectedPlaneCallback( ( *i )->getFace().plane3() );
if( test.getVolume().fill() )
return; // select only plane in camera
}
}
void selectReversedPlanes( Selector& selector, const SelectedPlanes& selectedPlanes ){
for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
( *i ).selectReversedPlane( selector, selectedPlanes );
}
}
void bestPlaneDirect( SelectionTest& test, Plane3& plane, SelectionIntersection& intersection ) const {
test.BeginMesh( localToWorld() );
for ( const FaceInstance& fi : m_faceInstances )
{
SelectionIntersection intersection_new;
fi.testSelect( test, intersection_new );
if( SelectionIntersection_closer( intersection_new, intersection ) ){
intersection = intersection_new;
plane = fi.getFace().plane3();
}
}
}
void bestPlaneIndirect( SelectionTest& test, Plane3& plane, Vector3& intersection, float& dist ) const {
test.BeginMesh( localToWorld() );
float dot = 1;
for ( const EdgeInstance& ei : m_edgeInstances )
{
ei.bestPlaneIndirect( test, plane, intersection, dist, dot );
}
}
void selectByPlane( const Plane3& plane ){
for ( FaceInstance& fi : m_faceInstances )
if( plane3_equal( plane, fi.getFace().plane3() ) || plane3_equal( plane, plane3_flipped( fi.getFace().plane3() ) ) )
fi.setSelected( SelectionSystem::eFace, true );
}
void gatherPolygonsByPlane( const Plane3& plane, std::vector<std::vector<Vector3>>& polygons ) const {
gatherPolygonsByPlane( plane, polygons, true );
}
void gatherPolygonsByPlane( const Plane3& plane, std::vector<std::vector<Vector3>>& polygons, const bool reversed_plane_also ) const {
for ( const FaceInstance& fi : m_faceInstances )
if( plane3_equal( plane, fi.getFace().plane3() ) || ( reversed_plane_also && plane3_equal( plane, plane3_flipped( fi.getFace().plane3() ) ) ) )
if( fi.getFace().contributes() ){
const Winding& winding = fi.getFace().getWinding();
polygons.emplace_back( winding.numpoints );
for( std::size_t i = 0; i < winding.numpoints; ++i ){
polygons.back()[i] = winding[i].vertex;
}
}
}
void selectVerticesOnPlane( const Plane3& plane ){
for ( FaceInstance& fi : m_faceInstances )
if( plane3_equal( plane, fi.getFace().plane3() ) || plane3_equal( plane, plane3_flipped( fi.getFace().plane3() ) ) )
for ( VertexInstance& vi : m_vertexInstances )
vi.selectVerticesOfFace( fi );
}
void transformComponents( const Matrix4& matrix );
const AABB& getSelectedComponentsBounds() const {
m_aabb_component = AABB();
for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
( *i ).iterate_selected( m_aabb_component );
}
return m_aabb_component;
}
void gatherSelectedComponents( const Vector3Callback& callback ) const {
for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
( *i ).gatherSelectedComponents( callback );
}
}
void insert_vertices( const Brush::VertexModeVertices& vertexModeVertices ){
if( !m_vertexInstances.empty() ){
m_brush.vertexModeInit();
m_brush.m_vertexModeVertices.reserve( m_vertexInstances.size() + 2 );
for ( const auto& i : m_vertexInstances ){
i.gather( m_brush.m_vertexModeVertices );
}
for ( const auto& i : vertexModeVertices ){
m_brush.m_vertexModeVertices.push_back( i );
if( i.m_faces.empty() )
m_brush.m_vertexModeVertices.back().m_faces.push_back( m_brush.m_vertexModeVertices[0].m_faces[0] );
}
m_transform.m_transformFrozen = false;
m_transform.setType( TRANSFORM_COMPONENT );
m_brush.transformChanged();
m_brush.evaluateBRep();
}
}
void remove_vertices(){
if( !m_vertexInstances.empty() ){
m_brush.vertexModeInit();
Brush::VertexModeVertices v;
v.reserve( m_vertexInstances.size() );
for ( const auto& i : m_vertexInstances ){
i.gather( v );
if( v.back().m_selected )
v.pop_back();
}
std::vector<bool> ok( v.size(), true );
gatherSelectedComponents( [&]( const DoubleVector3 & value ) {
for( std::size_t i = 0; i < v.size(); ++i )
if( vector3_length_squared( v[i].m_vertex - value ) < 0.05 * 0.05 )
ok[i] = false;
} );
m_brush.m_vertexModeVertices.reserve( v.size() );
for( std::size_t i = 0; i < v.size(); ++i ){
if( ok[i] )
m_brush.m_vertexModeVertices.push_back( v[i] );
}
m_brush.vertexModeBuildHull();
m_brush.evaluateBRep();
m_brush.vertexModeFree();
m_brush.freezeTransform();
}
}
void snapComponents( float snap ){
for ( const auto& fi : m_faceInstances ){
if( fi.selectedComponents( SelectionSystem::eVertex ) ){
vertex_snap( snap, false );
return;
}
}
for ( auto& fi : m_faceInstances )
fi.snapComponents( snap );
}
void evaluateTransform(){
if( m_transform.m_transformFrozen && m_transform.isIdentity() )
return;
if( m_transform.m_transformFrozen && !m_transform.isIdentity() ){ /* new transform */
m_transform.m_transformFrozen = false;
for( auto& i : m_faceInstances )
i.getFace().cacheCentroid();
if( m_transform.getType() == TRANSFORM_COMPONENT ){
for ( const auto& i : m_faceInstances ){
if( i.selectedComponents( SelectionSystem::eVertex ) ){
m_brush.vertexModeInit();
m_brush.m_vertexModeVertices.reserve( m_vertexInstances.size() );
for ( const auto& i : m_vertexInstances ){
i.gather( m_brush.m_vertexModeVertices );
}
break;
}
}
}
}
const Matrix4 matrix( m_transform.calculateTransform() );
if ( m_transform.getType() == TRANSFORM_PRIMITIVE ) {
m_brush.transform( matrix );
}
else
{
if( m_brush.m_vertexModeOn ){
m_brush.vertexModeTransform( matrix );
}
else{
const bool tmp = g_brush_texturelock_enabled;
/* do not want texture projection transformation while resizing brush */
if( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eDrag && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive )
g_brush_texturelock_enabled = false;
transformComponents( matrix );
g_brush_texturelock_enabled = tmp;
}
}
}
void applyTransform(){
if( !m_transform.isIdentity() ){
if( m_transform.m_transformFrozen ){ //not yet unfrozen by evaluateTransform(), so evaluate
// m_brush.revertTransform();
// evaluateTransform();
m_brush.evaluateBRep();
}
m_brush.freezeTransform();
m_transform.setIdentity();
}
m_brush.vertexModeFree();
}
typedef MemberCaller<BrushInstance, &BrushInstance::applyTransform> ApplyTransformCaller;
void setClipPlane( const Plane3& plane ){
m_clipPlane.setPlane( m_brush, plane );
}
bool testLight( const RendererLight& light ) const {
return light.testAABB( worldAABB() );
}
void insertLight( const RendererLight& light ){
const Matrix4& localToWorld = Instance::localToWorld();
for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
Face_addLight( *i, localToWorld, light );
}
}
void clearLights(){
for ( FaceInstances::const_iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
{
( *i ).m_lights.clear();
}
}
};
inline BrushInstance* Instance_getBrush( scene::Instance& instance ){
return InstanceTypeCast<BrushInstance>::cast( instance );
}
template<typename Functor>
class BrushSelectedVisitor : public SelectionSystem::Visitor
{
const Functor& m_functor;
public:
BrushSelectedVisitor( const Functor& functor ) : m_functor( functor ){
}
void visit( scene::Instance& instance ) const {
BrushInstance* brush = Instance_getBrush( instance );
if ( brush != 0 ) {
m_functor( *brush );
}
}
};
template<typename Functor>
inline const Functor& Scene_forEachSelectedBrush( const Functor& functor ){
GlobalSelectionSystem().foreachSelected( BrushSelectedVisitor<Functor>( functor ) );
return functor;
}
template<typename 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 );
}
}
};
template<typename Functor>
inline const Functor& Scene_forEachVisibleSelectedBrush( const Functor& functor ){
GlobalSelectionSystem().foreachSelected( BrushVisibleSelectedVisitor<Functor>( functor ) );
return functor;
}
class BrushForEachFace
{
const BrushInstanceVisitor& m_visitor;
public:
BrushForEachFace( const BrushInstanceVisitor& visitor ) : m_visitor( visitor ){
}
void operator()( BrushInstance& brush ) const {
brush.forEachFaceInstance( m_visitor );
}
};
template<class Functor>
class FaceInstanceVisitFace : public BrushInstanceVisitor
{
const Functor& functor;
public:
FaceInstanceVisitFace( const Functor& functor )
: functor( functor ){
}
void visit( FaceInstance& face ) const {
functor( face.getFace() );
}
};
template<typename Functor>
inline const Functor& Brush_forEachFace( BrushInstance& brush, const Functor& functor ){
brush.forEachFaceInstance( FaceInstanceVisitFace<Functor>( functor ) );
return functor;
}
template<class Functor>
class FaceVisitAll : public BrushVisitor
{
const Functor& functor;
public:
FaceVisitAll( const Functor& functor )
: functor( functor ){
}
void visit( Face& face ) const {
functor( face );
}
};
template<typename Functor>
inline const Functor& Brush_forEachFace( const Brush& brush, const Functor& functor ){
brush.forEachFace( FaceVisitAll<Functor>( functor ) );
return functor;
}
template<typename Functor>
inline const Functor& Brush_forEachFace( Brush& brush, const Functor& functor ){
brush.forEachFace( FaceVisitAll<Functor>( functor ) );
return functor;
}
template<class Functor>
class FaceInstanceVisitAll : public BrushInstanceVisitor
{
const Functor& functor;
public:
FaceInstanceVisitAll( const Functor& functor )
: functor( functor ){
}
void visit( FaceInstance& face ) const {
functor( face );
}
};
template<typename Functor>
inline const Functor& Brush_ForEachFaceInstance( BrushInstance& brush, const Functor& functor ){
brush.forEachFaceInstance( FaceInstanceVisitAll<Functor>( functor ) );
return functor;
}
template<typename Functor>
inline const Functor& Scene_forEachBrush( scene::Graph& graph, const Functor& functor ){
graph.traverse( InstanceWalker< InstanceApply<BrushInstance, Functor> >( functor ) );
return functor;
}
template<typename Type, typename Functor>
class InstanceIfVisible : public Functor
{
public:
InstanceIfVisible( const Functor& functor ) : Functor( functor ){
}
void operator()( scene::Instance& instance ){
if ( instance.path().top().get().visible() ) {
Functor::operator()( instance );
}
}
};
template<typename Functor>
class BrushVisibleWalker : public scene::Graph::Walker
{
const Functor& m_functor;
public:
BrushVisibleWalker( const Functor& functor ) : m_functor( functor ){
}
bool pre( const scene::Path& path, scene::Instance& instance ) const {
if ( path.top().get().visible() ) {
BrushInstance* brush = Instance_getBrush( instance );
if ( brush != 0 ) {
m_functor( *brush );
}
return true;
}
return false;
}
};
template<typename Functor>
inline const Functor& Scene_forEachVisibleBrush( scene::Graph& graph, const Functor& functor ){
graph.traverse( BrushVisibleWalker<Functor>( functor ) );
return functor;
}
template<typename Functor>
inline const Functor& Scene_ForEachBrush_ForEachFace( scene::Graph& graph, const Functor& functor ){
Scene_forEachBrush( graph, BrushForEachFace( FaceInstanceVisitFace<Functor>( functor ) ) );
return functor;
}
// d1223m
template<typename Functor>
inline const Functor& Scene_ForEachBrush_ForEachFaceInstance( scene::Graph& graph, const Functor& functor ){
Scene_forEachBrush( graph, BrushForEachFace( FaceInstanceVisitAll<Functor>( functor ) ) );
return functor;
}
template<typename Functor>
inline const Functor& Scene_ForEachSelectedBrush_ForEachFace( scene::Graph& graph, const Functor& functor ){
Scene_forEachSelectedBrush( BrushForEachFace( FaceInstanceVisitFace<Functor>( functor ) ) );
return functor;
}
template<typename Functor>
inline const Functor& Scene_ForEachSelectedBrush_ForEachFaceInstance( scene::Graph& graph, const Functor& functor ){
Scene_forEachSelectedBrush( BrushForEachFace( FaceInstanceVisitAll<Functor>( functor ) ) );
return functor;
}
template<typename Functor>
class FaceVisitorWrapper
{
const Functor& functor;
public:
FaceVisitorWrapper( const Functor& functor ) : functor( functor ){
}
void operator()( FaceInstance& faceInstance ) const {
functor( faceInstance.getFace() );
}
};
template<typename Functor>
inline const Functor& Scene_ForEachSelectedBrushFace( scene::Graph& graph, const Functor& functor ){
g_SelectedFaceInstances.foreach( FaceVisitorWrapper<Functor>( functor ) );
return functor;
}