]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/bobtoolz/funchandlers-GTK.cpp
some fixes, and patch bobtoolz improvements, from osxnetradiant
[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();
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 DoSplitPatchCols() {
511         UndoableCommand undo("bobToolz.splitPatchCols");
512         
513         DPatch patch;
514         
515         // ensure we have something selected
516         if( GlobalSelectionSystem().countSelected() != 1 )
517         {
518                 DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
519                 return; 
520         }
521         
522         scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
523         
524         if( !Node_isPatch(instance.path().top()) ) {
525                 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
526                 return; 
527         }
528         
529         patch.LoadFromPatch(instance);
530         
531         std::list<DPatch> patchList = patch.SplitCols();
532         for(std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
533                 (*patches).BuildInRadiant();
534         }
535         
536         Path_deleteTop(instance.path());
537 }
538
539 void DoSplitPatchRows() {
540         UndoableCommand undo("bobToolz.splitPatchRows");
541         
542         DPatch patch;
543         
544         // ensure we have something selected
545         if( GlobalSelectionSystem().countSelected() != 1 )
546         {
547                 DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
548                 return; 
549         }
550         
551         scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
552         
553         if( !Node_isPatch(instance.path().top()) ) {
554                 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
555                 return; 
556         }
557         
558         patch.LoadFromPatch(instance);
559         
560         std::list<DPatch> patchList = patch.SplitRows();
561         for(std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
562                 (*patches).BuildInRadiant();
563         }
564         
565         Path_deleteTop(instance.path());
566 }
567
568 void DoVisAnalyse()
569 {
570         char filename[1024];
571
572         if( GlobalSelectionSystem().countSelected() == 0 )
573         {
574                 if(g_VisView) 
575                 {
576                         delete g_VisView;
577                         return;
578                 }
579         }
580
581         // ensure we have something selected
582         if( GlobalSelectionSystem().countSelected() != 1 )
583         {
584                 DoMessageBox("Invalid number of objects selected, choose 1 only", "Error", eMB_OK);
585                 return; 
586         }
587
588   scene::Instance& brush = GlobalSelectionSystem().ultimateSelected();
589
590         DBrush orgBrush;
591         orgBrush.LoadFromBrush(brush, false);
592
593         orgBrush.BuildBounds();
594         vec3_t origin;
595         origin[0] = (orgBrush.bbox_max[0] + orgBrush.bbox_min[0])/2.f;
596         origin[1] = (orgBrush.bbox_max[1] + orgBrush.bbox_min[1])/2.f;
597         origin[2] = (orgBrush.bbox_max[2] + orgBrush.bbox_min[2])/2.f;
598
599
600   const char* rad_filename = GlobalRadiant().getMapName();
601         if(!rad_filename)
602         {
603                 DoMessageBox("An Error Occurred While Trying\n To Get The Map Filename", "Error", eMB_OK);
604                 return;
605         }
606
607         strcpy(filename, rad_filename);
608                 
609         char* ext = strrchr(filename, '.')+1;
610         strcpy(ext, "bsp");// rename the extension
611
612         std::list<DWinding*> *pointList = BuildTrace(filename, origin);
613
614         if(!g_VisView)
615         {
616                 g_VisView = new DVisDrawer;
617         }
618         
619         g_VisView->SetList(pointList);
620 }
621
622 void DoTrainPathPlot() {
623         if(g_TrainView) {
624                 delete g_TrainView;
625                 g_TrainView = NULL;
626         }
627
628         g_TrainView = new DTrainDrawer();
629 }
630
631 void DoCaulkSelection() {
632   UndoableCommand undo("bobToolz.caulkSelection");
633         DEntity world;
634         
635         float fScale[2] = { 0.5f, 0.5f };
636         float fShift[2] = { 0.0f, 0.0f };
637
638         int bResetScale[2] = { false, false };
639         int bResetShift[2] = { false, false };
640
641         world.LoadSelectedBrushes();
642         world.LoadSelectedPatches();
643         world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );
644 }
645
646 void DoTreePlanter() {
647   UndoableCommand undo("bobToolz.treePlanter");
648         if(g_TreePlanter) {
649                 delete g_TreePlanter;
650                 g_TreePlanter = NULL;
651                 return;
652         }
653
654         g_TreePlanter = new DTreePlanter();
655 }
656
657 void DoDropEnts() {
658   UndoableCommand undo("bobToolz.dropEntities");
659         if(g_TreePlanter) {
660                 g_TreePlanter->DropEntsToGround();
661         }
662 }
663
664 void DoMakeChain() {
665   UndoableCommand undo("bobToolz.makeChain");
666         DTreePlanter pl;
667         pl.MakeChain();
668 }
669
670 typedef DPoint* pntTripple[3];
671
672 bool bFacesNoTop[6] = {true, true, true, true, true, false};
673
674 void DoFlipTerrain() {
675   UndoableCommand undo("bobToolz.flipTerrain");
676         vec3_t vUp = { 0.f, 0.f, 1.f };
677   int i;
678
679         // ensure we have something selected
680         if( GlobalSelectionSystem().countSelected() != 2 )
681         {
682                 DoMessageBox("Invalid number of objects selected, choose 2 only", "Error", eMB_OK);
683                 return; 
684         }
685
686   scene::Instance* brushes[2];
687         brushes[0] = &GlobalSelectionSystem().ultimateSelected();
688         brushes[1] = &GlobalSelectionSystem().penultimateSelected();
689
690         DBrush Brushes[2];
691         DPlane* Planes[2];
692         pntTripple Points[2];
693         for( i = 0; i < 2; i++ ) {
694                 Brushes[i].LoadFromBrush( *brushes[i], false );
695                 if(!(Planes[i] = Brushes[i].FindPlaneWithClosestNormal( vUp )) || Brushes[i].FindPointsForPlane( Planes[i], Points[i], 3 ) != 3) {
696                         DoMessageBox("Error", "Error", eMB_OK);
697                         return;
698                 }
699         }
700
701         vec3_t mins1, mins2, maxs1, maxs2;
702         Brushes[0].GetBounds( mins1, maxs1 );
703         Brushes[1].GetBounds( mins2, maxs2 );
704
705
706
707         int dontmatch[2] = { -1, -1 };
708         bool found = false;
709         for( i = 0; i < 3; i++ ) {
710                 for( int j = 0; j < 3 && !found; j++ ) {
711                         if(VectorCompare( (Points[0])[i]->_pnt, (Points[1])[j]->_pnt )) {
712                                 found = true;
713                                 break;
714                         }
715                 }
716                 if(!found) {
717                         dontmatch[0] = i;
718                         break;
719                 }
720                 found = false;
721         }
722         if(dontmatch[0] == -1) {
723                 DoMessageBox("Error", "Error", eMB_OK);
724                 return;
725         }
726
727         for( i = 0; i < 3; i++ ) {
728                 for( int j = 0; j < 3 && !found; j++ ) {
729                         if(VectorCompare( (Points[1])[i]->_pnt, (Points[0])[j]->_pnt )) {
730                                 found = true;
731                                 break;
732                         }
733                 }
734                 if(!found) {
735                         dontmatch[1] = i;
736                         break;
737                 }
738                 found = false;
739         }
740         if(dontmatch[1] == -1) {
741                 DoMessageBox("Error", "Error", eMB_OK);
742                 return;
743         }
744
745         vec3_t plnpnts1[3];
746         vec3_t plnpnts2[3];
747         vec3_t plnpntsshr[3];
748
749         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts1[0] );
750         for( i = 0; i < 3; i++ ) {
751                 if( dontmatch[0] != i ) {
752                         VectorCopy( (Points[0])[i]->_pnt, plnpnts1[1] );
753                         break;
754                 }
755         }
756         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts1[2] );
757
758         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts2[0] );
759         for( i = 0; i < 3; i++ ) {
760                 if( dontmatch[1] != i && !VectorCompare( (Points[1])[i]->_pnt, plnpnts1[1] )) {
761                         VectorCopy( (Points[1])[i]->_pnt, plnpnts2[1] );
762                         break;
763                 }
764         }
765         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts2[2] );
766
767         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[0] );
768         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[1] );
769         if( (Points[1])[dontmatch[1]]->_pnt[2] < (Points[0])[dontmatch[0]]->_pnt[2] ) {
770                 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[2] );
771         } else {
772                 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[2] );
773         }
774         plnpntsshr[2][2] -= 16;
775
776         for( i = 0; i < 3; i++ ) {
777                 if( mins2[i] < mins1[i] ) {
778                         mins1[i] = mins2[i];
779                 }
780                 if( maxs2[i] > maxs1[i] ) {
781                         maxs1[i] = maxs2[i];
782                 }
783         }
784
785         DBrush* newBrushes[2];
786         newBrushes[0] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
787         newBrushes[1] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
788
789         vec3_t normal;
790         MakeNormal( plnpnts1[0], plnpnts1[1], plnpnts1[2], normal );
791         if( normal[2] >= 0 ) {
792                 newBrushes[0]->AddFace( plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true );
793         } else {
794                 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );
795         }
796
797         MakeNormal( plnpnts2[0], plnpnts2[1], plnpnts2[2], normal );
798         if( normal[2] >= 0 ) {
799                 newBrushes[1]->AddFace( plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true );
800         } else {
801                 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );
802         }
803
804         vec3_t vec;
805         MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );      
806         
807         VectorSubtract( plnpnts1[2], plnpnts1[1], vec );
808         if( DotProduct( vec, normal ) >= 0 ) {
809                 newBrushes[0]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
810         } else {
811                 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
812         }
813
814         VectorSubtract( plnpnts2[2], plnpnts2[1], vec );
815         if( DotProduct( vec, normal ) >= 0 ) {
816                 newBrushes[1]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
817         } else {
818                 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
819         }
820
821         for( i = 0; i < 2; i++ ) {
822                 newBrushes[i]->RemoveRedundantPlanes();
823                 newBrushes[i]->BuildInRadiant( false, NULL, brushes[i]->path().parent().get_pointer() );
824                 Path_deleteTop(brushes[i]->path());
825                 delete newBrushes[i];
826         }
827
828 }