X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=radiant%2Fcsg.cpp;h=5d20bf1c742b9753953e804201eee50a27109bc6;hb=a4f0ed00cccaa61ef5094dfaea610dcc62b55a6b;hp=c459804a549c3636b244eebfb0603fd05dadf085;hpb=ecf144c9acc4e8e5bc6d79d8402f0c4e31f4f5a1;p=xonotic%2Fnetradiant.git diff --git a/radiant/csg.cpp b/radiant/csg.cpp index c459804a..5d20bf1c 100644 --- a/radiant/csg.cpp +++ b/radiant/csg.cpp @@ -30,6 +30,7 @@ #include "brushnode.h" #include "grid.h" +/* void Face_makeBrush( Face& face, const Brush& brush, brush_vector_t& out, float offset ){ if ( face.contributes() ) { out.push_back( new Brush( brush ) ); @@ -56,39 +57,292 @@ void Face_extrude( Face& face, const Brush& brush, brush_vector_t& out, float of } } } +*/ +#include "preferences.h" +#include "texwindow.h" + +typedef std::vector doublevector_vector_t; + +enum eHollowType +{ + diag = 0, + wrap = 1, + extrude = 2, + pull = 3, + room = 4, +}; + +const char* getCaulkShader(){ + const char* gotShader = g_pGameDescription->getKeyValue( "shader_caulk" ); + if ( gotShader && *gotShader ){ + return gotShader; + } + return "textures/common/caulk"; +} +class CaulkFace +{ +DoubleVector3 ExclusionAxis; +double &mindot; +double &maxdot; +doublevector_vector_t &exclude_vec; +public: +CaulkFace( DoubleVector3 ExclusionAxis, + double &mindot, + double &maxdot, + doublevector_vector_t &exclude_vec ): + ExclusionAxis( ExclusionAxis ), + mindot( mindot ), + maxdot( maxdot ), + exclude_vec( exclude_vec ){} +void operator()( Face& face ) const { + double dot = vector3_dot( face.getPlane().plane3().normal(), ExclusionAxis ); + if( dot == 0 || ( dot > mindot + 0.005 && dot < maxdot - 0.005 ) ){ + if( !exclude_vec.empty() ){ + for ( doublevector_vector_t::const_iterator i = exclude_vec.begin(); i != exclude_vec.end(); ++i ){ + if( ( *i ) == face.getPlane().plane3().normal() ){ + return; + } + } + } + face.SetShader( getCaulkShader() ); + } +} +}; class FaceMakeBrush { const Brush& brush; brush_vector_t& out; float offset; -bool room; +eHollowType HollowType; +DoubleVector3 ExclusionAxis; +double &mindot; +double &maxdot; +doublevector_vector_t &exclude_vec; +bool caulk; +bool RemoveInner; public: -FaceMakeBrush( const Brush& brush, brush_vector_t& out, float offset, bool room ) - : brush( brush ), out( out ), offset( offset ), room( room ){ +FaceMakeBrush( const Brush& brush, + brush_vector_t& out, + float offset, + eHollowType HollowType, + DoubleVector3 ExclusionAxis, + double &mindot, + double &maxdot, + doublevector_vector_t &exclude_vec, + bool caulk, + bool RemoveInner ) + : brush( brush ), + out( out ), + offset( offset ), + HollowType( HollowType ), + ExclusionAxis( ExclusionAxis ), + mindot( mindot ), + maxdot( maxdot ), + exclude_vec( exclude_vec ), + caulk( caulk ), + RemoveInner( RemoveInner ){ } void operator()( Face& face ) const { - if( room ){ - Face_extrude( face, brush, out, offset ); + double dot = vector3_dot( face.getPlane().plane3().normal(), ExclusionAxis ); + if( dot == 0 || ( dot > mindot + 0.005 && dot < maxdot - 0.005 ) ){ + if( !exclude_vec.empty() ){ + for ( doublevector_vector_t::const_iterator i = exclude_vec.begin(); i != exclude_vec.end(); ++i ){ + if( ( *i ) == face.getPlane().plane3().normal() ){ + return; + } + } + } + + if( HollowType == pull ){ + if ( face.contributes() ) { + face.getPlane().offset( offset ); + face.planeChanged(); + out.push_back( new Brush( brush ) ); + face.getPlane().offset( -offset ); + face.planeChanged(); + + if( caulk ){ + Brush_forEachFace( *out.back(), CaulkFace( ExclusionAxis, mindot, maxdot, exclude_vec ) ); + } + std::shared_ptr newFace = out.back()->addFace( face ); + if ( newFace != 0 ) { + newFace->flipWinding(); + } + } + } + else if( HollowType == wrap ){ + //Face_makeBrush( face, brush, out, offset ); + if ( face.contributes() ) { + face.undoSave(); + out.push_back( new Brush( brush ) ); + if( !RemoveInner && caulk ) + face.SetShader( getCaulkShader() ); + std::shared_ptr newFace = out.back()->addFace( face ); + face.getPlane().offset( -offset ); + face.planeChanged(); + if( caulk ) + face.SetShader( getCaulkShader() ); + if ( newFace != 0 ) { + newFace->flipWinding(); + newFace->getPlane().offset( offset ); + newFace->planeChanged(); + } + } + } + else if( HollowType == extrude ){ + if ( face.contributes() ) { + //face.undoSave(); + out.push_back( new Brush( brush ) ); + out.back()->clear(); + + std::shared_ptr newFace = out.back()->addFace( face ); + if ( newFace != 0 ) { + newFace->getPlane().offset( offset ); + newFace->planeChanged(); + } + + if( !RemoveInner && caulk ) + face.SetShader( getCaulkShader() ); + newFace = out.back()->addFace( face ); + if ( newFace != 0 ) { + newFace->flipWinding(); + } + Winding& winding = face.getWinding(); + TextureProjection projection; + TexDef_Construct_Default( projection ); + for ( Winding::iterator j = winding.begin(); j != winding.end(); ++j ){ + std::size_t index = std::distance( winding.begin(), j ); + std::size_t next = Winding_next( winding, index ); + + out.back()->addPlane( winding[index].vertex, winding[next].vertex, winding[next].vertex + face.getPlane().plane3().normal() * offset, TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ), projection ); + } + } + } + else if( HollowType == diag ){ + if ( face.contributes() ) { + out.push_back( new Brush( brush ) ); + out.back()->clear(); + + std::shared_ptr newFace = out.back()->addFace( face ); + if ( newFace != 0 ) { + + newFace->planeChanged(); + } + newFace = out.back()->addFace( face ); + + if ( newFace != 0 ) { + if( !RemoveInner && caulk ) + newFace->SetShader( getCaulkShader() ); + newFace->flipWinding(); + newFace->getPlane().offset( offset ); + newFace->planeChanged(); + } + + Winding& winding = face.getWinding(); + TextureProjection projection; + TexDef_Construct_Default( projection ); + for ( Winding::iterator i = winding.begin(); i != winding.end(); ++i ){ + std::size_t index = std::distance( winding.begin(), i ); + std::size_t next = Winding_next( winding, index ); + Vector3 BestPoint; + float bestdist = 999999; + + for( Brush::const_iterator j = brush.begin(); j != brush.end(); ++j ){ + Winding& winding2 = ( *j )->getWinding(); + for ( Winding::iterator k = winding2.begin(); k != winding2.end(); ++k ){ + std::size_t index2 = std::distance( winding2.begin(), k ); + float testdist = vector3_length( winding[index].vertex - winding2[index2].vertex ); + if( testdist < bestdist ){ + bestdist = testdist; + BestPoint = winding2[index2].vertex; + } + } + } + out.back()->addPlane( winding[next].vertex, winding[index].vertex, BestPoint, caulk? getCaulkShader() : TextureBrowser_GetSelectedShader( GlobalTextureBrowser() ), projection ); + } + } + } } - else{ - Face_makeBrush( face, brush, out, offset ); } +}; + +class FaceExclude +{ +DoubleVector3 ExclusionAxis; +double &mindot; +double &maxdot; +public: +FaceExclude( DoubleVector3 ExclusionAxis, double &mindot, double &maxdot ) + : ExclusionAxis( ExclusionAxis ), mindot( mindot ), maxdot( maxdot ){ +} +void operator()( Face& face ) const { + if( vector3_length_squared( ExclusionAxis ) != 0 ){ + double dot = vector3_dot( face.getPlane().plane3().normal(), ExclusionAxis ); + if( dot < mindot ){ + mindot = dot; + } + else if( dot > maxdot ){ + maxdot = dot; + } + } } }; -void Brush_makeHollow( const Brush& brush, brush_vector_t& out, float offset, bool room ){ - Brush_forEachFace( brush, FaceMakeBrush( brush, out, offset, room ) ); +class FaceOffset +{ +float offset; +DoubleVector3 ExclusionAxis; +double &mindot; +double &maxdot; +doublevector_vector_t &exclude_vec; +public: +FaceOffset( float offset, DoubleVector3 ExclusionAxis, double &mindot, double &maxdot, doublevector_vector_t &exclude_vec ) + : offset( offset ), ExclusionAxis( ExclusionAxis ), mindot( mindot ), maxdot( maxdot ), exclude_vec( exclude_vec ){ } +void operator()( Face& face ) const { + double dot = vector3_dot( face.getPlane().plane3().normal(), ExclusionAxis ); + if( dot == 0 || ( dot > mindot + 0.005 && dot < maxdot - 0.005 ) ){ + if( !exclude_vec.empty() ){ + for ( doublevector_vector_t::const_iterator i = exclude_vec.begin(); i != exclude_vec.end(); ++i ){ + if( ( *i ) == face.getPlane().plane3().normal() ){ + return; + } + } + } + face.undoSave(); + face.getPlane().offset( offset ); + face.planeChanged(); + } +} +}; + +class FaceExcludeSelected +{ +doublevector_vector_t &outvec; +public: +FaceExcludeSelected( doublevector_vector_t &outvec ): outvec( outvec ){ +} +void operator()( FaceInstance& face ) const { + if( face.isSelected() ){ + outvec.push_back( face.getFace().getPlane().plane3().normal() ); + } +} +}; + + +DoubleVector3 getExclusion(); +bool getCaulk(); +bool getRemoveInner(); class BrushHollowSelectedWalker : public scene::Graph::Walker { -float m_offset; -bool room; +float offset; +eHollowType HollowType; public: -BrushHollowSelectedWalker( float offset, bool room ) - : m_offset( offset ), room( room ){ +BrushHollowSelectedWalker( float offset, eHollowType HollowType ) + : offset( offset ), HollowType( HollowType ){ } bool pre( const scene::Path& path, scene::Instance& instance ) const { if ( path.top().get().visible() ) { @@ -97,14 +351,56 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const { && Instance_getSelectable( instance )->isSelected() && path.size() > 1 ) { brush_vector_t out; - Brush_makeHollow( *brush, out, m_offset, room ); + doublevector_vector_t exclude_vec; + double mindot = 0; + double maxdot = 0; + if( HollowType != room ){ + Brush_forEachFace( *brush, FaceExclude( getExclusion(), mindot, maxdot ) ); + if( mindot == 0 && maxdot == 0 ){ + Brush_ForEachFaceInstance( *Instance_getBrush( instance ), FaceExcludeSelected( exclude_vec ) ); + } + } + if( HollowType == room ){ + Brush* tmpbrush = new Brush( *brush ); + tmpbrush->removeEmptyFaces(); + Brush_forEachFace( *brush, FaceMakeBrush( *brush, out, offset, pull, DoubleVector3( 0, 0, 0 ), mindot, maxdot, exclude_vec, true, true ) ); + delete tmpbrush; + } + else if( HollowType == pull ){ + if( !getRemoveInner() && getCaulk() ){ + Brush_forEachFace( *brush, CaulkFace( getExclusion(), mindot, maxdot, exclude_vec ) ); + } + Brush* tmpbrush = new Brush( *brush ); + tmpbrush->removeEmptyFaces(); + Brush_forEachFace( *tmpbrush, FaceMakeBrush( *tmpbrush, out, offset, HollowType, getExclusion(), mindot, maxdot, exclude_vec, getCaulk(), getRemoveInner() ) ); + delete tmpbrush; + } + else if( HollowType == diag ){ + Brush* tmpbrush = new Brush( *brush ); + Brush_forEachFace( *tmpbrush, FaceOffset( offset, getExclusion(), mindot, maxdot, exclude_vec ) ); + tmpbrush->removeEmptyFaces(); + Brush_forEachFace( *tmpbrush, FaceMakeBrush( *brush, out, offset, HollowType, getExclusion(), mindot, maxdot, exclude_vec, getCaulk(), getRemoveInner() ) ); + delete tmpbrush; + if( !getRemoveInner() && getCaulk() ){ + Brush_forEachFace( *brush, CaulkFace( getExclusion(), mindot, maxdot, exclude_vec ) ); + } + } + else{ + Brush_forEachFace( *brush, FaceMakeBrush( *brush, out, offset, HollowType, getExclusion(), mindot, maxdot, exclude_vec, getCaulk(), getRemoveInner() ) ); + } for ( brush_vector_t::const_iterator i = out.begin(); i != out.end(); ++i ) { ( *i )->removeEmptyFaces(); + if( ( *i )->hasContributingFaces() ){ NodeSmartReference node( ( new BrushNode() )->node() ); Node_getBrush( node )->copy( *( *i ) ); delete ( *i ); Node_getTraversable( path.parent() )->insert( node ); + //path.push( makeReference( node.get() ) ); + //selectPath( path, true ); + //Instance_getSelectable( *GlobalSceneGraph().find( path ) )->setSelected( true ); + //Path_deleteTop( path ); + } } } } @@ -133,7 +429,7 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const { return true; } }; - +/* class BrushDeleteSelected : public scene::Graph::Walker { public: @@ -151,31 +447,58 @@ void post( const scene::Path& path, scene::Instance& instance ) const { } } }; +*/ +#include "ientity.h" -void Scene_BrushMakeHollow_Selected( scene::Graph& graph, bool room ){ - GlobalSceneGraph().traverse( BrushHollowSelectedWalker( GetGridSize(), room ) ); - GlobalSceneGraph().traverse( BrushDeleteSelected() ); +class BrushDeleteSelected : public scene::Graph::Walker +{ +scene::Node* m_keepNode; +mutable bool m_eraseParent; +public: +BrushDeleteSelected( scene::Node* keepNode ): m_keepNode( keepNode ), m_eraseParent( false ){ +} +BrushDeleteSelected(): m_keepNode( NULL ), m_eraseParent( false ){ } +bool pre( const scene::Path& path, scene::Instance& instance ) const { + return true; +} +void post( const scene::Path& path, scene::Instance& instance ) const { + //globalOutputStream() << path.size() << "\n"; + if ( path.top().get().visible() ) { + Brush* brush = Node_getBrush( path.top() ); + if ( brush != 0 + && Instance_getSelectable( instance )->isSelected() + && path.size() > 1 ) { + scene::Node& parent = path.parent(); + Path_deleteTop( path ); + if( Node_getTraversable( parent )->empty() ){ + m_eraseParent = true; + //globalOutputStream() << "Empty node?!.\n"; + } + } + } + if( m_eraseParent && !Node_isPrimitive( path.top() ) && path.size() > 1 ){ + //globalOutputStream() << "about to Delete empty node!.\n"; + m_eraseParent = false; + Entity* entity = Node_getEntity( path.top() ); + if ( entity != 0 && path.top().get_pointer() != Map_FindWorldspawn( g_map ) + && Node_getTraversable( path.top() )->empty() && path.top().get_pointer() != m_keepNode ) { + //globalOutputStream() << "now Deleting empty node!.\n"; + Path_deleteTop( path ); + } + } +} +}; /* ============= - CSG_MakeHollow + CSG_MakeRoom ============= */ - -void CSG_MakeHollow( void ){ - UndoableCommand undo( "brushHollow" ); - - Scene_BrushMakeHollow_Selected( GlobalSceneGraph(), false ); - - SceneChangeNotify(); -} - void CSG_MakeRoom( void ){ UndoableCommand undo( "makeRoom" ); - - Scene_BrushMakeHollow_Selected( GlobalSceneGraph(), true ); - + GlobalSceneGraph().traverse( BrushHollowSelectedWalker( GetGridSize(), room ) ); + GlobalSceneGraph().traverse( BrushDeleteSelected() ); SceneChangeNotify(); } @@ -319,13 +642,17 @@ class SubtractBrushesFromUnselected : public scene::Graph::Walker const brush_vector_t& m_brushlist; std::size_t& m_before; std::size_t& m_after; +mutable bool m_eraseParent; public: SubtractBrushesFromUnselected( const brush_vector_t& brushlist, std::size_t& before, std::size_t& after ) - : m_brushlist( brushlist ), m_before( before ), m_after( after ){ + : m_brushlist( brushlist ), m_before( before ), m_after( after ), m_eraseParent( false ){ } bool pre( const scene::Path& path, scene::Instance& instance ) const { - return true; + if ( path.top().get().visible() ) { + return true; + } + return false; } void post( const scene::Path& path, scene::Instance& instance ) const { @@ -374,10 +701,22 @@ void post( const scene::Path& path, scene::Instance& instance ) const { } delete b; } + scene::Node& parent = path.parent(); Path_deleteTop( path ); + if( Node_getTraversable( parent )->empty() ){ + m_eraseParent = true; + } } } } + if( m_eraseParent && !Node_isPrimitive( path.top() ) && path.size() > 1 ){ + m_eraseParent = false; + Entity* entity = Node_getEntity( path.top() ); + if ( entity != 0 && path.top().get_pointer() != Map_FindWorldspawn( g_map ) + && Node_getTraversable( path.top() )->empty() ) { + Path_deleteTop( path ); + } + } } }; @@ -624,7 +963,7 @@ void CSG_Merge( void ){ ASSERT_MESSAGE( !brush->empty(), "brush left with no faces after merge" ); // free the original brushes - GlobalSceneGraph().traverse( BrushDeleteSelected() ); + GlobalSceneGraph().traverse( BrushDeleteSelected( merged_path.parent().get_pointer() ) ); merged_path.pop(); Node_getTraversable( merged_path.top() )->insert( node ); @@ -636,3 +975,327 @@ void CSG_Merge( void ){ SceneChangeNotify(); } } + + + + + + +/* + ============= + CSG_Tool + ============= + */ +#include "mainframe.h" +#include +#include "gtkutil/dialog.h" +#include "gtkutil/button.h" +#include "gtkutil/accelerator.h" +#include "xywindow.h" +#include "camwindow.h" + +struct CSGToolDialog +{ + GtkSpinButton* spin; + bool allocated{false}; + ui::Window window{ui::null}; + GtkToggleButton *radFaces, *radProj, *radCam, *caulk, *removeInner; +}; + +CSGToolDialog g_csgtool_dialog; + +DoubleVector3 getExclusion(){ + if( gtk_toggle_button_get_active( g_csgtool_dialog.radProj ) ){ + if( GlobalXYWnd_getCurrentViewType() == YZ ){ + return DoubleVector3( 1, 0, 0 ); + } + else if( GlobalXYWnd_getCurrentViewType() == XZ ){ + return DoubleVector3( 0, 1, 0 ); + } + else if( GlobalXYWnd_getCurrentViewType() == XY ){ + return DoubleVector3( 0, 0, 1 ); + } + } + if( gtk_toggle_button_get_active( g_csgtool_dialog.radCam ) ){ + Vector3 angles( Camera_getAngles( *g_pParentWnd->GetCamWnd() ) ); +// globalOutputStream() << angles << " angles\n"; + DoubleVector3 radangles( degrees_to_radians( angles[0] ), degrees_to_radians( angles[1] ), degrees_to_radians( angles[2] ) ); +// globalOutputStream() << radangles << " radangles\n"; +// x = cos(yaw)*cos(pitch) +// y = sin(yaw)*cos(pitch) +// z = sin(pitch) + DoubleVector3 viewvector; + viewvector[0] = cos( radangles[1] ) * cos( radangles[0] ); + viewvector[1] = sin( radangles[1] ) * cos( radangles[0] ); + viewvector[2] = sin( radangles[0] ); +// globalOutputStream() << viewvector << " viewvector\n"; + return viewvector; + } + return DoubleVector3( 0, 0, 0 ); +} + +bool getCaulk(){ + if( gtk_toggle_button_get_active( g_csgtool_dialog.caulk ) ){ + return true; + } + return false; +} + +bool getRemoveInner(){ + if( gtk_toggle_button_get_active( g_csgtool_dialog.removeInner ) ){ + return true; + } + return false; +} + +class BrushFaceOffset +{ +float offset; +public: +BrushFaceOffset( float offset ) + : offset( offset ){ +} +void operator()( BrushInstance& brush ) const { + double mindot = 0; + double maxdot = 0; + doublevector_vector_t exclude_vec; + Brush_forEachFace( brush, FaceExclude( getExclusion(), mindot, maxdot ) ); + if( mindot == 0 && maxdot == 0 ){ + Brush_ForEachFaceInstance( brush, FaceExcludeSelected( exclude_vec ) ); + } + Brush_forEachFace( brush, FaceOffset( offset, getExclusion(), mindot, maxdot, exclude_vec ) ); +} +}; + +//=================DLG + +static gboolean CSGdlg_HollowDiag( GtkWidget *widget, CSGToolDialog* dialog ){ + float offset = static_cast( gtk_spin_button_get_value( dialog->spin ) ); + UndoableCommand undo( "brushHollow::Diag" ); + GlobalSceneGraph().traverse( BrushHollowSelectedWalker( offset, diag ) ); + if( getRemoveInner() ) + GlobalSceneGraph().traverse( BrushDeleteSelected() ); + SceneChangeNotify(); + return TRUE; +} + +static gboolean CSGdlg_HollowWrap( GtkWidget *widget, CSGToolDialog* dialog ){ + float offset = static_cast( gtk_spin_button_get_value( dialog->spin ) ); + UndoableCommand undo( "brushHollow::Wrap" ); + GlobalSceneGraph().traverse( BrushHollowSelectedWalker( offset, wrap ) ); + if( getRemoveInner() ) + GlobalSceneGraph().traverse( BrushDeleteSelected() ); + SceneChangeNotify(); + return TRUE; +} + +static gboolean CSGdlg_HollowExtrude( GtkWidget *widget, CSGToolDialog* dialog ){ + float offset = static_cast( gtk_spin_button_get_value( dialog->spin ) ); + UndoableCommand undo( "brushHollow::Extrude" ); + GlobalSceneGraph().traverse( BrushHollowSelectedWalker( offset, extrude ) ); + if( getRemoveInner() ) + GlobalSceneGraph().traverse( BrushDeleteSelected() ); + SceneChangeNotify(); + return TRUE; +} + +static gboolean CSGdlg_HollowPull( GtkWidget *widget, CSGToolDialog* dialog ){ + float offset = static_cast( gtk_spin_button_get_value( dialog->spin ) ); + UndoableCommand undo( "brushHollow::Pull" ); + GlobalSceneGraph().traverse( BrushHollowSelectedWalker( offset, pull ) ); + if( getRemoveInner() ) + GlobalSceneGraph().traverse( BrushDeleteSelected() ); + SceneChangeNotify(); + return TRUE; +} + +static gboolean CSGdlg_BrushShrink( GtkWidget *widget, CSGToolDialog* dialog ){ + gtk_spin_button_update ( dialog->spin ); + float offset = static_cast( gtk_spin_button_get_value( dialog->spin ) ); + offset *= -1; + UndoableCommand undo( "Shrink brush" ); +// GlobalSceneGraph().traverse( OffsetBrushFacesSelectedWalker( offset ) ); + //Scene_ForEachSelectedBrush_ForEachFace( GlobalSceneGraph(), BrushFaceOffset( offset ) ); + Scene_forEachSelectedBrush( BrushFaceOffset( offset ) ); + SceneChangeNotify(); + return TRUE; +} + +static gboolean CSGdlg_BrushExpand( GtkWidget *widget, CSGToolDialog* dialog ){ + gtk_spin_button_update ( dialog->spin ); + float offset = static_cast( gtk_spin_button_get_value( dialog->spin ) ); + UndoableCommand undo( "Expand brush" ); +// GlobalSceneGraph().traverse( OffsetBrushFacesSelectedWalker( offset ) ); + //Scene_ForEachSelectedBrush_ForEachFace( GlobalSceneGraph(), BrushFaceOffset( offset ) ); + Scene_forEachSelectedBrush( BrushFaceOffset( offset ) ); + SceneChangeNotify(); + return TRUE; +} + +static gboolean CSGdlg_grid2spin( GtkWidget *widget, CSGToolDialog* dialog ){ + gtk_spin_button_set_value( dialog->spin, GetGridSize() ); + return TRUE; +} + +static gboolean CSGdlg_delete( GtkWidget *widget, GdkEventAny *event, CSGToolDialog* dialog ){ + gtk_widget_hide( GTK_WIDGET( dialog->window ) ); + return TRUE; +} + +void CSG_Tool(){ + // FIXME: there is probably improvements to do less raw GTK stuff, more GTK wrapper + if ( !g_csgtool_dialog.allocated ) { + g_csgtool_dialog.allocated = true; + g_csgtool_dialog.window = MainFrame_getWindow().create_dialog_window( "CSG Tool", G_CALLBACK( CSGdlg_delete ), &g_csgtool_dialog ); + gtk_window_set_type_hint( g_csgtool_dialog.window, GDK_WINDOW_TYPE_HINT_UTILITY ); + + //GtkAccelGroup* accel = gtk_accel_group_new(); + //gtk_window_add_accel_group( g_csgtool_dialog.window, accel ); + global_accel_connect_window( g_csgtool_dialog.window ); + + { + auto hbox = create_dialog_hbox( 4, 4 ); + gtk_container_add( GTK_CONTAINER( g_csgtool_dialog.window ), GTK_WIDGET( hbox ) ); + { + auto table = create_dialog_table( 3, 8, 4, 4 ); + gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 ); + { + //GtkWidget* label = gtk_label_new( "<->" ); + //gtk_widget_show( label ); + auto button = ui::Button( "Grid->" ); + table.attach( button, {0, 1, 0, 1}, {0, 0} ); + button.show(); + g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_grid2spin ), &g_csgtool_dialog ); + } + { + GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 16, 0, 9999, 1, 10, 0 ) ); + GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 3 ) ); + gtk_widget_show( GTK_WIDGET( spin ) ); + gtk_widget_set_tooltip_text( GTK_WIDGET( spin ), "Thickness" ); + gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 0, 1, + (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions) ( 0 ), 0, 0 ); + gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 ); + gtk_spin_button_set_numeric( spin, TRUE ); + + g_csgtool_dialog.spin = spin; + } + { + //radio button group for choosing the exclude axis + GtkWidget* radFaces = gtk_radio_button_new_with_label( NULL, "-faces" ); + gtk_widget_set_tooltip_text( radFaces, "Exclude selected faces" ); + GtkWidget* radProj = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(radFaces), "-proj" ); + gtk_widget_set_tooltip_text( radProj, "Exclude faces, most orthogonal to active projection" ); + GtkWidget* radCam = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(radFaces), "-cam" ); + gtk_widget_set_tooltip_text( radCam, "Exclude faces, most orthogonal to camera view" ); + + gtk_widget_show( radFaces ); + gtk_widget_show( radProj ); + gtk_widget_show( radCam ); + + gtk_table_attach( table, radFaces, 2, 3, 0, 1, + (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions) ( 0 ), 0, 0 ); + gtk_table_attach( table, radProj, 3, 4, 0, 1, + (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions) ( 0 ), 0, 0 ); + gtk_table_attach( table, radCam, 4, 5, 0, 1, + (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions) ( 0 ), 0, 0 ); + + g_csgtool_dialog.radFaces = GTK_TOGGLE_BUTTON( radFaces ); + g_csgtool_dialog.radProj = GTK_TOGGLE_BUTTON( radProj ); + g_csgtool_dialog.radCam = GTK_TOGGLE_BUTTON( radCam ); + } + { + GtkWidget* button = gtk_toggle_button_new(); + auto ubutton = ui::Button::from( button ); + button_set_icon( ubutton, "f-caulk.png" ); + gtk_button_set_relief( GTK_BUTTON( button ), GTK_RELIEF_NONE ); + table.attach( ubutton, { 6, 7, 0, 1 }, { GTK_EXPAND, 0 } ); + gtk_widget_set_tooltip_text( button, "Caulk some faces" ); + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( button ), TRUE ); + ubutton.show(); + g_csgtool_dialog.caulk = GTK_TOGGLE_BUTTON( button ); + } + { + GtkWidget* button = gtk_toggle_button_new(); + auto ubutton = ui::Button::from( button ); + button_set_icon( ubutton, "csgtool_removeinner.png" ); + gtk_button_set_relief( GTK_BUTTON( button ), GTK_RELIEF_NONE ); + table.attach( ubutton, { 7, 8, 0, 1 }, { GTK_EXPAND, 0 } ); + gtk_widget_set_tooltip_text( button, "Remove inner brush" ); + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( button ), TRUE ); + ubutton.show(); + g_csgtool_dialog.removeInner = GTK_TOGGLE_BUTTON( button ); + } + { + GtkWidget* sep = gtk_hseparator_new(); + gtk_widget_show( sep ); + gtk_table_attach( table, sep, 0, 8, 1, 2, + (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions) ( 0 ), 0, 0 ); + } + { + GtkWidget* button = gtk_button_new(); + auto ubutton = ui::Button::from( button ); + button_set_icon( ubutton, "csgtool_shrink.png" ); + table.attach( ubutton, { 0, 1, 2, 3 }, { GTK_EXPAND, 0 } ); + gtk_widget_set_tooltip_text( button, "Shrink brush" ); + ubutton.show(); + g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_BrushShrink ), &g_csgtool_dialog ); + } + { + GtkWidget* button = gtk_button_new(); + auto ubutton = ui::Button::from( button ); + button_set_icon( ubutton, "csgtool_expand.png" ); + table.attach( ubutton, { 1, 2, 2, 3 }, { GTK_EXPAND, 0 } ); + gtk_widget_set_tooltip_text( button, "Expand brush" ); + ubutton.show(); + g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_BrushExpand ), &g_csgtool_dialog ); + } + { + GtkWidget* button = gtk_button_new(); + auto ubutton = ui::Button::from( button ); + button_set_icon( ubutton, "csgtool_diagonal.png" ); + table.attach( ubutton, { 3, 4, 2, 3 }, { GTK_EXPAND, 0 } ); + gtk_widget_set_tooltip_text( button, "Hollow::diagonal joints" ); + ubutton.show(); + g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_HollowDiag ), &g_csgtool_dialog ); + } + { + GtkWidget* button = gtk_button_new(); + auto ubutton = ui::Button::from( button ); + button_set_icon( ubutton, "csgtool_wrap.png" ); + table.attach( ubutton, { 4, 5, 2, 3 }, { GTK_EXPAND, 0 } ); + gtk_widget_set_tooltip_text( button, "Hollow::warp" ); + ubutton.show(); + g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_HollowWrap ), &g_csgtool_dialog ); + } + { + GtkWidget* button = gtk_button_new(); + auto ubutton = ui::Button::from( button ); + button_set_icon( ubutton, "csgtool_extrude.png" ); + table.attach( ubutton, { 5, 6, 2, 3 }, { GTK_EXPAND, 0 } ); + gtk_widget_set_tooltip_text( button, "Hollow::extrude faces" ); + ubutton.show(); + g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_HollowExtrude ), &g_csgtool_dialog ); + } + { + GtkWidget* button = gtk_button_new(); + auto ubutton = ui::Button::from( button ); + button_set_icon( ubutton, "csgtool_pull.png" ); + table.attach( ubutton, { 6, 7, 2, 3 }, { GTK_EXPAND, 0 } ); + gtk_widget_set_tooltip_text( button, "Hollow::pull faces" ); + ubutton.show(); + g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_HollowPull ), &g_csgtool_dialog ); + } + + } + } + } + + gtk_widget_show( GTK_WIDGET( g_csgtool_dialog.window ) ); + gtk_window_present( g_csgtool_dialog.window ); +} +