From b3e8f2d6fb4a6e0af066efabb15503094d91348e Mon Sep 17 00:00:00 2001 From: "bobby.anguelov" Date: Sun, 29 Mar 2020 00:46:38 +0000 Subject: [PATCH] Add support for batch conversion and for querying file formats --- .gitignore | 4 + FbxFormatConverter.args.json | 32 ++- FbxFormatConverter.vcxproj | 2 + FbxFormatConverter.vcxproj.user | 8 +- cmdParser.h | 2 +- main.cpp | 478 ++++++++++++++++++++++++-------- readme.md | 38 ++- 7 files changed, 440 insertions(+), 124 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1649b9a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.ipch +Browse.VC.db +Browse.VC.opendb +.suo diff --git a/FbxFormatConverter.args.json b/FbxFormatConverter.args.json index f98c8f8..5f603c9 100644 --- a/FbxFormatConverter.args.json +++ b/FbxFormatConverter.args.json @@ -2,13 +2,31 @@ "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": "-i TestFile.fbx" + "Command": "-c SK_Chr_Attach_Female_Armor_02.fbx" }, { "Id": "778e8d89-1616-468a-a9b1-623270e645a3", @@ -16,7 +34,11 @@ }, { "Id": "12a719b6-1313-4bc5-802f-5255a8f72766", - "Command": "-f binary" + "Command": "-binary" + }, + { + "Id": "d8f98b79-6ee4-4bf0-bc49-bf92c8666b44", + "Command": "-ascii" } ] }, @@ -26,15 +48,15 @@ "Items": [ { "Id": "5a09faa8-7239-44a1-bd45-ab3d7e42064b", - "Command": "-i TestFileOut.fbx" + "Command": "-c TestData" }, { "Id": "730b2ceb-ba4c-4251-9406-2251cb22ba37", - "Command": "-o TestFileOut2.fbx" + "Command": "-o Converted.fbx" }, { "Id": "289c0524-0c30-4d5a-8195-359af124689c", - "Command": "-f ascii" + "Command": "-ascii" } ] } diff --git a/FbxFormatConverter.vcxproj b/FbxFormatConverter.vcxproj index 6a4df99..7835445 100644 --- a/FbxFormatConverter.vcxproj +++ b/FbxFormatConverter.vcxproj @@ -108,6 +108,7 @@ _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug + true Console @@ -139,6 +140,7 @@ NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded + true Console diff --git a/FbxFormatConverter.vcxproj.user b/FbxFormatConverter.vcxproj.user index 5716033..94fc672 100644 --- a/FbxFormatConverter.vcxproj.user +++ b/FbxFormatConverter.vcxproj.user @@ -4,11 +4,11 @@ true - -i TestFileOut.fbx -o TestFileOut2.fbx -f ascii - -i TestFileOut.fbx -o TestFileOut2.fbx -f ascii + -c TestData -o Converted.fbx -ascii + -c TestData -o Converted.fbx -ascii - -i TestFileOut.fbx -o TestFileOut2.fbx -f ascii - -i TestFileOut.fbx -o TestFileOut2.fbx -f ascii + -c TestData -o Converted.fbx + -c TestData -o Converted.fbx \ No newline at end of file diff --git a/cmdParser.h b/cmdParser.h index 2bb743f..54224c9 100644 --- a/cmdParser.h +++ b/cmdParser.h @@ -336,7 +336,7 @@ namespace cli { } else if ( current == nullptr ) { - error << no_default(); + //error << no_default(); return false; } else diff --git a/main.cpp b/main.cpp index 10c177b..aa1e1f9 100644 --- a/main.cpp +++ b/main.cpp @@ -3,14 +3,17 @@ #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" -#include #if _MSC_VER #pragma warning(pop) @@ -18,183 +21,436 @@ //------------------------------------------------------------------------- -enum class OutputFormat +enum class FileFormat { Unknown, Binary, Ascii }; -static std::string GetFullPathString( char const* pPath ) +//------------------------------------------------------------------------- + +namespace FileSystemHelpers { - assert( pPath != nullptr && pPath[0] != 0 ); + 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 ); + 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 ); + } - // Ensure directory paths have the final slash appended - DWORD const result = GetFileAttributesA( fullpath ); - if ( result != INVALID_FILE_ATTRIBUTES && ( result & FILE_ATTRIBUTE_DIRECTORY ) && fullpath[length - 1] != '\\' ) + static std::string GetFullPathString( std::string const& path ) { - fullpath[length] = '\\'; - fullpath[length + 1] = 0; + return GetFullPathString( path.c_str() ); } - return std::string( fullpath ); -} + 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; + } -int ConvertFbxFile( char const* pInputFilepath, char const* pOutputFilepath, OutputFormat format ) -{ - FbxManager* pManager = FbxManager::Create(); + 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; + } - // Import - //------------------------------------------------------------------------- + return false; + } - FbxImporter* pImporter = FbxImporter::Create( pManager, "FBX Importer" ); - if ( !pImporter->Initialize( pInputFilepath, -1, pManager->GetIOSettings() ) ) + static FileFormat GetFileFormat( std::string const& filePath ) { - printf( " >> Error! Failed to load specified FBX file ( %s ): %s\n", pInputFilepath, pImporter->GetStatus().GetErrorString() ); - return 1; - } + FileFormat fileFormat = FileFormat::Unknown; - printf( " >> File opened successfully! - %s\n", pInputFilepath ); + FILE* fp = nullptr; + int errcode = fopen_s( &fp, filePath.c_str(), "r" ); + if ( errcode != 0 ) + { + return fileFormat; + } - auto pScene = FbxScene::Create( pManager, "ImportScene" ); - if ( !pImporter->Import( pScene ) ) - { - printf( " >> Error! Failed to load specified FBX file ( %s ): %s\n", pInputFilepath, pImporter->GetStatus().GetErrorString() ); - pImporter->Destroy(); - return 1; - } - pImporter->Destroy(); + //------------------------------------------------------------------------- - printf( " >> File imported successfully!\n" ); + fseek( fp, 0, SEEK_END ); + size_t filesize = (size_t) ftell( fp ); + fseek( fp, 0, SEEK_SET ); - // Set output format - //------------------------------------------------------------------------- + void* pFileData = malloc( filesize ); + size_t readLength = fread( pFileData, 1, filesize, fp ); + fclose( fp ); - auto pIOPluginRegistry = pManager->GetIOPluginRegistry(); - - int binaryFileFormatID = -1; - int asciiFileFormatID = -1; + //------------------------------------------------------------------------- - // Find the IDs for the ascii and binary writers - int const numWriters = pIOPluginRegistry->GetWriterFormatCount(); - for ( int i = 0; i < numWriters; i++ ) + // 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 ( pIOPluginRegistry->WriterIsFBX( i ) ) + if ( !IsValidDirectoryPath( directoryPath ) ) { - char const* pDescription = pIOPluginRegistry->GetWriterFormatDescription( i ); - if ( strstr( pDescription, "FBX binary" ) != nullptr ) + 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 ) { - binaryFileFormatID = i; + continue; } - else if ( strstr( pDescription, "FBX ascii" ) != nullptr ) + + if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { - asciiFileFormatID = i; + 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 ); } - // This should never occur but I'm leaving it here in case someone updates the plugin with a new SDK and names change - if ( binaryFileFormatID == -1 || asciiFileFormatID == -1 ) + static bool MakeDir( char const* pDirectoryPath ) { - printf( " >> Error! Failed to determine FBX ascii/binary format IDs\n" ); - return 1; + assert( pDirectoryPath != nullptr ); + return SUCCEEDED( SHCreateDirectoryExA( nullptr, pDirectoryPath, nullptr ) ); } +} - int const fileFormatIDToUse = ( format == OutputFormat::Binary ) ? binaryFileFormatID : asciiFileFormatID; +//------------------------------------------------------------------------- - // Export - //------------------------------------------------------------------------- +class FbxConverter +{ +public: - FbxExporter* pExporter = FbxExporter::Create( pManager, "FBX Exporter" ); - if ( !pExporter->Initialize( pOutputFilepath, fileFormatIDToUse, pManager->GetIOSettings() ) ) + FbxConverter() + : m_pManager( FbxManager::Create() ) { - printf( " >> Error! Failed to initialize exporter: %s\n", pExporter->GetStatus().GetErrorString() ); - return 1; + 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 ); } - if ( pExporter->Export( pScene ) ) + ~FbxConverter() { - printf( " >> File exported Successfully! - %s\n\n", pOutputFilepath ); + m_pManager->Destroy(); + m_pManager = nullptr; } - else + + int ConvertFbxFile( std::string const& inputFilepath, std::string const& outputFilepath, FileFormat outputFormat ) { - printf( " >> File export failed: - %s\n\n", pExporter->GetStatus().GetErrorString() ); + // 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 ); } - pExporter->Destroy(); +private: - // Cleanup - //------------------------------------------------------------------------- + FbxConverter( FbxConverter const& ) = delete; + FbxConverter& operator=( FbxConverter const& ) = delete; - pManager->Destroy(); +private: - return 0; -} + FbxManager* m_pManager = nullptr; + int const m_binaryWriteID = -1; + int const m_asciiWriterID = -1; +}; //------------------------------------------------------------------------- -int main( int argc, char* argv[] ) +static void PrintErrorAndHelp( char const* pErrorMessage = nullptr ) { - printf( " ================================================\n" ); - printf( " FBX File Format Converter\n" ); - printf( " ================================================\n" ); - printf( " Usage: FbxFormatConverter.exe -i \"inputfile.fbx\" -o \"outputfile.fbx\" -f \n" ); - printf( " ------------------------------------------------\n\n" ); + 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_required( "i", "infile", "" ); - cmdParser.set_required( "o", "outfile", "" ); - cmdParser.set_required( "f", "format", "" ); + 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() ) { - auto inputFile = cmdParser.get( "i" ); - inputFile = GetFullPathString( inputFile.c_str() ); + FbxConverter fbxConverter; //------------------------------------------------------------------------- - auto outputFile = cmdParser.get( "o" ); - outputFile = GetFullPathString( outputFile.c_str() ); - - //------------------------------------------------------------------------- - - if ( inputFile == outputFile ) + auto inputConvertPath = cmdParser.get( "c" ); + if ( !inputConvertPath.empty() ) { - printf( " >> Error! Input file (%s) cannot be the same as the output file (%s)!\n\n", inputFile.c_str(), outputFile.c_str() ); - return 1; - } + bool const outputAsBinary = cmdParser.get( "binary" ); + bool const outputAsAscii = cmdParser.get( "ascii" ); - //------------------------------------------------------------------------- - - OutputFormat outputFormat = OutputFormat::Unknown; - - auto const format = cmdParser.get( "f" ); - if ( format == "binary" ) - { - outputFormat = OutputFormat::Binary; - } - else if ( format == "ascii" ) - { - outputFormat = OutputFormat::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 ( inputConvertPath.empty() ) + { + return fbxConverter.ConvertFbxFile( inputConvertPath, inputConvertPath, outputFormat ); + } + else + { + outputPath = FileSystemHelpers::GetFullPathString( outputPath ); + return fbxConverter.ConvertFbxFile( inputConvertPath, outputPath, outputFormat ); + } + } + } } - else + else // check for query cmd line arg { - printf( " >> Error! Invalid parameter for -f, either 'binary' or 'ascii' expected!\n\n" ); - return 1; + 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 ConvertFbxFile( inputFile.c_str(), outputFile.c_str(), outputFormat ); + return 0; + } + else + { + PrintErrorAndHelp(); } return 1; diff --git a/readme.md b/readme.md index 46bc38c..8c3aca8 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,12 @@ 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) @@ -10,11 +16,37 @@ This project allows you to convert binary fbx files to asciis and vice versa. Th * Open the sln file using visual studio and hit build. -## To use: +## 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` -FbxFormatConverter.exe -i \"inputfile.fbx\" -o \"outputfile.fbx\" -f +If you want to know if file "dancingbaby.fbx" is a binary file. - e.g. FbxFormatConverter.exe -i "Character.fbx" -o "CharacterAscii.fbx" -f ascii +`FbxFormatConverter.exe -q "c:\dancingbaby.fbx"` ## Notes: