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