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