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"
23 #pragma warning(disable : 4786)
26 #include "dialogs/dialogs-gtk.h"
39 #include "DVisDrawer.h"
40 #include "DTrainDrawer.h"
43 #include "scriptparser.h"
44 #include "DTreePlanter.h"
60 std::list<Str> exclusionList; // whole brush exclusion
61 std::list<Str> exclusionList_Face; // single face exclusion
63 bool el1Loaded = false;
64 bool el2Loaded = false;
65 bool clrLst1Loaded = false;
66 bool clrLst2Loaded = false;
68 DBobView* g_PathView = NULL;
69 DVisDrawer* g_VisView = NULL;
70 DTrainDrawer* g_TrainView = NULL;
71 DTreePlanter* g_TreePlanter = NULL;
74 //========================//
75 // Helper Functions //
76 //========================//
83 el1Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el1.txt"), &exclusionList);
85 el2Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el2.txt"), &exclusionList_Face);
89 //========================//
91 //========================//
95 UndoableCommand undo("bobToolz.intersect");
98 if(DoIntersectBox(&rs) == eIDCANCEL)
101 if(rs.nBrushOptions == BRUSH_OPT_SELECTED)
103 if( GlobalSelectionSystem().countSelected() < 2 )
105 DoMessageBox("Invalid number of brushes selected, choose at least 2", "Error", eMB_OK);
112 switch(rs.nBrushOptions)
114 case BRUSH_OPT_SELECTED:
116 world.LoadSelectedBrushes();
119 case BRUSH_OPT_WHOLE_MAP:
121 world.LoadFromEntity(GlobalRadiant().getMapWorldEntity(), false);
126 world.RemoveNonCheckBrushes(&exclusionList, rs.bUseDetail);
129 if(rs.bDuplicateOnly)
130 pbSelectList = world.BuildDuplicateList();
132 pbSelectList = world.BuildIntersectList();
134 world.SelectBrushes(pbSelectList);
136 delete[] pbSelectList;
146 UndoableCommand undo("bobToolz.polygons");
147 // ensure we have something selected
148 if( GlobalSelectionSystem().countSelected() != 1 )
150 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
156 // ask user for type, size, etc....
157 if(DoPolygonBox(&rs) == eIDOK)
164 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
165 VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
166 VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
168 Path_deleteTop(instance.path());
172 poly.BuildInversePrism(vMin, vMax, rs.nSides, rs.bAlignTop);
176 poly.BuildBorderedPrism(vMin, vMax, rs.nSides, rs.nBorderWidth, rs.bAlignTop);
178 poly.BuildRegularPrism(vMin, vMax, rs.nSides, rs.bAlignTop);
188 UndoableCommand undo("bobToolz.fixBrushes");
192 int count = world.FixBrushes();
194 globalOutputStream() << count << " invalid/duplicate planes removed\n";
197 void DoResetTextures()
199 UndoableCommand undo("bobToolz.resetTextures");
200 static ResetTextureRS rs;
203 if(1/*g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 1*/)
209 texName = GetCurrentTexture();
210 strcpy(rs.textureName, GetCurrentTexture());
213 EMessageBoxReturn ret;
214 if((ret = DoResetTextureBox(&rs)) == eIDCANCEL)
217 if(rs.bResetTextureName)
218 texName = rs.textureName;
223 world.LoadSelectedBrushes();
224 world.ResetTextures(texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
225 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation, true);
231 world.ResetTextures(texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
232 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation);
238 UndoableCommand undo("bobToolz.buildStairs");
241 strcpy(rs.mainTexture, GetCurrentTexture());
243 // ensure we have something selected
244 if( GlobalSelectionSystem().countSelected() != 1 )
246 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
250 // ask user for type, size, etc....
251 if(DoBuildStairsBox(&rs) == eIDOK)
256 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
257 VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
258 VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
263 VectorSubtract(vMax, vMin, size);
265 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);
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)
282 BuildCornerStairs(vMin, vMax, numSteps, rs.mainTexture, rs.riserTexture);
287 // Get Step Dimensions
288 float stairHeight = (float)rs.stairHeight;
290 if((rs.direction == MOVE_EAST) || (rs.direction == MOVE_WEST))
291 stairWidth = (size[0])/numSteps;
293 stairWidth = (size[1])/numSteps;
296 // Build Base For Stair (bob's style)
297 if(rs.style == STYLE_BOB)
298 Build_Wedge(rs.direction, vMin, vMax, true);
301 // Set First Step Starting Position
302 vMax[2] = vMin[2] + stairHeight;
303 SetInitialStairPos(rs.direction, vMin, vMax, stairWidth);
307 for(int i = 0; i < numSteps; i++)
309 if(rs.style == STYLE_BOB)
310 Build_StairStep_Wedge(rs.direction, vMin, vMax, rs.mainTexture, rs.riserTexture, rs.bUseDetail);
311 else if(rs.style == STYLE_ORIGINAL)
312 Build_StairStep(vMin, vMax, rs.mainTexture, rs.riserTexture, rs.direction);
314 // get step into next position
315 MoveBlock(rs.direction, vMin, vMax, stairWidth);
316 vMax[2] += stairHeight;
317 if(rs.style == STYLE_BOB)
318 vMin[2] += stairHeight; // wedge bottom must be raised
327 UndoableCommand undo("bobToolz.buildDoors");
328 // ensure we have something selected
329 if( GlobalSelectionSystem().countSelected() != 1 )
331 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
336 strcpy(rs.mainTexture, GetCurrentTexture());
338 if(DoDoorsBox(&rs) == eIDOK)
343 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
344 VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
345 VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
346 Path_deleteTop(instance.path());
349 BuildDoorsX2(vMin, vMax,
350 rs.bScaleMainH, rs.bScaleMainV,
351 rs.bScaleTrimH, rs.bScaleTrimV,
352 rs.mainTexture, rs.trimTexture,
353 rs.nOrientation); // shapes.cpp
359 UndoableCommand undo("bobToolz.pathPlotter");
361 EMessageBoxReturn ret = DoPathPlotterBox(&rs);
371 // ensure we have something selected
372 if( GlobalSelectionSystem().countSelected() != 1 )
374 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
378 Entity* entity = Node_getEntity(GlobalSelectionSystem().ultimateSelected().path().top());
381 DBobView_setEntity(*entity, rs.fMultiplier, rs.nPoints, rs.fGravity, rs.bNoUpdate, rs.bShowExtra);
387 UndoableCommand undo("bobToolz.pitBuilder");
388 // ensure we have something selected
389 if( GlobalSelectionSystem().countSelected() != 1 )
391 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
397 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
398 VectorSubtract(instance.worldAABB().origin, instance.worldAABB().extents, vMin);
399 VectorAdd(instance.worldAABB().origin, instance.worldAABB().extents, vMax);
403 if(pit.BuildPit(vMin, vMax))
407 Path_deleteTop(instance.path());
410 DoMessageBox("Failed To Make Pit\nTry Making The Brush Bigger", "Error", eMB_OK);
413 void DoMergePatches()
415 UndoableCommand undo("bobToolz.mergePatch");
416 patch_merge_t merge_info;
417 DPatch mrgPatches[2];
420 // ensure we have something selected
421 if( GlobalSelectionSystem().countSelected() != 2 )
423 DoMessageBox("Invalid number of patches selected, choose 2 only", "Error", eMB_OK);
427 scene::Instance* patches[2];
428 patches[0] = &GlobalSelectionSystem().ultimateSelected();
429 patches[1] = &GlobalSelectionSystem().penultimateSelected();
431 for (i = 0; i < 2; i++)
433 if (!Node_isPatch(patches[i]->path().top()))
435 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
439 mrgPatches[i].LoadFromPatch(*patches[i]);
442 /* mrgPatches[0].Transpose();
443 mrgPatches[0].RemoveFromRadiant();
444 mrgPatches[0].BuildInRadiant();*/
446 merge_info = mrgPatches[0].IsMergable(&mrgPatches[1]);
448 if (merge_info.mergable)
450 globalOutputStream() << merge_info.pos1 << " " << merge_info.pos2;
452 globalOutputStream() << "Patches Mergable\n";
453 DPatch* newPatch = mrgPatches[0].MergePatches(merge_info, &mrgPatches[0], &mrgPatches[1]);
455 /* mrgPatches[0].RemoveFromRadiant();
456 mrgPatches[0].BuildInRadiant();
458 mrgPatches[1].RemoveFromRadiant();
459 mrgPatches[1].BuildInRadiant();
468 Path_deleteTop(patches[0]->path());
469 Path_deleteTop(patches[1]->path());
471 newPatch->BuildInRadiant();
477 globalOutputStream() << "bobToolz.mergePatch: the selected patches are not mergable\n";
481 void DoSplitPatch() {
482 UndoableCommand undo("bobToolz.splitPatch");
486 // ensure we have something selected
487 if( GlobalSelectionSystem().countSelected() != 1 )
489 DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
493 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
495 if( !Node_isPatch(instance.path().top()) ) {
496 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
500 patch.LoadFromPatch(instance);
502 std::list<DPatch> patchList = patch.Split( true, true );
503 for(std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
504 (*patches).BuildInRadiant();
507 Path_deleteTop(instance.path());
514 if( GlobalSelectionSystem().countSelected() == 0 )
523 // ensure we have something selected
524 if( GlobalSelectionSystem().countSelected() != 1 )
526 DoMessageBox("Invalid number of objects selected, choose 1 only", "Error", eMB_OK);
530 scene::Instance& brush = GlobalSelectionSystem().ultimateSelected();
533 orgBrush.LoadFromBrush(brush, false);
535 orgBrush.BuildBounds();
537 origin[0] = (orgBrush.bbox_max[0] + orgBrush.bbox_min[0])/2.f;
538 origin[1] = (orgBrush.bbox_max[1] + orgBrush.bbox_min[1])/2.f;
539 origin[2] = (orgBrush.bbox_max[2] + orgBrush.bbox_min[2])/2.f;
542 const char* rad_filename = GlobalRadiant().getMapName();
545 DoMessageBox("An Error Occurred While Trying\n To Get The Map Filename", "Error", eMB_OK);
549 strcpy(filename, rad_filename);
551 char* ext = strrchr(filename, '.')+1;
552 strcpy(ext, "bsp");// rename the extension
554 std::list<DWinding*> *pointList = BuildTrace(filename, origin);
558 g_VisView = new DVisDrawer;
561 g_VisView->SetList(pointList);
564 void DoTrainPathPlot() {
570 g_TrainView = new DTrainDrawer();
573 void DoCaulkSelection() {
574 UndoableCommand undo("bobToolz.caulkSelection");
577 float fScale[2] = { 0.5f, 0.5f };
578 float fShift[2] = { 0.0f, 0.0f };
580 int bResetScale[2] = { false, false };
581 int bResetShift[2] = { false, false };
583 world.LoadSelectedBrushes();
584 world.LoadSelectedPatches();
585 world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );
588 void DoTreePlanter() {
589 UndoableCommand undo("bobToolz.treePlanter");
591 delete g_TreePlanter;
592 g_TreePlanter = NULL;
596 g_TreePlanter = new DTreePlanter();
600 UndoableCommand undo("bobToolz.dropEntities");
602 g_TreePlanter->DropEntsToGround();
607 UndoableCommand undo("bobToolz.makeChain");
612 typedef DPoint* pntTripple[3];
614 bool bFacesNoTop[6] = {true, true, true, true, true, false};
616 void DoFlipTerrain() {
617 UndoableCommand undo("bobToolz.flipTerrain");
618 vec3_t vUp = { 0.f, 0.f, 1.f };
621 // ensure we have something selected
622 if( GlobalSelectionSystem().countSelected() != 2 )
624 DoMessageBox("Invalid number of objects selected, choose 2 only", "Error", eMB_OK);
628 scene::Instance* brushes[2];
629 brushes[0] = &GlobalSelectionSystem().ultimateSelected();
630 brushes[1] = &GlobalSelectionSystem().penultimateSelected();
634 pntTripple Points[2];
635 for( i = 0; i < 2; i++ ) {
636 Brushes[i].LoadFromBrush( *brushes[i], false );
637 if(!(Planes[i] = Brushes[i].FindPlaneWithClosestNormal( vUp )) || Brushes[i].FindPointsForPlane( Planes[i], Points[i], 3 ) != 3) {
638 DoMessageBox("Error", "Error", eMB_OK);
643 vec3_t mins1, mins2, maxs1, maxs2;
644 Brushes[0].GetBounds( mins1, maxs1 );
645 Brushes[1].GetBounds( mins2, maxs2 );
649 int dontmatch[2] = { -1, -1 };
651 for( i = 0; i < 3; i++ ) {
652 for( int j = 0; j < 3 && !found; j++ ) {
653 if(VectorCompare( (Points[0])[i]->_pnt, (Points[1])[j]->_pnt )) {
664 if(dontmatch[0] == -1) {
665 DoMessageBox("Error", "Error", eMB_OK);
669 for( i = 0; i < 3; i++ ) {
670 for( int j = 0; j < 3 && !found; j++ ) {
671 if(VectorCompare( (Points[1])[i]->_pnt, (Points[0])[j]->_pnt )) {
682 if(dontmatch[1] == -1) {
683 DoMessageBox("Error", "Error", eMB_OK);
689 vec3_t plnpntsshr[3];
691 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts1[0] );
692 for( i = 0; i < 3; i++ ) {
693 if( dontmatch[0] != i ) {
694 VectorCopy( (Points[0])[i]->_pnt, plnpnts1[1] );
698 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts1[2] );
700 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts2[0] );
701 for( i = 0; i < 3; i++ ) {
702 if( dontmatch[1] != i && !VectorCompare( (Points[1])[i]->_pnt, plnpnts1[1] )) {
703 VectorCopy( (Points[1])[i]->_pnt, plnpnts2[1] );
707 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts2[2] );
709 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[0] );
710 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[1] );
711 if( (Points[1])[dontmatch[1]]->_pnt[2] < (Points[0])[dontmatch[0]]->_pnt[2] ) {
712 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[2] );
714 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[2] );
716 plnpntsshr[2][2] -= 16;
718 for( i = 0; i < 3; i++ ) {
719 if( mins2[i] < mins1[i] ) {
722 if( maxs2[i] > maxs1[i] ) {
727 DBrush* newBrushes[2];
728 newBrushes[0] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
729 newBrushes[1] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
732 MakeNormal( plnpnts1[0], plnpnts1[1], plnpnts1[2], normal );
733 if( normal[2] >= 0 ) {
734 newBrushes[0]->AddFace( plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true );
736 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );
739 MakeNormal( plnpnts2[0], plnpnts2[1], plnpnts2[2], normal );
740 if( normal[2] >= 0 ) {
741 newBrushes[1]->AddFace( plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true );
743 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );
747 MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );
749 VectorSubtract( plnpnts1[2], plnpnts1[1], vec );
750 if( DotProduct( vec, normal ) >= 0 ) {
751 newBrushes[0]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
753 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
756 VectorSubtract( plnpnts2[2], plnpnts2[1], vec );
757 if( DotProduct( vec, normal ) >= 0 ) {
758 newBrushes[1]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
760 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
763 for( i = 0; i < 2; i++ ) {
764 newBrushes[i]->RemoveRedundantPlanes();
765 newBrushes[i]->BuildInRadiant( false, NULL, brushes[i]->path().parent().get_pointer() );
766 Path_deleteTop(brushes[i]->path());
767 delete newBrushes[i];