binds... * shift + u: new uniform CSG wrap merge algorithm, merging selected brushes and/or components

This commit is contained in:
Garux 2018-10-16 01:20:09 +03:00
parent 614885f1fb
commit 0f7af6aaa4
10 changed files with 271 additions and 11 deletions

View File

@ -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;

View File

@ -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() ) {

View File

@ -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 )

View File

@ -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" );
}

View File

@ -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 );
}
}

View File

@ -25,6 +25,7 @@
void CSG_MakeRoom();
void CSG_Subtract();
void CSG_Merge();
void CSG_WrapMerge();
void CSG_Tool();
namespace scene

View File

@ -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>() );

View File

@ -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() );

View File

@ -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;

View File

@ -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 );