While I am working on a WEBGL project, I wanted to share some hints I had to work through with you. The FBX documentation isn't clear on every steps, is a bit out of date even if it fairly easy to identify the pieces to update and spread all over the place. I hope that post will help on this.

The project I am working on is a glTF importer / exporter. glTF is a specification (under development) for WEBGL asset (i.e. glTF - the runtime asset format for WebGL, and OpenGL ES). What is nice with that specification is that it is a format that would load onto the various javascript WEBGL viewer without the need to program too much on the javascript side. So you would not need to learn any particular javascript viewing technology such as Three.js, Montage,js, Babylon.js, ...
In this post, I am going to explain the exporter code, but the importer code is very close with few exception such as deriving from FbxReader vs FbxWriter for an exporter.
- First, we create a new DLL project using 'Multithreaded DLL' code generation. Visual Studio 2013 will create a Unicode project by default while the FBX SDK is MBCS still. Here you may change the character set to MBCS to avoid to have to convert back and force string from MBCS <-> UTF8/16.
- Next, we create a plug-in class derived from FbxPlugin
class IOglTF : public FbxPlugin { FBXSDK_PLUGIN_DECLARE(IOglTF) ; public: static const char *PLUGIN_NAME ; static const char *PLUGIN_VERSION ; protected: explicit IOglTF (const FbxPluginDef &pDefinition, FbxModule pLibHandle) : FbxPlugin (pDefinition, pLibHandle) { } // Implement Fbxmodules::FbxPlugin virtual bool SpecificInitialize () ; virtual bool SpecificTerminate () { return (true) ; } } ;
and the implementation (here we register the importer and export class that we will implement later)
FBXSDK_PLUGIN_IMPLEMENT(IOglTF) ; /*static*/ const char *IOglTF::PLUGIN_NAME ="IO-glTF" ; /*static*/ const char *IOglTF::PLUGIN_VERSION ="0.1.0" ; bool IOglTF::SpecificInitialize () { int registeredCount =0 ; int gltfReaderId =0, gltfWriterId =0 ; GetData ().mSDKManager->GetIOPluginRegistry ()->RegisterWriter (gltfWriter::Create_gltfWriter, gltfWriter::gltfFormatInfo, gltfWriterId, registeredCount, gltfWriter::FillIOSettings) ; GetData ().mSDKManager->GetIOPluginRegistry ()->RegisterReader (gltfReader::Create_gltfReader, gltfReader::gltfFormatInfo, gltfReaderId, registeredCount, gltfReader::FillIOSettings) ; return (true) ; }
- Finally, to complete the plug-in part itself, we implement and export the FBXPluginRegistration() function:
extern "C" { // The DLL is owner of the plug-in static IOglTF *pPlugin =nullptr ; // This function will be called when an application will request the plug-in #if defined(_WIN64) || defined (_WIN32) __declspec(dllexport) void FBXPluginRegistration (FbxPluginContainer &pContainer, FbxModule pLibHandle) { #else void FBXPluginRegistration (FbxPluginContainer &pContainer, FbxModule pLibHandle) { #endif if ( pPlugin == nullptr ) { // Create the plug-in definition which contains the information about the plug-in FbxPluginDef pluginDef ; pluginDef.mName =FbxString (IOglTF::PLUGIN_NAME) ; pluginDef.mVersion =FbxString (IOglTF::PLUGIN_VERSION) ; // Create an instance of the plug-in. The DLL has the ownership of the plug-in pPlugin =IOglTF::Create (pluginDef, pLibHandle) ; // Register the plug-in pContainer.Register (*pPlugin) ; } } }
- At step 2., we used the following symbols: Create_gltfWriter, gltfFormatInfo, FillIOSettings, that we need to implement now. These functions are used to describe what the importer/exporter classes are for, what file format they can handle, describe their IO options, etc... Here is the class declaration derived from FbxWriter
class gltfWriter : public FbxWriter { private: ... my class members ... public: gltfWriter (FbxManager &pManager, int id) ; virtual ~gltfWriter () ; virtual bool FileCreate (char *pFileName) ; virtual bool FileClose () ; virtual bool IsFileOpen () ; virtual void GetWriteOptions () ; virtual bool Write (FbxDocument *pDocument) ; virtual bool PreprocessScene (FbxScene &scene) ; virtual bool PostprocessScene (FbxScene &scene) ; static FbxWriter *Create_gltfWriter (FbxManager &manager, FbxExporter &exporter, int subID, int pluginID) ; static void *gltfFormatInfo (FbxWriter::EInfoRequest request, int id) ; static void FillIOSettings (FbxIOSettings &ios) ; } ;
and the basic implementation
gltfWriter::gltfWriter (FbxManager &pManager, int id) : FbxWriter(pManager, id, FbxStatusGlobal::GetRef ()) { } gltfWriter::~gltfWriter () { FileClose () ; } bool gltfWriter::FileCreate (char *pFileName) { FbxString fileName =FbxPathUtils::Clean (pFileName) ; FbxString path (FbxGetApplicationDirectory ()) ; if ( !FbxPathUtils::Create (FbxPathUtils::GetFolderName (fileName)) ) return (GetStatus ().SetCode (FbxStatus::eFailure, "Cannot create folder!"), false) ; <open your file here> return (IsFileOpen ()) ; } bool gltfWriter::FileClose () { <flush and close your file here> return (true) ; } bool gltfWriter::IsFileOpen () { <is your file open?> } void gltfWriter::GetWriteOptions () { } bool gltfWriter::Write (FbxDocument *pDocument) { FbxScene *pScene =FbxCast(pDocument) ; if ( !pScene ) return (GetStatus ().SetCode (FbxStatus::eFailure, "Document not supported!"), false) ; if ( !PreprocessScene (*pScene) ) return (false) ; FbxDocumentInfo *pSceneInfo =pScene->GetSceneInfo () ; <write data in your file here> if ( !PostprocessScene (*pScene) ) return (false) ; return (true) ; } bool gltfWriter::PreprocessScene (FbxScene &scene) { FbxNode *pRootNode =scene.GetRootNode () ; ... return (true) ; } bool gltfWriter::PostprocessScene (FbxScene &scene) { ... return (true) ; } // Create your own writer - Your writer will get a pPluginID and pSubID. /*static*/ FbxWriter *gltfWriter::Create_gltfWriter (FbxManager &manager, FbxExporter &exporter, int subID, int pluginID) { FbxWriter *writer =FbxNew (manager, pluginID) ; // Use FbxNew instead of new, since FBX will take charge its deletion writer->SetIOSettings (exporter.GetIOSettings ()) ; return (writer) ; } // Get extension, description or version info about MyOwnWriter /*static*/ void *gltfWriter::gltfFormatInfo (FbxWriter::EInfoRequest request, int id) { static const char *sExt [] = { "gltf", 0 } ; static const char *sDesc [] = { "glTF for WebGL (*.gltf)", 0 } ; static const char *sVersion [] = { "0.8", 0 } ; static const char *sInfoCompatible [] = { "-", 0 } ; static const char *sInfoUILabel [] = { "-", 0 } ; switch ( pRequest ) { case FbxWriter::eInfoExtension: return (sExt) ; case FbxWriter::eInfoDescriptions: return (sDesc) ; case FbxWriter::eInfoVersions: return (sVersion) ; case FbxWriter::eInfoCompatibleDesc: return (sInfoCompatible) ; case FbxWriter::eInfoUILabel: return (sInfoUILabel) ; default: return (0) ; } } /*static*/ void gltfWriter::FillIOSettings (FbxIOSettings &pIOS) { // Here you can write your own FbxIOSettings and parse them. // Example at: http://help.autodesk.com/view/FBX/2015/ENU/?guid=__files_GUID_75CD0DC4_05C8_4497_AC6E_EA11406EAE26_htm ... }
This is all you need to see the exporter to appear in any FBX host application, or FBX extension which loads plug-ins. For example, if you write an application and want to load plug-in you would add code like this in your host code:
FbxIOSettings *pIOSettings =FbxIOSettings::Create (_sdkManager, IOSROOT) ; pIOSettings->SetBoolProp (IMP_FBX_MATERIAL, true) ; ... pIOSettings->SetBoolProp (EXP_FBX_EMBEDDED, false) ; ... _sdkManager->SetIOSettings (pIOSettings) ; // Load plug-ins from the executable directory FbxString path =FbxGetApplicationDirectory () ; #if defined(_WIN64) || defined(_WIN32) FbxString extension ("dll") ; #elif defined(__APPLE__) FbxString extension ("dylib") ; #else // __linux FbxString extension ("so") ; #endif _sdkManager->LoadPluginsDirectory (path.Buffer ()/*, extension.Buffer ()*/) ; _sdkManager->FillIOSettingsForReadersRegistered (*pIOSettings) ; _sdkManager->FillIOSettingsForWritersRegistered (*pIOSettings) ;
Full source code posted on my github account here
Comments