]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/bobtoolz/funchandlers-GTK.cpp
changes 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                         globalErrorStream() << "bobToolz Intersect: Invalid number of brushes selected, choose at least 2.\n";
107                         return; 
108                 }
109         }
110
111         DEntity world;
112         switch(rs.nBrushOptions)
113         {
114         case BRUSH_OPT_SELECTED:
115                 {
116                         
117                         world.LoadFromEntity(GlobalRadiant().getMapWorldEntity(), false);
118                         world.LoadSelectedBrushes();
119                         break;
120                 }
121         case BRUSH_OPT_WHOLE_MAP:
122                 {
123                         world.LoadFromEntity(GlobalRadiant().getMapWorldEntity(), false);
124                         break;
125                 }
126         }
127         world.RemoveNonCheckBrushes(&exclusionList, rs.bUseDetail);
128
129         bool* pbSelectList;
130         if(rs.bDuplicateOnly)
131                 pbSelectList = world.BuildDuplicateList();
132         else
133                 pbSelectList = world.BuildIntersectList();
134
135         world.SelectBrushes(pbSelectList);
136         int brushCount = GlobalSelectionSystem().countSelected();
137         globalOutputStream() << "bobToolz Intersect: " << brushCount << " intersecting brushes found.\n";
138         delete[] pbSelectList;
139 }
140
141 void DoPolygonsTB()
142 {
143   DoPolygons();
144 }
145
146 void DoPolygons()
147 {
148   UndoableCommand undo("bobToolz.polygons");
149         // ensure we have something selected
150         if( GlobalSelectionSystem().countSelected() != 1 )
151         {
152                 //DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
153                 globalErrorStream() << "bobToolz Polygons: Invalid number of brushes selected, choose 1 only.\n";
154                 return; 
155         }
156
157         PolygonRS rs;
158         scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
159         if( !Node_isBrush(instance.path().top()) ) {
160                 //DoMessageBox("No brush selected, select ONLY one brush", "Error", eMB_OK);
161                 globalErrorStream() << "bobToolz Polygons: No brush selected, select ONLY one brush.\n";
162                 return; 
163         }
164         // ask user for type, size, etc....
165         if(DoPolygonBox(&rs) == eIDOK)
166         {
167                 DShape poly;
168
169     vec3_t vMin, vMax;
170
171     {
172
173       VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
174       VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
175
176       Path_deleteTop(instance.path());
177     }
178
179                 if(rs.bInverse)
180                         poly.BuildInversePrism(vMin, vMax, rs.nSides, rs.bAlignTop);
181                 else
182                 {
183                         if(rs.bUseBorder)
184                                 poly.BuildBorderedPrism(vMin, vMax, rs.nSides, rs.nBorderWidth, rs.bAlignTop);
185                         else
186                                 poly.BuildRegularPrism(vMin, vMax, rs.nSides, rs.bAlignTop);
187
188                 }
189
190                 poly.Commit();
191         }
192 }
193
194 void DoFixBrushes()
195 {
196   UndoableCommand undo("bobToolz.fixBrushes");
197         DMap world;
198         world.LoadAll();
199
200         int count = world.FixBrushes();
201         
202         globalOutputStream() << "bobToolz FixBrushes: " << count << " invalid/duplicate planes removed.\n";
203 }
204
205 void DoResetTextures()
206 {
207   UndoableCommand undo("bobToolz.resetTextures");
208         static ResetTextureRS rs;
209
210   const char* texName;
211         if(1/*g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 1*/)
212   {
213     texName = NULL;
214   }
215   else
216   {
217     texName = GetCurrentTexture();
218           strcpy(rs.textureName, GetCurrentTexture());
219   }
220
221   EMessageBoxReturn ret;
222         if((ret = DoResetTextureBox(&rs)) == eIDCANCEL)
223                 return;  
224
225   if(rs.bResetTextureName)
226     texName = rs.textureName;
227
228   if(ret == eIDOK)
229   {
230           DEntity world;
231           world.LoadSelectedBrushes();
232           world.ResetTextures(texName,              rs.fScale,      rs.fShift,      rs.rotation, rs.newTextureName, 
233                         rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation, true);
234   }
235   else
236   {
237           DMap world;
238     world.LoadAll(true);
239     world.ResetTextures(texName,              rs.fScale,      rs.fShift,      rs.rotation, rs.newTextureName, 
240                         rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation);
241   }
242 }
243
244 void DoBuildStairs()
245 {
246   UndoableCommand undo("bobToolz.buildStairs");
247         BuildStairsRS rs;
248
249         strcpy(rs.mainTexture, GetCurrentTexture());
250
251         // ensure we have something selected
252         if( GlobalSelectionSystem().countSelected() != 1 )
253         {
254                 //DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
255                 globalErrorStream() << "bobToolz BuildStairs: Invalid number of brushes selected, choose 1 only.\n";
256                 return; 
257         }
258
259         // ask user for type, size, etc....
260         if(DoBuildStairsBox(&rs) == eIDOK)
261         {
262     vec3_t vMin, vMax;
263
264     {
265       scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
266       VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
267       VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
268     }
269
270                 // calc brush size
271                 vec3_t size;
272                 VectorSubtract(vMax, vMin, size);
273
274                 if(((int)size[2] % rs.stairHeight) != 0)
275                 {
276                         // stairs must fit evenly into brush
277                         //DoMessageBox("Invalid stair height\nHeight of block must be divisable by stair height", "Error", eMB_OK);
278                         globalErrorStream() << "bobToolz BuildStairs: Invalid stair height. Height of block must be divisable by stair height.\n";
279                 }
280                 else
281                 {
282       {
283         scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
284         Path_deleteTop(instance.path());
285       }
286                                                 
287                         // Get Step Count
288                         int numSteps = (int)size[2] / rs.stairHeight;
289                         
290                         if(rs.style == STYLE_CORNER)
291                         {
292                                 BuildCornerStairs(vMin, vMax, numSteps, rs.mainTexture, rs.riserTexture);
293                         }
294                         else
295                         {
296
297                                 // Get Step Dimensions
298                                 float stairHeight = (float)rs.stairHeight;
299                                 float stairWidth;
300                                 if((rs.direction == MOVE_EAST) || (rs.direction == MOVE_WEST))
301                                         stairWidth = (size[0])/numSteps;
302                                 else
303                                         stairWidth = (size[1])/numSteps;
304
305
306                                 // Build Base For Stair (bob's style)
307                                 if(rs.style == STYLE_BOB)
308                                         Build_Wedge(rs.direction, vMin, vMax, true);
309
310
311                                 // Set First Step Starting Position
312                                 vMax[2] = vMin[2] + stairHeight;
313                                 SetInitialStairPos(rs.direction, vMin, vMax, stairWidth);
314
315
316                                 // Build The Steps
317                                 for(int i = 0; i < numSteps; i++)
318                                 {
319                                         if(rs.style == STYLE_BOB)
320                                                 Build_StairStep_Wedge(rs.direction, vMin, vMax, rs.mainTexture, rs.riserTexture, rs.bUseDetail);
321                                         else if(rs.style == STYLE_ORIGINAL)
322                                                 Build_StairStep(vMin, vMax, rs.mainTexture, rs.riserTexture, rs.direction);
323
324                                         // get step into next position
325                                         MoveBlock(rs.direction, vMin, vMax, stairWidth);
326                                         vMax[2] += stairHeight;
327                                         if(rs.style == STYLE_BOB)
328                                                 vMin[2] += stairHeight; // wedge bottom must be raised
329                                 }
330                         }
331                 }
332         }
333 }
334
335 void DoBuildDoors()
336 {
337   UndoableCommand undo("bobToolz.buildDoors");
338         // ensure we have something selected
339         if( GlobalSelectionSystem().countSelected() != 1 )
340         {
341                 //DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
342                 globalErrorStream() << "bobToolz BuildDoors: Invalid number of brushes selected, choose 1 only.\n";
343                 return; 
344         }
345
346   DoorRS rs;
347         strcpy(rs.mainTexture, GetCurrentTexture());
348
349         if(DoDoorsBox(&rs) == eIDOK)
350         {
351     vec3_t vMin, vMax;
352
353     {
354       scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
355       VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
356       VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
357       Path_deleteTop(instance.path());
358     }
359
360                 BuildDoorsX2(vMin, vMax, 
361                         rs.bScaleMainH, rs.bScaleMainV,
362                         rs.bScaleTrimH, rs.bScaleTrimV,
363                         rs.mainTexture, rs.trimTexture,
364                         rs.nOrientation);       // shapes.cpp
365         }
366 }
367
368 void DoPathPlotter()
369 {
370   UndoableCommand undo("bobToolz.pathPlotter");
371         PathPlotterRS rs;
372         EMessageBoxReturn ret = DoPathPlotterBox(&rs);
373         if(ret == eIDCANCEL)
374                 return;
375         if(ret == eIDNO)
376         {
377                 if(g_PathView)
378                         delete g_PathView;
379                 return;
380         }
381
382         // ensure we have something selected
383         /*
384          if( GlobalSelectionSystem().countSelected() != 1 )
385         {
386                 //DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
387                 globalOutputStream() << "bobToolz PathPlotter: Invalid number of entities selected, choose 1 trigger_push entity only.\n";
388                 return; 
389         }
390          */
391   Entity* entity = Node_getEntity(GlobalSelectionSystem().ultimateSelected().path().top());
392   if(entity != 0)
393   {
394     DBobView_setEntity(*entity, rs.fMultiplier, rs.nPoints, rs.fGravity, rs.bNoUpdate, rs.bShowExtra);
395   } 
396   else globalErrorStream() << "bobToolz PathPlotter: No trigger_push entitity selected, select 1 only (Use list to select it).\n";
397         return;
398 }
399
400 void DoPitBuilder()
401 {
402         UndoableCommand undo("bobToolz.pitBuilder");
403         // ensure we have something selected
404         if( GlobalSelectionSystem().countSelected() != 1 )
405         {
406                 //DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
407                 globalErrorStream() << "bobToolz PitBuilder: Invalid number of brushes selected, choose 1 only.\n";
408                 return; 
409         }
410
411         vec3_t vMin, vMax;
412
413         scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
414         //seems it does this also with a patch with valid dimensions.. but probably better to enforce a brush.
415         if( !Node_isBrush(instance.path().top()) ) {
416                 //DoMessageBox("No brush selected, select ONLY one brush", "Error", eMB_OK);
417                 globalErrorStream() << "bobToolz PitBuilder: No brush selected, select ONLY 1 brush.\n";
418                 return; 
419         }
420         
421         VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
422         VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
423
424         DShape pit;
425
426         if(pit.BuildPit(vMin, vMax))
427         {
428                 pit.Commit();
429                 Path_deleteTop(instance.path());
430         }
431         else
432         {
433                 //DoMessageBox("Failed To Make Pit\nTry Making The Brush Bigger", "Error", eMB_OK);
434                 globalErrorStream() << "bobToolz PitBuilder: Failed to make Pit, try making the brush bigger.\n";
435         }
436 }
437
438 void DoMergePatches()
439 {
440         UndoableCommand undo("bobToolz.mergePatches");
441         patch_merge_t merge_info;
442         DPatch mrgPatches[2];
443         int i;
444
445         // ensure we have something selected
446         if( GlobalSelectionSystem().countSelected() != 2 )
447         {
448                 globalErrorStream() << "bobToolz MergePatches: Invalid number of patches selected, choose 2 only.\n";
449                 //DoMessageBox("Invalid number of patches selected, choose 2 only", "Error", eMB_OK);
450                 return; 
451         }
452
453         scene::Instance* patches[2];
454         patches[0] = &GlobalSelectionSystem().ultimateSelected();
455         patches[1] = &GlobalSelectionSystem().penultimateSelected();
456
457         for (i = 0; i < 2; i++)
458         {
459                 if (!Node_isPatch(patches[i]->path().top()))
460                 {
461                         //DoMessageBox("No patches selected, select ONLY patches", "Error", eMB_OK);
462                         globalErrorStream() << "bobToolz MergePatches: Invalid number of patches selected, choose ONLY 2 patches.\n";
463                         return; 
464                 }
465
466                 mrgPatches[i].LoadFromPatch(*patches[i]);
467         }
468
469   /*  mrgPatches[0].Transpose();
470       mrgPatches[0].RemoveFromRadiant();
471       mrgPatches[0].BuildInRadiant();*/
472
473         merge_info = mrgPatches[0].IsMergable(&mrgPatches[1]);
474
475         if (merge_info.mergable)
476         {
477                 globalOutputStream() << merge_info.pos1 << " " <<  merge_info.pos2;
478                 //Message removed, No tools give feedback on success.
479                 //globalOutputStream() << "bobToolz MergePatches: Patches Mergable.\n";
480                 DPatch* newPatch = mrgPatches[0].MergePatches(merge_info, &mrgPatches[0], &mrgPatches[1]);
481
482     /*                mrgPatches[0].RemoveFromRadiant();
483     mrgPatches[0].BuildInRadiant();
484     
485       mrgPatches[1].RemoveFromRadiant();
486       mrgPatches[1].BuildInRadiant();
487       
488         
489     delete newPatch;*/
490
491                 if (!newPatch)
492                 {
493                 } else
494                 {
495                         Path_deleteTop(patches[0]->path());
496                         Path_deleteTop(patches[1]->path());
497
498                         newPatch->BuildInRadiant();
499                         delete newPatch;
500                 }
501         }
502         else
503         {
504                 globalErrorStream() << "bobToolz.mergePatch: The selected patches are not mergable.\n";
505                 
506         }
507 }
508
509 void DoSplitPatch() {
510   UndoableCommand undo("bobToolz.splitPatch");
511
512         DPatch patch;
513
514         // ensure we have something selected
515         if( GlobalSelectionSystem().countSelected() != 1 )
516         {
517                 //DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
518                 globalErrorStream() << "bobToolz SplitPatch: Invalid number of patches selected, choose only 1 patch.\n";
519                 return; 
520         }
521
522   scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
523
524         if( !Node_isPatch(instance.path().top()) ) {
525                 //DoMessageBox("No patch selected, select ONLY one patch", "Error", eMB_OK);
526                 globalErrorStream() << "bobToolz SplitPatch: No patch selected, select ONLY 1 patch.\n";
527                 return; 
528         }
529
530         patch.LoadFromPatch(instance);
531
532         std::list<DPatch> patchList = patch.Split();
533         for(std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
534                 (*patches).BuildInRadiant();
535         }
536
537         Path_deleteTop(instance.path());
538 }
539
540 void DoSplitPatchCols() {
541         UndoableCommand undo("bobToolz.splitPatchCols");
542         
543         DPatch patch;
544         
545         // ensure we have something selected
546         if( GlobalSelectionSystem().countSelected() != 1 )
547         {
548                 //DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
549                 globalErrorStream() << "bobToolz SplitPatchCols: Invalid number of patches selected, choose 1 only.\n";
550                 return; 
551         }
552         
553         scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
554         
555         if( !Node_isPatch(instance.path().top()) ) {
556                 //DoMessageBox("No patch selected, select ONLY one patch", "Error", eMB_OK);
557                 globalErrorStream() << "bobToolz SplitPatchCols: No patch selected, select ONLY 1 patch.\n";
558                 return; 
559         }
560         
561         patch.LoadFromPatch(instance);
562         
563         std::list<DPatch> patchList = patch.SplitCols();
564         for(std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
565                 (*patches).BuildInRadiant();
566         }
567         
568         Path_deleteTop(instance.path());
569 }
570
571 void DoSplitPatchRows() {
572         UndoableCommand undo("bobToolz.splitPatchRows");
573         
574         DPatch patch;
575         
576         // ensure we have something selected
577         if( GlobalSelectionSystem().countSelected() != 1 )
578         {
579                 //DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
580                 globalErrorStream() << "bobToolz SplitPatchRows: Invalid number of patches selected, choose 1 only.\n";
581                 return; 
582         }
583         
584         scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
585         
586         if( !Node_isPatch(instance.path().top()) ) {
587                 //DoMessageBox("No patch selected, select ONLY one patch", "Error", eMB_OK);
588                 globalErrorStream() << "bobToolz SplitPatchRows: No patch selected, select ONLY 1 patch.\n";
589                 return; 
590         }
591         
592         patch.LoadFromPatch(instance);
593         
594         std::list<DPatch> patchList = patch.SplitRows();
595         for(std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
596                 (*patches).BuildInRadiant();
597         }
598         
599         Path_deleteTop(instance.path());
600 }
601
602 void DoVisAnalyse()
603 {
604         char filename[1024];
605
606         if( GlobalSelectionSystem().countSelected() == 0 )
607         {
608                 globalErrorStream() << "bobToolz VisAnalyse: Invalid number of objects selected, choose 1 only.\n";
609                 if(g_VisView) 
610                 {
611                         delete g_VisView;
612                         return;
613                 }
614         }
615
616         // ensure we have something selected
617         if( GlobalSelectionSystem().countSelected() != 1 )
618         {
619                 //DoMessageBox("Invalid number of objects selected, choose 1 only", "Error", eMB_OK);
620                 globalErrorStream() << "bobToolz VisAnalyse: Invalid number of objects selected, choose 1 only.\n";
621                 return; 
622         }
623         
624         scene::Instance& brush = GlobalSelectionSystem().ultimateSelected();
625         //ensure we have a brush selected
626         if( !Node_isBrush(brush.path().top()) ) {
627                 //DoMessageBox("No brush selected, select ONLY one brush", "Error", eMB_OK);
628                 globalErrorStream() << "bobToolz VisAnalyse: No brush selected, select ONLY 1 brush.\n";
629                 return; 
630         }
631         DBrush orgBrush;
632         orgBrush.LoadFromBrush(brush, false);
633
634         orgBrush.BuildBounds();
635         vec3_t origin;
636         origin[0] = (orgBrush.bbox_max[0] + orgBrush.bbox_min[0])/2.f;
637         origin[1] = (orgBrush.bbox_max[1] + orgBrush.bbox_min[1])/2.f;
638         origin[2] = (orgBrush.bbox_max[2] + orgBrush.bbox_min[2])/2.f;
639
640
641   const char* rad_filename = GlobalRadiant().getMapName();
642         if(!rad_filename)
643         {
644                 //DoMessageBox("An ERROR occurred while trying\n to get the map filename", "Error", eMB_OK);
645                 globalErrorStream() << "bobToolz VisAnalyse: An ERROR occurred while trying to get the map filename.\n";
646                 return;
647         }
648
649         strcpy(filename, rad_filename);
650                 
651         char* ext = strrchr(filename, '.')+1;
652         strcpy(ext, "bsp");// rename the extension
653
654         std::list<DWinding*> *pointList = BuildTrace(filename, origin);
655
656         if(!g_VisView)
657         {
658                 g_VisView = new DVisDrawer;
659         }
660         
661         g_VisView->SetList(pointList);
662 }
663
664 void DoTrainPathPlot() {
665         if(g_TrainView) {
666                 delete g_TrainView;
667                 g_TrainView = NULL;
668         }
669
670         g_TrainView = new DTrainDrawer();
671 }
672
673 void DoCaulkSelection() {
674   UndoableCommand undo("bobToolz.caulkSelection");
675         DEntity world;
676         
677         float fScale[2] = { 0.5f, 0.5f };
678         float fShift[2] = { 0.0f, 0.0f };
679
680         int bResetScale[2] = { false, false };
681         int bResetShift[2] = { false, false };
682
683         world.LoadSelectedBrushes();
684         world.LoadSelectedPatches();
685         world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );
686 }
687
688 void DoTreePlanter() {
689   UndoableCommand undo("bobToolz.treePlanter");
690         if(g_TreePlanter) {
691                 delete g_TreePlanter;
692                 g_TreePlanter = NULL;
693                 return;
694         }
695
696         g_TreePlanter = new DTreePlanter();
697 }
698
699 void DoDropEnts() {
700   UndoableCommand undo("bobToolz.dropEntities");
701         if(g_TreePlanter) {
702                 g_TreePlanter->DropEntsToGround();
703         }
704 }
705
706 void DoMakeChain() {
707         MakeChainRS rs;
708         if(DoMakeChainBox(&rs) == eIDOK)
709         {
710                 if ( rs.linkNum > 1001 ) {
711                         globalErrorStream() << "bobToolz MakeChain: " << rs.linkNum << " to many Elemets, limited to 1000.\n";
712                         return;
713                 }
714                 UndoableCommand undo("bobToolz.makeChain");
715                 DTreePlanter pl;
716                 pl.MakeChain(rs.linkNum,rs.linkName);
717         }
718 }
719
720 typedef DPoint* pntTripple[3];
721
722 bool bFacesNoTop[6] = {true, true, true, true, true, false};
723
724 void DoFlipTerrain() {
725   UndoableCommand undo("bobToolz.flipTerrain");
726         vec3_t vUp = { 0.f, 0.f, 1.f };
727   int i;
728
729         // ensure we have something selected
730         if( GlobalSelectionSystem().countSelected() != 2 )
731         {
732                 //DoMessageBox("Invalid number of objects selected, choose 2 only", "Error", eMB_OK);
733                 globalErrorStream() << "bobToolz FlipTerrain: Invalid number of objects selected, choose 2 only.\n";
734                 return; 
735         }
736
737   scene::Instance* brushes[2];
738         brushes[0] = &GlobalSelectionSystem().ultimateSelected();
739         brushes[1] = &GlobalSelectionSystem().penultimateSelected();
740         //ensure we have only Brushes selected.
741         for (i = 0; i < 2; i++)
742         {
743                 if( !Node_isBrush(brushes[i]->path().top()) ) {
744                         //DoMessageBox("No brushes selected, select ONLY brushes", "Error", eMB_OK);
745                         globalErrorStream() << "bobToolz FlipTerrain: No brushes selected, select ONLY 2 brushes.\n";
746                         return; 
747                 }
748         }
749         DBrush Brushes[2];
750         DPlane* Planes[2];
751         pntTripple Points[2];
752         for( i = 0; i < 2; i++ ) {
753                 Brushes[i].LoadFromBrush( *brushes[i], false );
754                 if(!(Planes[i] = Brushes[i].FindPlaneWithClosestNormal( vUp )) || Brushes[i].FindPointsForPlane( Planes[i], Points[i], 3 ) != 3) {
755                         //DoMessageBox("Error", "Error", eMB_OK);
756                         globalErrorStream() << "bobToolz FlipTerrain: ERROR (FindPlaneWithClosestNormal/FindPointsForPlane).\n";
757                         return;
758                 }
759         }
760
761         vec3_t mins1, mins2, maxs1, maxs2;
762         Brushes[0].GetBounds( mins1, maxs1 );
763         Brushes[1].GetBounds( mins2, maxs2 );
764
765
766
767         int dontmatch[2] = { -1, -1 };
768         bool found = false;
769         for( i = 0; i < 3; i++ ) {
770                 for( int j = 0; j < 3 && !found; j++ ) {
771                         if(VectorCompare( (Points[0])[i]->_pnt, (Points[1])[j]->_pnt )) {
772                                 found = true;
773                                 break;
774                         }
775                 }
776                 if(!found) {
777                         dontmatch[0] = i;
778                         break;
779                 }
780                 found = false;
781         }
782         if(dontmatch[0] == -1) {
783                 //DoMessageBox("Error", "Error", eMB_OK);
784                 globalErrorStream() << "bobToolz FlipTerrain: ERROR (dontmatch[0]).\n";
785                 return;
786         }
787
788         for( i = 0; i < 3; i++ ) {
789                 for( int j = 0; j < 3 && !found; j++ ) {
790                         if(VectorCompare( (Points[1])[i]->_pnt, (Points[0])[j]->_pnt )) {
791                                 found = true;
792                                 break;
793                         }
794                 }
795                 if(!found) {
796                         dontmatch[1] = i;
797                         break;
798                 }
799                 found = false;
800         }
801         if(dontmatch[1] == -1) {
802                 //DoMessageBox("Error", "Error", eMB_OK);
803                 globalErrorStream() << "bobToolz FlipTerrain: ERROR (dontmatch[1]).\n";
804                 return;
805         }
806
807         vec3_t plnpnts1[3];
808         vec3_t plnpnts2[3];
809         vec3_t plnpntsshr[3];
810
811         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts1[0] );
812         for( i = 0; i < 3; i++ ) {
813                 if( dontmatch[0] != i ) {
814                         VectorCopy( (Points[0])[i]->_pnt, plnpnts1[1] );
815                         break;
816                 }
817         }
818         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts1[2] );
819
820         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts2[0] );
821         for( i = 0; i < 3; i++ ) {
822                 if( dontmatch[1] != i && !VectorCompare( (Points[1])[i]->_pnt, plnpnts1[1] )) {
823                         VectorCopy( (Points[1])[i]->_pnt, plnpnts2[1] );
824                         break;
825                 }
826         }
827         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts2[2] );
828
829         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[0] );
830         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[1] );
831         if( (Points[1])[dontmatch[1]]->_pnt[2] < (Points[0])[dontmatch[0]]->_pnt[2] ) {
832                 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[2] );
833         } else {
834                 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[2] );
835         }
836         plnpntsshr[2][2] -= 16;
837
838         for( i = 0; i < 3; i++ ) {
839                 if( mins2[i] < mins1[i] ) {
840                         mins1[i] = mins2[i];
841                 }
842                 if( maxs2[i] > maxs1[i] ) {
843                         maxs1[i] = maxs2[i];
844                 }
845         }
846
847         DBrush* newBrushes[2];
848         newBrushes[0] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
849         newBrushes[1] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
850
851         vec3_t normal;
852         MakeNormal( plnpnts1[0], plnpnts1[1], plnpnts1[2], normal );
853         if( normal[2] >= 0 ) {
854                 newBrushes[0]->AddFace( plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true );
855         } else {
856                 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );
857         }
858
859         MakeNormal( plnpnts2[0], plnpnts2[1], plnpnts2[2], normal );
860         if( normal[2] >= 0 ) {
861                 newBrushes[1]->AddFace( plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true );
862         } else {
863                 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );
864         }
865
866         vec3_t vec;
867         MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );      
868         
869         VectorSubtract( plnpnts1[2], plnpnts1[1], vec );
870         if( DotProduct( vec, normal ) >= 0 ) {
871                 newBrushes[0]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
872         } else {
873                 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
874         }
875
876         VectorSubtract( plnpnts2[2], plnpnts2[1], vec );
877         if( DotProduct( vec, normal ) >= 0 ) {
878                 newBrushes[1]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
879         } else {
880                 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
881         }
882
883         for( i = 0; i < 2; i++ ) {
884                 newBrushes[i]->RemoveRedundantPlanes();
885                 newBrushes[i]->BuildInRadiant( false, NULL, brushes[i]->path().parent().get_pointer() );
886                 Path_deleteTop(brushes[i]->path());
887                 delete newBrushes[i];
888         }
889
890 }