Repository: BobbyAnguelov/FbxFormatConverter Branch: master Commit: 8ac24511f3f5 Files: 10 Total size: 47.2 KB Directory structure: gitextract_0xistjo3/ ├── .gitignore ├── FbxFormatConverter.args.json ├── FbxFormatConverter.props ├── FbxFormatConverter.sln ├── FbxFormatConverter.vcxproj ├── FbxFormatConverter.vcxproj.filters ├── LICENSE ├── cmdParser.h ├── main.cpp └── readme.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.ipch Browse.VC.db Browse.VC.opendb .suo _Build/* Bin/* .vs/* *.user FbxFormatConverter.vcxproj.user ================================================ FILE: FbxFormatConverter.args.json ================================================ { "FileVersion": 2, "Id": "9e46203a-785a-41a3-bd18-2de2a7a7e4d7", "Items": [ { "Id": "048f9f3d-b4f7-421a-88a2-8208d935e75d", "Command": "", "Items": [ { "Id": "02d89d7e-4960-4457-bcb6-3425ba20ab07", "Command": "-q blah.fbx" }, { "Id": "28299385-031f-4af4-91dd-1c590319b3ef", "Command": "-q SK_Chr_Attach_Female_Armor_02.fbx" }, { "Id": "c55eea3c-9a11-4ef5-b3c6-dbf81f43e261", "Command": "-q TestData" } ] }, { "Id": "5fa48bfb-6393-481a-b2ab-c0bd71ed50f1", "Command": "", "Items": [ { "Id": "5ae255c1-876e-4bcd-b400-edd0be3b171f", "Command": "-c SM_Bld_Apartment_01.fbx" }, { "Id": "778e8d89-1616-468a-a9b1-623270e645a3", "Command": "-o TestFileOut.fbx" }, { "Id": "12a719b6-1313-4bc5-802f-5255a8f72766", "Command": "-binary" }, { "Id": "d8f98b79-6ee4-4bf0-bc49-bf92c8666b44", "Command": "-ascii" } ] }, { "Id": "3f90e0f3-43db-4bbe-9976-f82862fc5ebb", "Command": "", "Items": [ { "Id": "5a09faa8-7239-44a1-bd45-ab3d7e42064b", "Command": "-c TestData" }, { "Id": "730b2ceb-ba4c-4251-9406-2251cb22ba37", "Command": "-o Converted.fbx" }, { "Id": "289c0524-0c30-4d5a-8195-359af124689c", "Command": "-ascii" } ] } ] } ================================================ FILE: FbxFormatConverter.props ================================================  C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\ $(FBX_SDK_DIR)include\;%(AdditionalIncludeDirectories); %(PreprocessorDefinitions) $(FBX_SDK_DIR)lib\vs2017\x64\debug\;%(AdditionalLibraryDirectories) $(FBX_SDK_DIR)lib\vs2017\x64\release\;%(AdditionalLibraryDirectories) libfbxsdk-mt.lib;zlib-mt.lib;libxml2-mt.lib;%(AdditionalDependencies) ================================================ FILE: FbxFormatConverter.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29905.134 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FbxFormatConverter", "FbxFormatConverter.vcxproj", "{9E46203A-785A-41A3-BD18-2DE2A7A7E4D7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9E46203A-785A-41A3-BD18-2DE2A7A7E4D7}.Debug|x64.ActiveCfg = Debug|x64 {9E46203A-785A-41A3-BD18-2DE2A7A7E4D7}.Debug|x64.Build.0 = Debug|x64 {9E46203A-785A-41A3-BD18-2DE2A7A7E4D7}.Release|x64.ActiveCfg = Release|x64 {9E46203A-785A-41A3-BD18-2DE2A7A7E4D7}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DAC409EE-F3F3-4A2C-B22F-316A9B8CAEFA} EndGlobalSection EndGlobal ================================================ FILE: FbxFormatConverter.vcxproj ================================================ Debug Win32 Release Win32 Debug x64 Release x64 16.0 {9E46203A-785A-41A3-BD18-2DE2A7A7E4D7} FbxFormatConverter 10.0 Application true v142 Unicode Application false v142 true Unicode Application true v142 Unicode Application false v142 true Unicode true true _Build\$(Platform)_$(Configuration)\ $(SolutionDir)Bin\$(Platform)_$(Configuration)\ false false $(SolutionDir)Bin\$(Platform)_$(Configuration)\ _Build\$(Platform)_$(Configuration)\ Level3 true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true Level4 true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug true Console true Level3 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true true Level4 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded true Console true true true ================================================ FILE: FbxFormatConverter.vcxproj.filters ================================================  ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 Bobby Anguelov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: cmdParser.h ================================================ /* This file is part of the C++ CmdParser utility. Copyright (c) 2015 - 2016 Florian Rappl */ #pragma once #include #include #include #include #include #include namespace cli { struct CallbackArgs { const std::vector& arguments; std::ostream& output; std::ostream& error; }; class Parser { private: class CmdBase { public: explicit CmdBase( const std::string& name, const std::string& alternative, const std::string& description, bool required, bool dominant, bool variadic ) : name( name ), command( name.size() > 0 ? "-" + name : "" ), alternative( alternative.size() > 0 ? "--" + alternative : "" ), description( description ), required( required ), handled( false ), arguments( {} ), dominant( dominant ), variadic( variadic ) { } virtual ~CmdBase() { } std::string name; std::string command; std::string alternative; std::string description; bool required; bool handled; std::vector arguments; bool const dominant; bool const variadic; virtual std::string print_value() const = 0; virtual bool parse( std::ostream& output, std::ostream& error ) = 0; bool is( const std::string& given ) const { return given == command || given == alternative; } }; template struct ArgumentCountChecker { static constexpr bool Variadic = false; }; template struct ArgumentCountChecker> { static constexpr bool Variadic = true; }; template class CmdFunction final : public CmdBase { public: explicit CmdFunction( const std::string& name, const std::string& alternative, const std::string& description, bool required, bool dominant ) : CmdBase( name, alternative, description, required, dominant, ArgumentCountChecker::Variadic ) { } virtual bool parse( std::ostream& output, std::ostream& error ) { try { CallbackArgs args{ arguments, output, error }; value = callback( args ); return true; } catch ( ... ) { return false; } } virtual std::string print_value() const { return ""; } std::function callback; T value; }; template class CmdArgument final : public CmdBase { public: explicit CmdArgument( const std::string& name, const std::string& alternative, const std::string& description, bool required, bool dominant ) : CmdBase( name, alternative, description, required, dominant, ArgumentCountChecker::Variadic ) { } virtual bool parse( std::ostream&, std::ostream& ) { try { value = Parser::parse( arguments, value ); return true; } catch ( ... ) { return false; } } virtual std::string print_value() const { return stringify( value ); } T value; }; static int parse( const std::vector& elements, const int& ) { //if ( elements.size() != 1 ) // throw std::bad_cast(); return std::stoi( elements[0] ); } static bool parse( const std::vector& elements, const bool& defval ) { if ( elements.size() != 0 ) throw std::runtime_error( "A boolean command line parameter cannot have any arguments." ); return !defval; } static double parse( const std::vector& elements, const double& ) { //if ( elements.size() != 1 ) // throw std::bad_cast(); return std::stod( elements[0] ); } static float parse( const std::vector& elements, const float& ) { //if ( elements.size() != 1 ) // throw std::bad_cast(); return std::stof( elements[0] ); } static long double parse( const std::vector& elements, const long double& ) { //if ( elements.size() != 1 ) // throw std::bad_cast(); return std::stold( elements[0] ); } static unsigned int parse( const std::vector& elements, const unsigned int& ) { //if ( elements.size() != 1 ) // throw std::bad_cast(); return static_cast( std::stoul( elements[0] ) ); } static unsigned long parse( const std::vector& elements, const unsigned long& ) { //if ( elements.size() != 1 ) // throw std::bad_cast(); return std::stoul( elements[0] ); } static long parse( const std::vector& elements, const long& ) { //if ( elements.size() != 1 ) // throw std::bad_cast(); return std::stol( elements[0] ); } static std::string parse( const std::vector& elements, const std::string& ) { //if ( elements.size() != 1 ) // throw std::bad_cast(); return elements[0]; } template static std::vector parse( const std::vector& elements, const std::vector& ) { const T defval = T(); std::vector values{}; std::vector buffer( 1 ); for ( const auto& element : elements ) { buffer[0] = element; values.push_back( parse( buffer, defval ) ); } return values; } template static std::string stringify( const T& value ) { return std::to_string( value ); } template static std::string stringify( const std::vector& values ) { std::stringstream ss{}; ss << "[ "; for ( const auto& value : values ) { ss << stringify( value ) << " "; } ss << "]"; return ss.str(); } static std::string stringify( const std::string& str ) { return str; } public: explicit Parser( int argc, const char** argv ) : _appname( argv[0] ) { for ( int i = 1; i < argc; ++i ) { _arguments.push_back( argv[i] ); } enable_help(); } explicit Parser( int argc, char** argv ) : _appname( argv[0] ) { for ( int i = 1; i < argc; ++i ) { _arguments.push_back( argv[i] ); } enable_help(); } ~Parser() { for ( int i = 0, n = _commands.size(); i < n; ++i ) { delete _commands[i]; } } bool has_help() const { for ( const auto command : _commands ) { if ( command->name == "h" && command->alternative == "--help" ) { return true; } } return false; } void enable_help() { set_callback( "h", "help", std::function( [this] ( CallbackArgs& args ) { args.output << this->usage(); exit( 0 ); return false; } ), "", true ); } void disable_help() { for ( auto command = _commands.begin(); command != _commands.end(); ++command ) { if ( ( *command )->name == "h" && ( *command )->alternative == "--help" ) { _commands.erase( command ); break; } } } template void set_default( bool is_required, const std::string& description = "" ) { auto command = new CmdArgument{ "", "", description, is_required, false }; _commands.push_back( command ); } template void set_required( const std::string& name, const std::string& alternative, const std::string& description = "", bool dominant = false ) { auto command = new CmdArgument{ name, alternative, description, true, dominant }; _commands.push_back( command ); } template void set_optional( const std::string& name, const std::string& alternative, T defaultValue, const std::string& description = "", bool dominant = false ) { auto command = new CmdArgument{ name, alternative, description, false, dominant }; command->value = defaultValue; _commands.push_back( command ); } template void set_callback( const std::string& name, const std::string& alternative, std::function callback, const std::string& description = "", bool dominant = false ) { auto command = new CmdFunction{ name, alternative, description, false, dominant }; command->callback = callback; _commands.push_back( command ); } inline void run_and_exit_if_error() { if ( run() == false ) { exit( 1 ); } } inline bool run() { return run( std::cout, std::cerr ); } inline bool run( std::ostream& output ) { return run( output, std::cerr ); } bool run( std::ostream& output, std::ostream& error ) { if ( _arguments.size() > 0 ) { auto current = find_default(); for ( int i = 0, n = _arguments.size(); i < n; ++i ) { auto isarg = _arguments[i].size() > 0 && _arguments[i][0] == '-'; auto associated = isarg ? find( _arguments[i] ) : nullptr; if ( associated != nullptr ) { current = associated; associated->handled = true; } else if ( current == nullptr ) { //error << no_default(); return false; } else { current->arguments.push_back( _arguments[i] ); current->handled = true; if ( !current->variadic ) { // If the current command is not variadic, then no more arguments // should be added to it. In this case, switch back to the default // command. current = find_default(); } } } } // First, parse dominant arguments since they succeed even if required // arguments are missing. for ( auto command : _commands ) { if ( command->handled && command->dominant && !command->parse( output, error ) ) { error << howto_use( command ); return false; } } // Next, check for any missing arguments. for ( auto command : _commands ) { if ( command->required && !command->handled ) { error << howto_required( command ); return false; } } // Finally, parse all remaining arguments. for ( auto command : _commands ) { if ( command->handled && !command->dominant && !command->parse( output, error ) ) { error << howto_use( command ); return false; } } return true; } template T get( const std::string& name ) const { for ( const auto& command : _commands ) { if ( command->name == name ) { auto cmd = dynamic_cast*>( command ); if ( cmd == nullptr ) { throw std::runtime_error( "Invalid usage of the parameter " + name + " detected." ); } return cmd->value; } } throw std::runtime_error( "The parameter " + name + " could not be found." ); } template T get_if( const std::string& name, std::function callback ) const { auto value = get( name ); return callback( value ); } int requirements() const { int count = 0; for ( const auto& command : _commands ) { if ( command->required ) { ++count; } } return count; } int commands() const { return static_cast( _commands.size() ); } inline const std::string& app_name() const { return _appname; } protected: CmdBase* find( const std::string& name ) { for ( auto command : _commands ) { if ( command->is( name ) ) { return command; } } return nullptr; } CmdBase* find_default() { for ( auto command : _commands ) { if ( command->name == "" ) { return command; } } return nullptr; } std::string usage() const { std::stringstream ss{}; ss << "Available parameters:\n\n"; for ( const auto& command : _commands ) { ss << " " << command->command << "\t" << command->alternative; if ( command->required == true ) { ss << "\t(required)"; } ss << "\n " << command->description; if ( command->required == false ) { ss << "\n " << "This parameter is optional. The default value is '" + command->print_value() << "'."; } ss << "\n\n"; } return ss.str(); } void print_help( std::stringstream& ss ) const { if ( has_help() ) { ss << "For more help use --help or -h.\n"; } } std::string howto_required( CmdBase* command ) const { std::stringstream ss{}; ss << "The parameter " << command->name << " is required.\n"; ss << command->description << '\n'; print_help( ss ); return ss.str(); } std::string howto_use( CmdBase* command ) const { std::stringstream ss{}; ss << "The parameter " << command->name << " has invalid arguments.\n"; ss << command->description << '\n'; print_help( ss ); return ss.str(); } std::string no_default() const { std::stringstream ss{}; ss << "No default parameter has been specified.\n"; ss << "The given argument must be used with a parameter.\n"; print_help( ss ); return ss.str(); } private: const std::string _appname; std::vector _arguments; std::vector _commands; }; } ================================================ FILE: main.cpp ================================================ #include #include #include #include #include #include #include #include #if _MSC_VER #pragma warning(push, 0) #pragma warning(disable: 4702) #endif // Note: this has been modified for this application #include "cmdParser.h" #if _MSC_VER #pragma warning(pop) #endif //------------------------------------------------------------------------- enum class FileFormat { Unknown, Binary, Ascii }; //------------------------------------------------------------------------- namespace FileSystemHelpers { static std::string GetFullPathString( char const* pPath ) { assert( pPath != nullptr && pPath[0] != 0 ); char fullpath[256] = { 0 }; DWORD length = GetFullPathNameA( pPath, 256, fullpath, nullptr ); assert( length != 0 && length != 255 ); // We always append the trailing slash to simplify further operations DWORD const result = GetFileAttributesA( fullpath ); if ( result != INVALID_FILE_ATTRIBUTES && ( result & FILE_ATTRIBUTE_DIRECTORY ) && fullpath[length - 1] != '\\' ) { fullpath[length] = '\\'; fullpath[length + 1] = 0; } return std::string( fullpath ); } static std::string GetFullPathString( std::string const& path ) { return GetFullPathString( path.c_str() ); } static std::string GetParentDirectoryPath( std::string const& path ) { std::string dirPath; size_t const lastSlashIdx = path.rfind( '\\' ); if ( lastSlashIdx != std::string::npos ) { dirPath = path.substr( 0, lastSlashIdx + 1 ); } return dirPath; } static bool IsValidDirectoryPath( std::string const& directoryPath ) { DWORD const result = GetFileAttributesA( directoryPath.c_str() ); if ( result != INVALID_FILE_ATTRIBUTES && ( result & FILE_ATTRIBUTE_DIRECTORY ) ) { return true; } return false; } static FileFormat GetFileFormat( std::string const& filePath ) { FileFormat fileFormat = FileFormat::Unknown; FILE* fp = nullptr; int errcode = fopen_s( &fp, filePath.c_str(), "r" ); if ( errcode != 0 ) { return fileFormat; } //------------------------------------------------------------------------- fseek( fp, 0, SEEK_END ); size_t filesize = (size_t) ftell( fp ); fseek( fp, 0, SEEK_SET ); void* pFileData = malloc( filesize ); size_t readLength = fread( pFileData, 1, filesize, fp ); fclose( fp ); //------------------------------------------------------------------------- // Ascii files cannot contain the null character if ( memchr( pFileData, '\0', readLength ) != NULL ) { fileFormat = FileFormat::Binary; } else { fileFormat = FileFormat::Ascii; } return fileFormat; } static void GetDirectoryContents( std::string const& directoryPath, std::vector& directoryContents ) { if ( !IsValidDirectoryPath( directoryPath ) ) { printf( "Error! %s is not a valid directory!", directoryPath.c_str() ); return; } //------------------------------------------------------------------------- std::string const directorySearchPath = directoryPath + "*"; //------------------------------------------------------------------------- WIN32_FIND_DATAA findData; HANDLE foundFileHandle = FindFirstFileA( directorySearchPath.c_str(), &findData ); assert( foundFileHandle != INVALID_HANDLE_VALUE ); //------------------------------------------------------------------------- char stringBuffer[1024] = { 0 }; do { if ( strcmp( findData.cFileName, "." ) == 0 || strcmp( findData.cFileName, ".." ) == 0 ) { continue; } if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { sprintf_s( stringBuffer, 1024, "%s%s\\", directoryPath.c_str(), findData.cFileName ); GetDirectoryContents( stringBuffer, directoryContents ); } else { sprintf_s( stringBuffer, 1024, "%s%s", directoryPath.c_str(), findData.cFileName ); directoryContents.emplace_back( GetFullPathString( stringBuffer ) ); } } while ( FindNextFileA( foundFileHandle, &findData ) != 0 ); } static bool MakeDir( char const* pDirectoryPath ) { assert( pDirectoryPath != nullptr ); return SUCCEEDED( SHCreateDirectoryExA( nullptr, pDirectoryPath, nullptr ) ); } } //------------------------------------------------------------------------- class FbxConverter { public: FbxConverter() : m_pManager( FbxManager::Create() ) { assert( m_pManager != nullptr ); auto pIOPluginRegistry = m_pManager->GetIOPluginRegistry(); // Find the IDs for the ascii and binary writers int const numWriters = pIOPluginRegistry->GetWriterFormatCount(); for ( int i = 0; i < numWriters; i++ ) { if ( pIOPluginRegistry->WriterIsFBX( i ) ) { char const* pDescription = pIOPluginRegistry->GetWriterFormatDescription( i ); if ( strcmp( pDescription, "FBX binary (*.fbx)" ) == 0 ) { const_cast( m_binaryWriteID ) = i; } else if ( strcmp( pDescription, "FBX ascii (*.fbx)" ) == 0 ) { const_cast( m_asciiWriterID ) = i; } } } //------------------------------------------------------------------------- // This should never occur but I'm leaving it here in case someone updates the plugin with a new SDK and names change assert( m_binaryWriteID != -1 && m_asciiWriterID != -1 ); } ~FbxConverter() { m_pManager->Destroy(); m_pManager = nullptr; } int ConvertFbxFile( std::string const& inputFilepath, std::string const& outputFilepath, FileFormat outputFormat ) { // Import //------------------------------------------------------------------------- FbxImporter* pImporter = FbxImporter::Create( m_pManager, "FBX Importer" ); if ( !pImporter->Initialize( inputFilepath.c_str(), -1, m_pManager->GetIOSettings() ) ) { printf( "Error! Failed to load specified FBX file ( %s ): %s\n\n", inputFilepath.c_str(), pImporter->GetStatus().GetErrorString() ); return 1; } auto pScene = FbxScene::Create( m_pManager, "ImportScene" ); if ( !pImporter->Import( pScene ) ) { printf( "Error! Failed to import scene from file ( %s ): %s\n\n", inputFilepath.c_str(), pImporter->GetStatus().GetErrorString() ); pImporter->Destroy(); return 1; } pImporter->Destroy(); // Set output format //------------------------------------------------------------------------- int const fileFormatIDToUse = ( outputFormat == FileFormat::Binary ) ? m_binaryWriteID : m_asciiWriterID; // Export //------------------------------------------------------------------------- std::string const parentDirPath = FileSystemHelpers::GetParentDirectoryPath( outputFilepath ); if ( !FileSystemHelpers::MakeDir( parentDirPath.c_str() ) ) { printf( "Error! Failed to create output directory (%s)!\n\n", outputFilepath.c_str() ); } //------------------------------------------------------------------------- FbxExporter* pExporter = FbxExporter::Create( m_pManager, "FBX Exporter" ); if ( !pExporter->Initialize( outputFilepath.c_str(), fileFormatIDToUse, m_pManager->GetIOSettings() ) ) { printf( "Error! Failed to initialize exporter: %s\n\n", pExporter->GetStatus().GetErrorString() ); return 1; } if ( pExporter->Export( pScene ) ) { printf( "Success!\nIn: %s \nOut (%s): %s\n\n", inputFilepath.c_str(), outputFormat == FileFormat::Binary ? "binary" : "ascii", outputFilepath.c_str() ); } else { printf( "Error! File export failed: - %s\n\n", pExporter->GetStatus().GetErrorString() ); } pExporter->Destroy(); //------------------------------------------------------------------------- return 0; } bool IsFbxFile( std::string const& inputFilepath ) { assert( !inputFilepath.empty() ); int readerID = -1; auto pIOPluginRegistry = m_pManager->GetIOPluginRegistry(); pIOPluginRegistry->DetectReaderFileFormat( inputFilepath.c_str(), readerID ); return pIOPluginRegistry->ReaderIsFBX( readerID ); } private: FbxConverter( FbxConverter const& ) = delete; FbxConverter& operator=( FbxConverter const& ) = delete; private: FbxManager* m_pManager = nullptr; int const m_binaryWriteID = -1; int const m_asciiWriterID = -1; }; //------------------------------------------------------------------------- static void PrintErrorAndHelp( char const* pErrorMessage = nullptr ) { printf( "================================================\n" ); printf( "FBX File Format Converter\n" ); printf( "================================================\n" ); printf( "2020 - Bobby Anguelov - MIT License\n\n" ); if ( pErrorMessage != nullptr ) { printf( "Error! %s\n\n", pErrorMessage ); } printf( "Convert: -c [-o ] {-binary|-ascii}\n" ); printf( "Query: -q \n" ); } static void PrintFileFormat( std::string const& filePath ) { FileFormat const fileFormat = FileSystemHelpers::GetFileFormat( filePath.c_str() ); if ( fileFormat == FileFormat::Binary ) { printf( "%s - binary\n", filePath.c_str() ); } else if ( fileFormat == FileFormat::Ascii ) { printf( "%s - ascii\n", filePath.c_str() ); } else { printf( "%s doesnt exist or is not an FBX file!\n", filePath.c_str() ); } } //------------------------------------------------------------------------- int main( int argc, char* argv[] ) { cli::Parser cmdParser( argc, argv ); cmdParser.disable_help(); cmdParser.set_optional( "c", "convert", "" ); cmdParser.set_optional( "o", "output", "" ); cmdParser.set_optional( "q", "query", "" ); cmdParser.set_optional( "binary", "", false, "" ); cmdParser.set_optional( "ascii", "", false, "" ); if ( cmdParser.run() ) { FbxConverter fbxConverter; //------------------------------------------------------------------------- auto inputConvertPath = cmdParser.get( "c" ); if ( !inputConvertPath.empty() ) { bool const outputAsBinary = cmdParser.get( "binary" ); bool const outputAsAscii = cmdParser.get( "ascii" ); if ( outputAsAscii && outputAsBinary ) { PrintErrorAndHelp( "Having both -ascii and -binary arguments is not allowed." ); } else if ( !outputAsAscii && !outputAsBinary ) { PrintErrorAndHelp( "Either -ascii or -binary required!" ); } else { FileFormat const outputFormat = outputAsBinary ? FileFormat::Binary : FileFormat::Ascii; inputConvertPath = FileSystemHelpers::GetFullPathString( inputConvertPath ); if ( FileSystemHelpers::IsValidDirectoryPath( inputConvertPath ) ) { std::vector directoryContents; FileSystemHelpers::GetDirectoryContents( inputConvertPath, directoryContents ); auto outputPath = cmdParser.get( "o" ); if ( outputPath.empty() ) { for ( auto& filePath : directoryContents ) { if ( !fbxConverter.IsFbxFile( filePath.c_str() ) ) { continue; } fbxConverter.ConvertFbxFile( filePath.c_str(), filePath.c_str(), outputFormat ); } return 0; } else // Convert and copy { outputPath = FileSystemHelpers::GetFullPathString( outputPath ); for ( auto& filePath : directoryContents ) { if ( !fbxConverter.IsFbxFile( filePath ) ) { continue; } std::string newOutputPath = filePath; newOutputPath.replace( 0, inputConvertPath.length() - 1, outputPath.c_str() ); fbxConverter.ConvertFbxFile( filePath, newOutputPath, outputFormat ); } return 0; } } else { auto outputPath = cmdParser.get( "o" ); if ( outputPath.empty() ) { return fbxConverter.ConvertFbxFile( inputConvertPath, inputConvertPath, outputFormat ); } else { outputPath = FileSystemHelpers::GetFullPathString( outputPath ); return fbxConverter.ConvertFbxFile( inputConvertPath, outputPath, outputFormat ); } } } } else // check for query cmd line arg { auto inputQueryPath = cmdParser.get( "q" ); if ( !inputQueryPath.empty() ) { inputQueryPath = FileSystemHelpers::GetFullPathString( inputQueryPath ); if ( FileSystemHelpers::IsValidDirectoryPath( inputQueryPath ) ) { std::vector directoryContents; FileSystemHelpers::GetDirectoryContents( inputQueryPath, directoryContents ); for ( auto& filePath : directoryContents ) { if ( !fbxConverter.IsFbxFile( filePath ) ) { continue; } PrintFileFormat( filePath ); } } else { PrintFileFormat( inputQueryPath ); } } else { PrintErrorAndHelp( "Invalid Arguments!" ); } } return 0; } else { PrintErrorAndHelp(); } return 1; } ================================================ FILE: readme.md ================================================ # Fbx Format Converter This project allows you to convert binary fbx files to asciis and vice versa. This is especially useful when trying to import fbx files into blender since blender cannot read ascii FBX files. ## Features * Single file conversion between binary and ascii * Batch folder conversion * Single file/folder query ## To build: * You need to have the FBX SDK installed (https://www.autodesk.com/developer-network/platform-technologies/fbx-sdk-2020-0) * Open the FbxFormatConverter.props file and change the FBX_SDK_DIR macro to point to the FBXSDK install directory. * Open the sln file using visual studio and hit build. ## Conversion: If you want to convert an ascii file into a binary one or vice versa. `FbxFormatConverter.exe -c [-o ] {-ascii|-binary}` * -c : convert the file/folder specified * -o : (optional) the outputpath for the converted files, if not supplied then the source file will be overwritten * -binary/-ascii : the required output file format. Only one is allowed. ## Query: If you want to find out if an FBX file is an ascii or a binary file. `FbxFormatConverter.exe -q ` * -q : query the file/folder specified ## Examples If you want to covert file "anim_temp_final_0_v2.fbx" to binary. `FbxFormatConverter.exe -c "c:\anim_temp_final_0_v2.fbx" -binary` If you want to convert all the files in folder a to ascii and store the converted files in folder b: `FbxFormatConverter.exe -c "c:\a" -o "c:\b" -ascii` If you want to know if file "dancingbaby.fbx" is a binary file. `FbxFormatConverter.exe -q "c:\dancingbaby.fbx"` ## Notes: This project uses CmdParser ( https://github.com/FlorianRappl/CmdParser )