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 //========================//
97 if(DoIntersectBox(&rs) == eIDCANCEL)
100 if(rs.nBrushOptions == BRUSH_OPT_SELECTED)
102 if( GlobalSelectionSystem().countSelected() < 2 )
104 DoMessageBox("Invalid number of brushes selected, choose at least 2", "Error", eMB_OK);
111 switch(rs.nBrushOptions)
113 case BRUSH_OPT_SELECTED:
115 world.LoadSelectedBrushes();
118 case BRUSH_OPT_WHOLE_MAP:
120 world.LoadFromEntity(0, FALSE);
125 world.RemoveNonCheckBrushes(&exclusionList, rs.bUseDetail);
128 if(rs.bDuplicateOnly)
129 pbSelectList = world.BuildDuplicateList();
131 pbSelectList = world.BuildIntersectList();
133 world.SelectBrushes(pbSelectList);
135 delete[] pbSelectList;
145 // ensure we have something selected
146 if( GlobalSelectionSystem().countSelected() != 1 )
148 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
154 // ask user for type, size, etc....
155 if(DoPolygonBox(&rs) == eIDOK)
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);
166 Node_getTraversable(instance.path().parent())->erase(instance.path().top());
170 poly.BuildInversePrism(vMin, vMax, rs.nSides, rs.bAlignTop);
174 poly.BuildBorderedPrism(vMin, vMax, rs.nSides, rs.nBorderWidth, rs.bAlignTop);
176 poly.BuildRegularPrism(vMin, vMax, rs.nSides, rs.bAlignTop);
189 int count = world.FixBrushes();
191 Sys_Printf("%i invalid/duplicate planes removed\n", count);
194 void DoResetTextures()
196 static ResetTextureRS rs;
199 if(1/*g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 1*/)
205 texName = GetCurrentTexture();
206 strcpy(rs.textureName, GetCurrentTexture());
209 EMessageBoxReturn ret;
210 if((ret = DoResetTextureBox(&rs)) == eIDCANCEL)
213 if(rs.bResetTextureName)
214 texName = rs.textureName;
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);
227 world.ResetTextures(texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
228 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation);
236 strcpy(rs.mainTexture, GetCurrentTexture());
238 // ensure we have something selected
239 if( GlobalSelectionSystem().countSelected() != 1 )
241 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
245 // ask user for type, size, etc....
246 if(DoBuildStairsBox(&rs) == eIDOK)
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);
258 VectorSubtract(vMax, vMin, size);
260 if(((int)size[2] % rs.stairHeight) != 0)
262 // stairs must fit evenly into brush
263 DoMessageBox("Invalid stair height\nHeight of block must be divisable by stair height", "Error", eMB_OK);
268 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
269 Node_getTraversable(instance.path().parent())->erase(instance.path().top());
273 int numSteps = (int)size[2] / rs.stairHeight;
275 if(rs.style == STYLE_CORNER)
277 BuildCornerStairs(vMin, vMax, numSteps, rs.mainTexture, rs.riserTexture);
282 // Get Step Dimensions
283 float stairHeight = (float)rs.stairHeight;
285 if((rs.direction == MOVE_EAST) || (rs.direction == MOVE_WEST))
286 stairWidth = (size[0])/numSteps;
288 stairWidth = (size[1])/numSteps;
291 // Build Base For Stair (bob's style)
292 if(rs.style == STYLE_BOB)
293 Build_Wedge(rs.direction, vMin, vMax, TRUE);
296 // Set First Step Starting Position
297 vMax[2] = vMin[2] + stairHeight;
298 SetInitialStairPos(rs.direction, vMin, vMax, stairWidth);
302 for(int i = 0; i < numSteps; i++)
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);
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
322 // ensure we have something selected
323 if( GlobalSelectionSystem().countSelected() != 1 )
325 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
330 strcpy(rs.mainTexture, GetCurrentTexture());
332 if(DoDoorsBox(&rs) == eIDOK)
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());
343 BuildDoorsX2(vMin, vMax,
344 rs.bScaleMainH, rs.bScaleMainV,
345 rs.bScaleTrimH, rs.bScaleTrimV,
346 rs.mainTexture, rs.trimTexture,
347 rs.nOrientation); // shapes.cpp
354 EMessageBoxReturn ret = DoPathPlotterBox(&rs);
364 // ensure we have something selected
365 if( GlobalSelectionSystem().countSelected() != 1 )
367 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
371 Entity* entity = Node_getEntity(GlobalSelectionSystem().ultimateSelected().path().top());
374 DBobView_setEntity(*entity, rs.fMultiplier, rs.nPoints, rs.fGravity, rs.bNoUpdate, rs.bShowExtra);
380 // ensure we have something selected
381 if( GlobalSelectionSystem().countSelected() != 1 )
383 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
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);
395 if(pit.BuildPit(vMin, vMax))
399 Node_getTraversable(instance.path().parent())->erase(instance.path().top());
402 DoMessageBox("Failed To Make Pit\nTry Making The Brush Bigger", "Error", eMB_OK);
405 void DoMergePatches()
407 patch_merge_t merge_info;
408 DPatch mrgPatches[2];
411 // ensure we have something selected
412 if( GlobalSelectionSystem().countSelected() != 2 )
414 DoMessageBox("Invalid number of patches selected, choose 2 only", "Error", eMB_OK);
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();
425 for (i = 0; i < 2; i++)
427 if (!patches[i]->m_patch)
429 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
433 mrgPatches[0].LoadFromBrush(patches[i]);
436 /* mrgPatches[0].Transpose();
437 mrgPatches[0].RemoveFromRadiant();
438 mrgPatches[0].BuildInRadiant();*/
440 merge_info = mrgPatches[0].IsMergable(&mrgPatches[1]);
442 if (merge_info.mergable)
444 Sys_Printf("%i %i", merge_info.pos1, merge_info.pos2);
446 Sys_Printf("Patches Mergable\n");
447 DPatch* newPatch = mrgPatches[0].MergePatches(merge_info, &mrgPatches[0], &mrgPatches[1]);
449 /* mrgPatches[0].RemoveFromRadiant();
450 mrgPatches[0].BuildInRadiant();
452 mrgPatches[1].RemoveFromRadiant();
453 mrgPatches[1].BuildInRadiant();
462 Node_getTraversable(*ents[0])->erase(*patches[0]);
463 Node_getTraversable(*ents[1])->erase(*patches[1]);
465 newPatch->BuildInRadiant();
471 void DoSplitPatch() {
474 // ensure we have something selected
475 if( GlobalSelectionSystem().countSelected() != 1 )
477 DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
481 scene::Node* node = GlobalSelectionSystem().ultimateSelected().path().top();
483 if( !node->m_patch ) {
484 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
488 patch.LoadFromBrush(node);
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();
495 patch.RemoveFromRadiant();
502 if( GlobalSelectionSystem().countSelected() == 0 )
511 // ensure we have something selected
512 if( GlobalSelectionSystem().countSelected() != 1 )
514 DoMessageBox("Invalid number of objects selected, choose 1 only", "Error", eMB_OK);
518 scene::Node* brush = GlobalSelectionSystem().ultimateSelected().path().top();
521 orgBrush.LoadFromBrush(brush, false);
523 orgBrush.BuildBounds();
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;
530 const char* rad_filename = g_FuncTable.m_pfnGetMapName();
533 DoMessageBox("An Error Occurred While Trying\n To Get The Map Filename", "Error", eMB_OK);
537 strcpy(filename, rad_filename);
539 char* ext = strrchr(filename, '.')+1;
540 strcpy(ext, "bsp");// rename the extension
542 std::list<DWinding*> *pointList = BuildTrace(filename, origin);
546 g_VisView = new DVisDrawer;
547 g_VisView->Register();
550 g_VisView->SetList(pointList);
553 void DoTrainPathPlot() {
559 g_TrainView = new DTrainDrawer();
562 void DoCaulkSelection() {
565 float fScale[2] = { 0.5f, 0.5f };
566 float fShift[2] = { 0.0f, 0.0f };
568 int bResetScale[2] = { false, false };
569 int bResetShift[2] = { false, false };
571 world.LoadSelectedBrushes();
572 world.LoadSelectedPatches();
573 world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );
576 void DoTreePlanter() {
578 delete g_TreePlanter;
579 g_TreePlanter = NULL;
583 g_TreePlanter = new DTreePlanter();
588 g_TreePlanter->DropEntsToGround();
597 typedef DPoint* pntTripple[3];
599 bool bFacesNoTop[6] = {true, true, true, true, true, false};
601 void DoFlipTerrain() {
602 vec3_t vUp = { 0.f, 0.f, 1.f };
605 // ensure we have something selected
606 if( GlobalSelectionSystem().countSelected() != 2 )
608 DoMessageBox("Invalid number of objects selected, choose 2 only", "Error", eMB_OK);
612 scene::Node* brushes[2];
613 brushes[0] = GlobalSelectionSystem().ultimateSelected().path().top();
614 brushes[1] = GlobalSelectionSystem().penultimateSelected().path().top();
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);
627 vec3_t mins1, mins2, maxs1, maxs2;
628 Brushes[0].GetBounds( mins1, maxs1 );
629 Brushes[1].GetBounds( mins2, maxs2 );
631 scene::Node* ents[2];
632 ents[0] = GlobalSelectionSystem().ultimateSelected().path().parent();
633 ents[1] = GlobalSelectionSystem().penultimateSelected().path().parent();
635 for( i = 0; i < 2; i++ ) {
636 Node_getTraversable(*ents[i])->erase(*brushes[i]);
641 int dontmatch[2] = { -1, -1 };
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 )) {
656 if(dontmatch[0] == -1) {
657 DoMessageBox("Error", "Error", eMB_OK);
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 )) {
674 if(dontmatch[1] == -1) {
675 DoMessageBox("Error", "Error", eMB_OK);
681 vec3_t plnpntsshr[3];
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] );
690 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts1[2] );
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] );
699 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts2[2] );
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] );
706 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[2] );
708 plnpntsshr[2][2] -= 16;
710 for( i = 0; i < 3; i++ ) {
711 if( mins2[i] < mins1[i] ) {
714 if( maxs2[i] > maxs1[i] ) {
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);
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 );
728 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );
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 );
735 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );
739 MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );
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 );
745 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
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 );
752 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
755 for( i = 0; i < 2; i++ ) {
756 newBrushes[i]->RemoveRedundantPlanes();
757 newBrushes[i]->BuildInRadiant( false, NULL, ents[i] );
758 delete newBrushes[i];