]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/csg.cpp
uncrustify! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / radiant / csg.cpp
1 /*
2    Copyright (C) 1999-2007 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 "stdafx.h"
23 #include "winding.h"
24 #include "filters.h"
25
26 /*
27    =============
28    CSG_MakeHollow
29    =============
30  */
31
32 void Brush_Scale( brush_t* b ){
33         for ( face_t* f = b->brush_faces ; f ; f = f->next )
34         {
35                 for ( int i = 0 ; i < 3 ; i++ )
36                 {
37                         VectorScale( f->planepts[i], g_qeglobals.d_gridsize, f->planepts[i] );
38                 }
39         }
40 }
41
42 void CSG_MakeHollow( void ){
43         brush_t     *b, *front, *back, *next;
44         face_t      *f;
45         face_t split;
46         vec3_t move;
47         int i;
48
49         for ( b = selected_brushes.next ; b != &selected_brushes ; b = next )
50         {
51                 next = b->next;
52
53                 if ( b->owner->eclass->fixedsize || b->patchBrush || b->bFiltered ) {
54                         continue;
55                 }
56
57                 for ( f = b->brush_faces ; f ; f = f->next )
58                 {
59                         split = *f;
60                         VectorScale( f->plane.normal, g_qeglobals.d_gridsize, move );
61                         for ( i = 0 ; i < 3 ; i++ )
62                                 VectorSubtract( split.planepts[i], move, split.planepts[i] );
63
64                         Brush_SplitBrushByFace( b, &split, &front, &back );
65                         if ( back ) {
66                                 Brush_Free( back );
67                         }
68                         if ( front ) {
69                                 Brush_AddToList( front, &selected_brushes );
70                         }
71                 }
72                 Brush_Free( b );
73         }
74         Sys_UpdateWindows( W_ALL );
75 }
76
77 /*
78    =============
79    Brush_Merge
80
81    Returns a new brush that is created by merging brush1 and brush2.
82    May return NULL if brush1 and brush2 do not create a convex brush when merged.
83    The input brushes brush1 and brush2 stay intact.
84
85    if onlyshape is true then the merge is allowed based on the shape only
86    otherwise the texture/shader references of faces in the same plane have to
87    be the same as well.
88    =============
89  */
90 brush_t *Brush_Merge( brush_t *brush1, brush_t *brush2, int onlyshape ){
91         int i, shared;
92         brush_t *newbrush;
93         face_t *face1, *face2, *newface, *f;
94
95         // check for bounding box overlapp
96         for ( i = 0; i < 3; i++ )
97         {
98                 if ( brush1->mins[i] > brush2->maxs[i] + ON_EPSILON
99                          || brush1->maxs[i] < brush2->mins[i] - ON_EPSILON ) {
100                         // never merge if the brushes overlap
101                         return NULL;
102                 }
103         }
104         //
105         shared = 0;
106         // check if the new brush would be convex... flipped planes make a brush non-convex
107         for ( face1 = brush1->brush_faces; face1; face1 = face1->next )
108         {
109                 // don't check the faces of brush 1 and 2 touching each other
110                 for ( face2 = brush2->brush_faces; face2; face2 = face2->next )
111                 {
112                         if ( Plane_Equal( &face1->plane, &face2->plane, true ) ) {
113                                 shared++;
114                                 // there may only be ONE shared side
115                                 if ( shared > 1 ) {
116                                         return NULL;
117                                 }
118                                 break;
119                         }
120                 }
121                 // if this face plane is shared
122                 if ( face2 ) {
123                         continue;
124                 }
125                 //
126                 for ( face2 = brush2->brush_faces; face2; face2 = face2->next )
127                 {
128                         // don't check the faces of brush 1 and 2 touching each other
129                         for ( f = brush1->brush_faces; f; f = f->next )
130                         {
131                                 if ( Plane_Equal( &face2->plane, &f->plane, true ) ) {
132                                         break;
133                                 }
134                         }
135                         if ( f ) {
136                                 continue;
137                         }
138                         //
139                         if ( Plane_Equal( &face1->plane, &face2->plane, false ) ) {
140                                 //if the texture/shader references should be the same but are not
141                                 if ( !onlyshape && stricmp( face1->texdef.GetName(), face2->texdef.GetName() ) != 0 ) {
142                                         return NULL;
143                                 }
144                                 continue;
145                         }
146                         //
147                         if ( Winding_PlanesConcave( face1->face_winding, face2->face_winding,
148                                                                                 face1->plane.normal, face2->plane.normal,
149                                                                                 face1->plane.dist, face2->plane.dist ) ) {
150                                 return NULL;
151                         } //end if
152                 } //end for
153         } //end for
154           //
155         newbrush = Brush_Alloc();
156         //
157         for ( face1 = brush1->brush_faces; face1; face1 = face1->next )
158         {
159                 // don't add the faces of brush 1 and 2 touching each other
160                 for ( face2 = brush2->brush_faces; face2; face2 = face2->next )
161                 {
162                         if ( Plane_Equal( &face1->plane, &face2->plane, true ) ) {
163                                 break;
164                         }
165                 }
166                 if ( face2 ) {
167                         continue;
168                 }
169                 // don't add faces with the same plane twice
170                 for ( f = newbrush->brush_faces; f; f = f->next )
171                 {
172                         if ( Plane_Equal( &face1->plane, &f->plane, false ) ) {
173                                 break;
174                         }
175                         if ( Plane_Equal( &face1->plane, &f->plane, true ) ) {
176                                 break;
177                         }
178                 }
179                 if ( f ) {
180                         continue;
181                 }
182                 //
183                 newface = Face_Alloc();
184                 newface->texdef = face1->texdef;
185                 VectorCopy( face1->planepts[0], newface->planepts[0] );
186                 VectorCopy( face1->planepts[1], newface->planepts[1] );
187                 VectorCopy( face1->planepts[2], newface->planepts[2] );
188                 newface->plane = face1->plane;
189                 newface->next = newbrush->brush_faces;
190                 newbrush->brush_faces = newface;
191         }
192         //
193         for ( face2 = brush2->brush_faces; face2; face2 = face2->next )
194         {
195                 // don't add the faces of brush 1 and 2 touching each other
196                 for ( face1 = brush1->brush_faces; face1; face1 = face1->next )
197                 {
198                         if ( Plane_Equal( &face2->plane, &face1->plane, true ) ) {
199                                 break;
200                         }
201                 }
202                 if ( face1 ) {
203                         continue;
204                 }
205                 // don't add faces with the same plane twice
206                 for ( f = newbrush->brush_faces; f; f = f->next )
207                 {
208                         if ( Plane_Equal( &face2->plane, &f->plane, false ) ) {
209                                 break;
210                         }
211                         if ( Plane_Equal( &face2->plane, &f->plane, true ) ) {
212                                 break;
213                         }
214                 }
215                 if ( f ) {
216                         continue;
217                 }
218                 //
219                 newface = Face_Alloc();
220                 newface->texdef = face2->texdef;
221                 VectorCopy( face2->planepts[0], newface->planepts[0] );
222                 VectorCopy( face2->planepts[1], newface->planepts[1] );
223                 VectorCopy( face2->planepts[2], newface->planepts[2] );
224                 newface->plane = face2->plane;
225                 newface->next = newbrush->brush_faces;
226                 newbrush->brush_faces = newface;
227         }
228         // link the new brush to an entity
229         Entity_LinkBrush( brush1->owner, newbrush );
230         // build windings for the faces
231         Brush_BuildWindings( newbrush, false );
232
233         return newbrush;
234 }
235
236 /*
237    =============
238    Brush_MergeListPairs
239
240    Returns a list with merged brushes.
241    Tries to merge brushes pair wise.
242    The input list is destroyed.
243    Input and output should be a single linked list using .next
244    =============
245  */
246 brush_t *Brush_MergeListPairs( brush_t *brushlist, int onlyshape ){
247         int nummerges, merged;
248         brush_t *b1, *b2, *tail, *newbrush, *newbrushlist;
249         brush_t *lastb2;
250
251         if ( !brushlist ) {
252                 return NULL;
253         }
254
255         nummerges = 0;
256         do
257         {
258                 for ( tail = brushlist; tail; tail = tail->next )
259                 {
260                         if ( !tail->next ) {
261                                 break;
262                         }
263                 }
264                 merged = 0;
265                 newbrushlist = NULL;
266                 for ( b1 = brushlist; b1; b1 = brushlist )
267                 {
268                         lastb2 = b1;
269                         for ( b2 = b1->next; b2; b2 = b2->next )
270                         {
271                                 newbrush = Brush_Merge( b1, b2, onlyshape );
272                                 if ( newbrush ) {
273                                         tail->next = newbrush;
274                                         lastb2->next = b2->next;
275                                         brushlist = brushlist->next;
276                                         b1->next = b1->prev = NULL;
277                                         b2->next = b2->prev = NULL;
278                                         Brush_Free( b1 );
279                                         Brush_Free( b2 );
280                                         for ( tail = brushlist; tail; tail = tail->next )
281                                         {
282                                                 if ( !tail->next ) {
283                                                         break;
284                                                 }
285                                         } //end for
286                                         merged++;
287                                         nummerges++;
288                                         break;
289                                 }
290                                 lastb2 = b2;
291                         }
292                         //if b1 can't be merged with any of the other brushes
293                         if ( !b2 ) {
294                                 brushlist = brushlist->next;
295                                 //keep b1
296                                 b1->next = newbrushlist;
297                                 newbrushlist = b1;
298                         }
299                 }
300                 brushlist = newbrushlist;
301         } while ( merged );
302         return newbrushlist;
303 }
304
305 /*
306    =============
307    Brush_MergeList
308
309    Tries to merge all brushes in the list into one new brush.
310    The input brush list stays intact.
311    Returns NULL if no merged brush can be created.
312    To create a new brush the brushes in the list may not overlap and
313    the outer faces of the brushes together should make a new convex brush.
314
315    if onlyshape is true then the merge is allowed based on the shape only
316    otherwise the texture/shader references of faces in the same plane have to
317    be the same as well.
318    =============
319  */
320 brush_t *Brush_MergeList( brush_t *brushlist, int onlyshape ){
321         brush_t *brush1, *brush2, *brush3, *newbrush;
322         face_t *face1, *face2, *face3, *newface, *f;
323
324         if ( !brushlist ) {
325                 return NULL;
326         }
327         for ( brush1 = brushlist; brush1; brush1 = brush1->next )
328         {
329                 // check if the new brush would be convex... flipped planes make a brush concave
330                 for ( face1 = brush1->brush_faces; face1; face1 = face1->next )
331                 {
332                         // don't check face1 if it touches another brush
333                         for ( brush2 = brushlist; brush2; brush2 = brush2->next )
334                         {
335                                 if ( brush2 == brush1 ) {
336                                         continue;
337                                 }
338                                 for ( face2 = brush2->brush_faces; face2; face2 = face2->next )
339                                 {
340                                         if ( Plane_Equal( &face1->plane, &face2->plane, true ) ) {
341                                                 break;
342                                         }
343                                 }
344                                 if ( face2 ) {
345                                         break;
346                                 }
347                         }
348                         // if face1 touches another brush
349                         if ( brush2 ) {
350                                 continue;
351                         }
352                         //
353                         for ( brush2 = brush1->next; brush2; brush2 = brush2->next )
354                         {
355                                 // don't check the faces of brush 2 touching another brush
356                                 for ( face2 = brush2->brush_faces; face2; face2 = face2->next )
357                                 {
358                                         for ( brush3 = brushlist; brush3; brush3 = brush3->next )
359                                         {
360                                                 if ( brush3 == brush2 ) {
361                                                         continue;
362                                                 }
363                                                 for ( face3 = brush3->brush_faces; face3; face3 = face3->next )
364                                                 {
365                                                         if ( Plane_Equal( &face2->plane, &face3->plane, true ) ) {
366                                                                 break;
367                                                         }
368                                                 }
369                                                 if ( face3 ) {
370                                                         break;
371                                                 }
372                                         }
373                                         // if face2 touches another brush
374                                         if ( brush3 ) {
375                                                 continue;
376                                         }
377                                         //
378                                         if ( Plane_Equal( &face1->plane, &face2->plane, false ) ) {
379                                                 //if the texture/shader references should be the same but are not
380                                                 if ( !onlyshape && stricmp( face1->texdef.GetName(), face2->texdef.GetName() ) != 0 ) {
381                                                         return NULL;
382                                                 }
383                                                 continue;
384                                         }
385                                         //
386                                         if ( Winding_PlanesConcave( face1->face_winding, face2->face_winding,
387                                                                                                 face1->plane.normal, face2->plane.normal,
388                                                                                                 face1->plane.dist, face2->plane.dist ) ) {
389                                                 return NULL;
390                                         }
391                                 }
392                         }
393                 }
394         }
395         //
396         newbrush = Brush_Alloc();
397         //
398         for ( brush1 = brushlist; brush1; brush1 = brush1->next )
399         {
400                 for ( face1 = brush1->brush_faces; face1; face1 = face1->next )
401                 {
402                         // don't add face1 to the new brush if it touches another brush
403                         for ( brush2 = brushlist; brush2; brush2 = brush2->next )
404                         {
405                                 if ( brush2 == brush1 ) {
406                                         continue;
407                                 }
408                                 for ( face2 = brush2->brush_faces; face2; face2 = face2->next )
409                                 {
410                                         if ( Plane_Equal( &face1->plane, &face2->plane, true ) ) {
411                                                 break;
412                                         }
413                                 }
414                                 if ( face2 ) {
415                                         break;
416                                 }
417                         }
418                         if ( brush2 ) {
419                                 continue;
420                         }
421                         // don't add faces with the same plane twice
422                         for ( f = newbrush->brush_faces; f; f = f->next )
423                         {
424                                 if ( Plane_Equal( &face1->plane, &f->plane, false ) ) {
425                                         break;
426                                 }
427                                 if ( Plane_Equal( &face1->plane, &f->plane, true ) ) {
428                                         break;
429                                 }
430                         }
431                         if ( f ) {
432                                 continue;
433                         }
434                         //
435                         newface = Face_Alloc();
436                         newface->texdef = face1->texdef;
437                         VectorCopy( face1->planepts[0], newface->planepts[0] );
438                         VectorCopy( face1->planepts[1], newface->planepts[1] );
439                         VectorCopy( face1->planepts[2], newface->planepts[2] );
440                         newface->plane = face1->plane;
441                         newface->next = newbrush->brush_faces;
442                         newbrush->brush_faces = newface;
443                 }
444         }
445         // link the new brush to an entity
446         Entity_LinkBrush( brushlist->owner, newbrush );
447         // build windings for the faces
448         Brush_BuildWindings( newbrush, false );
449
450         return newbrush;
451 }
452
453 /*
454    =============
455    Brush_Subtract
456
457    Returns a list of brushes that remain after B is subtracted from A.
458    May by empty if A is contained inside B.
459    The originals are undisturbed.
460    =============
461  */
462 brush_t *Brush_Subtract( brush_t *a, brush_t *b ){
463         // a - b = out (list)
464         brush_t *front, *back;
465         brush_t *in, *out, *next;
466         face_t *f;
467
468         in = a;
469         out = NULL;
470         for ( f = b->brush_faces; f && in; f = f->next )
471         {
472                 Brush_SplitBrushByFace( in, f, &front, &back );
473                 if ( in != a ) {
474                         Brush_Free( in );
475                 }
476                 if ( front ) { // add to list
477                         front->next = out;
478                         out = front;
479                 }
480                 in = back;
481         }
482         //NOTE: in != a just in case brush b has no faces
483         if ( in && in != a ) {
484                 Brush_Free( in );
485         }
486         else
487         {   //didn't really intersect
488                 for ( b = out; b; b = next )
489                 {
490                         next = b->next;
491                         b->next = b->prev = NULL;
492                         Brush_Free( b );
493                 }
494                 return a;
495         }
496         return out;
497 }
498
499 /*
500    =============
501    CSG_Subtract
502    =============
503  */
504 void CSG_Subtract( void ){
505         brush_t     *b, *s, *fragments, *nextfragment, *frag, *next, *snext;
506         brush_t fragmentlist;
507         int i, numfragments, numbrushes;
508
509         Sys_Printf( "Subtracting...\n" );
510
511         if ( selected_brushes.next == &selected_brushes ) {
512                 Sys_Printf( "No brushes selected.\n" );
513                 return;
514         }
515
516         fragmentlist.next = &fragmentlist;
517         fragmentlist.prev = &fragmentlist;
518
519         numfragments = 0;
520         numbrushes = 0;
521         for ( b = selected_brushes.next ; b != &selected_brushes ; b = next )
522         {
523                 next = b->next;
524
525                 if ( b->owner->eclass->fixedsize ) {
526                         continue;   // can't use texture from a fixed entity, so don't subtract
527
528                 }
529                 // chop all fragments further up
530                 for ( s = fragmentlist.next; s != &fragmentlist; s = snext )
531                 {
532                         snext = s->next;
533
534                         for ( i = 0 ; i < 3 ; i++ )
535                                 if ( b->mins[i] >= s->maxs[i] - ON_EPSILON
536                                          || b->maxs[i] <= s->mins[i] + ON_EPSILON ) {
537                                         break;
538                                 }
539                         if ( i != 3 ) {
540                                 continue;   // definately don't touch
541                         }
542                         fragments = Brush_Subtract( s, b );
543                         // if the brushes did not really intersect
544                         if ( fragments == s ) {
545                                 continue;
546                         }
547                         // try to merge fragments
548                         fragments = Brush_MergeListPairs( fragments, true );
549                         // add the fragments to the list
550                         for ( frag = fragments; frag; frag = nextfragment )
551                         {
552                                 nextfragment = frag->next;
553                                 frag->next = NULL;
554                                 frag->owner = s->owner;
555                                 Brush_AddToList( frag, &fragmentlist );
556                         }
557                         // free the original brush
558                         Brush_Free( s );
559                 }
560
561                 // chop any active brushes up
562                 for ( s = active_brushes.next; s != &active_brushes; s = snext )
563                 {
564                         snext = s->next;
565
566                         if ( s->owner->eclass->fixedsize || s->patchBrush || s->bFiltered ) {
567                                 continue;
568                         }
569
570                         if ( s->brush_faces->pShader->getFlags() & QER_NOCARVE ) {
571                                 continue;
572                         }
573
574                         for ( i = 0 ; i < 3 ; i++ )
575                                 if ( b->mins[i] >= s->maxs[i] - ON_EPSILON
576                                          || b->maxs[i] <= s->mins[i] + ON_EPSILON ) {
577                                         break;
578                                 }
579                         if ( i != 3 ) {
580                                 continue;   // definately don't touch
581
582                         }
583                         fragments = Brush_Subtract( s, b );
584                         // if the brushes did not really intersect
585                         if ( fragments == s ) {
586                                 continue;
587                         }
588                         //
589                         Undo_AddBrush( s );
590                         // one extra brush chopped up
591                         numbrushes++;
592                         // try to merge fragments
593                         fragments = Brush_MergeListPairs( fragments, true );
594                         // add the fragments to the list
595                         for ( frag = fragments; frag; frag = nextfragment )
596                         {
597                                 nextfragment = frag->next;
598                                 frag->next = NULL;
599                                 frag->owner = s->owner;
600                                 Brush_AddToList( frag, &fragmentlist );
601                         }
602                         // free the original brush
603                         Brush_Free( s );
604                 }
605         }
606
607         // move all fragments to the active brush list
608         for ( frag = fragmentlist.next; frag != &fragmentlist; frag = nextfragment )
609         {
610                 nextfragment = frag->next;
611                 numfragments++;
612                 Brush_RemoveFromList( frag );
613                 Brush_AddToList( frag, &active_brushes );
614                 Undo_EndBrush( frag );
615         }
616
617         /*if (numfragments == 0)
618            {
619             Sys_Printf("Selected brush%s did not intersect with any other brushes.\n",
620                         (selected_brushes.next->next == &selected_brushes) ? "":"es");
621             return;
622            }*/
623         Sys_Printf( "done. (created %d fragment%s out of %d brush%s)\n", numfragments, ( numfragments == 1 ) ? "" : "s",
624                                 numbrushes, ( numbrushes == 1 ) ? "" : "es" );
625         Sys_UpdateWindows( W_ALL );
626 }
627
628 /*
629    =============
630    CSG_Merge
631    =============
632  */
633 void CSG_Merge( void ){
634         brush_t *b, *next, *newlist, *newbrush;
635         struct entity_s *owner;
636
637         Sys_Printf( "Merging...\n" );
638
639         if ( selected_brushes.next == &selected_brushes ) {
640                 Sys_Printf( "No brushes selected.\n" );
641                 return;
642         }
643
644         if ( selected_brushes.next->next == &selected_brushes ) {
645                 Sys_Printf( "At least two brushes have to be selected.\n" );
646                 return;
647         }
648
649         owner = selected_brushes.next->owner;
650
651         for ( b = selected_brushes.next; b != &selected_brushes; b = next )
652         {
653                 next = b->next;
654
655                 if ( b->owner->eclass->fixedsize ) {
656                         // can't use texture from a fixed entity, so don't subtract
657                         Sys_Printf( "Cannot add fixed size entities.\n" );
658                         return;
659                 }
660
661                 if ( b->patchBrush ) {
662                         Sys_Printf( "Cannot add patches.\n" );
663                         return;
664                 }
665
666                 // TTimo: cleanup
667                 // disable the qer_nocarve for CSG-MERGE operations
668 #if 0
669                 if ( b->brush_faces->d_texture->bFromShader && ( b->brush_faces->d_texture->nShaderFlags & QER_NOCARVE ) ) {
670                         Sys_Printf( "Cannot add brushes using shaders that don't allows CSG operations.\n" );
671                         return;
672                 }
673 #endif
674
675                 if ( b->owner != owner ) {
676                         Sys_Printf( "Cannot add brushes from different entities.\n" );
677                         return;
678                 }
679
680         }
681
682         newlist = NULL;
683         for ( b = selected_brushes.next; b != &selected_brushes; b = next )
684         {
685                 next = b->next;
686
687                 Brush_RemoveFromList( b );
688                 b->next = newlist;
689                 b->prev = NULL;
690                 newlist = b;
691         }
692
693         newbrush = Brush_MergeList( newlist, true );
694         // if the new brush would not be convex
695         if ( !newbrush ) {
696                 // add the brushes back into the selection
697                 for ( b = newlist; b; b = next )
698                 {
699                         next = b->next;
700                         b->next = NULL;
701                         b->prev = NULL;
702                         Brush_AddToList( b, &selected_brushes );
703                 }
704                 Sys_Printf( "Cannot add a set of brushes with a concave hull.\n" );
705                 return;
706         }
707         // free the original brushes
708         for ( b = newlist; b; b = next )
709         {
710                 next = b->next;
711                 b->next = NULL;
712                 b->prev = NULL;
713                 Brush_Free( b );
714         }
715
716         newbrush->bFiltered = FilterBrush( newbrush ); // spog - set filters for the new brush
717
718         Brush_AddToList( newbrush, &selected_brushes );
719
720         Sys_Printf( "done.\n" );
721         Sys_UpdateWindows( W_ALL );
722 }