ok
[xonotic/netradiant.git] / contrib / bobtoolz / funchandlers-GTK.cpp
1 /*
2 BobToolz plugin for GtkRadiant
3 Copyright (C) 2001 Gordon Biggans
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include "StdAfx.h"
21
22 #ifdef WIN32
23 #pragma warning(disable : 4786)
24 #endif
25
26 #include "dialogs/dialogs-gtk.h"
27
28 #include "gtkr_list.h"
29 #include "str.h"
30
31 #include "DPoint.h"
32 #include "DPlane.h"
33 #include "DBrush.h"
34 #include "DEPair.h"
35 #include "DPatch.h"
36 #include "DEntity.h"
37 #include "DShape.h"
38 #include "DBobView.h"
39 #include "DVisDrawer.h"
40 #include "DTrainDrawer.h"
41
42 #include "misc.h"
43 #include "scriptparser.h"
44 #include "DTreePlanter.h"
45
46 #include "shapes.h"
47 #include "lists.h"
48 #include "funchandlers.h"
49 #include "visfind.h"
50
51 #include "iundo.h"
52
53 #include "refcounted_ptr.h"
54
55 #include <vector>
56 #include <list>
57 #include <map>
58 #include <algorithm>
59
60 #include "scenelib.h"
61
62 // for autocaulk
63 list<Str> exclusionList;                // whole brush exclusion
64 list<Str> exclusionList_Face;   // single face exclusion
65
66 bool el1Loaded =                FALSE;
67 bool el2Loaded =                FALSE;
68 bool clrLst1Loaded =    FALSE;
69 bool clrLst2Loaded =    FALSE;
70
71 DBobView*               g_PathView =            NULL;
72 DVisDrawer*             g_VisView =                     NULL;
73 DTrainDrawer*   g_TrainView =           NULL;
74 DTreePlanter*   g_TreePlanter =         NULL;
75 // -------------
76
77 //========================//
78 //    Helper Functions    //
79 //========================//
80
81 void LoadLists()
82 {
83         char buffer[256];
84
85         if(!el1Loaded)
86                 el1Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el1.txt"), &exclusionList);
87         if(!el2Loaded)
88                 el2Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el2.txt"), &exclusionList_Face);
89 }
90
91
92 //========================//
93 //     Main Functions     //
94 //========================//
95
96 void DoIntersect()
97 {
98         IntersectRS rs;
99
100         if(DoIntersectBox(&rs) == eIDCANCEL)
101                 return;
102
103         if(rs.nBrushOptions == BRUSH_OPT_SELECTED)
104         {
105                 if( GlobalSelectionSystem().countSelected() < 2 )
106                 {
107                         DoMessageBox("Invalid number of brushes selected, choose at least 2", "Error", eMB_OK);
108                         return; 
109                 }
110         }
111
112         DEntity world;
113
114         switch(rs.nBrushOptions)
115         {
116         case BRUSH_OPT_SELECTED:
117                 {
118                         world.LoadSelectedBrushes();
119                         break;
120                 }
121         case BRUSH_OPT_WHOLE_MAP:
122                 {
123                         world.LoadFromEntity(0, FALSE);
124                         break;
125                 }
126         }
127
128         world.RemoveNonCheckBrushes(&exclusionList, rs.bUseDetail);
129
130         bool* pbSelectList;
131         if(rs.bDuplicateOnly)
132                 pbSelectList = world.BuildDuplicateList();
133         else
134                 pbSelectList = world.BuildIntersectList();
135
136         world.SelectBrushes(pbSelectList);
137
138         delete[] pbSelectList;
139 }
140
141 void DoPolygonsTB()
142 {
143   DoPolygons();
144 }
145
146 void DoPolygons()
147 {
148         // ensure we have something selected
149         if( GlobalSelectionSystem().countSelected() != 1 )
150         {
151                 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
152                 return; 
153         }
154
155         PolygonRS rs;
156
157         // ask user for type, size, etc....
158         if(DoPolygonBox(&rs) == eIDOK)
159         {
160                 DShape poly;
161
162     vec3_t vMin, vMax;
163
164     {
165       scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
166       VectorSubtract(instance.aabb_world().origin, instance.aabb_world().extents, vMin);
167       VectorAdd(instance.aabb_world().origin, instance.aabb_world().extents, vMax);
168
169       instance.path().parent()->m_traverse->erase(instance.path().top());
170     }
171
172                 if(rs.bInverse)
173                         poly.BuildInversePrism(vMin, vMax, rs.nSides, rs.bAlignTop);
174                 else
175                 {
176                         if(rs.bUseBorder)
177                                 poly.BuildBorderedPrism(vMin, vMax, rs.nSides, rs.nBorderWidth, rs.bAlignTop);
178                         else
179                                 poly.BuildRegularPrism(vMin, vMax, rs.nSides, rs.bAlignTop);
180
181                 }
182
183                 poly.Commit();
184         }
185 }
186
187 void DoFixBrushes()
188 {
189         DMap world;
190         world.LoadAll();
191
192         int count = world.FixBrushes();
193         
194         Sys_Printf("%i invalid/duplicate planes removed\n", count);
195 }
196
197 void DoResetTextures()
198 {
199         static ResetTextureRS rs;
200
201   const char* texName;
202         if(1/*g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 1*/)
203   {
204     texName = NULL;
205   }
206   else
207   {
208     texName = GetCurrentTexture();
209           strcpy(rs.textureName, GetCurrentTexture());
210   }
211
212   EMessageBoxReturn ret;
213         if((ret = DoResetTextureBox(&rs)) == eIDCANCEL)
214                 return;  
215
216   if(rs.bResetTextureName)
217     texName = rs.textureName;
218
219   if(ret == eIDOK)
220   {
221           DEntity world;
222           world.LoadSelectedBrushes();
223           world.ResetTextures(texName,              rs.fScale,      rs.fShift,      rs.rotation, rs.newTextureName, 
224                         rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation, TRUE);
225   }
226   else
227   {
228           DMap world;
229     world.LoadAll(TRUE);
230     world.ResetTextures(texName,              rs.fScale,      rs.fShift,      rs.rotation, rs.newTextureName, 
231                         rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation);
232   }
233 }
234
235 void DoBuildStairs()
236 {
237         BuildStairsRS rs;
238
239         strcpy(rs.mainTexture, GetCurrentTexture());
240
241         // ensure we have something selected
242         if( GlobalSelectionSystem().countSelected() != 1 )
243         {
244                 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
245                 return; 
246         }
247
248         // ask user for type, size, etc....
249         if(DoBuildStairsBox(&rs) == eIDOK)
250         {
251     vec3_t vMin, vMax;
252
253     {
254       scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
255       VectorSubtract(instance.aabb_world().origin, instance.aabb_world().extents, vMin);
256       VectorAdd(instance.aabb_world().origin, instance.aabb_world().extents, vMax);
257     }
258
259                 // calc brush size
260                 vec3_t size;
261                 VectorSubtract(vMax, vMin, size);
262
263                 if(((int)size[2] % rs.stairHeight) != 0)
264                 {
265                         // stairs must fit evenly into brush
266                         DoMessageBox("Invalid stair height\nHeight of block must be divisable by stair height", "Error", eMB_OK);
267                 }
268                 else
269                 {
270       {
271         scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
272         instance.path().parent()->m_traverse->erase(instance.path().top());
273       }
274                                                 
275                         // Get Step Count
276                         int numSteps = (int)size[2] / rs.stairHeight;
277                         
278                         if(rs.style == STYLE_CORNER)
279                         {
280                                 BuildCornerStairs(vMin, vMax, numSteps, rs.mainTexture, rs.riserTexture);
281                         }
282                         else
283                         {
284
285                                 // Get Step Dimensions
286                                 float stairHeight = (float)rs.stairHeight;
287                                 float stairWidth;
288                                 if((rs.direction == MOVE_EAST) || (rs.direction == MOVE_WEST))
289                                         stairWidth = (size[0])/numSteps;
290                                 else
291                                         stairWidth = (size[1])/numSteps;
292
293
294                                 // Build Base For Stair (bob's style)
295                                 if(rs.style == STYLE_BOB)
296                                         Build_Wedge(rs.direction, vMin, vMax, TRUE);
297
298
299                                 // Set First Step Starting Position
300                                 vMax[2] = vMin[2] + stairHeight;
301                                 SetInitialStairPos(rs.direction, vMin, vMax, stairWidth);
302
303
304                                 // Build The Steps
305                                 for(int i = 0; i < numSteps; i++)
306                                 {
307                                         if(rs.style == STYLE_BOB)
308                                                 Build_StairStep_Wedge(rs.direction, vMin, vMax, rs.mainTexture, rs.riserTexture, rs.bUseDetail);
309                                         else if(rs.style == STYLE_ORIGINAL)
310                                                 Build_StairStep(vMin, vMax, rs.mainTexture, rs.riserTexture, rs.direction);
311
312                                         // get step into next position
313                                         MoveBlock(rs.direction, vMin, vMax, stairWidth);
314                                         vMax[2] += stairHeight;
315                                         if(rs.style == STYLE_BOB)
316                                                 vMin[2] += stairHeight; // wedge bottom must be raised
317                                 }
318                         }
319                 }
320         }
321 }
322
323 void DoBuildDoors()
324 {
325         // ensure we have something selected
326         if( GlobalSelectionSystem().countSelected() != 1 )
327         {
328                 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
329                 return; 
330         }
331
332   DoorRS rs;
333         strcpy(rs.mainTexture, GetCurrentTexture());
334
335         if(DoDoorsBox(&rs) == eIDOK)
336         {
337     vec3_t vMin, vMax;
338
339     {
340       scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
341       VectorSubtract(instance.aabb_world().origin, instance.aabb_world().extents, vMin);
342       VectorAdd(instance.aabb_world().origin, instance.aabb_world().extents, vMax);
343       instance.path().parent()->m_traverse->erase(instance.path().top());
344     }
345
346                 BuildDoorsX2(vMin, vMax, 
347                         rs.bScaleMainH, rs.bScaleMainV,
348                         rs.bScaleTrimH, rs.bScaleTrimV,
349                         rs.mainTexture, rs.trimTexture,
350                         rs.nOrientation);       // shapes.cpp
351         }
352 }
353
354 void DoPathPlotter()
355 {
356         PathPlotterRS rs;
357         EMessageBoxReturn ret = DoPathPlotterBox(&rs);
358         if(ret == eIDCANCEL)
359                 return;
360         if(ret == eIDNO)
361         {
362                 if(g_PathView)
363                         delete g_PathView;
364                 return;
365         }
366
367         // ensure we have something selected
368         if( GlobalSelectionSystem().countSelected() != 1 )
369         {
370                 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
371                 return; 
372         }
373
374   scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
375
376         DEntity world;
377         world.LoadEPairList(instance.path().top()->m_entity);
378
379         DEPair* trigger_ep = world.FindEPairByKey("targetname");
380
381         if(trigger_ep)
382         {
383                 if(!strcmp(world.m_Classname, "trigger_push"))
384                 {
385                         DEPair* target_ep = world.FindEPairByKey("target");
386                         if(target_ep)
387                         {
388         scene::Path* entTarget = FindEntityFromTargetname(target_ep->value, NULL);
389                                 if(entTarget)
390                                 {
391                                         if(g_PathView)
392                                                 delete g_PathView;
393                                         g_PathView = new DBobView;
394
395                                         g_PathView->Begin(trigger_ep->value, target_ep->value, rs.fMultiplier, rs.nPoints, rs.fGravity, rs.bNoUpdate, rs.bShowExtra);
396                                 }
397                                 else
398                                         DoMessageBox("trigger_push target could not be found.", "Error", eMB_OK);
399                         }
400                         else
401                                 DoMessageBox("trigger_push has no target.", "Error", eMB_OK);
402                 }
403                 else
404                         DoMessageBox("You must select a 'trigger_push' entity.", "Error", eMB_OK);
405         }       
406         else
407                 DoMessageBox("Entity must have a targetname", "Error", eMB_OK);
408 }
409
410 void DoPitBuilder()
411 {
412         // ensure we have something selected
413         if( GlobalSelectionSystem().countSelected() != 1 )
414         {
415                 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
416                 return; 
417         }
418
419   vec3_t vMin, vMax;
420
421   scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
422   VectorSubtract(instance.aabb_world().origin, instance.aabb_world().extents, vMin);
423   VectorAdd(instance.aabb_world().origin, instance.aabb_world().extents, vMax);
424
425         DShape pit;
426
427         if(pit.BuildPit(vMin, vMax))
428         {
429                 pit.Commit();
430
431     instance.path().parent()->m_traverse->erase(instance.path().top());
432         }
433         else
434                 DoMessageBox("Failed To Make Pit\nTry Making The Brush Bigger", "Error", eMB_OK);
435 }
436
437 void DoMergePatches()
438 {
439   patch_merge_t merge_info;
440   DPatch mrgPatches[2];
441   int i;
442
443         // ensure we have something selected
444         if( GlobalSelectionSystem().countSelected() != 2 )
445         {
446                 DoMessageBox("Invalid number of patches selected, choose 2 only", "Error", eMB_OK);
447                 return; 
448         }
449
450   scene::Node* patches[2];
451   patches[0] = GlobalSelectionSystem().ultimateSelected().path().top();
452   patches[1] = GlobalSelectionSystem().penultimateSelected().path().top();
453
454   for (i = 0; i < 2; i++)
455   {
456     if (!patches[i]->m_patch)
457     {
458       DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
459       return; 
460     }
461
462     mrgPatches[0].LoadFromBrush(patches[i]);
463   }
464
465   /*  mrgPatches[0].Transpose();
466       mrgPatches[0].RemoveFromRadiant();
467       mrgPatches[0].BuildInRadiant();*/
468
469   merge_info = mrgPatches[0].IsMergable(&mrgPatches[1]);
470
471   if (merge_info.mergable)
472   {
473     Sys_Printf("%i %i", merge_info.pos1, merge_info.pos2);
474
475     Sys_Printf("Patches Mergable\n");
476     DPatch* newPatch = mrgPatches[0].MergePatches(merge_info, &mrgPatches[0], &mrgPatches[1]);
477
478     /*                mrgPatches[0].RemoveFromRadiant();
479     mrgPatches[0].BuildInRadiant();
480     
481       mrgPatches[1].RemoveFromRadiant();
482       mrgPatches[1].BuildInRadiant();
483       
484         
485     delete newPatch;*/
486
487     if (!newPatch)
488     {
489     } else
490     {
491       mrgPatches[0].RemoveFromRadiant();
492       mrgPatches[1].RemoveFromRadiant();
493
494       newPatch->BuildInRadiant();
495       delete newPatch;
496     }
497   }
498 }
499
500 void DoSplitPatch() {
501         DPatch patch;
502
503         // ensure we have something selected
504         if( GlobalSelectionSystem().countSelected() != 1 )
505         {
506                 DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
507                 return; 
508         }
509
510   scene::Node* node = GlobalSelectionSystem().ultimateSelected().path().top();
511
512         if( !node->m_patch ) {
513                 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
514                 return; 
515         }
516
517         patch.LoadFromBrush(node);
518
519         list<DPatch> patchList = patch.Split( true, true );
520         for(list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
521                 (*patches).BuildInRadiant();
522         }
523
524         patch.RemoveFromRadiant();
525 }
526
527 void DoVisAnalyse()
528 {
529         char filename[1024];
530
531         if( GlobalSelectionSystem().countSelected() == 0 )
532         {
533                 if(g_VisView) 
534                 {
535                         delete g_VisView;
536                         return;
537                 }
538         }
539
540         // ensure we have something selected
541         if( GlobalSelectionSystem().countSelected() != 1 )
542         {
543                 DoMessageBox("Invalid number of objects selected, choose 1 only", "Error", eMB_OK);
544                 return; 
545         }
546
547   scene::Node* brush = GlobalSelectionSystem().ultimateSelected().path().top();
548
549         DBrush orgBrush;
550         orgBrush.LoadFromBrush(brush, false);
551
552         orgBrush.BuildBounds();
553         vec3_t origin;
554         origin[0] = (orgBrush.bbox_max[0] + orgBrush.bbox_min[0])/2.f;
555         origin[1] = (orgBrush.bbox_max[1] + orgBrush.bbox_min[1])/2.f;
556         origin[2] = (orgBrush.bbox_max[2] + orgBrush.bbox_min[2])/2.f;
557
558
559   const char* rad_filename = g_FuncTable.m_pfnGetMapName();
560         if(!rad_filename)
561         {
562                 DoMessageBox("An Error Occurred While Trying\n To Get The Map Filename", "Error", eMB_OK);
563                 return;
564         }
565
566         strcpy(filename, rad_filename);
567                 
568         char* ext = strrchr(filename, '.')+1;
569         strcpy(ext, "bsp");// rename the extension
570
571         list<DWinding*> *pointList = BuildTrace(filename, origin);
572
573         if(!g_VisView)
574         {
575                 g_VisView = new DVisDrawer;
576                 g_VisView->Register();
577         }
578         
579         g_VisView->SetList(pointList);
580 }
581
582 void DoTrainPathPlot() {
583         if(g_TrainView) {
584                 delete g_TrainView;
585                 g_TrainView = NULL;
586         }
587
588         g_TrainView = new DTrainDrawer();
589 }
590
591 void DoCaulkSelection() {
592         DEntity world;
593         
594         float fScale[2] = { 0.5f, 0.5f };
595         float fShift[2] = { 0.0f, 0.0f };
596
597         int bResetScale[2] = { false, false };
598         int bResetShift[2] = { false, false };
599
600         world.LoadSelectedBrushes();
601         world.LoadSelectedPatches();
602         world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );
603 }
604
605 void DoTreePlanter() {
606         if(g_TreePlanter) {
607                 delete g_TreePlanter;
608                 g_TreePlanter = NULL;
609                 return;
610         }
611
612         g_TreePlanter = new DTreePlanter();
613 }
614
615 void DoDropEnts() {
616         if(g_TreePlanter) {
617                 g_TreePlanter->DropEntsToGround();
618         }
619 }
620
621 void DoMakeChain() {
622         DTreePlanter pl;
623         pl.MakeChain();
624 }
625
626 typedef DPoint* pntTripple[3];
627
628 bool bFacesNoTop[6] = {true, true, true, true, true, false};
629
630 void DoFlipTerrain() {
631         vec3_t vUp = { 0.f, 0.f, 1.f };
632   int i;
633
634         // ensure we have something selected
635         if( GlobalSelectionSystem().countSelected() != 2 )
636         {
637                 DoMessageBox("Invalid number of objects selected, choose 2 only", "Error", eMB_OK);
638                 return; 
639         }
640
641   scene::Node* brushes[2];
642         brushes[0] = GlobalSelectionSystem().ultimateSelected().path().top();
643         brushes[1] = GlobalSelectionSystem().penultimateSelected().path().top();
644
645         DBrush Brushes[2];
646         DPlane* Planes[2];
647         pntTripple Points[2];
648         for( i = 0; i < 2; i++ ) {
649                 Brushes[i].LoadFromBrush( brushes[i], false );
650                 if(!(Planes[i] = Brushes[i].FindPlaneWithClosestNormal( vUp )) || Brushes[i].FindPointsForPlane( Planes[i], Points[i], 3 ) != 3) {
651                         DoMessageBox("Error", "Error", eMB_OK);
652                         return;
653                 }
654         }
655
656         vec3_t mins1, mins2, maxs1, maxs2;
657         Brushes[0].GetBounds( mins1, maxs1 );
658         Brushes[1].GetBounds( mins2, maxs2 );
659
660   scene::Node* ents[2];
661         ents[0] = GlobalSelectionSystem().ultimateSelected().path().parent();
662         ents[1] = GlobalSelectionSystem().penultimateSelected().path().parent();
663
664         for( i = 0; i < 2; i++ ) {
665                 Brushes[i].RemoveFromRadiant();
666         }
667
668
669
670         int dontmatch[2] = { -1, -1 };
671         bool found = false;
672         for( i = 0; i < 3; i++ ) {
673                 for( int j = 0; j < 3 && !found; j++ ) {
674                         if(VectorCompare( (Points[0])[i]->_pnt, (Points[1])[j]->_pnt )) {
675                                 found = true;
676                                 break;
677                         }
678                 }
679                 if(!found) {
680                         dontmatch[0] = i;
681                         break;
682                 }
683                 found = false;
684         }
685         if(dontmatch[0] == -1) {
686                 DoMessageBox("Error", "Error", eMB_OK);
687                 return;
688         }
689
690         for( i = 0; i < 3; i++ ) {
691                 for( int j = 0; j < 3 && !found; j++ ) {
692                         if(VectorCompare( (Points[1])[i]->_pnt, (Points[0])[j]->_pnt )) {
693                                 found = true;
694                                 break;
695                         }
696                 }
697                 if(!found) {
698                         dontmatch[1] = i;
699                         break;
700                 }
701                 found = false;
702         }
703         if(dontmatch[1] == -1) {
704                 DoMessageBox("Error", "Error", eMB_OK);
705                 return;
706         }
707
708         vec3_t plnpnts1[3];
709         vec3_t plnpnts2[3];
710         vec3_t plnpntsshr[3];
711
712         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts1[0] );
713         for( i = 0; i < 3; i++ ) {
714                 if( dontmatch[0] != i ) {
715                         VectorCopy( (Points[0])[i]->_pnt, plnpnts1[1] );
716                         break;
717                 }
718         }
719         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts1[2] );
720
721         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts2[0] );
722         for( i = 0; i < 3; i++ ) {
723                 if( dontmatch[1] != i && !VectorCompare( (Points[1])[i]->_pnt, plnpnts1[1] )) {
724                         VectorCopy( (Points[1])[i]->_pnt, plnpnts2[1] );
725                         break;
726                 }
727         }
728         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts2[2] );
729
730         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[0] );
731         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[1] );
732         if( (Points[1])[dontmatch[1]]->_pnt[2] < (Points[0])[dontmatch[0]]->_pnt[2] ) {
733                 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[2] );
734         } else {
735                 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[2] );
736         }
737         plnpntsshr[2][2] -= 16;
738
739         for( i = 0; i < 3; i++ ) {
740                 if( mins2[i] < mins1[i] ) {
741                         mins1[i] = mins2[i];
742                 }
743                 if( maxs2[i] > maxs1[i] ) {
744                         maxs1[i] = maxs2[i];
745                 }
746         }
747
748         DBrush* newBrushes[2];
749         newBrushes[0] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
750         newBrushes[1] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
751
752         vec3_t normal;
753         MakeNormal( plnpnts1[0], plnpnts1[1], plnpnts1[2], normal );
754         if( normal[2] >= 0 ) {
755                 newBrushes[0]->AddFace( plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true );
756         } else {
757                 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );
758         }
759
760         MakeNormal( plnpnts2[0], plnpnts2[1], plnpnts2[2], normal );
761         if( normal[2] >= 0 ) {
762                 newBrushes[1]->AddFace( plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true );
763         } else {
764                 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );
765         }
766
767         vec3_t vec;
768         MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );      
769         
770         VectorSubtract( plnpnts1[2], plnpnts1[1], vec );
771         if( DotProduct( vec, normal ) >= 0 ) {
772                 newBrushes[0]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
773         } else {
774                 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
775         }
776
777         VectorSubtract( plnpnts2[2], plnpnts2[1], vec );
778         if( DotProduct( vec, normal ) >= 0 ) {
779                 newBrushes[1]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
780         } else {
781                 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
782         }
783
784         for( i = 0; i < 2; i++ ) {
785                 newBrushes[i]->RemoveRedundantPlanes();
786                 newBrushes[i]->BuildInRadiant( false, NULL, ents[i] );
787                 delete newBrushes[i];
788         }
789
790 }