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