From 12d010446bffa7eb22d6536600f1fc9a310f898a Mon Sep 17 00:00:00 2001 From: Garux Date: Sat, 23 Mar 2019 19:10:41 +0300 Subject: [PATCH] * Brush->AutoCaulk Selected (F4); camera must be there, where legit player respawn could be, i.e. close to selection and not inside of a brush --- radiant/brushmanip.cpp | 1 + radiant/map.cpp | 255 ++++++++++++++++++++++++++++++++++++++ tools/quake3/q3map2/bsp.c | 63 +++++++++- 3 files changed, 317 insertions(+), 2 deletions(-) diff --git a/radiant/brushmanip.cpp b/radiant/brushmanip.cpp index ebbcfd79..32408115 100644 --- a/radiant/brushmanip.cpp +++ b/radiant/brushmanip.cpp @@ -1578,6 +1578,7 @@ void Brush_constructMenu( GtkMenu* menu ){ create_menu_item_with_mnemonic( menu, "Reset Texture", "TextureReset/Cap" ); create_menu_item_with_mnemonic( menu, "Copy Face Texture", "Copy" ); create_menu_item_with_mnemonic( menu, "Paste Face Texture", "Paste" ); + create_menu_item_with_mnemonic( menu, "AutoCaulk Selected", "AutoCaulkSelected" ); command_connect_accelerator( "Brush3Sided" ); command_connect_accelerator( "Brush4Sided" ); diff --git a/radiant/map.cpp b/radiant/map.cpp index 1070f2bf..beb522e9 100644 --- a/radiant/map.cpp +++ b/radiant/map.cpp @@ -2330,6 +2330,260 @@ void DoFind(){ gtk_widget_destroy( GTK_WIDGET( window ) ); } + +#include "filterbar.h" +//// +void map_autocaulk_selected(){ + if ( Map_Unnamed( g_map ) ) { + if( !Map_SaveAs() ) + return; + } + + { + class DeselectTriggers : public scene::Graph::Walker + { + mutable const scene::Instance* m_trigger = 0; + public: + bool pre( const scene::Path& path, scene::Instance& instance ) const { + if( path.size() == 2 ){ + Entity* entity = Node_getEntity( path.top() ); + if( entity != 0 && entity->isContainer() && string_equal_nocase_n( entity->getEntityClass().name(), "trigger_", 8 ) + && ( instance.childSelected() || instance.isSelected() ) ) + m_trigger = &instance; + else + return false; + } + return true; + } + void post( const scene::Path& path, scene::Instance& instance ) const { + if( m_trigger ) + Instance_setSelected( instance, false ); + if( m_trigger == &instance ) + m_trigger = 0; + } + }; + GlobalSceneGraph().traverse( DeselectTriggers() ); + } + + if( GlobalSelectionSystem().countSelected() == 0 ){ + globalErrorStream() << "map_autocaulk_selected(): nothing is selected\n"; + return; + } + + ScopeDisableScreenUpdates disableScreenUpdates( "processing", "autocaulk" ); + + StringOutputStream filename( 256 ); + filename << StringRange( g_map.m_name.c_str(), path_get_filename_base_end( g_map.m_name.c_str() ) ) << "_ac.map"; + + {// write .map + const Vector3 spawn( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) ); + Vector3 mins, maxs; + Select_GetBounds( mins, maxs ); + mins -= Vector3( 1024, 1024, 1024 ); + maxs += Vector3( 1024, 1024, 1024 ); + + if( !aabb_intersects_point( aabb_for_minmax( mins, maxs ), spawn ) ){ + globalErrorStream() << "map_autocaulk_selected(): camera must be near selection!\n"; + return; + } + + TextFileOutputStream file( filename.c_str() ); + if ( file.failed() ) { + globalErrorStream() << "writting " << filename.c_str() << " failure\n"; + return; + } + + // all brushes to the worldspawn + file << "{\n" + "\"classname\" \"worldspawn\""; + TokenWriter& writer = GlobalScripLibModule::getTable().m_pfnNewSimpleTokenWriter( file ); + class WriteBrushesWalker : public scene::Traversable::Walker + { + TokenWriter& m_writer; + public: + WriteBrushesWalker( TokenWriter& writer ) + : m_writer( writer ){ + } + bool pre( scene::Node& node ) const { + if( Node_getBrush( node ) ){ + NodeTypeCast::cast( node )->exportTokens( m_writer ); + } + return true; + } + }; + Map_Traverse_Selected( GlobalSceneGraph().root(), WriteBrushesWalker( writer ) ); + // plus box + scene::Node* box[6]; + for ( std::size_t i = 0; i < 6; ++i ){ + box[i] = &GlobalBrushCreator().createBrush(); + box[i]->IncRef(); + } + ConstructRegionBrushes( box, mins, maxs ); + for ( std::size_t i = 0; i < 6; ++i ){ + NodeTypeCast::cast( *box[i] )->exportTokens( writer ); + box[i]->DecRef(); + } + // close world + file << "\n}\n"; + // spawn + file << "{\n" + "\"classname\" \"info_player_start\"\n" + "\"origin\" \"" << spawn[0] << " " << spawn[1] << " " << spawn[2] << "\"\n" + "}\n"; + // point entities + const MapFormat& format = MapFormat_forFile( filename.c_str() ); + auto traverse_selected_point_entities = []( scene::Node& root, const scene::Traversable::Walker& walker ){ + scene::Traversable* traversable = Node_getTraversable( root ); + if ( traversable != 0 ) { + class selected_point_entities_walker : public scene::Traversable::Walker + { + const scene::Traversable::Walker& m_walker; + mutable bool m_skip; + public: + selected_point_entities_walker( const scene::Traversable::Walker& walker ) + : m_walker( walker ), m_skip( false ){ + } + bool pre( scene::Node& node ) const { + Entity* entity = Node_getEntity( node ); + if( !node.isRoot() && entity != 0 && !entity->isContainer() && Node_instanceSelected( node ) ) { + m_walker.pre( node ); + } + else{ + m_skip = true; + } + return false; + } + void post( scene::Node& node ) const { + if( m_skip ) + m_skip = false; + else + m_walker.post( node ); + } + }; + traversable->traverse( selected_point_entities_walker( walker ) ); + } + }; + format.writeGraph( GlobalSceneGraph().root(), traverse_selected_point_entities, file ); + + writer.release(); + } + + { // compile + StringOutputStream str( 256 ); + str << AppPath_get() << "q3map2." << RADIANT_EXECUTABLE + << " -game quake3" + << " -fs_basepath \"" << EnginePath_get() + << "\" -fs_homepath \"" << g_qeglobals.m_userEnginePath.c_str() + << "\" -fs_game " << gamename_get() + << " -autocaulk -fulldetail" + << " \"" << filename.c_str() << "\""; + // run + Q_Exec( NULL, str.c_str(), NULL, false, true ); + } + + typedef std::map CaulkMap; + CaulkMap map; + { // load + filename.clear(); + filename << StringRange( g_map.m_name.c_str(), path_get_filename_base_end( g_map.m_name.c_str() ) ) << "_ac.caulk"; + + TextFileInputStream file( filename.c_str() ); + if( file.failed() ){ + globalErrorStream() << "reading " << filename.c_str() << " failure\n"; + return; + } + + Tokeniser& tokeniser = GlobalScripLibModule::getTable().m_pfnNewSimpleTokeniser( file ); + while( 1 ){ + const char* num = tokeniser.getToken(); + if( !num ) + break; + std::size_t n; + string_parse_size( num, n ); + + const char* faces = tokeniser.getToken(); + if( !faces ) + break; + tokeniser.nextLine(); + + map.emplace( n, faces ); + } + tokeniser.release(); + } + + { // apply + class CaulkBrushesWalker : public scene::Traversable::Walker + { + const CaulkMap& m_map; + mutable std::size_t m_brushIndex = 0; + const CopiedString m_caulk = GetCommonShader( "caulk" ); + const CopiedString m_watercaulk = GetCommonShader( "watercaulk" ); + const CopiedString m_lavacaulk = GetCommonShader( "lavacaulk" ); + const CopiedString m_slimecaulk = GetCommonShader( "slimecaulk" ); + const CopiedString m_nodraw = GetCommonShader( "nodraw" ); + const CopiedString m_nodrawnonsolid = GetCommonShader( "nodrawnonsolid" ); + public: + mutable std::size_t m_caulkedCount = 0; + CaulkBrushesWalker( CaulkMap& map ) + : m_map( map ){ + } + bool pre( scene::Node& node ) const { + Brush* brush = Node_getBrush( node ); + if( brush ){ + CaulkMap::const_iterator iter = m_map.find( m_brushIndex ); + if( iter != m_map.end() ){ + const char* faces = ( *iter ).second.c_str(); + for ( Brush::const_iterator f = brush->begin(); f != brush->end() && *faces; ++f ){ + Face& face = *( *f ); + if ( face.contributes() ){ + ++m_caulkedCount; + switch ( *faces ) + { + case 'c': + face.SetShader( m_caulk.c_str() ); + break; + case 'w': + face.SetShader( m_watercaulk.c_str() ); + break; + case 'l': + face.SetShader( m_lavacaulk.c_str() ); + break; + case 's': + face.SetShader( m_slimecaulk.c_str() ); + break; + case 'N': + face.SetShader( m_nodraw.c_str() ); + break; + case 'n': + face.SetShader( m_nodrawnonsolid.c_str() ); + break; + + default: + --m_caulkedCount; + break; + } + + ++faces; + } + } + } + ++m_brushIndex; + } + return true; + } + }; + CaulkBrushesWalker caulkBrushesWalker( map ); + GlobalUndoSystem().start(); + Map_Traverse_Selected( GlobalSceneGraph().root(), caulkBrushesWalker ); + StringOutputStream str( 32 ); + str << "AutoCaulk " << caulkBrushesWalker.m_caulkedCount << " faces"; + GlobalUndoSystem().finish( str.c_str() ); + } +} + + + + void Map_constructPreferences( PreferencesPage& page ){ page.appendCheckBox( "", "Load last map at startup", g_bLoadLastMap ); } @@ -2397,6 +2651,7 @@ void Map_Construct(){ GlobalCommands_insert( "RegionSetBrush", FreeCaller() ); //GlobalCommands_insert( "RegionSetSelection", FreeCaller(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) ); GlobalToggles_insert( "RegionSetSelection", FreeCaller(), ToggleItem::AddCallbackCaller( g_region_item ), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) ); + GlobalCommands_insert( "AutoCaulkSelected", FreeCaller(), Accelerator( GDK_F4 ) ); GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) ); GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) ); diff --git a/tools/quake3/q3map2/bsp.c b/tools/quake3/q3map2/bsp.c index b1d86421..f873965c 100644 --- a/tools/quake3/q3map2/bsp.c +++ b/tools/quake3/q3map2/bsp.c @@ -38,6 +38,56 @@ +qboolean g_autocaulk = qfalse; + +static void autocaulk_write(){ + char filename[1024]; + + Sys_FPrintf( SYS_VRB, "--- autocaulk_write ---\n" ); + sprintf( filename, "%s.caulk", source ); + Sys_Printf( "writing %s\n", filename ); + + FILE* file = fopen( filename, "w" ); + if ( !file ) { + Error( "Error opening %s", filename ); + } + + int fslime = 16; + ApplySurfaceParm( "slime", &fslime, NULL, NULL ); + int flava = 8; + ApplySurfaceParm( "lava", &flava, NULL, NULL ); + + for ( brush_t* b = entities[0].brushes; b; b = b->next ) { + fprintf( file, "%i ", b->brushNum ); + shaderInfo_t* contentShader = b->contentShader; + for( int i = 0; i < b->numsides; ++i ){ + if( b->sides[i].visibleHull || ( b->sides[i].compileFlags & C_NODRAW ) ){ + fprintf( file, "-" ); + } + else if( contentShader->compileFlags & C_LIQUID ){ + if( contentShader->contentFlags & flava ) + fprintf( file, "l" ); + else if( contentShader->contentFlags & fslime ) + fprintf( file, "s" ); + else + fprintf( file, "w" ); + } + else if( b->compileFlags & C_TRANSLUCENT ){ + if( contentShader->compileFlags & C_SOLID ) + fprintf( file, "N" ); + else + fprintf( file, "n" ); + } + else{ + fprintf( file, "c" ); + } + } + fprintf( file, "\n" ); + } + + fclose( file ); +} + /* ------------------------------------------------------------------------------- functions @@ -375,6 +425,11 @@ void ProcessWorldModel( void ){ MakeTreePortals( tree ); FilterStructuralBrushesIntoTree( e, tree ); + if( g_autocaulk == qtrue ){ + autocaulk_write(); + exit( 0 ); + } + /* ydnar: flood again for skybox */ if ( skyboxPresent ) { FloodEntities( tree ); @@ -1003,6 +1058,10 @@ int BSPMain( int argc, char **argv ){ Sys_Printf( "No oBs!\n" ); noob = qtrue; } + else if ( !strcmp( argv[ i ], "-autocaulk" ) ) { + Sys_Printf( "\trunning in autocaulk mode\n" ); + g_autocaulk = qtrue; + } else { Sys_Warning( "Unknown option \"%s\"\n", argv[ i ] ); @@ -1049,10 +1108,10 @@ int BSPMain( int argc, char **argv ){ /* load original file from temp spot in case it was renamed by the editor on the way in */ if ( strlen( tempSource ) > 0 ) { - LoadMapFile( tempSource, qfalse, qfalse ); + LoadMapFile( tempSource, qfalse, g_autocaulk ); } else{ - LoadMapFile( name, qfalse, qfalse ); + LoadMapFile( name, qfalse, g_autocaulk ); } /* div0: inject command line parameters */