diff --git a/docs/Complete_list_of_command_line_parameters.htm b/docs/Complete_list_of_command_line_parameters.htm index 29dbdf95..b6599809 100644 --- a/docs/Complete_list_of_command_line_parameters.htm +++ b/docs/Complete_list_of_command_line_parameters.htm @@ -423,6 +423,8 @@ td.formatted_questions ol { margin-top: 0px; margin-bottom: 0px; }
  • -json ... filename.bsp: Export/import BSP to/from json text files for debugging and editing purposes
  • -unpack: Unpack BSP to json
  • -pack: Pack json to BSP
  • +
  • -useflagnames: While packing, deduce surface/content flag values from their names in shaders.json (useful for conversion to a game with different flag values)
  • +
  • -skipflags: While -useflagnames, skip unknown flag names
  • diff --git a/tools/quake3/q3map2/convert_json.cpp b/tools/quake3/q3map2/convert_json.cpp index ef52a02d..2e832e1b 100644 --- a/tools/quake3/q3map2/convert_json.cpp +++ b/tools/quake3/q3map2/convert_json.cpp @@ -35,6 +35,8 @@ #include "rapidjson/document.h" #include "rapidjson/prettywriter.h" +#include + template class Span { @@ -147,8 +149,50 @@ static void write_json( const char *directory ){ for_indexed( const auto& shader : bspShaders ){ rapidjson::Value value( rapidjson::kObjectType ); value.AddMember( "shader", rapidjson::StringRef( shader.shader ), all ); - value.AddMember( "surfaceFlags", shader.surfaceFlags, all ); ///////!!!!!!! decompose bits? - value.AddMember( "contentFlags", shader.contentFlags, all ); + { + int flags = shader.surfaceFlags; + std::map map; // decompose flags + for( auto parm = g_game->surfaceParms.crbegin(); parm != g_game->surfaceParms.crend(); ++parm ){ // find known flags // traverse backwards to try fatter material flags 1st + if( parm->surfaceFlags == ( flags & parm->surfaceFlags ) && parm->surfaceFlags != 0 ){ + flags &= ~parm->surfaceFlags; + map.emplace( parm->surfaceFlags, parm->name ); + } + } + for( int bit = 0; bit < 32; ++bit ){ // handle unknown flags + if( flags & ( 1 << bit ) ){ + map.emplace( ( 1 << bit ), "?" ); + } + } + rapidjson::Value fvalue( rapidjson::kObjectType ); + for( auto&& [ flag, name ] : map ){ // save in hex format + char c[16]; + sprintf( c, "%#.8x", flag ); + fvalue.AddMember( rapidjson::StringRef( name ), rapidjson::Value( c, all ), all ); + } + value.AddMember( "surfaceFlags", fvalue, all ); + } + { + int flags = shader.contentFlags; + std::map map; // decompose flags + for( auto parm = g_game->surfaceParms.crbegin(); parm != g_game->surfaceParms.crend(); ++parm ){ // find known flags // traverse backwards to try fatter material flags 1st + if( parm->contentFlags == ( flags & parm->contentFlags ) && parm->contentFlags != 0 ){ + flags &= ~parm->contentFlags; + map.emplace( parm->contentFlags, parm->name ); + } + } + for( int bit = 0; bit < 32; ++bit ){ // handle unknown flags + if( flags & ( 1 << bit ) ){ + map.emplace( ( 1 << bit ), "?" ); + } + } + rapidjson::Value fvalue( rapidjson::kObjectType ); + for( auto&& [ flag, name ] : map ){ // save in hex format + char c[16]; + sprintf( c, "%#.8x", flag ); + fvalue.AddMember( rapidjson::StringRef( name ), rapidjson::Value( c, all ), all ); + } + value.AddMember( "contentFlags", fvalue, all ); + } doc.AddMember( rapidjson::Value( StringOutputStream( 16 )( "shader#", i ).c_str(), all ), value, all ); } write_doc( StringOutputStream( 256 )( directory, "shaders.json" ), doc ); @@ -375,14 +419,52 @@ inline rapidjson::Document load_json( const char *fileName ){ return doc; } -static void read_json( const char *directory ){ +static void read_json( const char *directory, bool useFlagNames, bool skipUnknownFlags ){ { const auto doc = load_json( StringOutputStream( 256 )( directory, "shaders.json" ) ); for( auto&& obj : doc.GetObj() ){ auto&& item = bspShaders.emplace_back(); strcpy( item.shader, obj.value["shader"].GetString() ); - item.surfaceFlags = obj.value["surfaceFlags"].GetInt(); - item.contentFlags = obj.value["contentFlags"].GetInt(); + for( auto&& flag : obj.value["surfaceFlags"].GetObj() ){ + auto name = flag.name.GetString(); + auto value = flag.value.GetString(); + if( useFlagNames ){ + auto&& parms = g_game->surfaceParms; + auto found = std::find_if( parms.begin(), parms.end(), [name]( const auto& parm ){ return striEqual( parm.name, name ); } ); + if( found != parms.end() ){ + item.surfaceFlags |= found->surfaceFlags; + } + else if( skipUnknownFlags ){ + Sys_Warning( "Flag name '%s' is unknown in shader '%s', skipping its value (%s)\n", name, item.shader, value ); + } + else{ + Sys_Warning( "Flag name '%s' is unknown in shader '%s', using its value (%s)\n", name, item.shader, value ); + item.surfaceFlags |= strtoul( value, nullptr, 0 ); + } + } + else + item.surfaceFlags |= strtoul( value, nullptr, 0 ); + } + for( auto&& flag : obj.value["contentFlags"].GetObj() ){ + auto name = flag.name.GetString(); + auto value = flag.value.GetString(); + if( useFlagNames ){ + auto&& parms = g_game->surfaceParms; + auto found = std::find_if( parms.begin(), parms.end(), [name]( const auto& parm ){ return striEqual( parm.name, name ); } ); + if( found != parms.end() ){ + item.contentFlags |= found->contentFlags; + } + else if( skipUnknownFlags ){ + Sys_Warning( "Flag name '%s' is unknown in shader '%s', skipping its value (%s)\n", name, item.shader, value ); + } + else{ + Sys_Warning( "Flag name '%s' is unknown in shader '%s', using its value (%s)\n", name, item.shader, value ); + item.contentFlags |= strtoul( value, nullptr, 0 ); + } + } + else + item.contentFlags |= strtoul( value, nullptr, 0 ); + } } } { @@ -556,11 +638,13 @@ static void read_json( const char *directory ){ int ConvertJsonMain( Args& args ){ /* arg checking */ if ( args.empty() ) { - Sys_Printf( "Usage: q3map2 -json <-unpack|-pack> [-v] \n" ); + Sys_Printf( "Usage: q3map2 -json <-unpack|-pack [-useflagnames[-skipflags]]> [-v] \n" ); return 0; } bool doPack = false; // unpack by default + bool useFlagNames = false; + bool skipUnknownFlags = false; /* process arguments */ const char *fileName = args.takeBack(); @@ -568,6 +652,14 @@ int ConvertJsonMain( Args& args ){ while ( args.takeArg( "-pack" ) ) { doPack = true; } + while ( args.takeArg( "-useflagnames" ) ) { + useFlagNames = true; + Sys_Printf( "Deducing surface/content flag values from their names in shaders.json\n" ); + } + while ( args.takeArg( "-skipflags" ) ) { + skipUnknownFlags = true; + Sys_Printf( "Skipping unknown surface/content flag names\n" ); + } } // LoadShaderInfo(); @@ -584,7 +676,7 @@ int ConvertJsonMain( Args& args ){ } else{ /* write bsp */ - read_json( StringOutputStream( 256 )( PathExtensionless( source ), '/' ) ); + read_json( StringOutputStream( 256 )( PathExtensionless( source ), '/' ), useFlagNames, skipUnknownFlags ); UnparseEntities(); path_set_extension( source, "_json.bsp" ); Sys_Printf( "Writing %s\n", source ); diff --git a/tools/quake3/q3map2/help.cpp b/tools/quake3/q3map2/help.cpp index ca03bfa1..bfc688f8 100644 --- a/tools/quake3/q3map2/help.cpp +++ b/tools/quake3/q3map2/help.cpp @@ -426,6 +426,8 @@ void HelpJson() {"-json [options] ", "Export/import BSP to/from json text files for debugging and editing purposes"}, {"-unpack", "Unpack BSP to json"}, {"-pack", "Pack json to BSP"}, + {"-useflagnames", "While packing, deduce surface/content flag values from their names in shaders.json (useful for conversion to a game with different flag values)"}, + {"-skipflags", "While -useflagnames, skip unknown flag names"}, }; HelpOptions("BSP json export/import", 0, 80, options);