]> git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/bobtoolz/funchandlers-GTK.cpp
Merge pull request #6 from bnoordhuis/cleanup
[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 "StdAfx.h"
21
22 #ifdef _WIN32
23 #pragma warning(disable : 4786)
24 #endif
25
26 #include "dialogs/dialogs-gtk.h"
27
28 #include "DEntity.h"
29 #include "DShape.h"
30 #include "DPatch.h"
31
32 #include "misc.h"
33 #include "shapes.h"
34 #include "lists.h"
35 #include "funchandlers.h"
36 #include "visfind.h"
37
38 // for autocaulk
39 list<Str> exclusionList;        // whole brush exclusion
40 list<Str> exclusionList_Face;   // single face exclusion
41
42 bool el1Loaded =        FALSE;
43 bool el2Loaded =        FALSE;
44 bool clrLst1Loaded =    FALSE;
45 bool clrLst2Loaded =    FALSE;
46
47 DBobView*       g_PathView =        NULL;
48 DVisDrawer*     g_VisView =         NULL;
49 DTrainDrawer*   g_TrainView =       NULL;
50 DTreePlanter*   g_TreePlanter =     NULL;
51 // -------------
52
53 //========================//
54 //    Helper Functions    //
55 //========================//
56
57 void LoadLists(){
58         char buffer[256];
59
60         if ( !el1Loaded ) {
61                 el1Loaded = LoadExclusionList( GetFilename( buffer, "bt/bt-el1.txt" ), &exclusionList );
62         }
63         if ( !el2Loaded ) {
64                 el2Loaded = LoadExclusionList( GetFilename( buffer, "bt/bt-el2.txt" ), &exclusionList_Face );
65         }
66 }
67
68
69 //========================//
70 //     Main Functions     //
71 //========================//
72
73 void DoIntersect(){
74         IntersectRS rs;
75
76         if ( DoIntersectBox( &rs ) == IDCANCEL ) {
77                 return;
78         }
79
80         if ( rs.nBrushOptions == BRUSH_OPT_SELECTED ) {
81                 if ( g_FuncTable.m_pfnSelectedBrushCount() < 2 ) {
82                         DoMessageBox( "Invalid number of brushes selected, choose at least 2", "Error", MB_OK );
83                         return;
84                 }
85         }
86
87         DEntity world;
88
89         switch ( rs.nBrushOptions )
90         {
91         case BRUSH_OPT_SELECTED:
92         {
93                 world.LoadSelectedBrushes();
94                 break;
95         }
96         case BRUSH_OPT_WHOLE_MAP:
97         {
98                 world.LoadFromEntity( 0, FALSE );
99                 break;
100         }
101         }
102
103         world.RemoveNonCheckBrushes( &exclusionList, rs.bUseDetail );
104
105         bool* pbSelectList;
106         if ( rs.bDuplicateOnly ) {
107                 pbSelectList = world.BuildDuplicateList();
108         }
109         else{
110                 pbSelectList = world.BuildIntersectList();
111         }
112
113         world.SelectBrushes( pbSelectList );
114
115         delete[] pbSelectList;
116 }
117
118 void DoPolygonsTB(){
119         vec3_t vMin, vMax;
120
121         // figure out vMin and vMax
122         g_FuncTable.m_pfnGetDispatchParams( vMin, vMax, NULL );
123
124         DoPolygons( vMin, vMax );
125 }
126
127 void DoPolygons( vec3_t vMin, vec3_t vMax ){
128         // ensure we have something selected
129         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
130                 DoMessageBox( "Invalid number of brushes selected, choose 1 only", "Error", MB_OK );
131                 return;
132         }
133
134         // tell Radiant we want to access the selected brushes
135         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
136
137         // get handle to size definition brush
138         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
139         // cant release until we delete the brush, if we do...
140
141         PolygonRS rs;
142
143         // ask user for type, size, etc....
144         if ( DoPolygonBox( &rs ) == IDOK ) {
145                 g_FuncTable.m_pfnDeleteBrushHandle( brush );
146
147                 DShape poly;
148
149                 if ( rs.bInverse ) {
150                         poly.BuildInversePrism( vMin, vMax, rs.nSides, rs.bAlignTop );
151                 }
152                 else
153                 {
154                         if ( rs.bUseBorder ) {
155                                 poly.BuildBorderedPrism( vMin, vMax, rs.nSides, rs.nBorderWidth, rs.bAlignTop );
156                         }
157                         else{
158                                 poly.BuildRegularPrism( vMin, vMax, rs.nSides, rs.bAlignTop );
159                         }
160
161                 }
162
163                 poly.Commit();
164         }
165
166
167         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
168 }
169
170 void DoFixBrushes(){
171         DMap world;
172         world.LoadAll();
173
174         int count = world.FixBrushes( TRUE );
175
176         Sys_Printf( "%i invalid/duplicate planes removed\n", count );
177 }
178
179 void DoResetTextures(){
180         static ResetTextureRS rs;
181
182         const char* texName;
183         if ( g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 1 ) {
184                 texName = NULL;
185         }
186         else
187         {
188                 texName = GetCurrentTexture();
189                 strcpy( rs.textureName, GetCurrentTexture() );
190         }
191
192         int ret;
193         if ( ( ret = DoResetTextureBox( &rs ) ) == IDCANCEL ) {
194                 return;
195         }
196
197         if ( rs.bResetTextureName ) {
198                 texName = rs.textureName;
199         }
200
201         if ( ret == IDOK ) {
202                 DEntity world;
203                 world.LoadSelectedBrushes();
204                 world.ResetTextures( texName,              rs.fScale,      rs.fShift,      rs.rotation, rs.newTextureName,
205                                                          rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation, TRUE );
206         }
207         else
208         {
209                 DMap world;
210                 world.LoadAll( TRUE );
211                 world.ResetTextures( texName,              rs.fScale,      rs.fShift,      rs.rotation, rs.newTextureName,
212                                                          rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation );
213         }
214 }
215
216 void DoBuildStairs( vec3_t vMin, vec3_t vMax ){
217         BuildStairsRS rs;
218
219         strcpy( rs.mainTexture, GetCurrentTexture() );
220
221         // ensure we have something selected
222         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
223                 DoMessageBox( "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
224                 return;
225         }
226
227         // tell Radiant we want to access the selected brushes
228         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
229
230         // get handle to size definition brush
231         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
232         // cant release until we delete the brush, if we do...
233
234
235         // ask user for type, size, etc....
236         if ( DoBuildStairsBox( &rs ) == IDOK ) {
237                 // calc brush size
238                 vec3_t size;
239                 VectorSubtract( vMax, vMin, size );
240
241                 if ( ( (int)size[2] % rs.stairHeight ) != 0 ) {
242                         // stairs must fit evenly into brush
243                         DoMessageBox( "Invalid stair height\nHeight of block must be divisable by stair height", "Error", MB_OK );
244                 }
245                 else
246                 {
247
248                         // Remove Size Brush
249                         g_FuncTable.m_pfnDeleteBrushHandle( brush );
250
251
252                         // Get Step Count
253                         int numSteps = (int)size[2] / rs.stairHeight;
254
255                         if ( rs.style == STYLE_CORNER ) {
256                                 BuildCornerStairs( vMin, vMax, numSteps, rs.mainTexture, rs.riserTexture );
257                         }
258                         else
259                         {
260
261                                 // Get Step Dimensions
262                                 float stairHeight = (float)rs.stairHeight;
263                                 float stairWidth;
264                                 if ( ( rs.direction == MOVE_EAST ) || ( rs.direction == MOVE_WEST ) ) {
265                                         stairWidth = ( size[0] ) / numSteps;
266                                 }
267                                 else{
268                                         stairWidth = ( size[1] ) / numSteps;
269                                 }
270
271
272                                 // Build Base For Stair (bob's style)
273                                 if ( rs.style == STYLE_BOB ) {
274                                         Build_Wedge( rs.direction, vMin, vMax, TRUE );
275                                 }
276
277
278                                 // Set First Step Starting Position
279                                 vMax[2] = vMin[2] + stairHeight;
280                                 SetInitialStairPos( rs.direction, vMin, vMax, stairWidth );
281
282
283                                 // Build The Steps
284                                 for ( int i = 0; i < numSteps; i++ )
285                                 {
286                                         if ( rs.style == STYLE_BOB ) {
287                                                 Build_StairStep_Wedge( rs.direction, vMin, vMax, rs.mainTexture, rs.riserTexture, rs.bUseDetail );
288                                         }
289                                         else if ( rs.style == STYLE_ORIGINAL ) {
290                                                 Build_StairStep( vMin, vMax, rs.mainTexture, rs.riserTexture, rs.direction );
291                                         }
292
293                                         // get step into next position
294                                         MoveBlock( rs.direction, vMin, vMax, stairWidth );
295                                         vMax[2] += stairHeight;
296                                         if ( rs.style == STYLE_BOB ) {
297                                                 vMin[2] += stairHeight; // wedge bottom must be raised
298                                         }
299                                 }
300                         }
301                 }
302         }
303
304         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
305 }
306
307 void DoBuildDoors( vec3_t vMin, vec3_t vMax ){
308         // ensure we have something selected
309         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
310                 DoMessageBox( "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
311                 return;
312         }
313
314         // tell Radiant we want to access the selected brushes
315         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
316
317         // get handle to size definition brush
318         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
319         // cant release until we delete the brush, if we do...
320
321         DoorRS rs;
322         strcpy( rs.mainTexture, GetCurrentTexture() );
323
324         if ( DoDoorsBox( &rs ) == IDOK ) {
325                 g_FuncTable.m_pfnDeleteBrushHandle( brush );
326
327                 BuildDoorsX2( vMin, vMax,
328                                           rs.bScaleMainH, rs.bScaleMainV,
329                                           rs.bScaleTrimH, rs.bScaleTrimV,
330                                           rs.mainTexture, rs.trimTexture,
331                                           rs.nOrientation ); // shapes.cpp
332         }
333
334         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
335 }
336
337 void DoPathPlotter(){
338         PathPlotterRS rs;
339         int ret = DoPathPlotterBox( &rs );
340         if ( ret == IDCANCEL ) {
341                 return;
342         }
343         if ( ret == IDNO ) {
344                 if ( g_PathView ) {
345                         delete g_PathView;
346                 }
347                 return;
348         }
349
350         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
351                 DoMessageBox( "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
352                 return;
353         }
354
355         // tell Radiant we want to access the selected brushes
356         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
357
358         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
359         // should be our trigger brush
360
361         DEntity world;
362         world.LoadEPairList( *g_EntityTable.m_pfnGetEntityKeyValList( brush->owner ) );
363
364         DEPair* trigger_ep = world.FindEPairByKey( "targetname" );
365
366         if ( trigger_ep ) {
367                 if ( !strcmp( world.m_Classname, "trigger_push" ) ) {
368                         DEPair* target_ep = world.FindEPairByKey( "target" );
369                         if ( target_ep ) {
370                                 entity_s* entTarget = FindEntityFromTargetname( target_ep->value, NULL );
371                                 if ( entTarget ) {
372                                         if ( g_PathView ) {
373                                                 delete g_PathView;
374                                         }
375                                         g_PathView = new DBobView;
376
377                                         g_PathView->Begin( trigger_ep->value, target_ep->value, rs.fMultiplier, rs.nPoints, rs.fGravity, rs.bNoUpdate, rs.bShowExtra );
378                                 }
379                                 else{
380                                         DoMessageBox( "trigger_push target could not be found.", "Error", MB_OK );
381                                 }
382                         }
383                         else{
384                                 DoMessageBox( "trigger_push has no target.", "Error", MB_OK );
385                         }
386                 }
387                 else{
388                         DoMessageBox( "You must select a 'trigger_push' entity.", "Error", MB_OK );
389                 }
390         }
391         else{
392                 DoMessageBox( "Entity must have a targetname", "Error", MB_OK );
393         }
394
395         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
396 }
397
398 void DoPitBuilder( vec3_t vMin, vec3_t vMax ){
399         // ensure we have something selected
400         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
401                 DoMessageBox( "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
402                 return;
403         }
404
405         // tell Radiant we want to access the selected brushes
406         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
407
408         // get handle to size definition brush
409         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
410         // cant release until we delete the brush, if we do...
411
412         DShape pit;
413
414         if ( pit.BuildPit( vMin, vMax ) ) {
415                 pit.Commit();
416
417                 g_FuncTable.m_pfnDeleteBrushHandle( brush );
418         }
419         else{
420                 DoMessageBox( "Failed To Make Pit\nTry Making The Brush Bigger", "Error", MB_OK );
421         }
422
423         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
424 }
425
426 void DoMergePatches(){
427         patch_merge_t merge_info;
428         DPatch mrgPatches[2];
429         int i;
430
431         // ensure we have something selected
432         if ( g_FuncTable.m_pfnSelectedBrushCount() != 2 ) {
433                 DoMessageBox( "Invalid number of objects selected, chose 2 only", "Error", MB_OK );
434                 return;
435         }
436
437
438         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
439
440         for ( i = 0; i < 2; i++ )
441         {
442                 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( i );
443
444                 if ( !brush->pPatch ) {
445                         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
446                         DoMessageBox( "You must select ONLY patches", "Error", MB_OK );
447                         return;
448                 }
449
450                 mrgPatches[i].LoadFromBrush_t( brush );
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                 Sys_Printf( "%i %i", merge_info.pos1, merge_info.pos2 );
461
462                 Sys_Printf( "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                 }
476                 else
477                 {
478                         mrgPatches[0].RemoveFromRadiant();
479                         mrgPatches[1].RemoveFromRadiant();
480
481                         newPatch->BuildInRadiant();
482                         delete newPatch;
483                 }
484         }
485
486         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
487 }
488
489 void DoSplitPatch() {
490         DPatch patch;
491
492         // ensure we have something selected
493         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
494                 DoMessageBox( "Invalid number of objects selected, select 1 patch only", "Error", MB_OK );
495                 return;
496         }
497
498         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
499
500         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
501
502         if ( !brush->pPatch ) {
503                 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
504                 DoMessageBox( "You must select ONLY patches", "Error", MB_OK );
505                 return;
506         }
507
508         patch.LoadFromBrush_t( brush );
509
510         list<DPatch> patchList = patch.Split( true, true );
511         for ( list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++ ) {
512                 ( *patches ).BuildInRadiant();
513         }
514
515         patch.RemoveFromRadiant();
516
517         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
518 }
519
520 void DoVisAnalyse(){
521         char filename[1024];
522
523         if ( g_FuncTable.m_pfnSelectedBrushCount() == 0 ) {
524                 if ( g_VisView ) {
525                         delete g_VisView;
526                         return;
527                 }
528         }
529
530         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
531                 DoMessageBox( "Invalid number of objects selected, select 1 only", "Error", MB_OK );
532                 return;
533         }
534
535         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
536
537         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
538
539         DBrush orgBrush;
540         orgBrush.LoadFromBrush_t( brush, false );
541
542         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
543
544         orgBrush.BuildBounds();
545         vec3_t origin;
546         origin[0] = ( orgBrush.bbox_max[0] + orgBrush.bbox_min[0] ) / 2.f;
547         origin[1] = ( orgBrush.bbox_max[1] + orgBrush.bbox_min[1] ) / 2.f;
548         origin[2] = ( orgBrush.bbox_max[2] + orgBrush.bbox_min[2] ) / 2.f;
549
550
551         char* rad_filename = g_FuncTable.m_pfnGetMapName();
552         if ( !rad_filename ) {
553                 DoMessageBox( "An Error Occurred While Trying\n To Get The Map Filename", "Error", MB_OK );
554                 return;
555         }
556
557         strcpy( filename, rad_filename );
558
559         char* ext = strrchr( filename, '.' ) + 1;
560         strcpy( ext, "bsp" ); // rename the extension
561
562         list<DWinding*> *pointList = BuildTrace( filename, origin );
563
564         if ( !g_VisView ) {
565                 g_VisView = new DVisDrawer;
566                 g_VisView->Register();
567         }
568
569         g_VisView->SetList( pointList );
570 }
571
572 void DoTrainPathPlot() {
573         if ( g_TrainView ) {
574                 delete g_TrainView;
575                 g_TrainView = NULL;
576         }
577
578         g_TrainView = new DTrainDrawer();
579 }
580
581 void DoCaulkSelection( void ) {
582         DEntity world;
583
584         float fScale[2] = { 0.5f, 0.5f };
585         float fShift[2] = { 0.0f, 0.0f };
586
587         int bResetScale[2] = { false, false };
588         int bResetShift[2] = { false, false };
589
590         world.LoadSelectedBrushes();
591         world.LoadSelectedPatches();
592         world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );
593 }
594
595 void DoTreePlanter( void ) {
596         if ( g_TreePlanter ) {
597                 delete g_TreePlanter;
598                 g_TreePlanter = NULL;
599                 return;
600         }
601
602         g_TreePlanter = new DTreePlanter();
603 }
604
605 void DoDropEnts( void ) {
606         if ( g_TreePlanter ) {
607                 g_TreePlanter->DropEntsToGround();
608         }
609 }
610
611 void DoMakeChain( void ) {
612         DTreePlanter pl;
613         pl.MakeChain();
614 }
615
616 typedef DPoint* pntTripple[3];
617
618 bool bFacesNoTop[6] = {true, true, true, true, true, false};
619
620 void DoFlipTerrain( void ) {
621         vec3_t vUp = { 0.f, 0.f, 1.f };
622         int i;
623
624         // ensure we have something selected
625         if ( g_FuncTable.m_pfnSelectedBrushCount() != 2 ) {
626                 DoMessageBox( "Invalid number of objects selected, chose 2 only", "Error", MB_OK );
627                 return;
628         }
629
630         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
631
632         brush_t* brushes[2];
633         for ( i = 0; i < 2; i++ ) {
634                 brushes[i] = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( i );
635         }
636
637         DBrush Brushes[2];
638         DPlane* Planes[2];
639         pntTripple Points[2];
640         for ( i = 0; i < 2; i++ ) {
641                 Brushes[i].LoadFromBrush_t( brushes[i], false );
642                 if ( !( Planes[i] = Brushes[i].FindPlaneWithClosestNormal( vUp ) ) || Brushes[i].FindPointsForPlane( Planes[i], Points[i], 3 ) != 3 ) {
643                         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
644                         DoMessageBox( "Error", "Error", MB_OK );
645                         return;
646                 }
647         }
648
649         vec3_t mins1, mins2, maxs1, maxs2;
650         Brushes[0].GetBounds( mins1, maxs1 );
651         Brushes[1].GetBounds( mins2, maxs2 );
652
653         entity_t* ents[2];
654         for ( i = 0; i < 2; i++ ) {
655                 ents[i] = brushes[i]->owner;
656                 Brushes[i].RemoveFromRadiant();
657         }
658
659         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
660
661
662
663
664         int dontmatch[2] = { -1, -1 };
665         bool found = false;
666         for ( i = 0; i < 3; i++ ) {
667                 for ( int j = 0; j < 3 && !found; j++ ) {
668                         if ( VectorCompare( ( Points[0] )[i]->_pnt, ( Points[1] )[j]->_pnt ) ) {
669                                 found = true;
670                                 break;
671                         }
672                 }
673                 if ( !found ) {
674                         dontmatch[0] = i;
675                         break;
676                 }
677                 found = false;
678         }
679         if ( dontmatch[0] == -1 ) {
680                 DoMessageBox( "Error", "Error", MB_OK );
681                 return;
682         }
683
684         for ( i = 0; i < 3; i++ ) {
685                 for ( int j = 0; j < 3 && !found; j++ ) {
686                         if ( VectorCompare( ( Points[1] )[i]->_pnt, ( Points[0] )[j]->_pnt ) ) {
687                                 found = true;
688                                 break;
689                         }
690                 }
691                 if ( !found ) {
692                         dontmatch[1] = i;
693                         break;
694                 }
695                 found = false;
696         }
697         if ( dontmatch[1] == -1 ) {
698                 DoMessageBox( "Error", "Error", MB_OK );
699                 return;
700         }
701
702         vec3_t plnpnts1[3];
703         vec3_t plnpnts2[3];
704         vec3_t plnpntsshr[3];
705
706         VectorCopy( ( Points[0] )[dontmatch[0]]->_pnt, plnpnts1[0] );
707         for ( i = 0; i < 3; i++ ) {
708                 if ( dontmatch[0] != i ) {
709                         VectorCopy( ( Points[0] )[i]->_pnt, plnpnts1[1] );
710                         break;
711                 }
712         }
713         VectorCopy( ( Points[1] )[dontmatch[1]]->_pnt, plnpnts1[2] );
714
715         VectorCopy( ( Points[1] )[dontmatch[1]]->_pnt, plnpnts2[0] );
716         for ( i = 0; i < 3; i++ ) {
717                 if ( dontmatch[1] != i && !VectorCompare( ( Points[1] )[i]->_pnt, plnpnts1[1] ) ) {
718                         VectorCopy( ( Points[1] )[i]->_pnt, plnpnts2[1] );
719                         break;
720                 }
721         }
722         VectorCopy( ( Points[0] )[dontmatch[0]]->_pnt, plnpnts2[2] );
723
724         VectorCopy( ( Points[0] )[dontmatch[0]]->_pnt, plnpntsshr[0] );
725         VectorCopy( ( Points[1] )[dontmatch[1]]->_pnt, plnpntsshr[1] );
726         if ( ( Points[1] )[dontmatch[1]]->_pnt[2] < ( Points[0] )[dontmatch[0]]->_pnt[2] ) {
727                 VectorCopy( ( Points[1] )[dontmatch[1]]->_pnt, plnpntsshr[2] );
728         }
729         else {
730                 VectorCopy( ( Points[0] )[dontmatch[0]]->_pnt, plnpntsshr[2] );
731         }
732         plnpntsshr[2][2] -= 16;
733
734         for ( i = 0; i < 3; i++ ) {
735                 if ( mins2[i] < mins1[i] ) {
736                         mins1[i] = mins2[i];
737                 }
738                 if ( maxs2[i] > maxs1[i] ) {
739                         maxs1[i] = maxs2[i];
740                 }
741         }
742
743         DBrush* newBrushes[2];
744         newBrushes[0] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true );
745         newBrushes[1] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true );
746
747         vec3_t normal;
748         MakeNormal( plnpnts1[0], plnpnts1[1], plnpnts1[2], normal );
749         if ( normal[2] >= 0 ) {
750                 newBrushes[0]->AddFace( plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true );
751         }
752         else {
753                 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );
754         }
755
756         MakeNormal( plnpnts2[0], plnpnts2[1], plnpnts2[2], normal );
757         if ( normal[2] >= 0 ) {
758                 newBrushes[1]->AddFace( plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true );
759         }
760         else {
761                 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );
762         }
763
764         vec3_t vec;
765         MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );
766
767         VectorSubtract( plnpnts1[2], plnpnts1[1], vec );
768         if ( DotProduct( vec, normal ) >= 0 ) {
769                 newBrushes[0]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
770         }
771         else {
772                 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
773         }
774
775         VectorSubtract( plnpnts2[2], plnpnts2[1], vec );
776         if ( DotProduct( vec, normal ) >= 0 ) {
777                 newBrushes[1]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
778         }
779         else {
780                 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
781         }
782
783         for ( i = 0; i < 2; i++ ) {
784                 newBrushes[i]->RemoveRedundantPlanes();
785                 newBrushes[i]->BuildInRadiant( false, NULL, ents[i] );
786                 delete newBrushes[i];
787         }
788
789 }