Merge branch '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 DoSplitPatchCols() {
603         UndoableCommand undo("bobToolz.splitPatchCols");
604         
605         DPatch patch;
606         
607         // ensure we have something selected
608         if( GlobalSelectionSystem().countSelected() != 1 )
609         {
610                 DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
611                 return; 
612         }
613         
614         scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
615         
616         if( !Node_isPatch(instance.path().top()) ) {
617                 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
618                 return; 
619         }
620         
621         patch.LoadFromPatch(instance);
622         
623         std::list<DPatch> patchList = patch.SplitCols();
624         for(std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
625                 (*patches).BuildInRadiant();
626         }
627         
628         Path_deleteTop(instance.path());
629 }
630
631 void DoSplitPatchRows() {
632         UndoableCommand undo("bobToolz.splitPatchRows");
633         
634         DPatch patch;
635         
636         // ensure we have something selected
637         if( GlobalSelectionSystem().countSelected() != 1 )
638         {
639                 DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
640                 return; 
641         }
642         
643         scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
644         
645         if( !Node_isPatch(instance.path().top()) ) {
646                 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
647                 return; 
648         }
649         
650         patch.LoadFromPatch(instance);
651         
652         std::list<DPatch> patchList = patch.SplitRows();
653         for(std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
654                 (*patches).BuildInRadiant();
655         }
656         
657         Path_deleteTop(instance.path());
658 }
659
660 void DoVisAnalyse()
661 {
662         char filename[1024];
663
664         if( GlobalSelectionSystem().countSelected() == 0 )
665         {
666                 globalErrorStream() << "bobToolz VisAnalyse: Invalid number of objects selected, choose 1 only.\n";
667                 if(g_VisView) 
668                 {
669                         delete g_VisView;
670                         return;
671                 }
672         }
673
674         // ensure we have something selected
675         if( GlobalSelectionSystem().countSelected() != 1 )
676         {
677                 //DoMessageBox("Invalid number of objects selected, choose 1 only", "Error", eMB_OK);
678                 globalErrorStream() << "bobToolz VisAnalyse: Invalid number of objects selected, choose 1 only.\n";
679                 return; 
680         }
681         
682         scene::Instance& brush = GlobalSelectionSystem().ultimateSelected();
683         //ensure we have a brush selected
684         if( !Node_isBrush(brush.path().top()) ) {
685                 //DoMessageBox("No brush selected, select ONLY one brush", "Error", eMB_OK);
686                 globalErrorStream() << "bobToolz VisAnalyse: No brush selected, select ONLY 1 brush.\n";
687                 return; 
688         }
689         DBrush orgBrush;
690         orgBrush.LoadFromBrush(brush, false);
691
692         orgBrush.BuildBounds();
693         vec3_t origin;
694         origin[0] = (orgBrush.bbox_max[0] + orgBrush.bbox_min[0])/2.f;
695         origin[1] = (orgBrush.bbox_max[1] + orgBrush.bbox_min[1])/2.f;
696         origin[2] = (orgBrush.bbox_max[2] + orgBrush.bbox_min[2])/2.f;
697
698
699   const char* rad_filename = GlobalRadiant().getMapName();
700         if(!rad_filename)
701         {
702                 //DoMessageBox("An ERROR occurred while trying\n to get the map filename", "Error", eMB_OK);
703                 globalErrorStream() << "bobToolz VisAnalyse: An ERROR occurred while trying to get the map filename.\n";
704                 return;
705         }
706
707         strcpy(filename, rad_filename);
708                 
709         char* ext = strrchr(filename, '.')+1;
710         strcpy(ext, "bsp");// rename the extension
711
712         std::list<DWinding*> *pointList = BuildTrace(filename, origin);
713
714         if(!g_VisView)
715         {
716                 g_VisView = new DVisDrawer;
717         }
718         
719         g_VisView->SetList(pointList);
720 }
721
722 void DoTrainPathPlot() {
723         if(g_TrainView) {
724                 delete g_TrainView;
725                 g_TrainView = NULL;
726         }
727
728         g_TrainView = new DTrainDrawer();
729 }
730
731 void DoCaulkSelection() {
732   UndoableCommand undo("bobToolz.caulkSelection");
733         DEntity world;
734         
735         float fScale[2] = { 0.5f, 0.5f };
736         float fShift[2] = { 0.0f, 0.0f };
737
738         int bResetScale[2] = { false, false };
739         int bResetShift[2] = { false, false };
740
741         world.LoadSelectedBrushes();
742         world.LoadSelectedPatches();
743         world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );
744 }
745
746 void DoTreePlanter() {
747   UndoableCommand undo("bobToolz.treePlanter");
748         if(g_TreePlanter) {
749                 delete g_TreePlanter;
750                 g_TreePlanter = NULL;
751                 return;
752         }
753
754         g_TreePlanter = new DTreePlanter();
755 }
756
757 void DoDropEnts() {
758   UndoableCommand undo("bobToolz.dropEntities");
759         if(g_TreePlanter) {
760                 g_TreePlanter->DropEntsToGround();
761         }
762 }
763
764 void DoMakeChain() {
765         MakeChainRS rs;
766         if(DoMakeChainBox(&rs) == eIDOK)
767         {
768                 if ( rs.linkNum > 1001 ) {
769                         globalErrorStream() << "bobToolz MakeChain: " << rs.linkNum << " to many Elemets, limited to 1000.\n";
770                         return;
771                 }
772                 UndoableCommand undo("bobToolz.makeChain");
773                 DTreePlanter pl;
774                 pl.MakeChain(rs.linkNum,rs.linkName);
775         }
776 }
777
778 typedef DPoint* pntTripple[3];
779
780 bool bFacesNoTop[6] = {true, true, true, true, true, false};
781
782 void DoFlipTerrain() {
783   UndoableCommand undo("bobToolz.flipTerrain");
784         vec3_t vUp = { 0.f, 0.f, 1.f };
785   int i;
786
787         // ensure we have something selected
788         if( GlobalSelectionSystem().countSelected() != 2 )
789         {
790                 //DoMessageBox("Invalid number of objects selected, choose 2 only", "Error", eMB_OK);
791                 globalErrorStream() << "bobToolz FlipTerrain: Invalid number of objects selected, choose 2 only.\n";
792                 return; 
793         }
794
795   scene::Instance* brushes[2];
796         brushes[0] = &GlobalSelectionSystem().ultimateSelected();
797         brushes[1] = &GlobalSelectionSystem().penultimateSelected();
798         //ensure we have only Brushes selected.
799         for (i = 0; i < 2; i++)
800         {
801                 if( !Node_isBrush(brushes[i]->path().top()) ) {
802                         //DoMessageBox("No brushes selected, select ONLY brushes", "Error", eMB_OK);
803                         globalErrorStream() << "bobToolz FlipTerrain: No brushes selected, select ONLY 2 brushes.\n";
804                         return; 
805                 }
806         }
807         DBrush Brushes[2];
808         DPlane* Planes[2];
809         pntTripple Points[2];
810         for( i = 0; i < 2; i++ ) {
811                 Brushes[i].LoadFromBrush( *brushes[i], false );
812                 if(!(Planes[i] = Brushes[i].FindPlaneWithClosestNormal( vUp )) || Brushes[i].FindPointsForPlane( Planes[i], Points[i], 3 ) != 3) {
813                         //DoMessageBox("Error", "Error", eMB_OK);
814                         globalErrorStream() << "bobToolz FlipTerrain: ERROR (FindPlaneWithClosestNormal/FindPointsForPlane).\n";
815                         return;
816                 }
817         }
818
819         vec3_t mins1, mins2, maxs1, maxs2;
820         Brushes[0].GetBounds( mins1, maxs1 );
821         Brushes[1].GetBounds( mins2, maxs2 );
822
823
824
825         int dontmatch[2] = { -1, -1 };
826         bool found = false;
827         for( i = 0; i < 3; i++ ) {
828                 for( int j = 0; j < 3 && !found; j++ ) {
829                         if(VectorCompare( (Points[0])[i]->_pnt, (Points[1])[j]->_pnt )) {
830                                 found = true;
831                                 break;
832                         }
833                 }
834                 if(!found) {
835                         dontmatch[0] = i;
836                         break;
837                 }
838                 found = false;
839         }
840         if(dontmatch[0] == -1) {
841                 //DoMessageBox("Error", "Error", eMB_OK);
842                 globalErrorStream() << "bobToolz FlipTerrain: ERROR (dontmatch[0]).\n";
843                 return;
844         }
845
846         for( i = 0; i < 3; i++ ) {
847                 for( int j = 0; j < 3 && !found; j++ ) {
848                         if(VectorCompare( (Points[1])[i]->_pnt, (Points[0])[j]->_pnt )) {
849                                 found = true;
850                                 break;
851                         }
852                 }
853                 if(!found) {
854                         dontmatch[1] = i;
855                         break;
856                 }
857                 found = false;
858         }
859         if(dontmatch[1] == -1) {
860                 //DoMessageBox("Error", "Error", eMB_OK);
861                 globalErrorStream() << "bobToolz FlipTerrain: ERROR (dontmatch[1]).\n";
862                 return;
863         }
864
865         vec3_t plnpnts1[3];
866         vec3_t plnpnts2[3];
867         vec3_t plnpntsshr[3];
868
869         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts1[0] );
870         for( i = 0; i < 3; i++ ) {
871                 if( dontmatch[0] != i ) {
872                         VectorCopy( (Points[0])[i]->_pnt, plnpnts1[1] );
873                         break;
874                 }
875         }
876         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts1[2] );
877
878         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts2[0] );
879         for( i = 0; i < 3; i++ ) {
880                 if( dontmatch[1] != i && !VectorCompare( (Points[1])[i]->_pnt, plnpnts1[1] )) {
881                         VectorCopy( (Points[1])[i]->_pnt, plnpnts2[1] );
882                         break;
883                 }
884         }
885         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts2[2] );
886
887         VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[0] );
888         VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[1] );
889         if( (Points[1])[dontmatch[1]]->_pnt[2] < (Points[0])[dontmatch[0]]->_pnt[2] ) {
890                 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[2] );
891         } else {
892                 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[2] );
893         }
894         plnpntsshr[2][2] -= 16;
895
896         for( i = 0; i < 3; i++ ) {
897                 if( mins2[i] < mins1[i] ) {
898                         mins1[i] = mins2[i];
899                 }
900                 if( maxs2[i] > maxs1[i] ) {
901                         maxs1[i] = maxs2[i];
902                 }
903         }
904
905         DBrush* newBrushes[2];
906         newBrushes[0] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
907         newBrushes[1] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
908
909         vec3_t normal;
910         MakeNormal( plnpnts1[0], plnpnts1[1], plnpnts1[2], normal );
911         if( normal[2] >= 0 ) {
912                 newBrushes[0]->AddFace( plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true );
913         } else {
914                 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );
915         }
916
917         MakeNormal( plnpnts2[0], plnpnts2[1], plnpnts2[2], normal );
918         if( normal[2] >= 0 ) {
919                 newBrushes[1]->AddFace( plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true );
920         } else {
921                 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );
922         }
923
924         vec3_t vec;
925         MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );      
926         
927         VectorSubtract( plnpnts1[2], plnpnts1[1], vec );
928         if( DotProduct( vec, normal ) >= 0 ) {
929                 newBrushes[0]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
930         } else {
931                 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
932         }
933
934         VectorSubtract( plnpnts2[2], plnpnts2[1], vec );
935         if( DotProduct( vec, normal ) >= 0 ) {
936                 newBrushes[1]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
937         } else {
938                 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
939         }
940
941         for( i = 0; i < 2; i++ ) {
942                 newBrushes[i]->RemoveRedundantPlanes();
943                 newBrushes[i]->BuildInRadiant( false, NULL, brushes[i]->path().parent().get_pointer() );
944                 Path_deleteTop(brushes[i]->path());
945                 delete newBrushes[i];
946         }
947
948 }