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