Переглянути джерело

Merge branch 'next' of https://github.com/blackberry-gaming/GamePlay into next-kcunney

Kieran Cunney 13 роки тому
батько
коміт
cbf6af8bab

+ 61 - 0
CHANGES.md

@@ -0,0 +1,61 @@
+## v1.3.0
+
+- Portrait mode games on mobile platforms.
+- Fullscreen and configurable game resolutions on desktop platforms.
+- User Interface support for scrolling with scrollbars on Container.
+- PVRTC, ATC and DXT texture compression support.
+- Performance improvements in user interface forms and text.
+- Performance improvements in animations on transforms.
+- Fixes for improvements in error handling throughout all systems.
+- Fixes supporting built-in Maya COLLADA exporter via DAE_FBX export.
+- Fixes for latest FBX SDK 2013 support.
+- Fixes for loading from some WAV files that were crashing.
+- Fixes for From/By animations.
+- Fixes allowing all inline properties loaded within .scene files. (breaks compat. for .scene)
+- Fixes in .scene files for collisionObject definitions (breaks compat. for .scene)
+- Fixes for depth/z-ordering of controls.
+
+## v1.2.0
+
+- BlackBerry 10 support.
+- iOS 5.1 support.
+- Android 2.3+ support.
+- User interface system with declaritive forms and themes.
+- Bluetooth keyboard/mouse support on BlackBerry platform.
+- Developer guide.
+- Sample/turorial for sample03-character.
+- Sample for sample04-particles.
+- Fixes for loading properties from URL.
+- Fixes on Win32/MacOSX for when mouse pointer leaves the window and returns.
+- Fixes to accelerometer for Android.
+- Fixes in animation blending.
+- Fixes to GPB for loading from single node and parent node. (breaks compat. for .gpb)
+
+## v1.1.0
+
+- FBX support in gameplay-encoder.
+- MacOSX platform support using XCode.
+- Offscreen rendering functionality using FrameBuffer.
+- Loading 3D scences using declaritive .scene files.
+- Loading audio from .ogg files using vorbis.
+- Loading AudioSources from .audio files.
+- Loading Animations from .animation files.
+- AnimationClip support for cross fading.
+- Physics support using Bullet Physics.
+- Cross-platform new project generator.
+- Overloaded operators in Math classes.
+- Font improvements for justify, clip, wrap and scaling.
+- Fixes for Font::drawText to use point size and not float scalar.
+- Fixes for memory leaks in and fixes to AnimationTarget.
+- Fixes for bumped and paralax shaders.
+- Fixes to simplify folders for resources in samples.
+- Fixes to the material/shader system.
+- Fixes to the ParticleEmitter.
+
+## v1.0.1
+
+- Initial release.
+
+
+
+

+ 8 - 8
README.md

@@ -1,9 +1,9 @@
-## gameplay v1.2.0
+## gameplay v1.3.0
 An open-source, cross-platform 3D native C++ game framework making it easy to learn and write mobile and desktop games. 
 
 ## Supported Mobile Platforms
-- BlackBerry PlayBook 2.0 (using BlackBerry Native SDK 2)
-- Google Android 2.3 (using Google Android NDK r7, SDK API level 9 and up)
+- BlackBerry 10 and PlayBook 2.0 (using BlackBerry Native SDK)
+- Google Android 2.3+ (using Google Android NDK r7, SDK API level 9+)
 - Apple iOS 5.1 (using Apple XCode 4.3.2)
 
 ## Supported Desktop Platforms
@@ -11,12 +11,12 @@ An open-source, cross-platform 3D native C++ game framework making it easy to le
 - Apple MacOS X (using Apple XCode 4.3.2)
 
 ## Roadmap for 'next' branch
-- Shadows
-- Lua Script Bindings
+- Gamepad support
+- Lua script bindings
+- Vehicle physics
 - Terrain
-- AI
-- Editor
-- Performance/Optimizations
+- Lightmaps
+- Shadows
 
 ## Licence
 The project is open sourced under the Apache 2.0 license.

+ 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

Різницю між файлами не показано, бо вона завелика
+ 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);

+ 19 - 19
gameplay-template/gameplay-template.xcodeproj/project.pbxproj

@@ -43,7 +43,7 @@
 
 /* Begin PBXFileReference section */
 		42438B521491AD2000D218B8 /* libgameplay.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgameplay.a; path = "~/Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug/libgameplay.a"; sourceTree = "<group>"; };
-		42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-macosx.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT-macosx.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		42C932C01491A0DB0098216A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
 		42C932ED1491A4CB0098216A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
 		42C932EF1491A5160098216A /* TemplateGame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TemplateGame.cpp; path = src/TemplateGame.cpp; sourceTree = SOURCE_ROOT; };
@@ -60,7 +60,7 @@
 		42C9332A1491A7390098216A /* libpng.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng.a; path = "GAMEPLAY_PATH/external-deps/libpng/lib/macosx/libpng.a"; sourceTree = "<group>"; };
 		42C9332D1491A7810098216A /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
 		5B61611214CCC2200073B857 /* TEMPLATE_PROJECT-macosx.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "TEMPLATE_PROJECT-macosx.plist"; sourceTree = "<group>"; };
-		5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT-ios.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		5B61612E14CCC24D0073B857 /* TEMPLATE_PROJECT-ios.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TEMPLATE_PROJECT-ios.plist"; sourceTree = "<group>"; };
 		5BAF2067152F2DDD003E2AC3 /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/CoreMotion.framework; sourceTree = DEVELOPER_DIR; };
 		5BAF2068152F2DDD003E2AC3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
@@ -133,8 +133,8 @@
 		42C932BD1491A0DB0098216A /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT.app */,
-				5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT.app */,
+				42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-macosx.app */,
+				5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-ios.app */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -209,9 +209,9 @@
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
-		42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX */ = {
+		42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-macosx */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-MacOSX" */;
+			buildConfigurationList = 42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-macosx" */;
 			buildPhases = (
 				42C932B81491A0DB0098216A /* Sources */,
 				42C932B91491A0DB0098216A /* Frameworks */,
@@ -223,28 +223,28 @@
 			);
 			dependencies = (
 			);
-			name = "TEMPLATE_PROJECT-MacOSX";
+			name = "TEMPLATE_PROJECT-macosx";
 			productName = TEMPLATE_PROJECT;
-			productReference = 42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT.app */;
+			productReference = 42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-macosx.app */;
 			productType = "com.apple.product-type.application";
 		};
-		5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-iOS */ = {
+		5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-ios */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 5B61612914CCC24C0073B857 /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-iOS" */;
+			buildConfigurationList = 5B61612914CCC24C0073B857 /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-ios" */;
 			buildPhases = (
 				5B61611514CCC24C0073B857 /* Sources */,
 				5B61611714CCC24C0073B857 /* Frameworks */,
 				5B61612414CCC24C0073B857 /* ShellScript */,
 				5B61612514CCC24C0073B857 /* Resources */,
-				5BAF20A3152F2FCE003E2AC3 /* Copy Gameplay Reousrces Run Script */,
+				5BAF20A3152F2FCE003E2AC3 /* Copy Gameplay Resources Run Script */,
 			);
 			buildRules = (
 			);
 			dependencies = (
 			);
-			name = "TEMPLATE_PROJECT-iOS";
+			name = "TEMPLATE_PROJECT-ios";
 			productName = TEMPLATE_PROJECT;
-			productReference = 5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT.app */;
+			productReference = 5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-ios.app */;
 			productType = "com.apple.product-type.application";
 		};
 /* End PBXNativeTarget section */
@@ -267,8 +267,8 @@
 			projectDirPath = "";
 			projectRoot = "";
 			targets = (
-				42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX */,
-				5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-iOS */,
+				42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-macosx */,
+				5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-ios */,
 			);
 		};
 /* End PBXProject section */
@@ -321,7 +321,7 @@
 			shellPath = /bin/sh;
 			shellScript = "touch -cm ${SRCROOT}/res";
 		};
-		5BAF20A3152F2FCE003E2AC3 /* Copy Gameplay Reousrces Run Script */ = {
+		5BAF20A3152F2FCE003E2AC3 /* Copy Gameplay Resources Run Script */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -333,7 +333,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "cp -rn GAMEPLAY_PATH/gameplay/res/shaders ${SRCROOT}/res\ncp -rn GAMEPLAY_PATH/gameplay/res/logo_powered_white.png ${SRCROOT}/res\ntouch -cm ${SRCROOT}/res";
+			shellScript = "cp -rn GAMEPLAY_PATH/gameplay/res/shaders ${SRCROOT}/res\ncp -rf GAMEPLAY_PATH/gameplay/res/logo_powered_white.png ${SRCROOT}/res\ntouch -cm ${SRCROOT}/res";
 		};
 		5BAF20D7152F30C3003E2AC3 /* Copy Gameplay Resources - Run Script */ = {
 			isa = PBXShellScriptBuildPhase;
@@ -544,7 +544,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-MacOSX" */ = {
+		42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-macosx" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				42C932DB1491A0DB0098216A /* Debug */,
@@ -553,7 +553,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		5B61612914CCC24C0073B857 /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-iOS" */ = {
+		5B61612914CCC24C0073B857 /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-ios" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				5B61612A14CCC24C0073B857 /* Debug */,

+ 39 - 82
gameplay.doxyfile

@@ -1,4 +1,4 @@
-# Doxyfile 1.8.0
+# Doxyfile 1.7.5.1
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project
@@ -32,7 +32,7 @@ PROJECT_NAME           = gameplay
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 
-PROJECT_NUMBER         = 1.2.0
+PROJECT_NUMBER         = 1.3.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description 
 # for a project that appears at the top of each page and should give viewer 
@@ -205,13 +205,6 @@ TAB_SIZE               = 8
 
 ALIASES                = 
 
-# This tag can be used to specify a number of word-keyword mappings (TCL only). 
-# A mapping has the form "name=value". For example adding 
-# "class=itcl::class" will allow you to use the command class in the 
-# itcl::class meaning.
-
-TCL_SUBST              = 
-
 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
 # sources only. Doxygen will then generate output that is more tailored for C. 
 # For instance, some of the names that are used will be different. The list 
@@ -250,15 +243,6 @@ OPTIMIZE_OUTPUT_VHDL   = NO
 
 EXTENSION_MAPPING      = 
 
-# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all 
-# comments according to the Markdown format, which allows for more readable 
-# documentation. See http://daringfireball.net/projects/markdown/ for details. 
-# The output of markdown processing is further processed by doxygen, so you 
-# can mix doxygen, HTML, and XML commands with Markdown formatting. 
-# Disable only in case of backward compatibilities issues.
-
-MARKDOWN_SUPPORT       = YES
-
 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
 # to include (a tag file for) the STL sources as input, then you should 
 # set this tag to YES in order to let doxygen match functions declarations and 
@@ -341,21 +325,10 @@ TYPEDEF_HIDES_STRUCT   = NO
 # a logarithmic scale so increasing the size by one will roughly double the 
 # memory usage. The cache size is given by this formula: 
 # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
-# corresponding to a cache size of 2^16 = 65536 symbols.
+# corresponding to a cache size of 2^16 = 65536 symbols
 
 SYMBOL_CACHE_SIZE      = 0
 
-# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be 
-# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given 
-# their name and scope. Since this can be an expensive process and often the 
-# same symbol appear multiple times in the code, doxygen keeps a cache of 
-# pre-resolved symbols. If the cache is too small doxygen will become slower. 
-# If the cache is too large, memory is wasted. The cache size is given by this 
-# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, 
-# corresponding to a cache size of 2^16 = 65536 symbols.
-
-LOOKUP_CACHE_SIZE      = 0
-
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
@@ -372,11 +345,6 @@ EXTRACT_ALL            = NO
 
 EXTRACT_PRIVATE        = NO
 
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
-# scope will be included in the documentation.
-
-EXTRACT_PACKAGE        = NO
-
 # If the EXTRACT_STATIC tag is set to YES all static members of a file 
 # will be included in the documentation.
 
@@ -606,8 +574,7 @@ LAYOUT_FILE            =
 # .bib extension is automatically appended if omitted. Using this command 
 # requires the bibtex tool to be installed. See also 
 # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style 
-# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this 
-# feature you need bibtex and perl available in the search path.
+# of the bibliography can be controlled using LATEX_BIB_STYLE.
 
 CITE_BIB_FILES         = 
 
@@ -698,15 +665,14 @@ FILE_PATTERNS          = *.h \
 
 RECURSIVE              = NO
 
-# The EXCLUDE tag can be used to specify files and/or directories that should be 
+# The EXCLUDE tag can be used to specify files and/or directories that should 
 # excluded from the INPUT source files. This way you can easily exclude a 
 # subdirectory from a directory tree whose root is specified with the INPUT tag. 
-# Note that relative paths are relative to the directory from which doxygen is 
-# run.
+# Note that relative paths are relative to directory from which doxygen is run.
 
 EXCLUDE                = 
 
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or 
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
 # directories that are symbolic links (a Unix file system feature) are excluded 
 # from the input.
 
@@ -891,7 +857,7 @@ HTML_FILE_EXTENSION    = .html
 # standard header. Note that when using a custom header you are responsible  
 # for the proper inclusion of any scripts and style sheets that doxygen 
 # needs, which is dependent on the configuration options used. 
-# It is advised to generate a default header using "doxygen -w html 
+# It is adviced to generate a default header using "doxygen -w html 
 # header.html footer.html stylesheet.css YourConfigFile" and then modify 
 # that header. Note that the header is subject to change so you typically 
 # have to redo this when upgrading to a newer version of doxygen or when 
@@ -910,7 +876,7 @@ HTML_FOOTER            =
 # fine-tune the look of the HTML output. If the tag is left blank doxygen 
 # will generate a default style sheet. Note that doxygen will try to copy 
 # the style sheet file to the HTML output directory, so don't put your own 
-# style sheet in the HTML output directory as well, or it will be erased!
+# stylesheet in the HTML output directory as well, or it will be erased!
 
 HTML_STYLESHEET        = 
 
@@ -924,7 +890,7 @@ HTML_STYLESHEET        =
 HTML_EXTRA_FILES       = 
 
 # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. 
-# Doxygen will adjust the colors in the style sheet and background images 
+# Doxygen will adjust the colors in the stylesheet and background images 
 # according to this color. Hue is specified as an angle on a colorwheel, 
 # see http://en.wikipedia.org/wiki/Hue for more information. 
 # For instance the value 0 represents red, 60 is yellow, 120 is green, 
@@ -1119,33 +1085,29 @@ GENERATE_ECLIPSEHELP   = NO
 
 ECLIPSE_DOC_ID         = org.doxygen.Project
 
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) 
-# at top of each HTML page. The value NO (the default) enables the index and 
-# the value YES disables it. Since the tabs have the same information as the 
-# navigation tree you can set this option to NO if you already set 
-# GENERATE_TREEVIEW to YES.
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
 
 DISABLE_INDEX          = NO
 
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML 
+# documentation. Note that a value of 0 will completely suppress the enum 
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 4
+
 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 
 # structure should be generated to display hierarchical information. 
 # If the tag value is set to YES, a side panel will be generated 
 # containing a tree-like index structure (just like the one that 
 # is generated for HTML Help). For this to work a browser that supports 
 # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). 
-# Windows users are probably better off using the HTML help feature. 
-# Since the tree basically has the same information as the tab index you 
-# could consider to set DISABLE_INDEX to NO when enabling this option.
+# Windows users are probably better off using the HTML help feature.
 
 GENERATE_TREEVIEW      = NO
 
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML 
-# documentation. Note that a value of 0 will completely suppress the enum 
-# values from appearing in the overview section.
-
-ENUM_VALUES_PER_LINE   = 4
-
 # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, 
 # and Class Hierarchy pages using a tree view instead of an ordered list.
 
@@ -1182,7 +1144,7 @@ FORMULA_TRANSPARENT    = YES
 # (see http://www.mathjax.org) which uses client side Javascript for the 
 # rendering instead of using prerendered bitmaps. Use this if you do not 
 # have LaTeX installed or if you want to formulas look prettier in the HTML 
-# output. When enabled you may also need to install MathJax separately and 
+# output. When enabled you also need to install MathJax separately and 
 # configure the path to it using the MATHJAX_RELPATH option.
 
 USE_MATHJAX            = NO
@@ -1191,10 +1153,10 @@ USE_MATHJAX            = NO
 # HTML output directory using the MATHJAX_RELPATH option. The destination 
 # directory should contain the MathJax.js script. For instance, if the mathjax 
 # directory is located at the same level as the HTML output directory, then 
-# MATHJAX_RELPATH should be ../mathjax. The default value points to 
-# the MathJax Content Delivery Network so you can quickly see the result without 
-# installing MathJax.  However, it is strongly recommended to install a local 
-# copy of MathJax from http://www.mathjax.org before deployment.
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the 
+# mathjax.org site, so you can quickly see the result without installing 
+# MathJax, but it is strongly recommended to install a local copy of MathJax 
+# before deployment.
 
 MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
 
@@ -1353,7 +1315,7 @@ COMPACT_RTF            = NO
 
 RTF_HYPERLINKS         = NO
 
-# Load style sheet definitions from file. Syntax is similar to doxygen's 
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
 # config file, i.e. a series of assignments. You only have to provide 
 # replacements, missing definitions are set to their default value.
 
@@ -1542,16 +1504,20 @@ SKIP_FUNCTION_MACROS   = YES
 # Configuration::additions related to external references
 #---------------------------------------------------------------------------
 
-# The TAGFILES option can be used to specify one or more tagfiles. For each 
-# tag file the location of the external documentation should be added. The 
-# format of a tag file without this location is as follows: 
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
 #   TAGFILES = file1 file2 ... 
 # Adding location for the tag files is done as follows: 
 #   TAGFILES = file1=loc1 "file2 = loc2" ... 
-# where "loc1" and "loc2" can be relative or absolute paths 
-# or URLs. Note that each tag file must have a unique name (where the name does 
-# NOT include the path). If a tag file is not located in the directory in which 
-# doxygen is run, you must also specify the path to the tagfile here.
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links. 
+# Note that each tag file must have a unique name 
+# (where the name does NOT include the path) 
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
 
 TAGFILES               = 
 
@@ -1642,7 +1608,7 @@ DOT_FONTPATH           =
 # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
 # will generate a graph for each documented class showing the direct and 
 # indirect inheritance relations. Setting this tag to YES will force the 
-# CLASS_DIAGRAMS tag to NO.
+# the CLASS_DIAGRAMS tag to NO.
 
 CLASS_GRAPH            = YES
 
@@ -1664,15 +1630,6 @@ GROUP_GRAPHS           = YES
 
 UML_LOOK               = NO
 
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside 
-# the class node. If there are many fields or methods and many nodes the 
-# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS 
-# threshold limits the number of items for each type to make the size more 
-# managable. Set this to 0 for no limit. Note that the threshold may be 
-# exceeded by 50% before the limit is enforced.
-
-UML_LIMIT_NUM_FIELDS   = 10
-
 # If set to YES, the inheritance and collaboration graphs will show the 
 # relations between templates and their instances.
 

+ 156 - 67
gameplay/src/Container.cpp

@@ -16,26 +16,21 @@
 namespace gameplay
 {
 
-    /**
-     * If the user stops scrolling for this amount of time (in millis) before
-     * touch/click release, don't apply inertia.
-     */
-    static const long STOP_TIME = 100L;
-
-    /**
-     * Factor to multiply friction by before applying to velocity.
-     */
-    static const float FRICTION_FACTOR = 5.0f;
+// If the user stops scrolling for this amount of time (in millis) before touch/click release, don't apply inertia.
+static const long SCROLL_INERTIA_DELAY = 100L;
+// Factor to multiply friction by before applying to velocity.
+static const float SCROLL_FRICTION_FACTOR = 5.0f;
 
 Container::Container()
     : _layout(NULL), _scrollBarTopCap(NULL), _scrollBarVertical(NULL), _scrollBarBottomCap(NULL),
       _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
       _scroll(SCROLL_NONE), _scrollBarBounds(Rectangle::empty()), _scrollPosition(Vector2::zero()),
-      _scrolling(false), _firstX(0), _firstY(0),
-      _lastX(0), _lastY(0),
-      _startTimeX(0), _startTimeY(0), _lastTime(0),
-      _velocity(Vector2::zero()), _friction(1.0f),
-      _goingRight(false), _goingDown(false), _zIndexDefault(0)
+      _scrollBarsAutoHide(false), _scrollBarOpacity(1.0f), _scrolling(false),
+       _scrollingFirstX(0), _scrollingFirstY(0),
+      _scrollingLastX(0), _scrollingLastY(0),
+      _scrollingStartTimeX(0), _scrollingStartTimeY(0), _scrollingLastTime(0),
+      _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f),
+      _scrollingRight(false), _scrollingDown(false), _scrollBarOpacityClip(NULL), _zIndexDefault(0)
 {
 }
 
@@ -50,7 +45,6 @@ Container::~Container()
     {
         SAFE_RELEASE((*it));
     }
-
     SAFE_RELEASE(_layout);
 }
 
@@ -84,7 +78,13 @@ Container* Container::create(Theme::Style* style, Properties* properties, Theme*
     Container* container = Container::create(getLayoutType(layoutString));
     container->initialize(style, properties);
     container->_scroll = getScroll(properties->getString("scroll"));
+    container->_scrollBarsAutoHide = properties->getBool("scrollBarsAutoHide");
+    if (container->_scrollBarsAutoHide)
+    {
+        container->_scrollBarOpacity = 0.0f;
+    }
     container->addControls(theme, properties);
+    container->_layout->update(container, container->_scrollPosition);
 
     return container;
 }
@@ -102,9 +102,16 @@ void Container::addControls(Theme* theme, Properties* properties)
 
         const char* controlStyleName = controlSpace->getString("style");
         Theme::Style* controlStyle = NULL;
-        GP_ASSERT(controlStyleName);
-        controlStyle = theme->getStyle(controlStyleName);
-        GP_ASSERT(controlStyle);
+        if (controlStyleName)
+        {
+            controlStyle = theme->getStyle(controlStyleName);
+        }
+        else
+        {
+            Theme::Style::Overlay* overlay = Theme::Style::Overlay::create();
+            controlStyle = new Theme::Style(theme, "", 1.0f / theme->_texture->getWidth(), 1.0f / theme->_texture->getHeight(),
+                Theme::Margin::empty(), Theme::Border::empty(), overlay, overlay, overlay, overlay);
+        }
 
         std::string controlName(controlSpace->getNamespace());
         std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);
@@ -159,6 +166,9 @@ void Container::addControls(Theme* theme, Properties* properties)
         // Get the next control.
         controlSpace = properties->getNextNamespace();
     }
+
+    // Sort controls by Z-Order.
+    std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
 }
 
 Layout* Container::getLayout()
@@ -265,6 +275,20 @@ Container::Scroll Container::getScroll() const
     return _scroll;
 }
 
+void Container::setScrollBarsAutoHide(bool autoHide)
+{
+    if (autoHide != _scrollBarsAutoHide)
+    {
+        _scrollBarsAutoHide = autoHide;
+        _dirty = true;
+    }
+}
+
+bool Container::isScrollBarsAutoHide() const
+{
+    return _scrollBarsAutoHide;
+}
+
 Animation* Container::getAnimation(const char* id) const
 {
     std::vector<Control*>::const_iterator itr = _controls.begin();
@@ -365,26 +389,29 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
         }
     }
 
-    if (_scroll != SCROLL_NONE)
+    if (_scroll != SCROLL_NONE && (_scrollBarOpacity > 0.0f))
     {
         // Draw scroll bars.
         Rectangle clipRegion(_viewportClipBounds);
 
         spriteBatch->begin();
 
-        if (_scrollBarBounds.height > 0 && (_scrolling || _velocity.y))
+        if (_scrollBarBounds.height > 0)
         {
             const Rectangle& topRegion = _scrollBarTopCap->getRegion();
             const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
             Vector4 topColor = _scrollBarTopCap->getColor();
+            topColor.w *= _scrollBarOpacity * _opacity;
 
             const Rectangle& verticalRegion = _scrollBarVertical->getRegion();
             const Theme::UVs& verticalUVs = _scrollBarVertical->getUVs();
             Vector4 verticalColor = _scrollBarVertical->getColor();
+            verticalColor.w *= _scrollBarOpacity * _opacity;
 
             const Rectangle& bottomRegion = _scrollBarBottomCap->getRegion();
             const Theme::UVs& bottomUVs = _scrollBarBottomCap->getUVs();
             Vector4 bottomColor = _scrollBarBottomCap->getColor();
+            bottomColor.w *= _scrollBarOpacity * _opacity;
 
             clipRegion.width += verticalRegion.width;
 
@@ -402,19 +429,22 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
             spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, bottomUVs.u1, bottomUVs.v1, bottomUVs.u2, bottomUVs.v2, bottomColor, clipRegion);
         }
 
-        if (_scrollBarBounds.width > 0 && (_scrolling || _velocity.x))
+        if (_scrollBarBounds.width > 0)
         {
             const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
             const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
             Vector4 leftColor = _scrollBarLeftCap->getColor();
+            leftColor.w *= _scrollBarOpacity * _opacity;
 
             const Rectangle& horizontalRegion = _scrollBarHorizontal->getRegion();
             const Theme::UVs& horizontalUVs = _scrollBarHorizontal->getUVs();
             Vector4 horizontalColor = _scrollBarHorizontal->getColor();
+            horizontalColor.w *= _scrollBarOpacity * _opacity;
 
             const Rectangle& rightRegion = _scrollBarRightCap->getRegion();
             const Theme::UVs& rightUVs = _scrollBarRightCap->getUVs();
             Vector4 rightColor = _scrollBarRightCap->getColor();
+            rightColor.w *= _scrollBarOpacity * _opacity;
 
             clipRegion.height += horizontalRegion.height;
         
@@ -434,7 +464,7 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
 
         spriteBatch->end();
 
-        if (_velocity.isZero())
+        if (_scrollingVelocity.isZero())
         {
             _dirty = false;
         }
@@ -641,47 +671,47 @@ void Container::updateScroll()
     float clipHeight = _bounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
 
     // Apply and dampen inertia.
-    if (!_scrolling && !_velocity.isZero())
+    if (!_scrolling && !_scrollingVelocity.isZero())
     {
         // Calculate the time passed since last update.
         float elapsedSecs = (float)elapsedTime * 0.001f;
 
-        _scrollPosition.x += _velocity.x * elapsedSecs;
-        _scrollPosition.y += _velocity.y * elapsedSecs;
+        _scrollPosition.x += _scrollingVelocity.x * elapsedSecs;
+        _scrollPosition.y += _scrollingVelocity.y * elapsedSecs;
 
-        float dampening = 1.0f - _friction * FRICTION_FACTOR * elapsedSecs;
-        _velocity.x *= dampening;
-        _velocity.y *= dampening;
+        float dampening = 1.0f - _scrollingFriction * SCROLL_FRICTION_FACTOR * elapsedSecs;
+        _scrollingVelocity.x *= dampening;
+        _scrollingVelocity.y *= dampening;
 
-        if (abs(_velocity.x) < 100.0f)
-            _velocity.x = 0.0f;
-        if (abs(_velocity.y) < 100.0f)
-            _velocity.y = 0.0f;
+        if (abs(_scrollingVelocity.x) < 100.0f)
+            _scrollingVelocity.x = 0.0f;
+        if (abs(_scrollingVelocity.y) < 100.0f)
+            _scrollingVelocity.y = 0.0f;
     }
 
     // Stop scrolling when the far edge is reached.
     if (-_scrollPosition.x > totalWidth - clipWidth)
     {
         _scrollPosition.x = -(totalWidth - clipWidth);
-        _velocity.x = 0;
+        _scrollingVelocity.x = 0;
     }
     
     if (-_scrollPosition.y > totalHeight - clipHeight)
     {
         _scrollPosition.y = -(totalHeight - clipHeight);
-        _velocity.y = 0;
+        _scrollingVelocity.y = 0;
     }
 
     if (_scrollPosition.x > 0)
     {
         _scrollPosition.x = 0;
-        _velocity.x = 0;
+        _scrollingVelocity.x = 0;
     }
 
     if (_scrollPosition.y > 0)
     {
         _scrollPosition.y = 0;
-        _velocity.y = 0;
+        _scrollingVelocity.y = 0;
     }
 
     float scrollWidth = 0;
@@ -696,6 +726,16 @@ void Container::updateScroll()
                          ((-_scrollPosition.y) / totalHeight) * clipHeight,
                          scrollWidth, scrollHeight);
 
+    // If scroll velocity is 0 and scrollbars are not always visible, trigger fade-out animation.
+    if (!_scrolling && _scrollingVelocity.isZero() && _scrollBarsAutoHide && _scrollBarOpacity == 1.0f)
+    {
+        float to = 0;
+        _scrollBarOpacity = 0.99f;
+        Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, 500L);
+        _scrollBarOpacityClip = animation->getClip();
+        _scrollBarOpacityClip->play();
+    }
+
     // Position controls within scroll area.
     _layout->update(this, _scrollPosition);
 }
@@ -705,49 +745,56 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
     switch(evt)
     {
     case Touch::TOUCH_PRESS:
-        _lastX = _firstX = x;
-        _lastY = _firstY = y;
-        _velocity.set(0, 0);
+        _scrollingLastX = _scrollingFirstX = x;
+        _scrollingLastY = _scrollingFirstY = y;
+        _scrollingVelocity.set(0, 0);
         _scrolling = true;
-        _startTimeX = _startTimeY = 0;
+        _scrollingStartTimeX = _scrollingStartTimeY = 0;
+        
+        if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
+        {
+            _scrollBarOpacityClip->stop();
+            _scrollBarOpacityClip = NULL;
+        }
+        _scrollBarOpacity = 1.0f;
         return true;
 
     case Touch::TOUCH_MOVE:
         if (_scrolling)
         {
             // Calculate the latest movement delta for the next update to use.
-            int vx = x - _lastX;
-            int vy = y - _lastY;
-            _velocity.set(vx, vy);
+            int vx = x - _scrollingLastX;
+            int vy = y - _scrollingLastY;
+            _scrollingVelocity.set(vx, vy);
             _scrollPosition.x += vx;
             _scrollPosition.y += vy;
-            _lastX = x;
-            _lastY = y;
+            _scrollingLastX = x;
+            _scrollingLastY = y;
 
             // If the user changes direction, reset the start time and position.
             bool goingRight = (vx > 0);
-            if (goingRight != _goingRight)
+            if (goingRight != _scrollingRight)
             {
-                _firstX = x;
-                _goingRight = goingRight;
-                _startTimeX = Game::getAbsoluteTime();
+                _scrollingFirstX = x;
+                _scrollingRight = goingRight;
+                _scrollingStartTimeX = Game::getAbsoluteTime();
             }
 
             bool goingDown = (vy > 0);
-            if (goingDown != _goingDown)
+            if (goingDown != _scrollingDown)
             {
-                _firstY = y;
-                _goingDown = goingDown;
-                _startTimeY = Game::getAbsoluteTime();
+                _scrollingFirstY = y;
+                _scrollingDown = goingDown;
+                _scrollingStartTimeY = Game::getAbsoluteTime();
             }
 
-            if (!_startTimeX)
-                _startTimeX = Game::getAbsoluteTime();
+            if (!_scrollingStartTimeX)
+                _scrollingStartTimeX = Game::getAbsoluteTime();
 
-            if (!_startTimeY)
-                _startTimeY = Game::getAbsoluteTime();
+            if (!_scrollingStartTimeY)
+                _scrollingStartTimeY = Game::getAbsoluteTime();
 
-            _lastTime = Game::getAbsoluteTime();
+            _scrollingLastTime = Game::getAbsoluteTime();
 
             return true;
         }
@@ -755,19 +802,19 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
 
     case Touch::TOUCH_RELEASE:
         _scrolling = false;
-        long timeSinceLastMove = Game::getAbsoluteTime() - _lastTime;
-        if (timeSinceLastMove > STOP_TIME)
+        long timeSinceLastMove = Game::getAbsoluteTime() - _scrollingLastTime;
+        if (timeSinceLastMove > SCROLL_INERTIA_DELAY)
         {
-            _velocity.set(0, 0);
+            _scrollingVelocity.set(0, 0);
             return true;
         }
 
-        int dx = _lastX - _firstX;
-        int dy = _lastY - _firstY;
+        int dx = _scrollingLastX - _scrollingFirstX;
+        int dy = _scrollingLastY - _scrollingFirstY;
 
-        long timeTakenX = Game::getAbsoluteTime() - _startTimeX;
+        long timeTakenX = Game::getAbsoluteTime() - _scrollingStartTimeX;
         float elapsedSecsX = (float)timeTakenX * 0.001f;
-        long timeTakenY = Game::getAbsoluteTime() - _startTimeY;
+        long timeTakenY = Game::getAbsoluteTime() - _scrollingStartTimeY;
         float elapsedSecsY = (float)timeTakenY * 0.001f;
 
         float vx = dx;
@@ -777,7 +824,7 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
         if (elapsedSecsY > 0)
             vy = (float)dy / elapsedSecsY;
 
-        _velocity.set(vx, vy);
+        _scrollingVelocity.set(vx, vy);
 
         return true;
     }
@@ -822,4 +869,46 @@ bool sortControlsByZOrder(Control* c1, Control* c2)
     return false;
 }
 
+unsigned int Container::getAnimationPropertyComponentCount(int propertyId) const
+{
+    switch(propertyId)
+    {
+    case ANIMATE_OPACITY:
+        return 1;
+    default:
+        return Control::getAnimationPropertyComponentCount(propertyId);
+    }
+}
+
+void Container::getAnimationPropertyValue(int propertyId, AnimationValue* value)
+{
+    GP_ASSERT(value);
+
+    switch(propertyId)
+    {
+    case ANIMATE_OPACITY:
+        value->setFloat(0, _scrollBarOpacity);
+        break;
+    default:
+        Control::getAnimationPropertyValue(propertyId, value);
+        break;
+    }
+}
+
+void Container::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
+{
+    GP_ASSERT(value);
+
+    switch(propertyId)
+    {
+    case ANIMATE_OPACITY:
+        _scrollBarOpacity = Curve::lerp(blendWeight, _opacity, value->getFloat(0));
+        _dirty = true;
+        break;
+    default:
+        Control::setAnimationPropertyValue(propertyId, value, blendWeight);
+        break;
+    }
+}
+
 }

+ 88 - 38
gameplay/src/Container.h

@@ -25,7 +25,8 @@ namespace gameplay
          size        = <width, height>   // Size of the container, measured in pixels.
          width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
          height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
-         scroll      = <Container::Scroll constant>
+         scroll      = <Container::Scroll constant> // Whether scrolling is allowed and in which directions.
+         scrollBarsAutoHide = <bool>    // Whether scrollbars fade out when not in use.
   
          // All the nested controls within this container.
          container 
@@ -46,6 +47,11 @@ class Container : public Control
 {
 public:
 
+    /**
+     * Constant used to auto-hide scrollbars.
+     */
+    static const int ANIMATE_SCROLLBAR_OPACITY = 8;
+
     /**
      * The definition for container scrolling.
      */
@@ -127,22 +133,53 @@ public:
     const std::vector<Control*>& getControls() const;
 
     /**
-     * Sets the scrolling for the container.
+     * Sets the allowed scroll directions for this container.
      *
-     * @param scroll The scroll for the 
+     * @param scroll The allowed scroll directions for this container.
      */
     void setScroll(Scroll scroll);
 
+    /**
+     * Gets the allowed scroll directions for this container.
+     *
+     * @return The allowed scroll directions for this container.
+     */
     Scroll getScroll() const;
 
     /**
-     * Gets the first animation in the control with the specified ID.
+     * Set whether scrollbars are always visible, or only visible while scrolling.
      *
-     * @param id The ID of the animation to get. Returns the first animation if ID is NULL.
-     * @return The first animation with the specified ID.
+     * @param alwaysVisible Whether scrollbars are always visible.
+     */
+    void setScrollBarsAutoHide(bool autoHide);
+
+    /**
+     * Whether scrollbars are always visible, or only visible while scrolling.
+     *
+     * @return Whether scrollbars are always visible.
+     */
+    bool isScrollBarsAutoHide() const;
+
+    /**
+     * @see AnimationTarget#getAnimation
      */
     Animation* getAnimation(const char* id = NULL) const;
 
+    /**
+     * @see AnimationTarget#getAnimationPropertyComponentCount
+     */
+    virtual unsigned int getAnimationPropertyComponentCount(int propertyId) const;
+
+    /**
+     * @see AnimationTarget#getAnimationProperty
+     */
+    virtual void getAnimationPropertyValue(int propertyId, AnimationValue* value);
+
+    /**
+     * @see AnimationTarget#setAnimationProperty
+     */
+    virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
+
 protected:
 
     /**
@@ -229,6 +266,9 @@ protected:
      */
     void addControls(Theme* theme, Properties* properties);
 
+    /**
+     * Draws a sprite batch for the specified clipping rect 
+     */
     virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight);
 
     /**
@@ -236,8 +276,22 @@ protected:
      */
     void updateScroll();
 
+    /**
+     * Applies touch events to scroll state.
+     *
+     * @return Whether the touch event was consumed by scrolling within this container.
+     *
+     * @see Touch::TouchEvent
+     */
     bool touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    /**
+     * Get a Scroll enum from a matching string.
+     *
+     * @param scroll A string representing a Scroll enum.
+     *
+     * @return The Scroll enum value that matches the given string.
+     */
     static Scroll getScroll(const char* scroll);
 
     /**
@@ -250,6 +304,7 @@ protected:
      */
     std::vector<Control*> _controls;
 
+    // Images used to draw scrollbars.
     Theme::ThemeImage* _scrollBarTopCap;
     Theme::ThemeImage* _scrollBarVertical;
     Theme::ThemeImage* _scrollBarBottomCap;
@@ -259,49 +314,44 @@ protected:
 
     // Flag representing whether scrolling is enabled, and in which directions.
     Scroll _scroll;
-
-    // Data required when scrolling is enabled.
-
-    /**
-     * x, width: Horizontal scrollbar position and size.
-     * y, height: Vertical scrollbar position and size.
-     */
+    // Scroll bar bounds
     Rectangle _scrollBarBounds;
-
     // How far this layout has been scrolled in each direction.
     Vector2 _scrollPosition;
-
-    // Whether the user is currently touching / holding the mouse down
-    // within this layout's container.
+    // Should the scrollbars auto hide. Default is false.
+    bool _scrollBarsAutoHide;
+    // Used to animate scrollbars fading out.
+    float _scrollBarOpacity;
+    // Whether the user is currently touching / holding the mouse down within this layout's container.
     bool _scrolling;
-
-    // First touch point.
-    int _firstX;
-    int _firstY;
-
-    // Latest touch point.
-    int _lastX;
-    int _lastY;
-
-    // Time recorded on touch down.
-    long _startTimeX;
-    long _startTimeY;
-    long _lastTime;
-
+    // First scrolling touch x position
+    int _scrollingFirstX;
+    // First scrolling touch y position
+    int _scrollingFirstY;
+    // The last y position when scrolling
+    int _scrollingLastX;
+    // The last x position when scrolling
+    int _scrollingLastY;
+    // Time we started scrolling in the x
+    long _scrollingStartTimeX;
+    // Time we started scrolling in the y
+    long _scrollingStartTimeY;
+    // The last time we were scrolling
+    long _scrollingLastTime;
     // Speed to continue scrolling at after touch release.
-    Vector2 _velocity;
-
+    Vector2 _scrollingVelocity;
     // Friction dampens velocity.
-    float _friction;
-
-    // Detect a change in scroll direction.
-    bool _goingRight;
-    bool _goingDown;
+    float _scrollingFriction;
+    // Are we scrolling to the right?
+    bool _scrollingRight;
+    // Are we scrolling down?
+    bool _scrollingDown;
 
 private:
 
     Container(const Container& copy);
 
+    AnimationClip* _scrollBarOpacityClip;
     int _zIndexDefault;
 };
 

+ 3 - 3
gameplay/src/Control.h

@@ -687,17 +687,17 @@ public:
     /**
      * @see AnimationTarget#getAnimationPropertyComponentCount
      */
-    unsigned int getAnimationPropertyComponentCount(int propertyId) const;
+    virtual unsigned int getAnimationPropertyComponentCount(int propertyId) const;
 
     /**
      * @see AnimationTarget#getAnimationProperty
      */
-    void getAnimationPropertyValue(int propertyId, AnimationValue* value);
+    virtual void getAnimationPropertyValue(int propertyId, AnimationValue* value);
 
     /**
      * @see AnimationTarget#setAnimationProperty
      */
-    void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
+    virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
 protected:
 

+ 1 - 1
gameplay/src/Curve.cpp

@@ -119,7 +119,7 @@ void Curve::setPoint(unsigned int index, float time, float* value, Interpolation
 
 void Curve::setPoint(unsigned int index, float time, float* value, InterpolationType type, float* inValue, float* outValue)
 {
-    assert(index < _pointCount && time >= 0.0f && time <= 1.0f && !(index == 0 && time != 0.0f) && !(_pointCount != 1 && index == _pointCount - 1 && time != 1.0f));
+    assert(index < _pointCount && time >= 0.0f && time <= 1.0f && !(_pointCount > 1 && index == 0 && time != 0.0f) && !(_pointCount != 1 && index == _pointCount - 1 && time != 1.0f));
 
     _points[index].time = time;
     _points[index].type = type;

+ 1 - 4
gameplay/src/FlowLayout.cpp

@@ -78,10 +78,7 @@ void FlowLayout::update(const Container* container, const Vector2& offset)
         yPosition = rowY + margin.top;
 
         control->setPosition(xPosition, yPosition);
-        if (control->isDirty() || control->isContainer())
-        {
-            control->update(container, offset);
-        }
+        control->update(container, offset);
 
         xPosition += bounds.width + margin.right;
 

+ 19 - 1
gameplay/src/Form.cpp

@@ -90,8 +90,19 @@ Form* Form::create(const char* url)
     Game* game = Game::getInstance();
     Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
 
+    Theme::Style* style = NULL;
     const char* styleName = formProperties->getString("style");
-    form->initialize(theme->getStyle(styleName), formProperties);
+    if (styleName)
+    {
+        style = theme->getStyle(styleName);
+    }
+    else
+    {
+        Theme::Style::Overlay* overlay = Theme::Style::Overlay::create();
+        style = new Theme::Style(theme, "", 1.0f / theme->_texture->getWidth(), 1.0f / theme->_texture->getHeight(),
+            Theme::Margin::empty(), Theme::Border::empty(), overlay, overlay, overlay, overlay);
+    }
+    form->initialize(style, formProperties);
 
     // Alignment
     if ((form->_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
@@ -113,10 +124,17 @@ Form* Form::create(const char* url)
     }
 
     form->_scroll = getScroll(formProperties->getString("scroll"));
+    form->_scrollBarsAutoHide = formProperties->getBool("scrollBarsAutoHide");
+    if (form->_scrollBarsAutoHide)
+    {
+        form->_scrollBarOpacity = 0.0f;
+    }
 
     // Add all the controls to the form.
     form->addControls(theme, formProperties);
 
+    form->update();
+
     SAFE_DELETE(properties);
 
     __forms.push_back(form);

+ 18 - 6
gameplay/src/MathUtil.h

@@ -3,6 +3,9 @@
 
 namespace gameplay
 {
+/**
+ * Math utility class. Used for internal math optimizations.
+ */
 class MathUtil
 {
 	friend class Matrix;
@@ -10,28 +13,37 @@ class MathUtil
 
 private:
 
-	/** Matrix **/
 	inline static void addMatrix(const float* m, float scalar, float* dst);
+
 	inline static void addMatrix(const float* m1, const float* m2, float* dst);
+
+	inline static void subtractMatrix(const float* m1, const float* m2, float* dst);
+
 	inline static void multiplyMatrix(const float* m, float scalar, float* dst);
+
 	inline static void multiplyMatrix(const float* m1, const float* m2, float* dst);
+
 	inline static void negateMatrix(const float* m, float* dst);
-	inline static void subtractMatrix(const float* m1, const float* m2, float* dst);
-	inline static void transformVectorMatrix(const float* m, float x, float y, float z, float w, float* dst);
-	inline static void transformVectorMatrix(const float* m, const float* v, float* dst);
+
 	inline static void transposeMatrix(const float* m, float* dst);
 
-	/** Vector3 **/
+	inline static void transformVector4(const float* m, float x, float y, float z, float w, float* dst);
+
+	inline static void transformVector4(const float* m, const float* v, float* dst);
+
 	inline static void crossVector3(const float* v1, const float* v2, float* dst);
 
 	MathUtil();
 };
+
 }
 
+#define MATRIX_SIZE ( sizeof(float) * 16)
+
 #ifdef USE_NEON
 #include "MathUtilNeon.inl"
 #else
 #include "MathUtil.inl"
 #endif
 
-#endif /* MATHUTIL_H_ */
+#endif

+ 30 - 32
gameplay/src/MathUtil.inl

@@ -1,5 +1,3 @@
-#define MATRIX_SIZE ( sizeof(float) * 16)
-
 namespace gameplay
 {
 
@@ -43,6 +41,26 @@ inline void MathUtil::addMatrix(const float* m1, const float* m2, float* dst)
 	dst[15] = m1[15] + m2[15];
 }
 
+inline void MathUtil::subtractMatrix(const float* m1, const float* m2, float* dst)
+{
+	dst[0]  = m1[0]  - m2[0];
+	dst[1]  = m1[1]  - m2[1];
+	dst[2]  = m1[2]  - m2[2];
+	dst[3]  = m1[3]  - m2[3];
+	dst[4]  = m1[4]  - m2[4];
+	dst[5]  = m1[5]  - m2[5];
+	dst[6]  = m1[6]  - m2[6];
+	dst[7]  = m1[7]  - m2[7];
+	dst[8]  = m1[8]  - m2[8];
+	dst[9]  = m1[9]  - m2[9];
+	dst[10] = m1[10] - m2[10];
+	dst[11] = m1[11] - m2[11];
+	dst[12] = m1[12] - m2[12];
+	dst[13] = m1[13] - m2[13];
+	dst[14] = m1[14] - m2[14];
+	dst[15] = m1[15] - m2[15];
+}
+
 inline void MathUtil::multiplyMatrix(const float* m, float scalar, float* dst)
 {
 	dst[0]  = m[0]  * scalar;
@@ -111,34 +129,25 @@ inline void MathUtil::negateMatrix(const float* m, float* dst)
 	dst[15] = -m[15];
 }
 
-inline void MathUtil::subtractMatrix(const float* m1, const float* m2, float* dst)
+inline void MathUtil::transposeMatrix(const float* m, float* dst)
 {
-	dst[0]  = m1[0]  - m2[0];
-	dst[1]  = m1[1]  - m2[1];
-	dst[2]  = m1[2]  - m2[2];
-	dst[3]  = m1[3]  - m2[3];
-	dst[4]  = m1[4]  - m2[4];
-	dst[5]  = m1[5]  - m2[5];
-	dst[6]  = m1[6]  - m2[6];
-	dst[7]  = m1[7]  - m2[7];
-	dst[8]  = m1[8]  - m2[8];
-	dst[9]  = m1[9]  - m2[9];
-	dst[10] = m1[10] - m2[10];
-	dst[11] = m1[11] - m2[11];
-	dst[12] = m1[12] - m2[12];
-	dst[13] = m1[13] - m2[13];
-	dst[14] = m1[14] - m2[14];
-	dst[15] = m1[15] - m2[15];
+	float t[16] = {
+		m[0], m[4], m[8], m[12],
+		m[1], m[5], m[9], m[13],
+		m[2], m[6], m[10], m[14],
+		m[3], m[7], m[11], m[15]
+	};
+	memcpy(dst, t, MATRIX_SIZE);
 }
 
-inline void MathUtil::transformVectorMatrix(const float* m, float x, float y, float z, float w, float* dst)
+inline void MathUtil::transformVector4(const float* m, float x, float y, float z, float w, float* dst)
 {
 	dst[0] = x * m[0] + y * m[4] + z * m[8] + w * m[12];
 	dst[1] = x * m[1] + y * m[5] + z * m[9] + w * m[13];
 	dst[2] = x * m[2] + y * m[6] + z * m[10] + w * m[14];
 }
 
-inline void MathUtil::transformVectorMatrix(const float* m, const float* v, float* dst)
+inline void MathUtil::transformVector4(const float* m, const float* v, float* dst)
 {
     // Handle case where v == dst.
     float x = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + v[3] * m[12];
@@ -152,17 +161,6 @@ inline void MathUtil::transformVectorMatrix(const float* m, const float* v, floa
 	dst[3] = w;
 }
 
-inline void MathUtil::transposeMatrix(const float* m, float* dst)
-{
-	float t[16] = {
-		m[0], m[4], m[8], m[12],
-		m[1], m[5], m[9], m[13],
-		m[2], m[6], m[10], m[14],
-		m[3], m[7], m[11], m[15]
-	};
-	memcpy(dst, t, MATRIX_SIZE);
-}
-
 inline void MathUtil::crossVector3(const float* v1, const float* v2, float* dst)
 {
 	float x = (v1[1] * v2[2]) - (v1[2] * v2[1]);

+ 32 - 32
gameplay/src/MathUtilNeon.inl

@@ -45,6 +45,27 @@ inline void MathUtil::addMatrix(const float* m1, const float* m2, float* dst)
 	);
 }
 
+inline void MathUtil::subtractMatrix(const float* m1, const float* m2, float* dst)
+{
+	asm volatile(
+		"vld1.32 	{q0, q1}, 	[%1]! 	\n\t" // M1[m0-m7]
+		"vld1.32 	{q2, q3}, 	[%1] 	\n\t" // M1[m8-m15]
+		"vld1.32 	{q8, q9}, 	[%2]! 	\n\t" // M2[m0-m7]
+		"vld1.32 	{q10, q11}, [%2] 	\n\t" // M2[m8-m15]
+
+		"vsub.f32   q12, q0, q8 		\n\t" // DST->M[m0-m3] = M1[m0-m3] - M2[m0-m3]
+		"vsub.f32   q13, q1, q9			\n\t" // DST->M[m4-m7] = M1[m4-m7] - M2[m4-m7]
+		"vsub.f32   q14, q2, q10		\n\t" // DST->M[m8-m11] = M1[m8-m11] - M2[m8-m11]
+		"vsub.f32   q15, q3, q11		\n\t" // DST->M[m12-m15] = M1[m12-m15] - M2[m12-m15]
+
+		"vst1.32    {q12, q13}, [%0]!   \n\t" // DST->M[m0-m7]
+		"vst1.32    {q14, q15}, [%0]    \n\t" // DST->M[m8-m15]
+		:
+		: "r"(dst), "r"(m1), "r"(m2)
+		: "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory"
+	);
+}
+
 inline void MathUtil::multiplyMatrix(const float* m, float scalar, float* dst)
 {
 	asm volatile(
@@ -121,28 +142,23 @@ inline void MathUtil::negateMatrix(const float* m, float* dst)
 	);
 }
 
-inline void MathUtil::subtractMatrix(const float* m1, const float* m2, float* dst)
+inline void MathUtil::transposeMatrix(const float* m, float* dst)
 {
 	asm volatile(
-		"vld1.32 	{q0, q1}, 	[%1]! 	\n\t" // M1[m0-m7]
-		"vld1.32 	{q2, q3}, 	[%1] 	\n\t" // M1[m8-m15]
-		"vld1.32 	{q8, q9}, 	[%2]! 	\n\t" // M2[m0-m7]
-		"vld1.32 	{q10, q11}, [%2] 	\n\t" // M2[m8-m15]
-
-		"vsub.f32   q12, q0, q8 		\n\t" // DST->M[m0-m3] = M1[m0-m3] - M2[m0-m3]
-		"vsub.f32   q13, q1, q9			\n\t" // DST->M[m4-m7] = M1[m4-m7] - M2[m4-m7]
-		"vsub.f32   q14, q2, q10		\n\t" // DST->M[m8-m11] = M1[m8-m11] - M2[m8-m11]
-		"vsub.f32   q15, q3, q11		\n\t" // DST->M[m12-m15] = M1[m12-m15] - M2[m12-m15]
+		"vld4.32 {d0[0], d2[0], d4[0], d6[0]}, [%1]! 	\n\t" // DST->M[m0, m4, m8, m12] = M[m0-m3]
+		"vld4.32 {d0[1], d2[1], d4[1], d6[1]}, [%1]!	\n\t" // DST->M[m1, m5, m9, m12] = M[m4-m7]
+		"vld4.32 {d1[0], d3[0], d5[0], d7[0]}, [%1]!	\n\t" // DST->M[m2, m6, m10, m12] = M[m8-m11]
+		"vld4.32 {d1[1], d3[1], d5[1], d7[1]}, [%1] 	\n\t" // DST->M[m3, m7, m11, m12] = M[m12-m15]
 
-		"vst1.32    {q12, q13}, [%0]!   \n\t" // DST->M[m0-m7]
-		"vst1.32    {q14, q15}, [%0]    \n\t" // DST->M[m8-m15]
+		"vst1.32 {q0-q1}, [%0]! 						\n\t" // DST->M[m0-m7]
+		"vst1.32 {q2-q3}, [%0] 							\n\t" // DST->M[m8-m15]
 		:
-		: "r"(dst), "r"(m1), "r"(m2)
-		: "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory"
+		: "r"(dst), "r"(m)
+		: "q0", "q1", "q2", "q3", "memory"
 	);
 }
 
-inline void MathUtil::transformVectorMatrix(const float* m, float x, float y, float z, float w, float* dst)
+inline void MathUtil::transformVector4(const float* m, float x, float y, float z, float w, float* dst)
 {
 	asm volatile(
 		"vld1.32	{d0[0]},		[%1]	\n\t"	// V[x]
@@ -165,7 +181,7 @@ inline void MathUtil::transformVectorMatrix(const float* m, float x, float y, fl
 	);
 }
 
-inline void MathUtil::transformVectorMatrix(const float* m, const float* v, float* dst)
+inline void MathUtil::transformVector4(const float* m, const float* v, float* dst)
 {
 	asm volatile
 	(
@@ -185,22 +201,6 @@ inline void MathUtil::transformVectorMatrix(const float* m, const float* v, floa
 	);
 }
 
-inline void MathUtil::transposeMatrix(const float* m, float* dst)
-{
-	asm volatile(
-		"vld4.32 {d0[0], d2[0], d4[0], d6[0]}, [%1]! 	\n\t" // DST->M[m0, m4, m8, m12] = M[m0-m3]
-		"vld4.32 {d0[1], d2[1], d4[1], d6[1]}, [%1]!	\n\t" // DST->M[m1, m5, m9, m12] = M[m4-m7]
-		"vld4.32 {d1[0], d3[0], d5[0], d7[0]}, [%1]!	\n\t" // DST->M[m2, m6, m10, m12] = M[m8-m11]
-		"vld4.32 {d1[1], d3[1], d5[1], d7[1]}, [%1] 	\n\t" // DST->M[m3, m7, m11, m12] = M[m12-m15]
-
-		"vst1.32 {q0-q1}, [%0]! 						\n\t" // DST->M[m0-m7]
-		"vst1.32 {q2-q3}, [%0] 							\n\t"  // DST->M[m8-m15]
-		:
-		: "r"(dst), "r"(m)
-		: "q0", "q1", "q2", "q3", "memory"
-	);
-}
-
 inline void MathUtil::crossVector3(const float* v1, const float* v2, float* dst)
 {
 	asm volatile(

+ 2 - 6
gameplay/src/Matrix.cpp

@@ -3,10 +3,6 @@
 #include "Quaternion.h"
 #include "MathUtil.h"
 
-#ifndef MATRIX_SIZE
-#define MATRIX_SIZE     ( sizeof(float) * 16 )
-#endif
-
 namespace gameplay
 {
 
@@ -846,7 +842,7 @@ void Matrix::transformVector(float x, float y, float z, float w, Vector3* dst) c
 {
     GP_ASSERT(dst);
 
-    MathUtil::transformVectorMatrix(m, x, y, z, w, (float*)dst);
+    MathUtil::transformVector4(m, x, y, z, w, (float*)dst);
 }
 
 void Matrix::transformVector(Vector4* vector) const
@@ -859,7 +855,7 @@ void Matrix::transformVector(const Vector4& vector, Vector4* dst) const
 {
     GP_ASSERT(dst);
 
-    MathUtil::transformVectorMatrix(m, (const float*) &vector, (float*)dst);
+    MathUtil::transformVector4(m, (const float*) &vector, (float*)dst);
 }
 
 void Matrix::translate(float x, float y, float z)

+ 3 - 1
gameplay/src/ThemeStyle.cpp

@@ -85,7 +85,9 @@ Theme::Style::Overlay* Theme::Style::Overlay::create()
     return overlay;
 }
 
-Theme::Style::Overlay::Overlay() : _skin(NULL), _cursor(NULL), _imageList(NULL), _font(NULL)
+Theme::Style::Overlay::Overlay()
+    : _skin(NULL), _cursor(NULL), _imageList(NULL), _font(NULL),
+    _fontSize(0), _alignment(Font::ALIGN_TOP_LEFT), _textRightToLeft(false), _textColor(Vector4::one()), _opacity(1.0f)
 {
 }
 

+ 4 - 0
gameplay/src/ThemeStyle.h

@@ -22,6 +22,8 @@ class Theme::Style
 {
     friend class Theme;
     friend class Control;
+    friend class Container;
+    friend class Form;
 
 private:
 
@@ -45,6 +47,8 @@ private:
         friend class Theme;
         friend class Theme::Style;
         friend class Control;
+        friend class Container;
+        friend class Form;
 
     private:
 

Деякі файли не було показано, через те що забагато файлів було змінено