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