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