2 BobToolz plugin for GtkRadiant
\r
3 Copyright (C) 2001 Gordon Biggans
\r
5 This library is free software; you can redistribute it and/or
\r
6 modify it under the terms of the GNU Lesser General Public
\r
7 License as published by the Free Software Foundation; either
\r
8 version 2.1 of the License, or (at your option) any later version.
\r
10 This library is distributed in the hope that it will be useful,
\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
13 Lesser General Public License for more details.
\r
15 You should have received a copy of the GNU Lesser General Public
\r
16 License along with this library; if not, write to the Free Software
\r
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
23 #pragma warning(disable : 4786)
\r
26 #include "dialogs/dialogs-gtk.h"
\r
28 #include "DEntity.h"
\r
35 #include "funchandlers.h"
\r
36 #include "visfind.h"
\r
39 list<Str> exclusionList; // whole brush exclusion
\r
40 list<Str> exclusionList_Face; // single face exclusion
\r
42 bool el1Loaded = FALSE;
\r
43 bool el2Loaded = FALSE;
\r
44 bool clrLst1Loaded = FALSE;
\r
45 bool clrLst2Loaded = FALSE;
\r
47 DBobView* g_PathView = NULL;
\r
48 DVisDrawer* g_VisView = NULL;
\r
49 DTrainDrawer* g_TrainView = NULL;
\r
50 DTreePlanter* g_TreePlanter = NULL;
\r
53 //========================//
\r
54 // Helper Functions //
\r
55 //========================//
\r
62 el1Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el1.txt"), &exclusionList);
\r
64 el2Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el2.txt"), &exclusionList_Face);
\r
68 //========================//
\r
69 // Main Functions //
\r
70 //========================//
\r
76 if(DoIntersectBox(&rs) == IDCANCEL)
\r
79 if(rs.nBrushOptions == BRUSH_OPT_SELECTED)
\r
81 if( g_FuncTable.m_pfnSelectedBrushCount() < 2 )
\r
83 DoMessageBox("Invalid number of brushes selected, choose at least 2", "Error", MB_OK);
\r
90 switch(rs.nBrushOptions)
\r
92 case BRUSH_OPT_SELECTED:
\r
94 world.LoadSelectedBrushes();
\r
97 case BRUSH_OPT_WHOLE_MAP:
\r
99 world.LoadFromEntity(0, FALSE);
\r
104 world.RemoveNonCheckBrushes(&exclusionList, rs.bUseDetail);
\r
106 bool* pbSelectList;
\r
107 if(rs.bDuplicateOnly)
\r
108 pbSelectList = world.BuildDuplicateList();
\r
110 pbSelectList = world.BuildIntersectList();
\r
112 world.SelectBrushes(pbSelectList);
\r
114 delete[] pbSelectList;
\r
117 void DoPolygonsTB()
\r
121 // figure out vMin and vMax
\r
122 g_FuncTable.m_pfnGetDispatchParams( vMin, vMax, NULL );
\r
124 DoPolygons( vMin, vMax );
\r
127 void DoPolygons(vec3_t vMin, vec3_t vMax)
\r
129 // ensure we have something selected
\r
130 if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )
\r
132 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", MB_OK);
\r
136 // tell Radiant we want to access the selected brushes
\r
137 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
\r
139 // get handle to size definition brush
\r
140 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);
\r
141 // cant release until we delete the brush, if we do...
\r
145 // ask user for type, size, etc....
\r
146 if(DoPolygonBox(&rs) == IDOK)
\r
148 g_FuncTable.m_pfnDeleteBrushHandle(brush);
\r
153 poly.BuildInversePrism(vMin, vMax, rs.nSides, rs.bAlignTop);
\r
157 poly.BuildBorderedPrism(vMin, vMax, rs.nSides, rs.nBorderWidth, rs.bAlignTop);
\r
159 poly.BuildRegularPrism(vMin, vMax, rs.nSides, rs.bAlignTop);
\r
167 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
170 void DoFixBrushes()
\r
175 int count = world.FixBrushes(TRUE);
\r
177 Sys_Printf("%i invalid/duplicate planes removed\n", count);
\r
180 void DoResetTextures()
\r
182 static ResetTextureRS rs;
\r
184 const char* texName;
\r
185 if(g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 1)
\r
191 texName = GetCurrentTexture();
\r
192 strcpy(rs.textureName, GetCurrentTexture());
\r
196 if((ret = DoResetTextureBox(&rs)) == IDCANCEL)
\r
199 if(rs.bResetTextureName)
\r
200 texName = rs.textureName;
\r
205 world.LoadSelectedBrushes();
\r
206 world.ResetTextures(texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
\r
207 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation, TRUE);
\r
212 world.LoadAll(TRUE);
\r
213 world.ResetTextures(texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
\r
214 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation);
\r
218 void DoBuildStairs(vec3_t vMin, vec3_t vMax)
\r
222 strcpy(rs.mainTexture, GetCurrentTexture());
\r
224 // ensure we have something selected
\r
225 if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )
\r
227 DoMessageBox("Invalid number of brushes selected, chose 1 only", "Error", MB_OK);
\r
231 // tell Radiant we want to access the selected brushes
\r
232 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
\r
234 // get handle to size definition brush
\r
235 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);
\r
236 // cant release until we delete the brush, if we do...
\r
239 // ask user for type, size, etc....
\r
240 if(DoBuildStairsBox(&rs) == IDOK)
\r
244 VectorSubtract(vMax, vMin, size);
\r
246 if(((int)size[2] % rs.stairHeight) != 0)
\r
248 // stairs must fit evenly into brush
\r
249 DoMessageBox("Invalid stair height\nHeight of block must be divisable by stair height", "Error", MB_OK);
\r
254 // Remove Size Brush
\r
255 g_FuncTable.m_pfnDeleteBrushHandle(brush);
\r
259 int numSteps = (int)size[2] / rs.stairHeight;
\r
261 if(rs.style == STYLE_CORNER)
\r
263 BuildCornerStairs(vMin, vMax, numSteps, rs.mainTexture, rs.riserTexture);
\r
268 // Get Step Dimensions
\r
269 float stairHeight = (float)rs.stairHeight;
\r
271 if((rs.direction == MOVE_EAST) || (rs.direction == MOVE_WEST))
\r
272 stairWidth = (size[0])/numSteps;
\r
274 stairWidth = (size[1])/numSteps;
\r
277 // Build Base For Stair (bob's style)
\r
278 if(rs.style == STYLE_BOB)
\r
279 Build_Wedge(rs.direction, vMin, vMax, TRUE);
\r
282 // Set First Step Starting Position
\r
283 vMax[2] = vMin[2] + stairHeight;
\r
284 SetInitialStairPos(rs.direction, vMin, vMax, stairWidth);
\r
288 for(int i = 0; i < numSteps; i++)
\r
290 if(rs.style == STYLE_BOB)
\r
291 Build_StairStep_Wedge(rs.direction, vMin, vMax, rs.mainTexture, rs.riserTexture, rs.bUseDetail);
\r
292 else if(rs.style == STYLE_ORIGINAL)
\r
293 Build_StairStep(vMin, vMax, rs.mainTexture, rs.riserTexture, rs.direction);
\r
295 // get step into next position
\r
296 MoveBlock(rs.direction, vMin, vMax, stairWidth);
\r
297 vMax[2] += stairHeight;
\r
298 if(rs.style == STYLE_BOB)
\r
299 vMin[2] += stairHeight; // wedge bottom must be raised
\r
305 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
308 void DoBuildDoors(vec3_t vMin, vec3_t vMax)
\r
310 // ensure we have something selected
\r
311 if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )
\r
313 DoMessageBox("Invalid number of brushes selected, chose 1 only", "Error", MB_OK);
\r
317 // tell Radiant we want to access the selected brushes
\r
318 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
\r
320 // get handle to size definition brush
\r
321 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);
\r
322 // cant release until we delete the brush, if we do...
\r
325 strcpy(rs.mainTexture, GetCurrentTexture());
\r
327 if(DoDoorsBox(&rs) == IDOK)
\r
329 g_FuncTable.m_pfnDeleteBrushHandle(brush);
\r
331 BuildDoorsX2(vMin, vMax,
\r
332 rs.bScaleMainH, rs.bScaleMainV,
\r
333 rs.bScaleTrimH, rs.bScaleTrimV,
\r
334 rs.mainTexture, rs.trimTexture,
\r
335 rs.nOrientation); // shapes.cpp
\r
338 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
341 void DoPathPlotter()
\r
344 int ret = DoPathPlotterBox(&rs);
\r
345 if(ret == IDCANCEL)
\r
354 if( g_FuncTable.m_pfnSelectedBrushCount() != 1)
\r
356 DoMessageBox("Invalid number of brushes selected, chose 1 only", "Error", MB_OK);
\r
360 // tell Radiant we want to access the selected brushes
\r
361 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
\r
363 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);
\r
364 // should be our trigger brush
\r
367 world.LoadEPairList(*g_EntityTable.m_pfnGetEntityKeyValList(brush->owner));
\r
369 DEPair* trigger_ep = world.FindEPairByKey("targetname");
\r
373 if(!strcmp(world.m_Classname, "trigger_push"))
\r
375 DEPair* target_ep = world.FindEPairByKey("target");
\r
378 entity_s* entTarget = FindEntityFromTargetname(target_ep->value, NULL);
\r
383 g_PathView = new DBobView;
\r
385 g_PathView->Begin(trigger_ep->value, target_ep->value, rs.fMultiplier, rs.nPoints, rs.fGravity, rs.bNoUpdate, rs.bShowExtra);
\r
388 DoMessageBox("trigger_push target could not be found.", "Error", MB_OK);
\r
391 DoMessageBox("trigger_push has no target.", "Error", MB_OK);
\r
394 DoMessageBox("You must select a 'trigger_push' entity.", "Error", MB_OK);
\r
397 DoMessageBox("Entity must have a targetname", "Error", MB_OK);
\r
399 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
402 void DoPitBuilder(vec3_t vMin, vec3_t vMax)
\r
404 // ensure we have something selected
\r
405 if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )
\r
407 DoMessageBox("Invalid number of brushes selected, chose 1 only", "Error", MB_OK);
\r
411 // tell Radiant we want to access the selected brushes
\r
412 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
\r
414 // get handle to size definition brush
\r
415 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);
\r
416 // cant release until we delete the brush, if we do...
\r
420 if(pit.BuildPit(vMin, vMax))
\r
424 g_FuncTable.m_pfnDeleteBrushHandle(brush);
\r
427 DoMessageBox("Failed To Make Pit\nTry Making The Brush Bigger", "Error", MB_OK);
\r
429 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
432 void DoMergePatches()
\r
434 patch_merge_t merge_info;
\r
435 DPatch mrgPatches[2];
\r
438 // ensure we have something selected
\r
439 if ( g_FuncTable.m_pfnSelectedBrushCount() != 2 )
\r
441 DoMessageBox("Invalid number of objects selected, chose 2 only", "Error", MB_OK);
\r
446 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
\r
448 for (i = 0; i < 2; i++)
\r
450 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(i);
\r
452 if (!brush->pPatch)
\r
454 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
455 DoMessageBox("You must select ONLY patches", "Error", MB_OK);
\r
459 mrgPatches[i].LoadFromBrush_t(brush);
\r
462 /* mrgPatches[0].Transpose();
\r
463 mrgPatches[0].RemoveFromRadiant();
\r
464 mrgPatches[0].BuildInRadiant();*/
\r
466 merge_info = mrgPatches[0].IsMergable(&mrgPatches[1]);
\r
468 if (merge_info.mergable)
\r
470 Sys_Printf("%i %i", merge_info.pos1, merge_info.pos2);
\r
472 Sys_Printf("Patches Mergable\n");
\r
473 DPatch* newPatch = mrgPatches[0].MergePatches(merge_info, &mrgPatches[0], &mrgPatches[1]);
\r
475 /* mrgPatches[0].RemoveFromRadiant();
\r
476 mrgPatches[0].BuildInRadiant();
\r
478 mrgPatches[1].RemoveFromRadiant();
\r
479 mrgPatches[1].BuildInRadiant();
\r
488 mrgPatches[0].RemoveFromRadiant();
\r
489 mrgPatches[1].RemoveFromRadiant();
\r
491 newPatch->BuildInRadiant();
\r
496 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
499 void DoSplitPatch() {
\r
502 // ensure we have something selected
\r
503 if( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
\r
504 DoMessageBox("Invalid number of objects selected, select 1 patch only", "Error", MB_OK);
\r
508 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
\r
510 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);
\r
512 if( !brush->pPatch ) {
\r
513 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
514 DoMessageBox("You must select ONLY patches", "Error", MB_OK);
\r
518 patch.LoadFromBrush_t(brush);
\r
520 list<DPatch> patchList = patch.Split( true, true );
\r
521 for(list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
\r
522 (*patches).BuildInRadiant();
\r
525 patch.RemoveFromRadiant();
\r
527 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
530 void DoVisAnalyse()
\r
532 char filename[1024];
\r
534 if( g_FuncTable.m_pfnSelectedBrushCount() == 0 )
\r
543 if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )
\r
545 DoMessageBox("Invalid number of objects selected, select 1 only", "Error", MB_OK);
\r
549 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
\r
551 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);
\r
554 orgBrush.LoadFromBrush_t(brush, false);
\r
556 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
558 orgBrush.BuildBounds();
\r
560 origin[0] = (orgBrush.bbox_max[0] + orgBrush.bbox_min[0])/2.f;
\r
561 origin[1] = (orgBrush.bbox_max[1] + orgBrush.bbox_min[1])/2.f;
\r
562 origin[2] = (orgBrush.bbox_max[2] + orgBrush.bbox_min[2])/2.f;
\r
565 char* rad_filename = g_FuncTable.m_pfnGetMapName();
\r
568 DoMessageBox("An Error Occurred While Trying\n To Get The Map Filename", "Error", MB_OK);
\r
572 strcpy(filename, rad_filename);
\r
574 char* ext = strrchr(filename, '.')+1;
\r
575 strcpy(ext, "bsp");// rename the extension
\r
577 list<DWinding*> *pointList = BuildTrace(filename, origin);
\r
581 g_VisView = new DVisDrawer;
\r
582 g_VisView->Register();
\r
585 g_VisView->SetList(pointList);
\r
588 void DoTrainPathPlot() {
\r
590 delete g_TrainView;
\r
591 g_TrainView = NULL;
\r
594 g_TrainView = new DTrainDrawer();
\r
597 void DoCaulkSelection( void ) {
\r
600 float fScale[2] = { 0.5f, 0.5f };
\r
601 float fShift[2] = { 0.0f, 0.0f };
\r
603 int bResetScale[2] = { false, false };
\r
604 int bResetShift[2] = { false, false };
\r
606 world.LoadSelectedBrushes();
\r
607 world.LoadSelectedPatches();
\r
608 world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );
\r
611 void DoTreePlanter( void ) {
\r
612 if(g_TreePlanter) {
\r
613 delete g_TreePlanter;
\r
614 g_TreePlanter = NULL;
\r
618 g_TreePlanter = new DTreePlanter();
\r
621 void DoDropEnts( void ) {
\r
622 if(g_TreePlanter) {
\r
623 g_TreePlanter->DropEntsToGround();
\r
627 void DoMakeChain( void ) {
\r
632 typedef DPoint* pntTripple[3];
\r
634 bool bFacesNoTop[6] = {true, true, true, true, true, false};
\r
636 void DoFlipTerrain( void ) {
\r
637 vec3_t vUp = { 0.f, 0.f, 1.f };
\r
640 // ensure we have something selected
\r
641 if( g_FuncTable.m_pfnSelectedBrushCount() != 2 ) {
\r
642 DoMessageBox("Invalid number of objects selected, chose 2 only", "Error", MB_OK);
\r
646 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
\r
648 brush_t* brushes[2];
\r
649 for( i = 0; i < 2; i++ ) {
\r
650 brushes[i] = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(i);
\r
655 pntTripple Points[2];
\r
656 for( i = 0; i < 2; i++ ) {
\r
657 Brushes[i].LoadFromBrush_t( brushes[i], false );
\r
658 if(!(Planes[i] = Brushes[i].FindPlaneWithClosestNormal( vUp )) || Brushes[i].FindPointsForPlane( Planes[i], Points[i], 3 ) != 3) {
\r
659 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
660 DoMessageBox("Error", "Error", MB_OK);
\r
665 vec3_t mins1, mins2, maxs1, maxs2;
\r
666 Brushes[0].GetBounds( mins1, maxs1 );
\r
667 Brushes[1].GetBounds( mins2, maxs2 );
\r
670 for( i = 0; i < 2; i++ ) {
\r
671 ents[i] = brushes[i]->owner;
\r
672 Brushes[i].RemoveFromRadiant();
\r
675 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
\r
680 int dontmatch[2] = { -1, -1 };
\r
681 bool found = false;
\r
682 for( i = 0; i < 3; i++ ) {
\r
683 for( int j = 0; j < 3 && !found; j++ ) {
\r
684 if(VectorCompare( (Points[0])[i]->_pnt, (Points[1])[j]->_pnt )) {
\r
695 if(dontmatch[0] == -1) {
\r
696 DoMessageBox("Error", "Error", MB_OK);
\r
700 for( i = 0; i < 3; i++ ) {
\r
701 for( int j = 0; j < 3 && !found; j++ ) {
\r
702 if(VectorCompare( (Points[1])[i]->_pnt, (Points[0])[j]->_pnt )) {
\r
713 if(dontmatch[1] == -1) {
\r
714 DoMessageBox("Error", "Error", MB_OK);
\r
718 vec3_t plnpnts1[3];
\r
719 vec3_t plnpnts2[3];
\r
720 vec3_t plnpntsshr[3];
\r
722 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts1[0] );
\r
723 for( i = 0; i < 3; i++ ) {
\r
724 if( dontmatch[0] != i ) {
\r
725 VectorCopy( (Points[0])[i]->_pnt, plnpnts1[1] );
\r
729 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts1[2] );
\r
731 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts2[0] );
\r
732 for( i = 0; i < 3; i++ ) {
\r
733 if( dontmatch[1] != i && !VectorCompare( (Points[1])[i]->_pnt, plnpnts1[1] )) {
\r
734 VectorCopy( (Points[1])[i]->_pnt, plnpnts2[1] );
\r
738 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts2[2] );
\r
740 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[0] );
\r
741 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[1] );
\r
742 if( (Points[1])[dontmatch[1]]->_pnt[2] < (Points[0])[dontmatch[0]]->_pnt[2] ) {
\r
743 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[2] );
\r
745 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[2] );
\r
747 plnpntsshr[2][2] -= 16;
\r
749 for( i = 0; i < 3; i++ ) {
\r
750 if( mins2[i] < mins1[i] ) {
\r
751 mins1[i] = mins2[i];
\r
753 if( maxs2[i] > maxs1[i] ) {
\r
754 maxs1[i] = maxs2[i];
\r
758 DBrush* newBrushes[2];
\r
759 newBrushes[0] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
\r
760 newBrushes[1] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
\r
763 MakeNormal( plnpnts1[0], plnpnts1[1], plnpnts1[2], normal );
\r
764 if( normal[2] >= 0 ) {
\r
765 newBrushes[0]->AddFace( plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true );
\r
767 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );
\r
770 MakeNormal( plnpnts2[0], plnpnts2[1], plnpnts2[2], normal );
\r
771 if( normal[2] >= 0 ) {
\r
772 newBrushes[1]->AddFace( plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true );
\r
774 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );
\r
778 MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );
\r
780 VectorSubtract( plnpnts1[2], plnpnts1[1], vec );
\r
781 if( DotProduct( vec, normal ) >= 0 ) {
\r
782 newBrushes[0]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
\r
784 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
\r
787 VectorSubtract( plnpnts2[2], plnpnts2[1], vec );
\r
788 if( DotProduct( vec, normal ) >= 0 ) {
\r
789 newBrushes[1]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
\r
791 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
\r
794 for( i = 0; i < 2; i++ ) {
\r
795 newBrushes[i]->RemoveRedundantPlanes();
\r
796 newBrushes[i]->BuildInRadiant( false, NULL, ents[i] );
\r
797 delete newBrushes[i];
\r