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
23 #pragma warning(disable : 4786)
26 #include "dialogs/dialogs-gtk.h"
28 #include "gtkr_list.h"
39 #include "DVisDrawer.h"
40 #include "DTrainDrawer.h"
43 #include "scriptparser.h"
44 #include "DTreePlanter.h"
48 #include "funchandlers.h"
53 #include "refcounted_ptr.h"
63 list<Str> exclusionList; // whole brush exclusion
64 list<Str> exclusionList_Face; // single face exclusion
66 bool el1Loaded = FALSE;
67 bool el2Loaded = FALSE;
68 bool clrLst1Loaded = FALSE;
69 bool clrLst2Loaded = FALSE;
71 DBobView* g_PathView = NULL;
72 DVisDrawer* g_VisView = NULL;
73 DTrainDrawer* g_TrainView = NULL;
74 DTreePlanter* g_TreePlanter = NULL;
77 //========================//
78 // Helper Functions //
79 //========================//
86 el1Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el1.txt"), &exclusionList);
88 el2Loaded = LoadExclusionList(GetFilename(buffer, "bt/bt-el2.txt"), &exclusionList_Face);
92 //========================//
94 //========================//
100 if(DoIntersectBox(&rs) == eIDCANCEL)
103 if(rs.nBrushOptions == BRUSH_OPT_SELECTED)
105 if( GlobalSelectionSystem().countSelected() < 2 )
107 DoMessageBox("Invalid number of brushes selected, choose at least 2", "Error", eMB_OK);
114 switch(rs.nBrushOptions)
116 case BRUSH_OPT_SELECTED:
118 world.LoadSelectedBrushes();
121 case BRUSH_OPT_WHOLE_MAP:
123 world.LoadFromEntity(0, FALSE);
128 world.RemoveNonCheckBrushes(&exclusionList, rs.bUseDetail);
131 if(rs.bDuplicateOnly)
132 pbSelectList = world.BuildDuplicateList();
134 pbSelectList = world.BuildIntersectList();
136 world.SelectBrushes(pbSelectList);
138 delete[] pbSelectList;
148 // ensure we have something selected
149 if( GlobalSelectionSystem().countSelected() != 1 )
151 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
157 // ask user for type, size, etc....
158 if(DoPolygonBox(&rs) == eIDOK)
165 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
166 VectorSubtract(instance.aabb_world().origin, instance.aabb_world().extents, vMin);
167 VectorAdd(instance.aabb_world().origin, instance.aabb_world().extents, vMax);
169 instance.path().parent()->m_traverse->erase(instance.path().top());
173 poly.BuildInversePrism(vMin, vMax, rs.nSides, rs.bAlignTop);
177 poly.BuildBorderedPrism(vMin, vMax, rs.nSides, rs.nBorderWidth, rs.bAlignTop);
179 poly.BuildRegularPrism(vMin, vMax, rs.nSides, rs.bAlignTop);
192 int count = world.FixBrushes();
194 Sys_Printf("%i invalid/duplicate planes removed\n", count);
197 void DoResetTextures()
199 static ResetTextureRS rs;
202 if(1/*g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 1*/)
208 texName = GetCurrentTexture();
209 strcpy(rs.textureName, GetCurrentTexture());
212 EMessageBoxReturn ret;
213 if((ret = DoResetTextureBox(&rs)) == eIDCANCEL)
216 if(rs.bResetTextureName)
217 texName = rs.textureName;
222 world.LoadSelectedBrushes();
223 world.ResetTextures(texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
224 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation, TRUE);
230 world.ResetTextures(texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
231 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation);
239 strcpy(rs.mainTexture, GetCurrentTexture());
241 // ensure we have something selected
242 if( GlobalSelectionSystem().countSelected() != 1 )
244 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
248 // ask user for type, size, etc....
249 if(DoBuildStairsBox(&rs) == eIDOK)
254 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
255 VectorSubtract(instance.aabb_world().origin, instance.aabb_world().extents, vMin);
256 VectorAdd(instance.aabb_world().origin, instance.aabb_world().extents, vMax);
261 VectorSubtract(vMax, vMin, size);
263 if(((int)size[2] % rs.stairHeight) != 0)
265 // stairs must fit evenly into brush
266 DoMessageBox("Invalid stair height\nHeight of block must be divisable by stair height", "Error", eMB_OK);
271 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
272 instance.path().parent()->m_traverse->erase(instance.path().top());
276 int numSteps = (int)size[2] / rs.stairHeight;
278 if(rs.style == STYLE_CORNER)
280 BuildCornerStairs(vMin, vMax, numSteps, rs.mainTexture, rs.riserTexture);
285 // Get Step Dimensions
286 float stairHeight = (float)rs.stairHeight;
288 if((rs.direction == MOVE_EAST) || (rs.direction == MOVE_WEST))
289 stairWidth = (size[0])/numSteps;
291 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);
299 // Set First Step Starting Position
300 vMax[2] = vMin[2] + stairHeight;
301 SetInitialStairPos(rs.direction, vMin, vMax, stairWidth);
305 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);
312 // get step into next position
313 MoveBlock(rs.direction, vMin, vMax, stairWidth);
314 vMax[2] += stairHeight;
315 if(rs.style == STYLE_BOB)
316 vMin[2] += stairHeight; // wedge bottom must be raised
325 // ensure we have something selected
326 if( GlobalSelectionSystem().countSelected() != 1 )
328 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
333 strcpy(rs.mainTexture, GetCurrentTexture());
335 if(DoDoorsBox(&rs) == eIDOK)
340 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
341 VectorSubtract(instance.aabb_world().origin, instance.aabb_world().extents, vMin);
342 VectorAdd(instance.aabb_world().origin, instance.aabb_world().extents, vMax);
343 instance.path().parent()->m_traverse->erase(instance.path().top());
346 BuildDoorsX2(vMin, vMax,
347 rs.bScaleMainH, rs.bScaleMainV,
348 rs.bScaleTrimH, rs.bScaleTrimV,
349 rs.mainTexture, rs.trimTexture,
350 rs.nOrientation); // shapes.cpp
357 EMessageBoxReturn ret = DoPathPlotterBox(&rs);
367 // ensure we have something selected
368 if( GlobalSelectionSystem().countSelected() != 1 )
370 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
374 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
377 world.LoadEPairList(instance.path().top()->m_entity);
379 DEPair* trigger_ep = world.FindEPairByKey("targetname");
383 if(!strcmp(world.m_Classname, "trigger_push"))
385 DEPair* target_ep = world.FindEPairByKey("target");
388 scene::Path* entTarget = FindEntityFromTargetname(target_ep->value, NULL);
393 g_PathView = new DBobView;
395 g_PathView->Begin(trigger_ep->value, target_ep->value, rs.fMultiplier, rs.nPoints, rs.fGravity, rs.bNoUpdate, rs.bShowExtra);
398 DoMessageBox("trigger_push target could not be found.", "Error", eMB_OK);
401 DoMessageBox("trigger_push has no target.", "Error", eMB_OK);
404 DoMessageBox("You must select a 'trigger_push' entity.", "Error", eMB_OK);
407 DoMessageBox("Entity must have a targetname", "Error", eMB_OK);
412 // ensure we have something selected
413 if( GlobalSelectionSystem().countSelected() != 1 )
415 DoMessageBox("Invalid number of brushes selected, choose 1 only", "Error", eMB_OK);
421 scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
422 VectorSubtract(instance.aabb_world().origin, instance.aabb_world().extents, vMin);
423 VectorAdd(instance.aabb_world().origin, instance.aabb_world().extents, vMax);
427 if(pit.BuildPit(vMin, vMax))
431 instance.path().parent()->m_traverse->erase(instance.path().top());
434 DoMessageBox("Failed To Make Pit\nTry Making The Brush Bigger", "Error", eMB_OK);
437 void DoMergePatches()
439 patch_merge_t merge_info;
440 DPatch mrgPatches[2];
443 // ensure we have something selected
444 if( GlobalSelectionSystem().countSelected() != 2 )
446 DoMessageBox("Invalid number of patches selected, choose 2 only", "Error", eMB_OK);
450 scene::Node* patches[2];
451 patches[0] = GlobalSelectionSystem().ultimateSelected().path().top();
452 patches[1] = GlobalSelectionSystem().penultimateSelected().path().top();
454 for (i = 0; i < 2; i++)
456 if (!patches[i]->m_patch)
458 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
462 mrgPatches[0].LoadFromBrush(patches[i]);
465 /* mrgPatches[0].Transpose();
466 mrgPatches[0].RemoveFromRadiant();
467 mrgPatches[0].BuildInRadiant();*/
469 merge_info = mrgPatches[0].IsMergable(&mrgPatches[1]);
471 if (merge_info.mergable)
473 Sys_Printf("%i %i", merge_info.pos1, merge_info.pos2);
475 Sys_Printf("Patches Mergable\n");
476 DPatch* newPatch = mrgPatches[0].MergePatches(merge_info, &mrgPatches[0], &mrgPatches[1]);
478 /* mrgPatches[0].RemoveFromRadiant();
479 mrgPatches[0].BuildInRadiant();
481 mrgPatches[1].RemoveFromRadiant();
482 mrgPatches[1].BuildInRadiant();
491 mrgPatches[0].RemoveFromRadiant();
492 mrgPatches[1].RemoveFromRadiant();
494 newPatch->BuildInRadiant();
500 void DoSplitPatch() {
503 // ensure we have something selected
504 if( GlobalSelectionSystem().countSelected() != 1 )
506 DoMessageBox("Invalid number of patches selected, choose 1 only", "Error", eMB_OK);
510 scene::Node* node = GlobalSelectionSystem().ultimateSelected().path().top();
512 if( !node->m_patch ) {
513 DoMessageBox("You must select ONLY patches", "Error", eMB_OK);
517 patch.LoadFromBrush(node);
519 list<DPatch> patchList = patch.Split( true, true );
520 for(list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++) {
521 (*patches).BuildInRadiant();
524 patch.RemoveFromRadiant();
531 if( GlobalSelectionSystem().countSelected() == 0 )
540 // ensure we have something selected
541 if( GlobalSelectionSystem().countSelected() != 1 )
543 DoMessageBox("Invalid number of objects selected, choose 1 only", "Error", eMB_OK);
547 scene::Node* brush = GlobalSelectionSystem().ultimateSelected().path().top();
550 orgBrush.LoadFromBrush(brush, false);
552 orgBrush.BuildBounds();
554 origin[0] = (orgBrush.bbox_max[0] + orgBrush.bbox_min[0])/2.f;
555 origin[1] = (orgBrush.bbox_max[1] + orgBrush.bbox_min[1])/2.f;
556 origin[2] = (orgBrush.bbox_max[2] + orgBrush.bbox_min[2])/2.f;
559 const char* rad_filename = g_FuncTable.m_pfnGetMapName();
562 DoMessageBox("An Error Occurred While Trying\n To Get The Map Filename", "Error", eMB_OK);
566 strcpy(filename, rad_filename);
568 char* ext = strrchr(filename, '.')+1;
569 strcpy(ext, "bsp");// rename the extension
571 list<DWinding*> *pointList = BuildTrace(filename, origin);
575 g_VisView = new DVisDrawer;
576 g_VisView->Register();
579 g_VisView->SetList(pointList);
582 void DoTrainPathPlot() {
588 g_TrainView = new DTrainDrawer();
591 void DoCaulkSelection() {
594 float fScale[2] = { 0.5f, 0.5f };
595 float fShift[2] = { 0.0f, 0.0f };
597 int bResetScale[2] = { false, false };
598 int bResetShift[2] = { false, false };
600 world.LoadSelectedBrushes();
601 world.LoadSelectedPatches();
602 world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );
605 void DoTreePlanter() {
607 delete g_TreePlanter;
608 g_TreePlanter = NULL;
612 g_TreePlanter = new DTreePlanter();
617 g_TreePlanter->DropEntsToGround();
626 typedef DPoint* pntTripple[3];
628 bool bFacesNoTop[6] = {true, true, true, true, true, false};
630 void DoFlipTerrain() {
631 vec3_t vUp = { 0.f, 0.f, 1.f };
634 // ensure we have something selected
635 if( GlobalSelectionSystem().countSelected() != 2 )
637 DoMessageBox("Invalid number of objects selected, choose 2 only", "Error", eMB_OK);
641 scene::Node* brushes[2];
642 brushes[0] = GlobalSelectionSystem().ultimateSelected().path().top();
643 brushes[1] = GlobalSelectionSystem().penultimateSelected().path().top();
647 pntTripple Points[2];
648 for( i = 0; i < 2; i++ ) {
649 Brushes[i].LoadFromBrush( brushes[i], false );
650 if(!(Planes[i] = Brushes[i].FindPlaneWithClosestNormal( vUp )) || Brushes[i].FindPointsForPlane( Planes[i], Points[i], 3 ) != 3) {
651 DoMessageBox("Error", "Error", eMB_OK);
656 vec3_t mins1, mins2, maxs1, maxs2;
657 Brushes[0].GetBounds( mins1, maxs1 );
658 Brushes[1].GetBounds( mins2, maxs2 );
660 scene::Node* ents[2];
661 ents[0] = GlobalSelectionSystem().ultimateSelected().path().parent();
662 ents[1] = GlobalSelectionSystem().penultimateSelected().path().parent();
664 for( i = 0; i < 2; i++ ) {
665 Brushes[i].RemoveFromRadiant();
670 int dontmatch[2] = { -1, -1 };
672 for( i = 0; i < 3; i++ ) {
673 for( int j = 0; j < 3 && !found; j++ ) {
674 if(VectorCompare( (Points[0])[i]->_pnt, (Points[1])[j]->_pnt )) {
685 if(dontmatch[0] == -1) {
686 DoMessageBox("Error", "Error", eMB_OK);
690 for( i = 0; i < 3; i++ ) {
691 for( int j = 0; j < 3 && !found; j++ ) {
692 if(VectorCompare( (Points[1])[i]->_pnt, (Points[0])[j]->_pnt )) {
703 if(dontmatch[1] == -1) {
704 DoMessageBox("Error", "Error", eMB_OK);
710 vec3_t plnpntsshr[3];
712 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts1[0] );
713 for( i = 0; i < 3; i++ ) {
714 if( dontmatch[0] != i ) {
715 VectorCopy( (Points[0])[i]->_pnt, plnpnts1[1] );
719 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts1[2] );
721 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpnts2[0] );
722 for( i = 0; i < 3; i++ ) {
723 if( dontmatch[1] != i && !VectorCompare( (Points[1])[i]->_pnt, plnpnts1[1] )) {
724 VectorCopy( (Points[1])[i]->_pnt, plnpnts2[1] );
728 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpnts2[2] );
730 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[0] );
731 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[1] );
732 if( (Points[1])[dontmatch[1]]->_pnt[2] < (Points[0])[dontmatch[0]]->_pnt[2] ) {
733 VectorCopy( (Points[1])[dontmatch[1]]->_pnt, plnpntsshr[2] );
735 VectorCopy( (Points[0])[dontmatch[0]]->_pnt, plnpntsshr[2] );
737 plnpntsshr[2][2] -= 16;
739 for( i = 0; i < 3; i++ ) {
740 if( mins2[i] < mins1[i] ) {
743 if( maxs2[i] > maxs1[i] ) {
748 DBrush* newBrushes[2];
749 newBrushes[0] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
750 newBrushes[1] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true);
753 MakeNormal( plnpnts1[0], plnpnts1[1], plnpnts1[2], normal );
754 if( normal[2] >= 0 ) {
755 newBrushes[0]->AddFace( plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true );
757 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );
760 MakeNormal( plnpnts2[0], plnpnts2[1], plnpnts2[2], normal );
761 if( normal[2] >= 0 ) {
762 newBrushes[1]->AddFace( plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true );
764 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );
768 MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );
770 VectorSubtract( plnpnts1[2], plnpnts1[1], vec );
771 if( DotProduct( vec, normal ) >= 0 ) {
772 newBrushes[0]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
774 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
777 VectorSubtract( plnpnts2[2], plnpnts2[1], vec );
778 if( DotProduct( vec, normal ) >= 0 ) {
779 newBrushes[1]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
781 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
784 for( i = 0; i < 2; i++ ) {
785 newBrushes[i]->RemoveRedundantPlanes();
786 newBrushes[i]->BuildInRadiant( false, NULL, ents[i] );
787 delete newBrushes[i];