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