2 BobToolz plugin for GtkRadiant
3 Copyright (C) 2001 Gordon Biggans
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.
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.
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
20 #include "funchandlers.h"
21 #include "globaldefs.h"
23 #if GDEF_COMPILER_MSVC
24 #pragma warning(disable : 4786)
27 #include "dialogs/dialogs-gtk.h"
40 #include "DVisDrawer.h"
41 #include "DTrainDrawer.h"
44 #include "ScriptParser.h"
45 #include "DTreePlanter.h"
61 std::list<Str> exclusionList; // whole brush exclusion
62 std::list<Str> exclusionList_Face; // single face exclusion
64 bool el1Loaded = false;
65 bool el2Loaded = false;
66 bool clrLst1Loaded = false;
67 bool clrLst2Loaded = false;
69 DBobView *g_PathView = NULL;
70 DVisDrawer *g_VisView = NULL;
71 DTrainDrawer *g_TrainView = NULL;
72 DTreePlanter *g_TreePlanter = NULL;
75 //========================//
76 // Helper Functions //
77 //========================//
84 el1Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el1.txt"), &exclusionList);
87 el2Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el2.txt"), &exclusionList_Face);
92 //========================//
94 //========================//
98 UndoableCommand undo("bobToolz.intersect");
101 if (DoIntersectBox(&rs) == eIDCANCEL) {
105 if (rs.nBrushOptions == BRUSH_OPT_SELECTED) {
106 if (GlobalSelectionSystem().countSelected() < 2) {
107 //DoMessageBox("Invalid number of brushes selected, choose at least 2", "Error", eMB_OK);
108 globalErrorStream() << "bobToolz Intersect: Invalid number of brushes selected, choose at least 2.\n";
114 switch (rs.nBrushOptions) {
115 case BRUSH_OPT_SELECTED: {
117 world.LoadFromEntity(GlobalRadiant().getMapWorldEntity(), false);
118 world.LoadSelectedBrushes();
121 case BRUSH_OPT_WHOLE_MAP: {
122 world.LoadFromEntity(GlobalRadiant().getMapWorldEntity(), false);
126 world.RemoveNonCheckBrushes(&exclusionList, rs.bUseDetail);
129 if (rs.bDuplicateOnly) {
130 pbSelectList = world.BuildDuplicateList();
132 pbSelectList = world.BuildIntersectList();
135 world.SelectBrushes(pbSelectList);
136 int brushCount = GlobalSelectionSystem().countSelected();
137 globalOutputStream() << "bobToolz Intersect: " << brushCount << " intersecting brushes found.\n";
138 delete[] pbSelectList;
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";
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";
163 // ask user for type, size, etc....
164 if (DoPolygonBox(&rs) == eIDOK) {
171 VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
172 VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
174 Path_deleteTop(instance.path());
178 poly.BuildInversePrism(vMin, vMax, rs.nSides, rs.bAlignTop);
181 poly.BuildBorderedPrism(vMin, vMax, rs.nSides, rs.nBorderWidth, rs.bAlignTop);
183 poly.BuildRegularPrism(vMin, vMax, rs.nSides, rs.bAlignTop);
194 UndoableCommand undo("bobToolz.fixBrushes");
198 int count = world.FixBrushes();
200 globalOutputStream() << "bobToolz FixBrushes: " << count << " invalid/duplicate planes removed.\n";
203 void DoResetTextures()
205 UndoableCommand undo("bobToolz.resetTextures");
206 static ResetTextureRS rs;
209 if (1 /*g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 1*/ ) {
212 texName = GetCurrentTexture();
213 strcpy(rs.textureName, GetCurrentTexture());
216 EMessageBoxReturn ret;
217 if ((ret = DoResetTextureBox(&rs)) == eIDCANCEL) {
221 if (rs.bResetTextureName) {
222 texName = rs.textureName;
227 world.LoadSelectedBrushes();
228 world.ResetTextures(texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
229 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation, true);
233 world.ResetTextures(texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
234 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation);
240 UndoableCommand undo("bobToolz.buildStairs");
243 strcpy(rs.mainTexture, GetCurrentTexture());
245 // ensure we have something selected
246 if (GlobalSelectionSystem().countSelected() != 1) {
247 //DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
248 globalErrorStream() << "bobToolz BuildStairs: Invalid number of brushes selected, choose 1 only.\n";
252 // ask user for type, size, etc....
253 if (DoBuildStairsBox(&rs) == eIDOK) {
257 scene::Instance &instance = GlobalSelectionSystem().ultimateSelected();
258 VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
259 VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
264 VectorSubtract(vMax, vMin, size);
266 if (((int) size[2] % rs.stairHeight) != 0) {
267 // stairs must fit evenly into brush
268 //DoMessageBox("Invalid stair height\nHeight of block must be divisable by stair height", "Error", eMB_OK);
270 << "bobToolz BuildStairs: Invalid stair height. Height of block must be divisable by stair height.\n";
273 scene::Instance &instance = GlobalSelectionSystem().ultimateSelected();
274 Path_deleteTop(instance.path());
278 int numSteps = (int) size[2] / rs.stairHeight;
280 if (rs.style == STYLE_CORNER) {
281 BuildCornerStairs(vMin, vMax, numSteps, rs.mainTexture, rs.riserTexture);
284 // Get Step Dimensions
285 float stairHeight = (float) rs.stairHeight;
287 if ((rs.direction == MOVE_EAST) || (rs.direction == MOVE_WEST)) {
288 stairWidth = (size[0]) / numSteps;
290 stairWidth = (size[1]) / numSteps;
294 // Build Base For Stair (bob's style)
295 if (rs.style == STYLE_BOB) {
296 Build_Wedge(rs.direction, vMin, vMax, true);
300 // Set First Step Starting Position
301 vMax[2] = vMin[2] + stairHeight;
302 SetInitialStairPos(rs.direction, vMin, vMax, stairWidth);
306 for (int i = 0; i < numSteps; i++) {
307 if (rs.style == STYLE_BOB) {
308 Build_StairStep_Wedge(rs.direction, vMin, vMax, rs.mainTexture, rs.riserTexture, rs.bUseDetail);
309 } else if (rs.style == STYLE_ORIGINAL) {
310 Build_StairStep(vMin, vMax, rs.mainTexture, rs.riserTexture, rs.direction);
313 // get step into next position
314 MoveBlock(rs.direction, vMin, vMax, stairWidth);
315 vMax[2] += stairHeight;
316 if (rs.style == STYLE_BOB) {
317 vMin[2] += stairHeight; // wedge bottom must be raised
327 UndoableCommand undo("bobToolz.buildDoors");
328 // ensure we have something selected
329 if (GlobalSelectionSystem().countSelected() != 1) {
330 //DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
331 globalErrorStream() << "bobToolz BuildDoors: Invalid number of brushes selected, choose 1 only.\n";
336 strcpy(rs.mainTexture, GetCurrentTexture());
338 if (DoDoorsBox(&rs) == eIDOK) {
342 scene::Instance &instance = GlobalSelectionSystem().ultimateSelected();
343 VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
344 VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
345 Path_deleteTop(instance.path());
348 BuildDoorsX2(vMin, vMax,
349 rs.bScaleMainH, rs.bScaleMainV,
350 rs.bScaleTrimH, rs.bScaleTrimV,
351 rs.mainTexture, rs.trimTexture,
352 rs.nOrientation); // shapes.cpp
358 UndoableCommand undo("bobToolz.pathPlotter");
360 EMessageBoxReturn ret = DoPathPlotterBox(&rs);
361 if (ret == eIDCANCEL) {
371 // ensure we have something selected
373 if( GlobalSelectionSystem().countSelected() != 1 )
375 //DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
376 globalOutputStream() << "bobToolz PathPlotter: Invalid number of entities selected, choose 1 trigger_push entity only.\n";
380 Entity *entity = Node_getEntity(GlobalSelectionSystem().ultimateSelected().path().top());
382 DBobView_setEntity(*entity, rs.fMultiplier, rs.nPoints, rs.fGravity, rs.bNoUpdate, rs.bShowExtra);
385 << "bobToolz PathPlotter: No trigger_push entitity selected, select 1 only (Use list to select it).\n";
392 UndoableCommand undo("bobToolz.pitBuilder");
393 // ensure we have something selected
394 if (GlobalSelectionSystem().countSelected() != 1) {
395 //DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
396 globalErrorStream() << "bobToolz PitBuilder: Invalid number of brushes selected, choose 1 only.\n";
402 scene::Instance &instance = GlobalSelectionSystem().ultimateSelected();
403 //seems it does this also with a patch with valid dimensions.. but probably better to enforce a brush.
404 if (!Node_isBrush(instance.path().top())) {
405 //DoMessageBox("No brush selected, select ONLY one brush", "Error", eMB_OK);
406 globalErrorStream() << "bobToolz PitBuilder: No brush selected, select ONLY 1 brush.\n";
410 VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
411 VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
415 if (pit.BuildPit(vMin, vMax)) {
417 Path_deleteTop(instance.path());
419 //DoMessageBox("Failed To Make Pit\nTry Making The Brush Bigger", "Error", eMB_OK);
420 globalErrorStream() << "bobToolz PitBuilder: Failed to make Pit, try making the brush bigger.\n";
424 void DoMergePatches()
426 UndoableCommand undo("bobToolz.mergePatches");
427 patch_merge_t merge_info;
428 DPatch mrgPatches[2];
431 // ensure we have something selected
432 if (GlobalSelectionSystem().countSelected() != 2) {
433 globalErrorStream() << "bobToolz MergePatches: Invalid number of patches selected, choose 2 only.\n";
434 //DoMessageBox("Invalid number of patches selected, choose 2 only", "Error", eMB_OK);
438 scene::Instance *patches[2];
439 patches[0] = &GlobalSelectionSystem().ultimateSelected();
440 patches[1] = &GlobalSelectionSystem().penultimateSelected();
442 for (i = 0; i < 2; i++) {
443 if (!Node_isPatch(patches[i]->path().top())) {
444 //DoMessageBox("No patches selected, select ONLY patches", "Error", eMB_OK);
446 << "bobToolz MergePatches: Invalid number of patches selected, choose ONLY 2 patches.\n";
450 mrgPatches[i].LoadFromPatch(*patches[i]);
453 /* mrgPatches[0].Transpose();
454 mrgPatches[0].RemoveFromRadiant();
455 mrgPatches[0].BuildInRadiant();*/
457 merge_info = mrgPatches[0].IsMergable(&mrgPatches[1]);
459 if (merge_info.mergable) {
460 globalOutputStream() << merge_info.pos1 << " " << merge_info.pos2;
461 //Message removed, No tools give feedback on success.
462 //globalOutputStream() << "bobToolz MergePatches: Patches Mergable.\n";
463 DPatch *newPatch = mrgPatches[0].MergePatches(merge_info, &mrgPatches[0], &mrgPatches[1]);
465 /* mrgPatches[0].RemoveFromRadiant();
466 mrgPatches[0].BuildInRadiant();
468 mrgPatches[1].RemoveFromRadiant();
469 mrgPatches[1].BuildInRadiant();
476 Path_deleteTop(patches[0]->path());
477 Path_deleteTop(patches[1]->path());
479 newPatch->BuildInRadiant();
483 globalErrorStream() << "bobToolz.mergePatch: The selected patches are not mergable.\n";
490 UndoableCommand undo("bobToolz.splitPatch");
494 // ensure we have something selected
495 if (GlobalSelectionSystem().countSelected() != 1) {
496 //DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
497 globalErrorStream() << "bobToolz SplitPatch: Invalid number of patches selected, choose only 1 patch.\n";
501 scene::Instance &instance = GlobalSelectionSystem().ultimateSelected();
503 if (!Node_isPatch(instance.path().top())) {
504 //DoMessageBox("No patch selected, select ONLY one patch", "Error", eMB_OK);
505 globalErrorStream() << "bobToolz SplitPatch: No patch selected, select ONLY 1 patch.\n";
509 patch.LoadFromPatch(instance);
511 std::list<DPatch> patchList = patch.Split();
512 for (std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
513 (*patches).BuildInRadiant();
516 Path_deleteTop(instance.path());
519 void DoSplitPatchCols()
521 UndoableCommand undo("bobToolz.splitPatchCols");
525 // ensure we have something selected
526 if (GlobalSelectionSystem().countSelected() != 1) {
527 //DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
528 globalErrorStream() << "bobToolz SplitPatchCols: Invalid number of patches selected, choose 1 only.\n";
532 scene::Instance &instance = GlobalSelectionSystem().ultimateSelected();
534 if (!Node_isPatch(instance.path().top())) {
535 //DoMessageBox("No patch selected, select ONLY one patch", "Error", eMB_OK);
536 globalErrorStream() << "bobToolz SplitPatchCols: No patch selected, select ONLY 1 patch.\n";
540 patch.LoadFromPatch(instance);
542 std::list<DPatch> patchList = patch.SplitCols();
543 for (std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
544 (*patches).BuildInRadiant();
547 Path_deleteTop(instance.path());
550 void DoSplitPatchRows()
552 UndoableCommand undo("bobToolz.splitPatchRows");
556 // ensure we have something selected
557 if (GlobalSelectionSystem().countSelected() != 1) {
558 //DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
559 globalErrorStream() << "bobToolz SplitPatchRows: Invalid number of patches selected, choose 1 only.\n";
563 scene::Instance &instance = GlobalSelectionSystem().ultimateSelected();
565 if (!Node_isPatch(instance.path().top())) {
566 //DoMessageBox("No patch selected, select ONLY one patch", "Error", eMB_OK);
567 globalErrorStream() << "bobToolz SplitPatchRows: No patch selected, select ONLY 1 patch.\n";
571 patch.LoadFromPatch(instance);
573 std::list<DPatch> patchList = patch.SplitRows();
574 for (std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
575 (*patches).BuildInRadiant();
578 Path_deleteTop(instance.path());
585 if (GlobalSelectionSystem().countSelected() == 0) {
586 globalErrorStream() << "bobToolz VisAnalyse: Invalid number of objects selected, choose 1 only.\n";
593 // ensure we have something selected
594 if (GlobalSelectionSystem().countSelected() != 1) {
595 //DoMessageBox("Invalid number of objects selected, choose 1 only", "Error", eMB_OK);
596 globalErrorStream() << "bobToolz VisAnalyse: Invalid number of objects selected, choose 1 only.\n";
600 scene::Instance &brush = GlobalSelectionSystem().ultimateSelected();
601 //ensure we have a brush selected
602 if (!Node_isBrush(brush.path().top())) {
603 //DoMessageBox("No brush selected, select ONLY one brush", "Error", eMB_OK);
604 globalErrorStream() << "bobToolz VisAnalyse: No brush selected, select ONLY 1 brush.\n";
608 orgBrush.LoadFromBrush(brush, false);
610 orgBrush.BuildBounds();
612 origin[0] = (orgBrush.bbox_max[0] + orgBrush.bbox_min[0]) / 2.f;
613 origin[1] = (orgBrush.bbox_max[1] + orgBrush.bbox_min[1]) / 2.f;
614 origin[2] = (orgBrush.bbox_max[2] + orgBrush.bbox_min[2]) / 2.f;
617 const char *rad_filename = GlobalRadiant().getMapName();
619 //DoMessageBox("An ERROR occurred while trying\n to get the map filename", "Error", eMB_OK);
620 globalErrorStream() << "bobToolz VisAnalyse: An ERROR occurred while trying to get the map filename.\n";
624 strcpy(filename, rad_filename);
626 char *ext = strrchr(filename, '.') + 1;
627 strcpy(ext, "bsp"); // rename the extension
629 std::list<DWinding *> *pointList = BuildTrace(filename, origin);
632 g_VisView = new DVisDrawer;
635 g_VisView->SetList(pointList);
638 void DoTrainPathPlot()
645 g_TrainView = new DTrainDrawer();
648 void DoCaulkSelection()
650 UndoableCommand undo("bobToolz.caulkSelection");
653 float fScale[2] = {0.5f, 0.5f};
654 float fShift[2] = {0.0f, 0.0f};
656 int bResetScale[2] = {false, false};
657 int bResetShift[2] = {false, false};
659 world.LoadSelectedBrushes();
660 world.LoadSelectedPatches();
661 world.ResetTextures(NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true);
666 UndoableCommand undo("bobToolz.treePlanter");
668 delete g_TreePlanter;
669 g_TreePlanter = NULL;
673 g_TreePlanter = new DTreePlanter();
678 UndoableCommand undo("bobToolz.dropEntities");
680 g_TreePlanter->DropEntsToGround();
687 if (DoMakeChainBox(&rs) == eIDOK) {
688 if (rs.linkNum > 1001) {
689 globalErrorStream() << "bobToolz MakeChain: " << rs.linkNum << " to many Elemets, limited to 1000.\n";
692 UndoableCommand undo("bobToolz.makeChain");
694 pl.MakeChain(rs.linkNum, rs.linkName);
698 typedef DPoint *pntTripple[3];
700 bool bFacesNoTop[6] = {true, true, true, true, true, false};
704 UndoableCommand undo("bobToolz.flipTerrain");
705 vec3_t vUp = {0.f, 0.f, 1.f};
708 // ensure we have something selected
709 if (GlobalSelectionSystem().countSelected() != 2) {
710 //DoMessageBox("Invalid number of objects selected, choose 2 only", "Error", eMB_OK);
711 globalErrorStream() << "bobToolz FlipTerrain: Invalid number of objects selected, choose 2 only.\n";
715 scene::Instance *brushes[2];
716 brushes[0] = &GlobalSelectionSystem().ultimateSelected();
717 brushes[1] = &GlobalSelectionSystem().penultimateSelected();
718 //ensure we have only Brushes selected.
719 for (i = 0; i < 2; i++) {
720 if (!Node_isBrush(brushes[i]->path().top())) {
721 //DoMessageBox("No brushes selected, select ONLY brushes", "Error", eMB_OK);
722 globalErrorStream() << "bobToolz FlipTerrain: No brushes selected, select ONLY 2 brushes.\n";
728 pntTripple Points[2];
729 for (i = 0; i < 2; i++) {
730 Brushes[i].LoadFromBrush(*brushes[i], false);
731 if (!(Planes[i] = Brushes[i].FindPlaneWithClosestNormal(vUp)) ||
732 Brushes[i].FindPointsForPlane(Planes[i], Points[i], 3) != 3) {
733 //DoMessageBox("Error", "Error", eMB_OK);
734 globalErrorStream() << "bobToolz FlipTerrain: ERROR (FindPlaneWithClosestNormal/FindPointsForPlane).\n";
739 vec3_t mins1, mins2, maxs1, maxs2;
740 Brushes[0].GetBounds(mins1, maxs1);
741 Brushes[1].GetBounds(mins2, maxs2);
744 int dontmatch[2] = {-1, -1};
746 for (i = 0; i < 3; i++) {
747 for (int j = 0; j < 3 && !found; j++) {
748 if (VectorCompare((Points[0])[i]->_pnt, (Points[1])[j]->_pnt)) {
759 if (dontmatch[0] == -1) {
760 //DoMessageBox("Error", "Error", eMB_OK);
761 globalErrorStream() << "bobToolz FlipTerrain: ERROR (dontmatch[0]).\n";
765 for (i = 0; i < 3; i++) {
766 for (int j = 0; j < 3 && !found; j++) {
767 if (VectorCompare((Points[1])[i]->_pnt, (Points[0])[j]->_pnt)) {
778 if (dontmatch[1] == -1) {
779 //DoMessageBox("Error", "Error", eMB_OK);
780 globalErrorStream() << "bobToolz FlipTerrain: ERROR (dontmatch[1]).\n";
786 vec3_t plnpntsshr[3];
788 VectorCopy((Points[0])[dontmatch[0]]->_pnt, plnpnts1[0]);
789 for (i = 0; i < 3; i++) {
790 if (dontmatch[0] != i) {
791 VectorCopy((Points[0])[i]->_pnt, plnpnts1[1]);
795 VectorCopy((Points[1])[dontmatch[1]]->_pnt, plnpnts1[2]);
797 VectorCopy((Points[1])[dontmatch[1]]->_pnt, plnpnts2[0]);
798 for (i = 0; i < 3; i++) {
799 if (dontmatch[1] != i && !VectorCompare((Points[1])[i]->_pnt, plnpnts1[1])) {
800 VectorCopy((Points[1])[i]->_pnt, plnpnts2[1]);
804 VectorCopy((Points[0])[dontmatch[0]]->_pnt, plnpnts2[2]);
806 VectorCopy((Points[0])[dontmatch[0]]->_pnt, plnpntsshr[0]);
807 VectorCopy((Points[1])[dontmatch[1]]->_pnt, plnpntsshr[1]);
808 if ((Points[1])[dontmatch[1]]->_pnt[2] < (Points[0])[dontmatch[0]]->_pnt[2]) {
809 VectorCopy((Points[1])[dontmatch[1]]->_pnt, plnpntsshr[2]);
811 VectorCopy((Points[0])[dontmatch[0]]->_pnt, plnpntsshr[2]);
813 plnpntsshr[2][2] -= 16;
815 for (i = 0; i < 3; i++) {
816 if (mins2[i] < mins1[i]) {
819 if (maxs2[i] > maxs1[i]) {
824 DBrush *newBrushes[2];
825 newBrushes[0] = DShape::GetBoundingCube_Ext(mins1, maxs1, "textures/common/caulk", bFacesAll, true);
826 newBrushes[1] = DShape::GetBoundingCube_Ext(mins1, maxs1, "textures/common/caulk", bFacesAll, true);
829 MakeNormal(plnpnts1[0], plnpnts1[1], plnpnts1[2], normal);
830 if (normal[2] >= 0) {
831 newBrushes[0]->AddFace(plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true);
833 newBrushes[0]->AddFace(plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true);
836 MakeNormal(plnpnts2[0], plnpnts2[1], plnpnts2[2], normal);
837 if (normal[2] >= 0) {
838 newBrushes[1]->AddFace(plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true);
840 newBrushes[1]->AddFace(plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true);
844 MakeNormal(plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal);
846 VectorSubtract(plnpnts1[2], plnpnts1[1], vec);
847 if (DotProduct(vec, normal) >= 0) {
848 newBrushes[0]->AddFace(plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true);
850 newBrushes[0]->AddFace(plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true);
853 VectorSubtract(plnpnts2[2], plnpnts2[1], vec);
854 if (DotProduct(vec, normal) >= 0) {
855 newBrushes[1]->AddFace(plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true);
857 newBrushes[1]->AddFace(plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true);
860 for (i = 0; i < 2; i++) {
861 newBrushes[i]->RemoveRedundantPlanes();
862 newBrushes[i]->BuildInRadiant(false, NULL, brushes[i]->path().parent().get_pointer());
863 Path_deleteTop(brushes[i]->path());
864 delete newBrushes[i];