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 //========================//
61 el1Loaded = LoadExclusionList( GetFilename( buffer, "bt/bt-el1.txt" ), &exclusionList );
64 el2Loaded = LoadExclusionList( GetFilename( buffer, "bt/bt-el2.txt" ), &exclusionList_Face );
69 //========================//
71 //========================//
76 if ( DoIntersectBox( &rs ) == IDCANCEL ) {
80 if ( rs.nBrushOptions == BRUSH_OPT_SELECTED ) {
81 if ( g_FuncTable.m_pfnSelectedBrushCount() < 2 ) {
82 DoMessageBox( "Invalid number of brushes selected, choose at least 2", "Error", MB_OK );
89 switch ( rs.nBrushOptions )
91 case BRUSH_OPT_SELECTED:
93 world.LoadSelectedBrushes();
96 case BRUSH_OPT_WHOLE_MAP:
98 world.LoadFromEntity( 0, FALSE );
103 world.RemoveNonCheckBrushes( &exclusionList, rs.bUseDetail );
106 if ( rs.bDuplicateOnly ) {
107 pbSelectList = world.BuildDuplicateList();
110 pbSelectList = world.BuildIntersectList();
113 world.SelectBrushes( pbSelectList );
115 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 ){
128 // ensure we have something selected
129 if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
130 DoMessageBox( "Invalid number of brushes selected, choose 1 only", "Error", MB_OK );
134 // tell Radiant we want to access the selected brushes
135 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
137 // get handle to size definition brush
138 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
139 // cant release until we delete the brush, if we do...
143 // ask user for type, size, etc....
144 if ( DoPolygonBox( &rs ) == IDOK ) {
145 g_FuncTable.m_pfnDeleteBrushHandle( brush );
150 poly.BuildInversePrism( vMin, vMax, rs.nSides, rs.bAlignTop );
154 if ( rs.bUseBorder ) {
155 poly.BuildBorderedPrism( vMin, vMax, rs.nSides, rs.nBorderWidth, rs.bAlignTop );
158 poly.BuildRegularPrism( vMin, vMax, rs.nSides, rs.bAlignTop );
167 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
174 int count = world.FixBrushes( TRUE );
176 Sys_Printf( "%i invalid/duplicate planes removed\n", count );
179 void DoResetTextures(){
180 static ResetTextureRS rs;
183 if ( g_SelectedFaceTable.m_pfnGetSelectedFaceCount() != 1 ) {
188 texName = GetCurrentTexture();
189 strcpy( rs.textureName, GetCurrentTexture() );
193 if ( ( ret = DoResetTextureBox( &rs ) ) == IDCANCEL ) {
197 if ( rs.bResetTextureName ) {
198 texName = rs.textureName;
203 world.LoadSelectedBrushes();
204 world.ResetTextures( texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
205 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation, TRUE );
210 world.LoadAll( TRUE );
211 world.ResetTextures( texName, rs.fScale, rs.fShift, rs.rotation, rs.newTextureName,
212 rs.bResetTextureName, rs.bResetScale, rs.bResetShift, rs.bResetRotation );
216 void DoBuildStairs( vec3_t vMin, vec3_t vMax ){
219 strcpy( rs.mainTexture, GetCurrentTexture() );
221 // ensure we have something selected
222 if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
223 DoMessageBox( "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
227 // tell Radiant we want to access the selected brushes
228 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
230 // get handle to size definition brush
231 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
232 // cant release until we delete the brush, if we do...
235 // ask user for type, size, etc....
236 if ( DoBuildStairsBox( &rs ) == IDOK ) {
239 VectorSubtract( vMax, vMin, size );
241 if ( ( (int)size[2] % rs.stairHeight ) != 0 ) {
242 // stairs must fit evenly into brush
243 DoMessageBox( "Invalid stair height\nHeight of block must be divisable by stair height", "Error", MB_OK );
249 g_FuncTable.m_pfnDeleteBrushHandle( brush );
253 int numSteps = (int)size[2] / rs.stairHeight;
255 if ( rs.style == STYLE_CORNER ) {
256 BuildCornerStairs( vMin, vMax, numSteps, rs.mainTexture, rs.riserTexture );
261 // Get Step Dimensions
262 float stairHeight = (float)rs.stairHeight;
264 if ( ( rs.direction == MOVE_EAST ) || ( rs.direction == MOVE_WEST ) ) {
265 stairWidth = ( size[0] ) / numSteps;
268 stairWidth = ( size[1] ) / numSteps;
272 // Build Base For Stair (bob's style)
273 if ( rs.style == STYLE_BOB ) {
274 Build_Wedge( rs.direction, vMin, vMax, TRUE );
278 // Set First Step Starting Position
279 vMax[2] = vMin[2] + stairHeight;
280 SetInitialStairPos( rs.direction, vMin, vMax, stairWidth );
284 for ( int i = 0; i < numSteps; i++ )
286 if ( rs.style == STYLE_BOB ) {
287 Build_StairStep_Wedge( rs.direction, vMin, vMax, rs.mainTexture, rs.riserTexture, rs.bUseDetail );
289 else if ( rs.style == STYLE_ORIGINAL ) {
290 Build_StairStep( vMin, vMax, rs.mainTexture, rs.riserTexture, rs.direction );
293 // get step into next position
294 MoveBlock( rs.direction, vMin, vMax, stairWidth );
295 vMax[2] += stairHeight;
296 if ( rs.style == STYLE_BOB ) {
297 vMin[2] += stairHeight; // wedge bottom must be raised
304 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
307 void DoBuildDoors( vec3_t vMin, vec3_t vMax ){
308 // ensure we have something selected
309 if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
310 DoMessageBox( "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
314 // tell Radiant we want to access the selected brushes
315 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
317 // get handle to size definition brush
318 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
319 // cant release until we delete the brush, if we do...
322 strcpy( rs.mainTexture, GetCurrentTexture() );
324 if ( DoDoorsBox( &rs ) == IDOK ) {
325 g_FuncTable.m_pfnDeleteBrushHandle( brush );
327 BuildDoorsX2( vMin, vMax,
328 rs.bScaleMainH, rs.bScaleMainV,
329 rs.bScaleTrimH, rs.bScaleTrimV,
330 rs.mainTexture, rs.trimTexture,
331 rs.nOrientation ); // shapes.cpp
334 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
337 void DoPathPlotter(){
339 int ret = DoPathPlotterBox( &rs );
340 if ( ret == IDCANCEL ) {
350 if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
351 DoMessageBox( "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
355 // tell Radiant we want to access the selected brushes
356 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
358 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
359 // should be our trigger brush
362 world.LoadEPairList( *g_EntityTable.m_pfnGetEntityKeyValList( brush->owner ) );
364 DEPair* trigger_ep = world.FindEPairByKey( "targetname" );
367 if ( !strcmp( world.m_Classname, "trigger_push" ) ) {
368 DEPair* target_ep = world.FindEPairByKey( "target" );
370 entity_s* entTarget = FindEntityFromTargetname( target_ep->value, NULL );
375 g_PathView = new DBobView;
377 g_PathView->Begin( trigger_ep->value, target_ep->value, rs.fMultiplier, rs.nPoints, rs.fGravity, rs.bNoUpdate, rs.bShowExtra );
380 DoMessageBox( "trigger_push target could not be found.", "Error", MB_OK );
384 DoMessageBox( "trigger_push has no target.", "Error", MB_OK );
388 DoMessageBox( "You must select a 'trigger_push' entity.", "Error", MB_OK );
392 DoMessageBox( "Entity must have a targetname", "Error", MB_OK );
395 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
398 void DoPitBuilder( vec3_t vMin, vec3_t vMax ){
399 // ensure we have something selected
400 if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
401 DoMessageBox( "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
405 // tell Radiant we want to access the selected brushes
406 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
408 // get handle to size definition brush
409 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
410 // cant release until we delete the brush, if we do...
414 if ( pit.BuildPit( vMin, vMax ) ) {
417 g_FuncTable.m_pfnDeleteBrushHandle( brush );
420 DoMessageBox( "Failed To Make Pit\nTry Making The Brush Bigger", "Error", MB_OK );
423 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
426 void DoMergePatches(){
427 patch_merge_t merge_info;
428 DPatch mrgPatches[2];
431 // ensure we have something selected
432 if ( g_FuncTable.m_pfnSelectedBrushCount() != 2 ) {
433 DoMessageBox( "Invalid number of objects selected, chose 2 only", "Error", MB_OK );
438 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
440 for ( i = 0; i < 2; i++ )
442 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( i );
444 if ( !brush->pPatch ) {
445 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
446 DoMessageBox( "You must select ONLY patches", "Error", MB_OK );
450 mrgPatches[i].LoadFromBrush_t( brush );
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 Sys_Printf( "%i %i", merge_info.pos1, merge_info.pos2 );
462 Sys_Printf( "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();
478 mrgPatches[0].RemoveFromRadiant();
479 mrgPatches[1].RemoveFromRadiant();
481 newPatch->BuildInRadiant();
486 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
489 void DoSplitPatch() {
492 // ensure we have something selected
493 if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
494 DoMessageBox( "Invalid number of objects selected, select 1 patch only", "Error", MB_OK );
498 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
500 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
502 if ( !brush->pPatch ) {
503 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
504 DoMessageBox( "You must select ONLY patches", "Error", MB_OK );
508 patch.LoadFromBrush_t( brush );
510 list<DPatch> patchList = patch.Split( true, true );
511 for ( list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++ ) {
512 ( *patches ).BuildInRadiant();
515 patch.RemoveFromRadiant();
517 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
523 if ( g_FuncTable.m_pfnSelectedBrushCount() == 0 ) {
530 if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
531 DoMessageBox( "Invalid number of objects selected, select 1 only", "Error", MB_OK );
535 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
537 brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
540 orgBrush.LoadFromBrush_t( brush, false );
542 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
544 orgBrush.BuildBounds();
546 origin[0] = ( orgBrush.bbox_max[0] + orgBrush.bbox_min[0] ) / 2.f;
547 origin[1] = ( orgBrush.bbox_max[1] + orgBrush.bbox_min[1] ) / 2.f;
548 origin[2] = ( orgBrush.bbox_max[2] + orgBrush.bbox_min[2] ) / 2.f;
551 char* rad_filename = g_FuncTable.m_pfnGetMapName();
552 if ( !rad_filename ) {
553 DoMessageBox( "An Error Occurred While Trying\n To Get The Map Filename", "Error", MB_OK );
557 strcpy( filename, rad_filename );
559 char* ext = strrchr( filename, '.' ) + 1;
560 strcpy( ext, "bsp" ); // rename the extension
562 list<DWinding*> *pointList = BuildTrace( filename, origin );
565 g_VisView = new DVisDrawer;
566 g_VisView->Register();
569 g_VisView->SetList( pointList );
572 void DoTrainPathPlot() {
578 g_TrainView = new DTrainDrawer();
581 void DoCaulkSelection( void ) {
584 float fScale[2] = { 0.5f, 0.5f };
585 float fShift[2] = { 0.0f, 0.0f };
587 int bResetScale[2] = { false, false };
588 int bResetShift[2] = { false, false };
590 world.LoadSelectedBrushes();
591 world.LoadSelectedPatches();
592 world.ResetTextures( NULL, fScale, fShift, 0, "textures/common/caulk", true, bResetScale, bResetShift, false, true );
595 void DoTreePlanter( void ) {
596 if ( g_TreePlanter ) {
597 delete g_TreePlanter;
598 g_TreePlanter = NULL;
602 g_TreePlanter = new DTreePlanter();
605 void DoDropEnts( void ) {
606 if ( g_TreePlanter ) {
607 g_TreePlanter->DropEntsToGround();
611 void DoMakeChain( void ) {
616 typedef DPoint* pntTripple[3];
618 bool bFacesNoTop[6] = {true, true, true, true, true, false};
620 void DoFlipTerrain( void ) {
621 vec3_t vUp = { 0.f, 0.f, 1.f };
624 // ensure we have something selected
625 if ( g_FuncTable.m_pfnSelectedBrushCount() != 2 ) {
626 DoMessageBox( "Invalid number of objects selected, chose 2 only", "Error", MB_OK );
630 g_FuncTable.m_pfnAllocateSelectedBrushHandles();
633 for ( i = 0; i < 2; i++ ) {
634 brushes[i] = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( i );
639 pntTripple Points[2];
640 for ( i = 0; i < 2; i++ ) {
641 Brushes[i].LoadFromBrush_t( brushes[i], false );
642 if ( !( Planes[i] = Brushes[i].FindPlaneWithClosestNormal( vUp ) ) || Brushes[i].FindPointsForPlane( Planes[i], Points[i], 3 ) != 3 ) {
643 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
644 DoMessageBox( "Error", "Error", MB_OK );
649 vec3_t mins1, mins2, maxs1, maxs2;
650 Brushes[0].GetBounds( mins1, maxs1 );
651 Brushes[1].GetBounds( mins2, maxs2 );
654 for ( i = 0; i < 2; i++ ) {
655 ents[i] = brushes[i]->owner;
656 Brushes[i].RemoveFromRadiant();
659 g_FuncTable.m_pfnReleaseSelectedBrushHandles();
664 int dontmatch[2] = { -1, -1 };
666 for ( i = 0; i < 3; i++ ) {
667 for ( int j = 0; j < 3 && !found; j++ ) {
668 if ( VectorCompare( ( Points[0] )[i]->_pnt, ( Points[1] )[j]->_pnt ) ) {
679 if ( dontmatch[0] == -1 ) {
680 DoMessageBox( "Error", "Error", MB_OK );
684 for ( i = 0; i < 3; i++ ) {
685 for ( int j = 0; j < 3 && !found; j++ ) {
686 if ( VectorCompare( ( Points[1] )[i]->_pnt, ( Points[0] )[j]->_pnt ) ) {
697 if ( dontmatch[1] == -1 ) {
698 DoMessageBox( "Error", "Error", MB_OK );
704 vec3_t plnpntsshr[3];
706 VectorCopy( ( Points[0] )[dontmatch[0]]->_pnt, plnpnts1[0] );
707 for ( i = 0; i < 3; i++ ) {
708 if ( dontmatch[0] != i ) {
709 VectorCopy( ( Points[0] )[i]->_pnt, plnpnts1[1] );
713 VectorCopy( ( Points[1] )[dontmatch[1]]->_pnt, plnpnts1[2] );
715 VectorCopy( ( Points[1] )[dontmatch[1]]->_pnt, plnpnts2[0] );
716 for ( i = 0; i < 3; i++ ) {
717 if ( dontmatch[1] != i && !VectorCompare( ( Points[1] )[i]->_pnt, plnpnts1[1] ) ) {
718 VectorCopy( ( Points[1] )[i]->_pnt, plnpnts2[1] );
722 VectorCopy( ( Points[0] )[dontmatch[0]]->_pnt, plnpnts2[2] );
724 VectorCopy( ( Points[0] )[dontmatch[0]]->_pnt, plnpntsshr[0] );
725 VectorCopy( ( Points[1] )[dontmatch[1]]->_pnt, plnpntsshr[1] );
726 if ( ( Points[1] )[dontmatch[1]]->_pnt[2] < ( Points[0] )[dontmatch[0]]->_pnt[2] ) {
727 VectorCopy( ( Points[1] )[dontmatch[1]]->_pnt, plnpntsshr[2] );
730 VectorCopy( ( Points[0] )[dontmatch[0]]->_pnt, plnpntsshr[2] );
732 plnpntsshr[2][2] -= 16;
734 for ( i = 0; i < 3; i++ ) {
735 if ( mins2[i] < mins1[i] ) {
738 if ( maxs2[i] > maxs1[i] ) {
743 DBrush* newBrushes[2];
744 newBrushes[0] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true );
745 newBrushes[1] = DShape::GetBoundingCube_Ext( mins1, maxs1, "textures/common/caulk", bFacesAll, true );
748 MakeNormal( plnpnts1[0], plnpnts1[1], plnpnts1[2], normal );
749 if ( normal[2] >= 0 ) {
750 newBrushes[0]->AddFace( plnpnts1[0], plnpnts1[1], plnpnts1[2], "textures/common/terrain", true );
753 newBrushes[0]->AddFace( plnpnts1[2], plnpnts1[1], plnpnts1[0], "textures/common/terrain", true );
756 MakeNormal( plnpnts2[0], plnpnts2[1], plnpnts2[2], normal );
757 if ( normal[2] >= 0 ) {
758 newBrushes[1]->AddFace( plnpnts2[0], plnpnts2[1], plnpnts2[2], "textures/common/terrain", true );
761 newBrushes[1]->AddFace( plnpnts2[2], plnpnts2[1], plnpnts2[0], "textures/common/terrain", true );
765 MakeNormal( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], normal );
767 VectorSubtract( plnpnts1[2], plnpnts1[1], vec );
768 if ( DotProduct( vec, normal ) >= 0 ) {
769 newBrushes[0]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
772 newBrushes[0]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
775 VectorSubtract( plnpnts2[2], plnpnts2[1], vec );
776 if ( DotProduct( vec, normal ) >= 0 ) {
777 newBrushes[1]->AddFace( plnpntsshr[0], plnpntsshr[1], plnpntsshr[2], "textures/common/caulk", true );
780 newBrushes[1]->AddFace( plnpntsshr[2], plnpntsshr[1], plnpntsshr[0], "textures/common/caulk", true );
783 for ( i = 0; i < 2; i++ ) {
784 newBrushes[i]->RemoveRedundantPlanes();
785 newBrushes[i]->BuildInRadiant( false, NULL, ents[i] );
786 delete newBrushes[i];