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