Browse Source

Updated the encoder to use FBX SDK 2013.1
Added encoder argument so that you can choose the output path and filename.

Darryl Gough 13 years ago
parent
commit
fe7da340ea

+ 7 - 7
gameplay-encoder/README.md

@@ -21,13 +21,13 @@ You must then rebuild gameplay-encoder with the follow platform/tooling instruct
 - Edit the project properties of "gameplay-encoder"
 - Add Preprocessor Definition "USE_FBX" (C++/Preprocessor)
 - Add the FBX SDK include directory to Additional Include Directories (C++/General)
-  * Example: C:/Program Files/Autodesk/FBX/FbxSdk/2012.2/include
+  * Example: C:/Program Files/Autodesk/FBX/FbxSdk/2013.1/include
 - Add the FBX lib directory to the Additional Library Directories (Linker/General)
-  * Example: C:/Program Files/Autodesk/FBX/FbxSdk/2012.2/lib/vs2010/x86
-- Add "fbxsdk-2012.2-mdd.lib" and "wininet.lib" to the Additional Dependencies (Linker/Input)
-  * Example: fbxsdk-2012.2-mdd.lib;wininet.lib
+  * Example: C:/Program Files/Autodesk/FBX/FbxSdk/2013.1/lib/vs2010/x86
+- Add "fbxsdk-2013.1-mdd.lib" and "wininet.lib" to the Additional Dependencies (Linker/Input)
+  * Example: fbxsdk-2013.1-mdd.lib;wininet.lib
 - Add a post build event to copy the DLL (Build Events/Post-Build Event)
-  * Example: copy /Y "C:\Program Files\Autodesk\FBX\FbxSdk\2012.2\lib\vs2010\x86\fbxsdk-2012.2d.dll" "$(TargetDir)"
+  * Example: copy /Y "C:\Program Files\Autodesk\FBX\FbxSdk\2013.1\lib\vs2010\x86\fbxsdk-2013.1d.dll" "$(TargetDir)"
 - Build gameplay-encoder
 
 ### Building FBX Support on Mac OS X using XCode 4.3.2+
@@ -35,9 +35,9 @@ You must then rebuild gameplay-encoder with the follow platform/tooling instruct
 - Edit the project properties of target "gameplay-encoder".
 - Add Preprocessor Macro "USE_FBX" to both Debug/Release sections. (Build Settings)
 - Add the FBX include directory to Header Search Paths: (Build Settings)
-  * Example: /Applications/Autodesk/FBXSDK20122/include
+  * Example: /Applications/Autodesk/FBXSDK20131/include
 - Add the FBX library and dependency Library/Frameworks: (Build Phases -> Link Binary with Libraries)
-  * Example: /Applications/Autodesk/FBXSDK20122/lib/gcc4/ub/libfbxsdk-2012.2-static.a  (Add Other)
+  * Example: /Applications/Autodesk/FBXSDK20131/lib/gcc4/ub/libfbxsdk-2013.1-static.a  (Add Other)
   * Example: libiconv.dylib, Cocoa.framework, SystemConfiguration.framework
 - Build gameplay-encoder
 

+ 21 - 17
gameplay-encoder/src/DAESceneEncoder.cpp

@@ -54,7 +54,7 @@ void DAESceneEncoder::optimizeCOLLADA(const EncoderArguments& arguments, domCOLL
     {
         if (!_collada->writeTo(arguments.getFilePath(), arguments.getDAEOutputPath()))
         {
-            fprintf(stderr,"Error: COLLADA failed to write the dom for file:%s\n", arguments.getDAEOutputPath().c_str());
+            fprintf(stderr,"Error: COLLADA failed to write the dom for file: %s\n", arguments.getDAEOutputPath().c_str());
         }
     }
 }
@@ -227,10 +227,6 @@ void DAESceneEncoder::write(const std::string& filepath, const EncoderArguments&
 {
     _begin = clock();
     const char* nodeId = arguments.getNodeId();
-    bool text = arguments.textOutputEnabled();
-
-    std::string filenameOnly = getFilenameFromFilePath(filepath);
-    std::string dstPath = filepath.substr(0, filepath.find_last_of('/'));
     
     // Load the collada document
     _collada = new DAE();
@@ -239,7 +235,7 @@ void DAESceneEncoder::write(const std::string& filepath, const EncoderArguments&
     end("Open file");
     if (!_dom)
     {
-        fprintf(stderr,"Error: COLLADA failed to open file:%s\n", filepath.c_str());
+        fprintf(stderr,"Error: COLLADA failed to open file: %s\n", filepath.c_str());
         if (_collada)
         {
             delete _collada;
@@ -309,24 +305,32 @@ void DAESceneEncoder::write(const std::string& filepath, const EncoderArguments&
     loadAnimations(_dom);
     end("loadAnimations");
 
-    std::string dstFilename = dstPath;
-    dstFilename.append(1, '/');
-    dstFilename.append(getFilenameNoExt(filenameOnly));
-
     _gamePlayFile.adjust();
 
-    if (text)
+    // Write the output file
+    std::string outputFilePath = arguments.getOutputFilePath();
+    if (arguments.textOutputEnabled())
     {
-        std::string outFile = dstFilename + ".xml";
-        fprintf(stderr, "Saving debug file: %s\n", outFile.c_str());
-        _gamePlayFile.saveText(outFile);
+        int pos = outputFilePath.find_last_of('.');
+        if (pos > 2)
+        {
+            std::string path = outputFilePath.substr(0, pos);
+            path.append(".xml");
+            fprintf(stderr, "Saving debug file: %s\n", path.c_str());
+            if (!_gamePlayFile.saveText(path))
+            {
+                fprintf(stderr,"Error writing text file: %s\n", path.c_str());
+            }
+        }
     }
     else
     {
-        std::string outFile = dstFilename + ".gpb";
-        fprintf(stderr, "Saving binary file: %s\n", outFile.c_str());
+        fprintf(stderr, "Saving binary file: %s\n", outputFilePath.c_str());
         begin();
-        _gamePlayFile.saveBinary(outFile);
+        if (!_gamePlayFile.saveBinary(outputFilePath))
+        {
+            fprintf(stderr,"Error writing binary file: %s\n", outputFilePath.c_str());
+        }
         end("save binary");
     }
     

+ 4 - 1
gameplay-encoder/src/DAEUtil.cpp

@@ -412,7 +412,10 @@ domVisual_scene* getVisualScene(const domCOLLADA::domSceneRef& domScene)
         for (size_t j = 0; j < visualSceneCount; ++j)
         {
             domVisual_sceneRef visualScene = visualScenes.get(j);
-            return visualScene.cast();
+            if (domVisual_scene* v = visualScene.cast())
+            {
+                return v;
+            }
         }
     }
     return NULL;

+ 165 - 17
gameplay-encoder/src/EncoderArguments.cpp

@@ -24,26 +24,30 @@ EncoderArguments::EncoderArguments(size_t argc, const char** argv) :
 
     if (argc > 1)
     {
-        size_t filePathIndex = argc - 1;
-        if (argv[filePathIndex])
-        {
-            _filePath.assign(getRealPath(argv[filePathIndex]));
-        }
-        
         // read the options
-        std::vector<std::string> options;
-        for (size_t i = 1; i < filePathIndex; ++i)
+        std::vector<std::string> arguments;
+        for (size_t i = 1; i < argc; ++i)
         {
-            options.push_back(argv[i]);
+            arguments.push_back(argv[i]);
         }
-        
-        for (size_t i = 0; i < options.size(); ++i)
+        size_t index = 0;
+        for (size_t i = 0; i < arguments.size(); ++i)
         {
-            if (options[i][0] == '-')
+            if (arguments[i][0] == '-')
             {
-                readOption(options, &i);
+                readOption(arguments, &i);
+                index = i + 1;
             }
         }
+        if (arguments.size() - index == 2)
+        {
+            setInputfilePath(arguments[index]);
+            setOutputfilePath(arguments[index + 1]);
+        }
+        else if (arguments.size() - index == 1)
+        {
+            setInputfilePath(arguments[index]);
+        }
     }
     else
     {
@@ -70,10 +74,42 @@ const char* EncoderArguments::getFilePathPointer() const
     return _filePath.c_str();
 }
 
-std::string EncoderArguments::getOutputPath() const
+std::string EncoderArguments::getOutputDirPath() const
 {
-    int pos = _filePath.find_last_of('/');
-    return (pos == -1 ? _filePath : _filePath.substr(0, pos));
+    if (_fileOutputPath.size() > 0)
+    {
+        int pos = _fileOutputPath.find_last_of('/');
+        return (pos == -1 ? _fileOutputPath : _fileOutputPath.substr(0, pos));
+    }
+    else
+    {
+        int pos = _filePath.find_last_of('/');
+        return (pos == -1 ? _filePath : _filePath.substr(0, pos));
+    }
+}
+
+std::string EncoderArguments::getOutputFilePath() const
+{
+    if (_fileOutputPath.size() > 0)
+    {
+        return _fileOutputPath;
+    }
+    else
+    {
+        int pos = _filePath.find_last_of('.');
+        if (pos > 0)
+        {
+            std::string outputFilePath(_filePath.substr(0, pos));
+            outputFilePath.append(".gpb");
+            return outputFilePath;
+        }
+        else
+        {
+            std::string outputFilePath(_filePath);
+            outputFilePath.append(".gpb");
+            return outputFilePath;
+        }
+    }
 }
 
 const std::string& EncoderArguments::getDAEOutputPath() const
@@ -134,7 +170,7 @@ bool EncoderArguments::fileExists() const
 
 void EncoderArguments::printUsage() const
 {
-    fprintf(stderr,"Usage: gameplay-encoder [options] <filepath>\n\n");
+    fprintf(stderr,"Usage: gameplay-encoder [options] <input filepath> <output filepath>\n\n");
     fprintf(stderr,"Supported file extensions:\n");
     fprintf(stderr,"  .dae\t(COLLADA)\n");
     fprintf(stderr,"  .fbx\t(FBX)\n");
@@ -343,6 +379,43 @@ void EncoderArguments::readOption(const std::vector<std::string>& options, size_
     }
 }
 
+void EncoderArguments::setInputfilePath(const std::string& inputPath)
+{
+    _filePath.assign(getRealPath(inputPath));
+}
+
+void EncoderArguments::setOutputfilePath(const std::string& outputPath)
+{
+    if (outputPath.size() > 0 && outputPath[0] != '\0')
+    {
+        std::string realPath = getRealPath(outputPath);
+        if (endsWith(realPath.c_str(), ".gpb"))
+        {
+            _fileOutputPath.assign(realPath);
+        }
+        else if (endsWith(outputPath.c_str(), "/"))
+        {
+            std::string filenameNoExt = getFilenameNoExt(getFilenameFromFilePath(_filePath));
+
+            _fileOutputPath.assign(outputPath);
+            _fileOutputPath.append(filenameNoExt);
+            _fileOutputPath.append(".gpb");
+        }
+        else
+        {
+            std::string filenameNoExt = getFilenameNoExt(getFilenameFromFilePath(realPath));
+            int pos = realPath.find_last_of("/");
+            if (pos)
+            {
+                _fileOutputPath = realPath.substr(0, pos);
+                _fileOutputPath.append("/");
+                _fileOutputPath.append(filenameNoExt);
+                _fileOutputPath.append(".gpb");
+            }
+        }
+    }
+}
+
 std::string EncoderArguments::getRealPath(const std::string& filepath)
 {
     char path[PATH_MAX + 1]; /* not sure about the "+ 1" */
@@ -362,4 +435,79 @@ void EncoderArguments::replace_char(char* str, char oldChar, char newChar)
     }
 }
 
+std::string concat(const std::string& a, const char* b)
+{
+    std::string str(a);
+    str.append(b);
+    return str;
+}
+
+
+void unittestsEncoderArguments()
+{
+    std::string dir = EncoderArguments::getRealPath(".");
+    std::string exePath = EncoderArguments::getRealPath(".");
+    exePath.append("/gameplay-encoder.exe");
+    const char* exe = exePath.c_str();
+    {
+        const char* argv[] = {exe, "-groupAnimations", "root", "movements", "C:\\Git\\gaming\\GamePlay\\gameplay-encoder\\res\\seymour.dae"};
+        EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
+        assert(equals(args.getAnimationId("root"), ("movements")));
+        assert(equals(args.getGroupAnimationNodeId()[0], ("root")));
+        assert(equals(args.getOutputFilePath(), "C:/Git/gaming/GamePlay/gameplay-encoder/res/seymour.gpb"));
+    }
+    {
+        // Test with only input file name (relative)
+        const char* argv[] = {exe, "input.dae"};
+        EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
+        assert(equals(args.getFilePath(), concat(dir, "/input.dae")));
+        assert(equals(args.getOutputFilePath(), concat(dir, "/input.gpb")));
+        equals(args.getOutputDirPath(), dir);
+    }
+    {
+        // Test specifying a relative output path
+        const char* argv[] = {exe, "input.dae", "output.gpb"};
+        EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
+        assert(equals(args.getFilePath(), concat(dir, "/input.dae")));
+        assert(equals(args.getOutputFilePath(), concat(dir, "/output.gpb")));
+    }
+    {
+        // Test specifying a relative output path
+        const char* argv[] = {exe, "input.fbx", "output.gpb"};
+        EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
+        assert(equals(args.getFilePath(), concat(dir, "/input.fbx")));
+        assert(equals(args.getOutputFilePath(), concat(dir, "/output.gpb")));
+    }
+    {
+        // Test specifying a relative output path to a directory
+        const char* argv[] = {exe, "input.dae", "stuff/output.gpb"};
+        EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
+        assert(equals(args.getFilePath(), concat(dir, "/input.dae")));
+        assert(equals(args.getOutputFilePath(), concat(dir, "/stuff/output.gpb")));
+    }
+    {
+        // Test parsing some arguments
+        const char* argv[] = {exe, "-dae", "collada.dae", "-t", "input.dae", "output.gpb"};
+        EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
+        assert(equals(args.getFilePath(), concat(dir, "/input.dae")));
+        assert(equals(args.getOutputFilePath(), concat(dir, "/output.gpb")));
+        assert(args.textOutputEnabled());
+        //assert(equals(args.getDAEOutputPath(), concat(dir, "/collada.dae")));
+    }
+    {
+        // Test output file with no file extension
+        const char* argv[] = {exe, "input.dae", "output"};
+        EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
+        assert(equals(args.getFilePath(), concat(dir, "/input.dae")));
+        assert(equals(args.getOutputFilePath(), concat(dir, "/output.gpb")));
+    }
+    {
+        // Test output file with wrong file extension
+        const char* argv[] = {exe, "input.dae", "output.dae"};
+        EncoderArguments args(sizeof(argv) / sizeof(char*), (const char**)argv);
+        assert(equals(args.getFilePath(), concat(dir, "/input.dae")));
+        assert(equals(args.getOutputFilePath(), concat(dir, "/output.gpb")));
+    }
+}
+
 }

+ 21 - 2
gameplay-encoder/src/EncoderArguments.h

@@ -57,8 +57,15 @@ public:
 
     /**
      * Returns the output path/folder.
+     * Example: "C:/dir"
      */
-    std::string getOutputPath() const;
+    std::string getOutputDirPath() const;
+
+    /**
+     * Returns the output file path.
+     * Example: "C:/dir/scene.gpb"
+     */
+    std::string getOutputFilePath() const;
 
     const std::vector<std::string>& getGroupAnimationNodeId() const;
     const std::vector<std::string>& getGroupAnimationAnimationId() const;
@@ -92,6 +99,9 @@ public:
     const char* getNodeId() const;
     unsigned int getFontSize() const;
 
+
+    static std::string getRealPath(const std::string& filepath);
+
 private:
 
     /**
@@ -103,7 +113,13 @@ private:
      */
     void readOption(const std::vector<std::string>& options, size_t *index);
 
-    static std::string getRealPath(const std::string& filepath);
+    void setInputfilePath(const std::string& inputPath);
+
+    /**
+     * Sets the output file path that the encoder will write to.
+     */
+    void setOutputfilePath(const std::string& outputPath);
+    
 
     /**
      * Replaces all instance of oldChar with newChar in str.
@@ -113,6 +129,7 @@ private:
 private:
     
     std::string _filePath;
+    std::string _fileOutputPath;
     std::string _nodeId;
     std::string _daeOutputPath;
 
@@ -129,6 +146,8 @@ private:
 
 };
 
+void unittestsEncoderArguments();
+
 }
 
 #endif

File diff suppressed because it is too large
+ 208 - 169
gameplay-encoder/src/FBXSceneEncoder.cpp


+ 18 - 16
gameplay-encoder/src/FBXSceneEncoder.h

@@ -3,6 +3,8 @@
 
 #ifdef USE_FBX
 
+#define FBXSDK_NEW_API
+
 #include <iostream>
 #include <list>
 #include <vector>
@@ -65,7 +67,7 @@ private:
      * 
      * @param fbxScene The FBX scene to load.
      */
-    void loadScene(KFbxScene* fbxScene);
+    void loadScene(FbxScene* fbxScene);
 
     /**
      * Loads all of the animatiosn from the given FBX scene.
@@ -73,7 +75,7 @@ private:
      * @param fbxScene The scene to load animations from.
      * @param arguments The command line arguments passed to the encoder.
      */
-    void loadAnimations(KFbxScene* fbxScene, const EncoderArguments& arguments);
+    void loadAnimations(FbxScene* fbxScene, const EncoderArguments& arguments);
 
     /**
      * Loads the animations from the given FBX animation layer recursively starting from fbxNode.
@@ -82,7 +84,7 @@ private:
      * @param fbxNode The node to start loading animations from.
      * @param arguments The command line arguments passed to the encoder.
      */
-    void loadAnimationLayer(KFbxAnimLayer* fbxAnimLayer, KFbxNode* fbxNode, const EncoderArguments& arguments);
+    void loadAnimationLayer(FbxAnimLayer* fbxAnimLayer, FbxNode* fbxNode, const EncoderArguments& arguments);
 
     /**
      * Loads animation channels from the given node and adds the channels to the given animation.
@@ -91,14 +93,14 @@ private:
      * @param fbxNode The node to load animation channels from.
      * @param animation The animation to add the channels to.
      */
-    void loadAnimationChannels(KFbxAnimLayer* pAnimLayer, KFbxNode* fbxNode, Animation* animation);
+    void loadAnimationChannels(FbxAnimLayer* pAnimLayer, FbxNode* fbxNode, Animation* animation);
 
     /**
      * Loads the bind shape for all mesh skins that have be loaded so far.
      * 
      * @param fbxScene The FBX scene to read the bind shapes from.
      */
-    void loadBindShapes(KFbxScene* fbxScene);
+    void loadBindShapes(FbxScene* fbxScene);
 
     /**
      * Loads the camera from the given FBX node and adds to it to the given GamePlay node.
@@ -106,7 +108,7 @@ private:
      * @param fbxNode The FBX node to load from.
      * @param node The GamePlay node to add to.
      */
-    void loadCamera(KFbxNode* fbxNode, Node* node);
+    void loadCamera(FbxNode* fbxNode, Node* node);
 
     /**
      * Loads the light from the given FBX node and adds to it to the given GamePlay node.
@@ -114,7 +116,7 @@ private:
      * @param fbxNode The FBX node to load from.
      * @param node The GamePlay node to add to.
      */
-    void loadLight(KFbxNode* fbxNode, Node* node);
+    void loadLight(FbxNode* fbxNode, Node* node);
     
     /**
      * Loads the model from the given FBX node and adds to it to the given GamePlay node.
@@ -122,7 +124,7 @@ private:
      * @param fbxNode The FBX node to load from.
      * @param node The GamePlay node to add to.
      */
-    void loadModel(KFbxNode* fbxNode, Node* node);
+    void loadModel(FbxNode* fbxNode, Node* node);
 
     /**
      * Loads the mesh skin from the given FBX mesh and adds it to the given GamePlay model.
@@ -130,7 +132,7 @@ private:
      * @param fbxMesh The FBX mesh to load the skin from.
      * @param model The model to add the skin to.
      */
-    void loadSkin(KFbxMesh* fbxMesh, Model* model);
+    void loadSkin(FbxMesh* fbxMesh, Model* model);
     
     /**
      * Loads the FBX Node and creates a GamePlay Node.
@@ -139,7 +141,7 @@ private:
      * 
      * @return The newly created Node or NULL if the node could not be loaded.
      */
-    Node* loadNode(KFbxNode* fbxNode);
+    Node* loadNode(FbxNode* fbxNode);
     
     /**
      * Loads the FbxMesh and returns a GamePlay mesh.
@@ -149,7 +151,7 @@ private:
      * 
      * @return The GamePlay mesh that was loaded from the FBX Mesh.
      */
-    Mesh* loadMesh(KFbxMesh* fbxMesh);
+    Mesh* loadMesh(FbxMesh* fbxMesh);
 
     /**
      * Gets the Mesh that was saved with the given ID. Returns NULL if a match is not found.
@@ -158,7 +160,7 @@ private:
      * 
      * @return The mesh that was saved with the ID or NULL if none was found.
      */
-    Mesh* getMesh(size_t meshId);
+    Mesh* getMesh(FbxUInt64 meshId);
 
     /**
      * Saves the Mesh with the given id.
@@ -166,7 +168,7 @@ private:
      * @param meshId The ID of the FbxMesh to use as a key.
      * @param mesh The mesh to save.
      */
-    void saveMesh(size_t meshId, Mesh* mesh);
+    void saveMesh(FbxUInt64 meshId, Mesh* mesh);
     
     /**
      * Prints a message.
@@ -181,14 +183,14 @@ private:
      * @param fbxNode The FBX node to get the transfrom data from
      * @param node The GamePlay Node to copy the transform to.
      */
-    void transformNode(KFbxNode* fbxNode, Node* node);
+    void transformNode(FbxNode* fbxNode, Node* node);
 
     /**
      * Recursively triangules the meshes starting from the given node.
      * 
      * @param fbxNode The node to start triangulating from.
      */
-    static void triangulateRecursive(KFbxNode* fbxNode);
+    static void triangulateRecursive(FbxNode* fbxNode);
 
     /**
      * Prints a warning message.
@@ -210,7 +212,7 @@ private:
     /**
      * The collection of meshes for the purpose of making sure that the same model is not loaded twice. (Mesh instancing)
      */
-    std::map<size_t, Mesh*> _meshes;
+    std::map<FbxUInt64, Mesh*> _meshes;
 
     /**
      * The animation that channels should be added to it the user is using the -groupAnimation command line argument. May be NULL.

+ 34 - 5
gameplay-encoder/src/GPBFile.cpp

@@ -29,16 +29,33 @@ GPBFile* GPBFile::getInstance()
     return __instance;
 }
 
-void GPBFile::saveBinary(const std::string& filepath)
+bool GPBFile::saveBinary(const std::string& filepath)
 {
     _file = fopen(filepath.c_str(), "w+b");
+    if (!_file)
+    {
+        return false;
+    }
+    size_t n = 0;
 
     // identifier
     char identifier[] = { '«', 'G', 'P', 'B', '»', '\r', '\n', '\x1A', '\n' };
-    fwrite(identifier, 1, sizeof(identifier), _file);
+    n = fwrite(identifier, 1, sizeof(identifier), _file);
+    if (n != sizeof(identifier))
+    {
+        fclose(_file);
+        return false;
+    }
 
     // version
-    fwrite(GPB_VERSION, 1, sizeof(GPB_VERSION), _file);
+    n = fwrite(GPB_VERSION, 1, sizeof(GPB_VERSION), _file);
+    if (n != sizeof(GPB_VERSION))
+    {
+        fclose(_file);
+        return false;
+    }
+
+    // TODO: Check for errors on all file writing.
 
     // write refs
     _refTable.writeBinary(_file);
@@ -60,13 +77,24 @@ void GPBFile::saveBinary(const std::string& filepath)
     _refTable.updateOffsets(_file);
     
     fclose(_file);
+    return true;
 }
 
-void GPBFile::saveText(const std::string& filepath)
+bool GPBFile::saveText(const std::string& filepath)
 {
     _file = fopen(filepath.c_str(), "w");
+    if (!_file)
+    {
+        return false;
+    }
+
+    if (fprintf(_file, "<root>\n") <= 0)
+    {
+        fclose(_file);
+        return false;
+    }
 
-    fprintf(_file, "<root>\n");
+    // TODO: Check for errors on all file writing.
 
     // write refs
     _refTable.writeText(_file);
@@ -86,6 +114,7 @@ void GPBFile::saveText(const std::string& filepath)
     fprintf(_file, "</root>");
 
     fclose(_file);
+    return true;
 }
 
 void GPBFile::add(Object* obj)

+ 6 - 2
gameplay-encoder/src/GPBFile.h

@@ -49,15 +49,19 @@ public:
      * Saves the GPBFile as a binary file at filepath.
      *
      * @param filepath The file name and path to save to.
+     * 
+     * @return True if successful, false if error.
      */
-    void saveBinary(const std::string& filepath);
+    bool saveBinary(const std::string& filepath);
 
     /**
      * Saves the GPBFile as a text file at filepath. Useful for debugging.
      *
      * @param filepath The file name and path to save to.
+     * 
+     * @return True if successful, false if error.
      */
-    void saveText(const std::string& filepath);
+    bool saveText(const std::string& filepath);
     
     void add(Object* obj);
     void addScene(Scene* scene);

+ 1 - 1
gameplay-encoder/src/Node.cpp

@@ -132,7 +132,7 @@ void Node::generateHeightmap()
         {
             DEBUGPRINT_VARG("> Generating heightmap for node: %s\n", getId().c_str());
 
-            std::string heightmapFilename(EncoderArguments::getInstance()->getOutputPath());
+            std::string heightmapFilename(EncoderArguments::getInstance()->getOutputDirPath());
             heightmapFilename += "/heightmap_";
             heightmapFilename += getId();
             heightmapFilename += ".png";

+ 11 - 1
gameplay-encoder/src/StringUtil.cpp

@@ -77,11 +77,21 @@ bool endsWith(const char* str, const char* suffix, bool ignoreCase)
     return true;
 }
 
+bool endsWith(const std::string& str, const char* suffix, bool ignoreCase)
+{
+    return endsWith(str.c_str(), suffix, ignoreCase);
+}
+
 bool equals(const std::string& a, const char* b)
 {
     return (a.compare(b) == 0);
 }
 
+bool equals(const std::string& a, const std::string& b)
+{
+    return (a.compare(b) == 0);
+}
+
 bool equalsIgnoreCase(const std::string& a, const char* b)
 {
     size_t bLength = strlen(b);
@@ -103,7 +113,7 @@ std::string getFilenameFromFilePath(const std::string& filepath)
 {
     if (filepath.find_last_of("/") != std::string::npos)
     {
-        return filepath.substr(filepath.find_last_of("/")+1);
+        return filepath.substr(filepath.find_last_of("/") + 1);
     }
     return "";
 }

+ 12 - 0
gameplay-encoder/src/StringUtil.h

@@ -4,19 +4,31 @@
 namespace gameplay
 {
 
+/**
+ * Returns true if the given string starts with the prefix.
+ */
 bool startsWith(const char* str, const char* prefix, bool ignoreCase = true);
+
+/**
+ * Returns true if the given string ends with the suffix.
+ */
 bool endsWith(const char* str, const char* suffix, bool ignoreCase = true);
+bool endsWith(const std::string& str, const char* suffix, bool ignoreCase = true);
 
 /**
  * Return true if the strings are equal. Case sensitive.
  */
 bool equals(const std::string& a, const char* b);
+bool equals(const std::string& a, const std::string& b);
 
 /**
  * Returns true if the strings are equal. Case insensitive.
  */
 bool equalsIgnoreCase(const std::string& a, const char* b);
 
+/**
+ * Returns the filename from the given real path.
+ */
 std::string getFilenameFromFilePath(const std::string& filepath);
 
 std::string getFilenameNoExt(const std::string& filename);

Some files were not shown because too many files changed in this diff