]> git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/csg.cpp
9de2dca24f705960f6adc8f5c24b14e0818f093a
[xonotic/netradiant.git] / radiant / csg.cpp
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "csg.h"
23
24 #include "debugging/debugging.h"
25
26 #include <list>
27
28 #include "map.h"
29 #include "brushmanip.h"
30 #include "brushnode.h"
31 #include "grid.h"
32 /*
33 void Face_makeBrush( Face& face, const Brush& brush, brush_vector_t& out, float offset ){
34         if ( face.contributes() ) {
35                 out.push_back( new Brush( brush ) );
36                 Face* newFace = out.back()->addFace( face );
37                 if ( newFace != 0 ) {
38                         newFace->flipWinding();
39                         newFace->getPlane().offset( offset );
40                         newFace->planeChanged();
41                 }
42         }
43 }
44 */
45
46 void Face_makeBrush( Face& face, const Brush& brush, brush_vector_t& out, float offset ){
47         if ( face.contributes() ) {
48                 out.push_back( new Brush( brush ) );
49                 //face.getPlane().offset( -offset );
50                 //face.planeChanged();
51                 Face* newFace = out.back()->addFace( face );
52                 if ( newFace != 0 ) {
53                         newFace->flipWinding();
54                         newFace->getPlane().offset( offset );
55                         newFace->planeChanged();
56                 }
57         }
58 }
59
60 void Face_makeRoom( Face &face, const Brush &brush, brush_vector_t &out, float offset ){
61         if ( face.contributes() ) {
62                 face.getPlane().offset( offset );
63                 out.push_back( new Brush( brush ) );
64                 face.getPlane().offset( -offset );
65                 Face* newFace = out.back()->addFace( face );
66                 if ( newFace != 0 ) {
67                         newFace->flipWinding();
68                         newFace->planeChanged();
69                 }
70         }
71 }
72
73 void Brush_makeHollow( const Brush &brush, brush_vector_t &out, float offset ){
74         Brush_forEachFace( brush, [&]( Face &face ) {
75                 Face_makeBrush( face, brush, out, offset );
76         } );
77 }
78
79 void Brush_makeRoom( const Brush &brush, brush_vector_t &out, float offset ){
80         Brush_forEachFace( brush, [&]( Face &face ) {
81                 Face_makeRoom( face, brush, out, offset );
82         } );
83 }
84
85 class BrushHollowSelectedWalker : public scene::Graph::Walker
86 {
87 float m_offset;
88 bool m_makeRoom;
89 public:
90 BrushHollowSelectedWalker( float offset, bool makeRoom )
91         : m_offset( offset ), m_makeRoom( makeRoom ){
92 }
93
94 bool pre( const scene::Path& path, scene::Instance& instance ) const {
95         if ( path.top().get().visible() ) {
96                 Brush* brush = Node_getBrush( path.top() );
97                 if ( brush != 0
98                          && Instance_getSelectable( instance )->isSelected()
99                          && path.size() > 1 ) {
100                         brush_vector_t out;
101
102                         if ( m_makeRoom ) {
103                                 Brush_makeRoom(* brush, out, m_offset );
104                         }
105                         else
106                         {
107                                 Brush_makeHollow(* brush, out, m_offset );
108                         }
109
110                         for ( brush_vector_t::const_iterator i = out.begin(); i != out.end(); ++i )
111                         {
112                                 ( *i )->removeEmptyFaces();
113                                 NodeSmartReference node( ( new BrushNode() )->node() );
114                                 Node_getBrush( node )->copy( *( *i ) );
115                                 delete ( *i );
116                                 Node_getTraversable( path.parent() )->insert( node );
117                         }
118                 }
119         }
120         return true;
121 }
122 };
123
124 typedef std::list<Brush*> brushlist_t;
125
126 class BrushGatherSelected : public scene::Graph::Walker
127 {
128 brush_vector_t& m_brushlist;
129 public:
130 BrushGatherSelected( brush_vector_t& brushlist )
131         : m_brushlist( brushlist ){
132 }
133
134 bool pre( const scene::Path& path, scene::Instance& instance ) const {
135         if ( path.top().get().visible() ) {
136                 Brush* brush = Node_getBrush( path.top() );
137                 if ( brush != 0
138                          && Instance_getSelectable( instance )->isSelected() ) {
139                         m_brushlist.push_back( brush );
140                 }
141         }
142         return true;
143 }
144 };
145
146 class BrushDeleteSelected : public scene::Graph::Walker
147 {
148 public:
149 bool pre( const scene::Path& path, scene::Instance& instance ) const {
150         return true;
151 }
152 void post( const scene::Path& path, scene::Instance& instance ) const {
153         if ( path.top().get().visible() ) {
154                 Brush* brush = Node_getBrush( path.top() );
155                 if ( brush != 0
156                          && Instance_getSelectable( instance )->isSelected()
157                          && path.size() > 1 ) {
158                         Path_deleteTop( path );
159                 }
160         }
161 }
162 };
163
164 void Scene_BrushMakeHollow_Selected( scene::Graph& graph, bool makeRoom ){
165         GlobalSceneGraph().traverse( BrushHollowSelectedWalker( GetGridSize(), makeRoom ) );
166         GlobalSceneGraph().traverse( BrushDeleteSelected() );
167 }
168
169 /*
170    =============
171    CSG_MakeHollow
172    =============
173  */
174
175 void CSG_MakeHollow( void ){
176         UndoableCommand undo( "brushHollow" );
177
178         Scene_BrushMakeHollow_Selected( GlobalSceneGraph(), false );
179
180         SceneChangeNotify();
181 }
182
183 void CSG_MakeRoom( void ){
184         UndoableCommand undo( "brushRoom" );
185
186         Scene_BrushMakeHollow_Selected( GlobalSceneGraph(), true );
187
188         SceneChangeNotify();
189 }
190
191 template<typename Type>
192 class RemoveReference
193 {
194 public:
195 typedef Type type;
196 };
197
198 template<typename Type>
199 class RemoveReference<Type&>
200 {
201 public:
202 typedef Type type;
203 };
204
205 template<typename Functor>
206 class Dereference
207 {
208 const Functor& functor;
209 public:
210 Dereference( const Functor& functor ) : functor( functor ){
211 }
212 get_result_type<Functor> operator()( typename RemoveReference<get_argument<Functor, 0>>::type *firstArgument ) const {
213         return functor( *firstArgument );
214 }
215 };
216
217 template<typename Functor>
218 inline Dereference<Functor> makeDereference( const Functor& functor ){
219         return Dereference<Functor>( functor );
220 }
221
222 typedef Face* FacePointer;
223 const FacePointer c_nullFacePointer = 0;
224
225 template<typename Predicate>
226 Face* Brush_findIf( const Brush& brush, const Predicate& predicate ){
227         Brush::const_iterator i = std::find_if( brush.begin(), brush.end(), makeDereference( predicate ) );
228         return i == brush.end() ? c_nullFacePointer : *i; // uses c_nullFacePointer instead of 0 because otherwise gcc 4.1 attempts conversion to int
229 }
230
231 template<typename Caller>
232 class BindArguments1
233 {
234 typedef get_argument<Caller, 1> FirstBound;
235 FirstBound firstBound;
236 public:
237 BindArguments1( FirstBound firstBound )
238         : firstBound( firstBound ){
239 }
240
241 get_result_type<Caller> operator()( get_argument<Caller, 0> firstArgument ) const {
242         return Caller::call( firstArgument, firstBound );
243 }
244 };
245
246 template<typename Caller>
247 class BindArguments2
248 {
249 typedef get_argument<Caller, 1> FirstBound;
250 typedef get_argument<Caller, 2> SecondBound;
251 FirstBound firstBound;
252 SecondBound secondBound;
253 public:
254 BindArguments2( FirstBound firstBound, SecondBound secondBound )
255         : firstBound( firstBound ), secondBound( secondBound ){
256 }
257
258 get_result_type<Caller> operator()( get_argument<Caller, 0> firstArgument ) const {
259         return Caller::call( firstArgument, firstBound, secondBound );
260 }
261 };
262
263 template<typename Caller, typename FirstBound, typename SecondBound>
264 BindArguments2<Caller> bindArguments( const Caller& caller, FirstBound firstBound, SecondBound secondBound ){
265         return BindArguments2<Caller>( firstBound, secondBound );
266 }
267
268 inline bool Face_testPlane( const Face& face, const Plane3& plane, bool flipped ){
269         return face.contributes() && !Winding_TestPlane( face.getWinding(), plane, flipped );
270 }
271
272 typedef Function<bool ( const Face &, const Plane3 &, bool ), Face_testPlane> FaceTestPlane;
273
274
275 /// \brief Returns true if
276 /// \li !flipped && brush is BACK or ON
277 /// \li flipped && brush is FRONT or ON
278 bool Brush_testPlane( const Brush& brush, const Plane3& plane, bool flipped ){
279         brush.evaluateBRep();
280 #if 1
281         for ( Brush::const_iterator i( brush.begin() ); i != brush.end(); ++i )
282         {
283                 if ( Face_testPlane( *( *i ), plane, flipped ) ) {
284                         return false;
285                 }
286         }
287         return true;
288 #else
289         return Brush_findIf( brush, bindArguments( FaceTestPlane(), makeReference( plane ), flipped ) ) == 0;
290 #endif
291 }
292
293 brushsplit_t Brush_classifyPlane( const Brush& brush, const Plane3& plane ){
294         brush.evaluateBRep();
295         brushsplit_t split;
296         for ( Brush::const_iterator i( brush.begin() ); i != brush.end(); ++i )
297         {
298                 if ( ( *i )->contributes() ) {
299                         split += Winding_ClassifyPlane( ( *i )->getWinding(), plane );
300                 }
301         }
302         return split;
303 }
304
305 bool Brush_subtract( const Brush& brush, const Brush& other, brush_vector_t& ret_fragments ){
306         if ( aabb_intersects_aabb( brush.localAABB(), other.localAABB() ) ) {
307                 brush_vector_t fragments;
308                 fragments.reserve( other.size() );
309                 Brush back( brush );
310
311                 for ( Brush::const_iterator i( other.begin() ); i != other.end(); ++i )
312                 {
313                         if ( ( *i )->contributes() ) {
314                                 brushsplit_t split = Brush_classifyPlane( back, ( *i )->plane3() );
315                                 if ( split.counts[ePlaneFront] != 0
316                                          && split.counts[ePlaneBack] != 0 ) {
317                                         fragments.push_back( new Brush( back ) );
318                                         Face* newFace = fragments.back()->addFace( *( *i ) );
319                                         if ( newFace != 0 ) {
320                                                 newFace->flipWinding();
321                                         }
322                                         back.addFace( *( *i ) );
323                                 }
324                                 else if ( split.counts[ePlaneBack] == 0 ) {
325                                         for ( brush_vector_t::iterator i = fragments.begin(); i != fragments.end(); ++i )
326                                         {
327                                                 delete( *i );
328                                         }
329                                         return false;
330                                 }
331                         }
332                 }
333                 ret_fragments.insert( ret_fragments.end(), fragments.begin(), fragments.end() );
334                 return true;
335         }
336         return false;
337 }
338
339 class SubtractBrushesFromUnselected : public scene::Graph::Walker
340 {
341 const brush_vector_t& m_brushlist;
342 std::size_t& m_before;
343 std::size_t& m_after;
344 public:
345 SubtractBrushesFromUnselected( const brush_vector_t& brushlist, std::size_t& before, std::size_t& after )
346         : m_brushlist( brushlist ), m_before( before ), m_after( after ){
347 }
348
349 bool pre( const scene::Path& path, scene::Instance& instance ) const {
350         return true;
351 }
352
353 void post( const scene::Path& path, scene::Instance& instance ) const {
354         if ( path.top().get().visible() ) {
355                 Brush* brush = Node_getBrush( path.top() );
356                 if ( brush != 0
357                          && !Instance_getSelectable( instance )->isSelected() ) {
358                         brush_vector_t buffer[2];
359                         bool swap = false;
360                         Brush* original = new Brush( *brush );
361                         buffer[static_cast<std::size_t>( swap )].push_back( original );
362
363                         {
364                                 for ( brush_vector_t::const_iterator i( m_brushlist.begin() ); i != m_brushlist.end(); ++i )
365                                 {
366                                         for ( brush_vector_t::iterator j( buffer[static_cast<std::size_t>( swap )].begin() ); j != buffer[static_cast<std::size_t>( swap )].end(); ++j )
367                                         {
368                                                 if ( Brush_subtract( *( *j ), *( *i ), buffer[static_cast<std::size_t>( !swap )] ) ) {
369                                                         delete ( *j );
370                                                 }
371                                                 else
372                                                 {
373                                                         buffer[static_cast<std::size_t>( !swap )].push_back( ( *j ) );
374                                                 }
375                                         }
376                                         buffer[static_cast<std::size_t>( swap )].clear();
377                                         swap = !swap;
378                                 }
379                         }
380
381                         brush_vector_t& out = buffer[static_cast<std::size_t>( swap )];
382
383                         if ( out.size() == 1 && out.back() == original ) {
384                                 delete original;
385                         }
386                         else
387                         {
388                                 ++m_before;
389                                 for ( brush_vector_t::const_iterator i = out.begin(); i != out.end(); ++i )
390                                 {
391                                         ++m_after;
392                                         ( *i )->removeEmptyFaces();
393                                         if ( !( *i )->empty() ) {
394                                                 NodeSmartReference node( ( new BrushNode() )->node() );
395                                                 Node_getBrush( node )->copy( *( *i ) );
396                                                 delete ( *i );
397                                                 Node_getTraversable( path.parent() )->insert( node );
398                                         }
399                                         else{
400                                                 delete ( *i );
401                                         }
402                                 }
403                                 Path_deleteTop( path );
404                         }
405                 }
406         }
407 }
408 };
409
410 void CSG_Subtract(){
411         brush_vector_t selected_brushes;
412         GlobalSceneGraph().traverse( BrushGatherSelected( selected_brushes ) );
413
414         if ( selected_brushes.empty() ) {
415                 globalOutputStream() << "CSG Subtract: No brushes selected.\n";
416         }
417         else
418         {
419                 globalOutputStream() << "CSG Subtract: Subtracting " << Unsigned( selected_brushes.size() ) << " brushes.\n";
420
421                 UndoableCommand undo( "brushSubtract" );
422
423                 // subtract selected from unselected
424                 std::size_t before = 0;
425                 std::size_t after = 0;
426                 GlobalSceneGraph().traverse( SubtractBrushesFromUnselected( selected_brushes, before, after ) );
427                 globalOutputStream() << "CSG Subtract: Result: "
428                                                          << Unsigned( after ) << " fragment" << ( after == 1 ? "" : "s" )
429                                                          << " from " << Unsigned( before ) << " brush" << ( before == 1 ? "" : "es" ) << ".\n";
430
431                 SceneChangeNotify();
432         }
433 }
434
435 class BrushSplitByPlaneSelected : public scene::Graph::Walker
436 {
437 const Vector3& m_p0;
438 const Vector3& m_p1;
439 const Vector3& m_p2;
440 const char* m_shader;
441 const TextureProjection& m_projection;
442 EBrushSplit m_split;
443 public:
444 BrushSplitByPlaneSelected( const Vector3& p0, const Vector3& p1, const Vector3& p2, const char* shader, const TextureProjection& projection, EBrushSplit split )
445         : m_p0( p0 ), m_p1( p1 ), m_p2( p2 ), m_shader( shader ), m_projection( projection ), m_split( split ){
446 }
447
448 bool pre( const scene::Path& path, scene::Instance& instance ) const {
449         return true;
450 }
451
452 void post( const scene::Path& path, scene::Instance& instance ) const {
453         if ( path.top().get().visible() ) {
454                 Brush* brush = Node_getBrush( path.top() );
455                 if ( brush != 0
456                          && Instance_getSelectable( instance )->isSelected() ) {
457                         Plane3 plane( plane3_for_points( m_p0, m_p1, m_p2 ) );
458                         if ( plane3_valid( plane ) ) {
459                                 brushsplit_t split = Brush_classifyPlane( *brush, m_split == eFront ? plane3_flipped( plane ) : plane );
460                                 if ( split.counts[ePlaneBack] && split.counts[ePlaneFront] ) {
461                                         // the plane intersects this brush
462                                         if ( m_split == eFrontAndBack ) {
463                                                 NodeSmartReference node( ( new BrushNode() )->node() );
464                                                 Brush* fragment = Node_getBrush( node );
465                                                 fragment->copy( *brush );
466                                                 Face* newFace = fragment->addPlane( m_p0, m_p1, m_p2, m_shader, m_projection );
467                                                 if ( newFace != 0 && m_split != eFront ) {
468                                                         newFace->flipWinding();
469                                                 }
470                                                 fragment->removeEmptyFaces();
471                                                 ASSERT_MESSAGE( !fragment->empty(), "brush left with no faces after split" );
472
473                                                 Node_getTraversable( path.parent() )->insert( node );
474                                                 {
475                                                         scene::Path fragmentPath = path;
476                                                         fragmentPath.top() = makeReference( node.get() );
477                                                         selectPath( fragmentPath, true );
478                                                 }
479                                         }
480
481                                         Face* newFace = brush->addPlane( m_p0, m_p1, m_p2, m_shader, m_projection );
482                                         if ( newFace != 0 && m_split == eFront ) {
483                                                 newFace->flipWinding();
484                                         }
485                                         brush->removeEmptyFaces();
486                                         ASSERT_MESSAGE( !brush->empty(), "brush left with no faces after split" );
487                                 }
488                                 else
489                                 // the plane does not intersect this brush
490                                 if ( m_split != eFrontAndBack && split.counts[ePlaneBack] != 0 ) {
491                                         // the brush is "behind" the plane
492                                         Path_deleteTop( path );
493                                 }
494                         }
495                 }
496         }
497 }
498 };
499
500 void Scene_BrushSplitByPlane( scene::Graph& graph, const Vector3& p0, const Vector3& p1, const Vector3& p2, const char* shader, EBrushSplit split ){
501         TextureProjection projection;
502         TexDef_Construct_Default( projection );
503         graph.traverse( BrushSplitByPlaneSelected( p0, p1, p2, shader, projection, split ) );
504         SceneChangeNotify();
505 }
506
507
508 class BrushInstanceSetClipPlane : public scene::Graph::Walker
509 {
510 Plane3 m_plane;
511 public:
512 BrushInstanceSetClipPlane( const Plane3& plane )
513         : m_plane( plane ){
514 }
515
516 bool pre( const scene::Path& path, scene::Instance& instance ) const {
517         BrushInstance* brush = Instance_getBrush( instance );
518         if ( brush != 0
519                  && path.top().get().visible()
520                  && brush->isSelected() ) {
521                 BrushInstance& brushInstance = *brush;
522                 brushInstance.setClipPlane( m_plane );
523         }
524         return true;
525 }
526 };
527
528 void Scene_BrushSetClipPlane( scene::Graph& graph, const Plane3& plane ){
529         graph.traverse( BrushInstanceSetClipPlane( plane ) );
530 }
531
532 /*
533    =============
534    CSG_Merge
535    =============
536  */
537 bool Brush_merge( Brush& brush, const brush_vector_t& in, bool onlyshape ){
538         // gather potential outer faces
539
540         {
541                 typedef std::vector<const Face*> Faces;
542                 Faces faces;
543                 for ( brush_vector_t::const_iterator i( in.begin() ); i != in.end(); ++i )
544                 {
545                         ( *i )->evaluateBRep();
546                         for ( Brush::const_iterator j( ( *i )->begin() ); j != ( *i )->end(); ++j )
547                         {
548                                 if ( !( *j )->contributes() ) {
549                                         continue;
550                                 }
551
552                                 const Face& face1 = *( *j );
553
554                                 bool skip = false;
555                                 // test faces of all input brushes
556                                 //!\todo SPEEDUP: Flag already-skip faces and only test brushes from i+1 upwards.
557                                 for ( brush_vector_t::const_iterator k( in.begin() ); !skip && k != in.end(); ++k )
558                                 {
559                                         if ( k != i ) { // don't test a brush against itself
560                                                 for ( Brush::const_iterator l( ( *k )->begin() ); !skip && l != ( *k )->end(); ++l )
561                                                 {
562                                                         const Face& face2 = *( *l );
563
564                                                         // face opposes another face
565                                                         if ( plane3_opposing( face1.plane3(), face2.plane3() ) ) {
566                                                                 // skip opposing planes
567                                                                 skip  = true;
568                                                                 break;
569                                                         }
570                                                 }
571                                         }
572                                 }
573
574                                 // check faces already stored
575                                 for ( Faces::const_iterator m = faces.begin(); !skip && m != faces.end(); ++m )
576                                 {
577                                         const Face& face2 = *( *m );
578
579                                         // face equals another face
580                                         if ( plane3_equal( face1.plane3(), face2.plane3() ) ) {
581                                                 //if the texture/shader references should be the same but are not
582                                                 if ( !onlyshape && !shader_equal( face1.getShader().getShader(), face2.getShader().getShader() ) ) {
583                                                         return false;
584                                                 }
585                                                 // skip duplicate planes
586                                                 skip = true;
587                                                 break;
588                                         }
589
590                                         // face1 plane intersects face2 winding or vice versa
591                                         if ( Winding_PlanesConcave( face1.getWinding(), face2.getWinding(), face1.plane3(), face2.plane3() ) ) {
592                                                 // result would not be convex
593                                                 return false;
594                                         }
595                                 }
596
597                                 if ( !skip ) {
598                                         faces.push_back( &face1 );
599                                 }
600                         }
601                 }
602                 for ( Faces::const_iterator i = faces.begin(); i != faces.end(); ++i )
603                 {
604                         if ( !brush.addFace( *( *i ) ) ) {
605                                 // result would have too many sides
606                                 return false;
607                         }
608                 }
609         }
610
611         brush.removeEmptyFaces();
612
613         return true;
614 }
615
616 void CSG_Merge( void ){
617         brush_vector_t selected_brushes;
618
619         // remove selected
620         GlobalSceneGraph().traverse( BrushGatherSelected( selected_brushes ) );
621
622         if ( selected_brushes.empty() ) {
623                 globalOutputStream() << "CSG Merge: No brushes selected.\n";
624                 return;
625         }
626
627         if ( selected_brushes.size() < 2 ) {
628                 globalOutputStream() << "CSG Merge: At least two brushes have to be selected.\n";
629                 return;
630         }
631
632         globalOutputStream() << "CSG Merge: Merging " << Unsigned( selected_brushes.size() ) << " brushes.\n";
633
634         UndoableCommand undo( "brushMerge" );
635
636         scene::Path merged_path = GlobalSelectionSystem().ultimateSelected().path();
637
638         NodeSmartReference node( ( new BrushNode() )->node() );
639         Brush* brush = Node_getBrush( node );
640         // if the new brush would not be convex
641         if ( !Brush_merge( *brush, selected_brushes, true ) ) {
642                 globalOutputStream() << "CSG Merge: Failed - result would not be convex.\n";
643         }
644         else
645         {
646                 ASSERT_MESSAGE( !brush->empty(), "brush left with no faces after merge" );
647
648                 // free the original brushes
649                 GlobalSceneGraph().traverse( BrushDeleteSelected() );
650
651                 merged_path.pop();
652                 Node_getTraversable( merged_path.top() )->insert( node );
653                 merged_path.push( makeReference( node.get() ) );
654
655                 selectPath( merged_path, true );
656
657                 globalOutputStream() << "CSG Merge: Succeeded.\n";
658                 SceneChangeNotify();
659         }
660 }