binds... * shift + u: new uniform CSG wrap merge algorithm, merging selected brushes and/or components
This commit is contained in:
parent
614885f1fb
commit
0f7af6aaa4
|
|
@ -49,12 +49,15 @@ virtual void setSelectedComponents( bool select, SelectionSystem::EComponentMode
|
|||
virtual void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ) = 0;
|
||||
};
|
||||
|
||||
typedef Callback1<const Vector3&> Vector3Callback;
|
||||
|
||||
class ComponentEditable
|
||||
{
|
||||
public:
|
||||
STRING_CONSTANT( Name, "ComponentEditable" );
|
||||
|
||||
virtual const AABB& getSelectedComponentsBounds() const = 0;
|
||||
virtual void gatherSelectedComponents( const Vector3Callback& callback ) const = 0;
|
||||
};
|
||||
|
||||
class ComponentSnappable
|
||||
|
|
@ -932,8 +935,6 @@ virtual void increment() = 0;
|
|||
virtual void decrement() = 0;
|
||||
};
|
||||
|
||||
#include "generic/callback.h"
|
||||
|
||||
class SimpleCounter : public Counter
|
||||
{
|
||||
Callback m_countChanged;
|
||||
|
|
|
|||
|
|
@ -560,6 +560,8 @@ const AABB& getSelectedComponentsBounds() const {
|
|||
m_curveCatmullRom.forEachSelected( ControlPointAddBounds( m_aabb_component ) );
|
||||
return m_aabb_component;
|
||||
}
|
||||
void gatherSelectedComponents( const Vector3Callback& callback ) const {
|
||||
}
|
||||
|
||||
void snapComponents( float snap ){
|
||||
if ( m_curveNURBS.isSelected() ) {
|
||||
|
|
|
|||
|
|
@ -2670,6 +2670,27 @@ 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;
|
||||
|
|
@ -3694,6 +3715,12 @@ const AABB& getSelectedComponentsBounds() const {
|
|||
|
||||
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 snapComponents( float snap ){
|
||||
for ( FaceInstances::iterator i = m_faceInstances.begin(); i != m_faceInstances.end(); ++i )
|
||||
|
|
|
|||
|
|
@ -1566,6 +1566,7 @@ void Brush_constructMenu( GtkMenu* menu ){
|
|||
}
|
||||
create_menu_item_with_mnemonic( menu_in_menu, "CSG _Subtract", "CSGSubtract" );
|
||||
create_menu_item_with_mnemonic( menu_in_menu, "CSG _Merge", "CSGMerge" );
|
||||
create_menu_item_with_mnemonic( menu_in_menu, "CSG _Wrap Merge", "CSGWrapMerge" );
|
||||
create_menu_item_with_mnemonic( menu_in_menu, "Make _Room", "CSGroom" );
|
||||
create_menu_item_with_mnemonic( menu_in_menu, "CSG _Tool", "CSGTool" );
|
||||
}
|
||||
|
|
|
|||
228
radiant/csg.cpp
228
radiant/csg.cpp
|
|
@ -911,6 +911,24 @@ bool Brush_merge( Brush& brush, const brush_vector_t& in, bool onlyshape ){
|
|||
return true;
|
||||
}
|
||||
|
||||
scene::Path ultimate_group_path(){
|
||||
if ( GlobalSelectionSystem().countSelected() != 0 ) {
|
||||
scene::Path path = GlobalSelectionSystem().ultimateSelected().path();
|
||||
scene::Node& node = path.top();
|
||||
if( Node_isPrimitive( node ) ){
|
||||
path.pop();
|
||||
return path;
|
||||
}
|
||||
if ( Node_isEntity( node ) && node_is_group( node ) ){
|
||||
return path;
|
||||
}
|
||||
}
|
||||
scene::Path path;
|
||||
path.push( makeReference( GlobalSceneGraph().root() ) );
|
||||
path.push( makeReference( Map_FindOrInsertWorldspawn( g_map ) ) );
|
||||
return path;
|
||||
}
|
||||
|
||||
void CSG_Merge( void ){
|
||||
brush_vector_t selected_brushes;
|
||||
|
||||
|
|
@ -931,8 +949,6 @@ void CSG_Merge( void ){
|
|||
|
||||
UndoableCommand undo( "brushMerge" );
|
||||
|
||||
scene::Path merged_path = GlobalSelectionSystem().ultimateSelected().path();
|
||||
|
||||
NodeSmartReference node( ( new BrushNode() )->node() );
|
||||
Brush* brush = Node_getBrush( node );
|
||||
// if the new brush would not be convex
|
||||
|
|
@ -943,14 +959,15 @@ void CSG_Merge( void ){
|
|||
{
|
||||
ASSERT_MESSAGE( !brush->empty(), "brush left with no faces after merge" );
|
||||
|
||||
scene::Path path = ultimate_group_path();
|
||||
|
||||
// free the original brushes
|
||||
GlobalSceneGraph().traverse( BrushDeleteSelected( merged_path.parent().get_pointer() ) );
|
||||
GlobalSceneGraph().traverse( BrushDeleteSelected( path.top().get_pointer() ) );
|
||||
|
||||
merged_path.pop();
|
||||
Node_getTraversable( merged_path.top() )->insert( node );
|
||||
merged_path.push( makeReference( node.get() ) );
|
||||
Node_getTraversable( path.top() )->insert( node );
|
||||
path.push( makeReference( node.get() ) );
|
||||
|
||||
selectPath( merged_path, true );
|
||||
selectPath( path, true );
|
||||
|
||||
globalOutputStream() << "CSG Merge: Succeeded.\n";
|
||||
SceneChangeNotify();
|
||||
|
|
@ -959,6 +976,203 @@ void CSG_Merge( void ){
|
|||
|
||||
|
||||
|
||||
class MergeVertices
|
||||
{
|
||||
typedef std::vector<Vector3> Vertices;
|
||||
Vertices m_vertices;
|
||||
public:
|
||||
typedef Vertices::const_iterator const_iterator;
|
||||
void insert( const Vector3& vertex ){
|
||||
for( const_iterator i = begin(); i != end(); ++i )
|
||||
if( Edge_isDegenerate( vertex, *i ) )
|
||||
return;
|
||||
m_vertices.push_back( vertex );
|
||||
}
|
||||
const_iterator begin() const {
|
||||
return m_vertices.begin();
|
||||
}
|
||||
const_iterator end() const {
|
||||
return m_vertices.end();
|
||||
}
|
||||
std::size_t size() const {
|
||||
return m_vertices.size();
|
||||
}
|
||||
brushsplit_t classify_plane( const Plane3& plane ) const {
|
||||
brushsplit_t split;
|
||||
for( const_iterator i = begin(); i != end(); ++i ){
|
||||
WindingVertex_ClassifyPlane( ( *i ), plane, split );
|
||||
if( ( split.counts[ePlaneFront] != 0 ) && ( split.counts[ePlaneBack] != 0 ) )
|
||||
break;
|
||||
}
|
||||
return split;
|
||||
}
|
||||
};
|
||||
|
||||
class Scene_gatherSelectedComponents : public scene::Graph::Walker
|
||||
{
|
||||
MergeVertices& m_mergeVertices;
|
||||
void call( const Vector3& value ) const {
|
||||
m_mergeVertices.insert( value );
|
||||
}
|
||||
typedef ConstMemberCaller1<Scene_gatherSelectedComponents, const Vector3&, &Scene_gatherSelectedComponents::call> Caller;
|
||||
const Vector3Callback m_callback;
|
||||
public:
|
||||
Scene_gatherSelectedComponents( MergeVertices& mergeVertices )
|
||||
: m_mergeVertices( mergeVertices ), m_callback( Vector3Callback( Scene_gatherSelectedComponents::Caller( *this ) ) ){
|
||||
}
|
||||
bool pre( const scene::Path& path, scene::Instance& instance ) const {
|
||||
if ( path.top().get().visible() ) {
|
||||
ComponentEditable* componentEditable = Instance_getComponentEditable( instance );
|
||||
if ( componentEditable ) {
|
||||
componentEditable->gatherSelectedComponents( m_callback );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class MergePlane
|
||||
{
|
||||
public:
|
||||
Plane3 m_plane;
|
||||
const Face* m_face;
|
||||
Vector3 m_v1;
|
||||
Vector3 m_v2;
|
||||
Vector3 m_v3;
|
||||
MergePlane( const Plane3& plane, const Face* face ) : m_plane( plane ), m_face( face ){
|
||||
}
|
||||
MergePlane( const Plane3& plane, const Vector3& v1, const Vector3& v2, const Vector3& v3 ) : m_plane( plane ), m_face( 0 ), m_v1( v1 ), m_v2( v2 ), m_v3( v3 ){
|
||||
}
|
||||
};
|
||||
|
||||
class MergePlanes
|
||||
{
|
||||
typedef std::vector<MergePlane> Planes;
|
||||
Planes m_planes;
|
||||
public:
|
||||
typedef Planes::const_iterator const_iterator;
|
||||
void insert( const MergePlane& plane ){
|
||||
for( const_iterator i = begin(); i != end(); ++i )
|
||||
if( plane3_equal( plane.m_plane, i->m_plane ) )
|
||||
return;
|
||||
m_planes.push_back( plane );
|
||||
}
|
||||
const_iterator begin() const {
|
||||
return m_planes.begin();
|
||||
}
|
||||
const_iterator end() const {
|
||||
return m_planes.end();
|
||||
}
|
||||
std::size_t size() const {
|
||||
return m_planes.size();
|
||||
}
|
||||
};
|
||||
|
||||
void CSG_WrapMerge(){
|
||||
const bool primit = ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive );
|
||||
brush_vector_t selected_brushes;
|
||||
if( primit )
|
||||
GlobalSceneGraph().traverse( BrushGatherSelected( selected_brushes ) );
|
||||
|
||||
if ( !GlobalSelectionSystem().countSelected() && !GlobalSelectionSystem().countSelectedComponents() ) {
|
||||
globalWarningStream() << "CSG Wrap Merge: No brushes or components selected.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
MergeVertices mergeVertices;
|
||||
/* gather unique vertices */
|
||||
for ( brush_vector_t::const_iterator b = selected_brushes.begin(); b != selected_brushes.end(); ++b )
|
||||
for ( Brush::const_iterator f = ( *b )->begin(); f != ( *b )->end(); ++f )
|
||||
if ( ( *f )->contributes() ){
|
||||
const Winding& winding = ( *f )->getWinding();
|
||||
for ( std::size_t w = 0; w != winding.numpoints; ++w )
|
||||
mergeVertices.insert( winding[w].vertex );
|
||||
}
|
||||
|
||||
GlobalSceneGraph().traverse( Scene_gatherSelectedComponents( mergeVertices ) );
|
||||
|
||||
//globalOutputStream() << mergeVertices.size() << " mergeVertices.size()\n";
|
||||
if( mergeVertices.size() < 4 ){
|
||||
globalWarningStream() << "CSG Wrap Merge: Too few vertices: " << mergeVertices.size() << ".\n";
|
||||
return;
|
||||
}
|
||||
|
||||
MergePlanes mergePlanes;
|
||||
/* gather unique && worthy planes */
|
||||
for ( brush_vector_t::const_iterator b = selected_brushes.begin(); b != selected_brushes.end(); ++b )
|
||||
for ( Brush::const_iterator f = ( *b )->begin(); f != ( *b )->end(); ++f ){
|
||||
const Face& face = *( *f );
|
||||
if ( face.contributes() ){
|
||||
const brushsplit_t split = mergeVertices.classify_plane( face.getPlane().plane3() );
|
||||
if( ( split.counts[ePlaneFront] == 0 ) != ( split.counts[ePlaneBack] == 0 ) )
|
||||
mergePlanes.insert( MergePlane( face.getPlane().plane3(), &face ) );
|
||||
}
|
||||
}
|
||||
/* bruteforce new planes */
|
||||
for( MergeVertices::const_iterator i = mergeVertices.begin() + 0; i != mergeVertices.end() - 2; ++i )
|
||||
for( MergeVertices::const_iterator j = i + 1; j != mergeVertices.end() - 1; ++j )
|
||||
for( MergeVertices::const_iterator k = j + 1; k != mergeVertices.end() - 0; ++k ){
|
||||
const Plane3 plane = plane3_for_points( *i, *j, *k );
|
||||
if( plane3_valid( plane ) ){
|
||||
const brushsplit_t split = mergeVertices.classify_plane( plane );
|
||||
if( ( split.counts[ePlaneFront] == 0 ) != ( split.counts[ePlaneBack] == 0 ) ){
|
||||
if( split.counts[ePlaneFront] != 0 )
|
||||
mergePlanes.insert( MergePlane( plane3_flipped( plane ), *i, *j, *k ) );
|
||||
else
|
||||
mergePlanes.insert( MergePlane( plane, *i, *k, *j ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//globalOutputStream() << mergePlanes.size() << " mergePlanes.size()\n";
|
||||
if( mergePlanes.size() < 4 ){
|
||||
globalWarningStream() << "CSG Wrap Merge: Too few planes: " << mergePlanes.size() << ".\n";
|
||||
return;
|
||||
}
|
||||
|
||||
UndoableCommand undo( "brushWrapMerge" );
|
||||
|
||||
NodeSmartReference node( ( new BrushNode() )->node() );
|
||||
Brush* brush = Node_getBrush( node );
|
||||
|
||||
{
|
||||
const char* shader = TextureBrowser_GetSelectedShader();
|
||||
TextureProjection projection;
|
||||
TexDef_Construct_Default( projection );
|
||||
for( MergePlanes::const_iterator i = mergePlanes.begin(); i != mergePlanes.end(); ++i ){
|
||||
if( i->m_face )
|
||||
brush->addFace( *( i->m_face ) );
|
||||
else
|
||||
brush->addPlane( i->m_v1, i->m_v2, i->m_v3, shader, projection );
|
||||
// globalOutputStream() << i->m_plane.normal() << " " << i->m_plane.dist() << " i->m_plane\n";
|
||||
}
|
||||
brush->removeEmptyFaces();
|
||||
}
|
||||
|
||||
// if the new brush would not be convex
|
||||
if ( !brush->hasContributingFaces() ) {
|
||||
globalWarningStream() << "CSG Wrap Merge: Failed - result would not be convex.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT_MESSAGE( !brush->empty(), "brush left with no faces after merge" );
|
||||
|
||||
scene::Path path = ultimate_group_path();
|
||||
|
||||
// free the original brushes
|
||||
if( primit )
|
||||
GlobalSceneGraph().traverse( BrushDeleteSelected( path.top().get_pointer() ) );
|
||||
|
||||
Node_getTraversable( path.top() )->insert( node );
|
||||
path.push( makeReference( node.get() ) );
|
||||
|
||||
selectPath( path, true );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
void CSG_MakeRoom();
|
||||
void CSG_Subtract();
|
||||
void CSG_Merge();
|
||||
void CSG_WrapMerge();
|
||||
void CSG_Tool();
|
||||
|
||||
namespace scene
|
||||
|
|
|
|||
|
|
@ -2459,7 +2459,7 @@ void Select_constructToolbar( GtkToolbar* toolbar ){
|
|||
|
||||
void CSG_constructToolbar( GtkToolbar* toolbar ){
|
||||
toolbar_append_button( toolbar, "CSG Subtract (SHIFT + U)", "selection_csgsubtract.png", "CSGSubtract" );
|
||||
toolbar_append_button( toolbar, "CSG Merge (CTRL + U)", "selection_csgmerge.png", "CSGMerge" );
|
||||
toolbar_append_button( toolbar, "CSG Wrap Merge (CTRL + U)", "selection_csgmerge.png", "CSGWrapMerge" );
|
||||
toolbar_append_button( toolbar, "Room", "selection_makeroom.png", "CSGroom" );
|
||||
toolbar_append_button( toolbar, "CSG Tool", "ellipsis.png", "CSGTool" );
|
||||
}
|
||||
|
|
@ -3564,7 +3564,8 @@ void MainFrame_Construct(){
|
|||
|
||||
|
||||
GlobalCommands_insert( "CSGSubtract", FreeCaller<CSG_Subtract>(), Accelerator( 'U', (GdkModifierType)GDK_SHIFT_MASK ) );
|
||||
GlobalCommands_insert( "CSGMerge", FreeCaller<CSG_Merge>(), Accelerator( 'U', (GdkModifierType)GDK_CONTROL_MASK ) );
|
||||
GlobalCommands_insert( "CSGMerge", FreeCaller<CSG_Merge>() );
|
||||
GlobalCommands_insert( "CSGWrapMerge", FreeCaller<CSG_WrapMerge>(), Accelerator( 'U', (GdkModifierType)GDK_CONTROL_MASK ) );
|
||||
GlobalCommands_insert( "CSGroom", FreeCaller<CSG_MakeRoom>() );
|
||||
GlobalCommands_insert( "CSGTool", FreeCaller<CSG_Tool>() );
|
||||
|
||||
|
|
|
|||
|
|
@ -1554,6 +1554,14 @@ const AABB& getSelectedComponentsBounds() const {
|
|||
|
||||
return m_aabb_component;
|
||||
}
|
||||
void gatherSelectedComponents( const Vector3Callback& callback ) const {
|
||||
for ( PatchControlInstances::const_iterator i = m_ctrl_instances.begin(); i != m_ctrl_instances.end(); ++i )
|
||||
{
|
||||
if ( ( *i ).m_selectable.isSelected() ) {
|
||||
callback( ( *i ).m_ctrl->m_vertex );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ){
|
||||
test.BeginMesh( localToWorld() );
|
||||
|
|
|
|||
|
|
@ -183,6 +183,10 @@ brushsplit_t Winding_ClassifyPlane( const Winding& winding, const Plane3& plane
|
|||
return split;
|
||||
}
|
||||
|
||||
void WindingVertex_ClassifyPlane( const Vector3& vertex, const Plane3& plane, brushsplit_t& split ){
|
||||
++split.counts[Winding_ClassifyDistance( plane3_distance_to_point( plane, vertex ), ON_EPSILON )];
|
||||
}
|
||||
|
||||
|
||||
#define DEBUG_EPSILON ON_EPSILON
|
||||
const double DEBUG_EPSILON_SQUARED = DEBUG_EPSILON * DEBUG_EPSILON;
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ struct brushsplit_t
|
|||
};
|
||||
|
||||
brushsplit_t Winding_ClassifyPlane( const Winding& w, const Plane3& plane );
|
||||
void WindingVertex_ClassifyPlane( const Vector3& vertex, const Plane3& plane, brushsplit_t& split );
|
||||
|
||||
bool Winding_PlanesConcave( const Winding& w1, const Winding& w2, const Plane3& plane1, const Plane3& plane2 );
|
||||
bool Winding_TestPlane( const Winding& w, const Plane3& plane, bool flipped );
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user