ソースを参照

Merge remote-tracking branch 'upstream/next' into next

Brandon Slack 13 年 前
コミット
3882a05099
68 ファイル変更2124 行追加960 行削除
  1. 61 0
      CHANGES.md
  2. 8 8
      README.md
  3. 7 7
      gameplay-encoder/README.md
  4. 3 4
      gameplay-encoder/gameplay-encoder.xcodeproj/xcshareddata/xcschemes/gameplay-encoder.xcscheme
  5. 21 17
      gameplay-encoder/src/DAESceneEncoder.cpp
  6. 4 1
      gameplay-encoder/src/DAEUtil.cpp
  7. 165 17
      gameplay-encoder/src/EncoderArguments.cpp
  8. 21 2
      gameplay-encoder/src/EncoderArguments.h
  9. 208 169
      gameplay-encoder/src/FBXSceneEncoder.cpp
  10. 18 16
      gameplay-encoder/src/FBXSceneEncoder.h
  11. 34 5
      gameplay-encoder/src/GPBFile.cpp
  12. 6 2
      gameplay-encoder/src/GPBFile.h
  13. 1 1
      gameplay-encoder/src/Node.cpp
  14. 11 1
      gameplay-encoder/src/StringUtil.cpp
  15. 12 0
      gameplay-encoder/src/StringUtil.h
  16. 19 19
      gameplay-template/gameplay-template.xcodeproj/project.pbxproj
  17. 39 82
      gameplay.doxyfile
  18. 15 2
      gameplay/.cproject
  19. 1 1
      gameplay/android/jni/Android.mk
  20. 7 1
      gameplay/gameplay.vcxproj
  21. 19 1
      gameplay/gameplay.vcxproj.filters
  22. 24 0
      gameplay/gameplay.xcodeproj/project.pbxproj
  23. 2 2
      gameplay/src/AbsoluteLayout.cpp
  24. 1 1
      gameplay/src/AbsoluteLayout.h
  25. 3 1
      gameplay/src/Base.h
  26. 15 2
      gameplay/src/Bundle.cpp
  27. 2 2
      gameplay/src/CheckBox.cpp
  28. 2 2
      gameplay/src/CheckBox.h
  29. 191 97
      gameplay/src/Container.cpp
  30. 92 42
      gameplay/src/Container.h
  31. 11 5
      gameplay/src/Control.cpp
  32. 18 9
      gameplay/src/Control.h
  33. 1 1
      gameplay/src/Curve.cpp
  34. 3 2
      gameplay/src/FileSystem.cpp
  35. 2 5
      gameplay/src/FlowLayout.cpp
  36. 1 1
      gameplay/src/FlowLayout.h
  37. 1 2
      gameplay/src/Font.cpp
  38. 23 5
      gameplay/src/Form.cpp
  39. 9 1
      gameplay/src/FrameBuffer.cpp
  40. 4 0
      gameplay/src/FrameBuffer.h
  41. 2 1
      gameplay/src/Game.cpp
  42. 219 0
      gameplay/src/Joystick.cpp
  43. 147 0
      gameplay/src/Joystick.h
  44. 34 0
      gameplay/src/Joystick.inl
  45. 2 2
      gameplay/src/Label.cpp
  46. 2 2
      gameplay/src/Label.h
  47. 2 1
      gameplay/src/Layout.h
  48. 49 0
      gameplay/src/MathUtil.h
  49. 177 0
      gameplay/src/MathUtil.inl
  50. 229 0
      gameplay/src/MathUtilNeon.inl
  51. 10 321
      gameplay/src/Matrix.cpp
  52. 2 2
      gameplay/src/Plane.cpp
  53. 87 59
      gameplay/src/Properties.cpp
  54. 2 2
      gameplay/src/RadioButton.cpp
  55. 2 2
      gameplay/src/RadioButton.h
  56. 38 7
      gameplay/src/SceneLoader.cpp
  57. 1 0
      gameplay/src/SceneLoader.h
  58. 2 2
      gameplay/src/Slider.cpp
  59. 2 2
      gameplay/src/Slider.h
  60. 2 2
      gameplay/src/TextBox.cpp
  61. 2 2
      gameplay/src/TextBox.h
  62. 11 0
      gameplay/src/Texture.cpp
  63. 3 1
      gameplay/src/ThemeStyle.cpp
  64. 4 0
      gameplay/src/ThemeStyle.h
  65. 3 12
      gameplay/src/Vector3.cpp
  66. 3 3
      gameplay/src/VerticalLayout.cpp
  67. 1 1
      gameplay/src/VerticalLayout.h
  68. 1 0
      gameplay/src/gameplay.h

+ 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. 
 An open-source, cross-platform 3D native C++ game framework making it easy to learn and write mobile and desktop games. 
 
 
 ## Supported Mobile Platforms
 ## 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)
 - Apple iOS 5.1 (using Apple XCode 4.3.2)
 
 
 ## Supported Desktop Platforms
 ## 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)
 - Apple MacOS X (using Apple XCode 4.3.2)
 
 
 ## Roadmap for 'next' branch
 ## Roadmap for 'next' branch
-- Shadows
-- Lua Script Bindings
+- Gamepad support
+- Lua script bindings
+- Vehicle physics
 - Terrain
 - Terrain
-- AI
-- Editor
-- Performance/Optimizations
+- Lightmaps
+- Shadows
 
 
 ## Licence
 ## Licence
 The project is open sourced under the Apache 2.0 license.
 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"
 - Edit the project properties of "gameplay-encoder"
 - Add Preprocessor Definition "USE_FBX" (C++/Preprocessor)
 - Add Preprocessor Definition "USE_FBX" (C++/Preprocessor)
 - Add the FBX SDK include directory to Additional Include Directories (C++/General)
 - 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)
 - 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)
 - 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
 - Build gameplay-encoder
 
 
 ### Building FBX Support on Mac OS X using XCode 4.3.2+
 ### 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".
 - Edit the project properties of target "gameplay-encoder".
 - Add Preprocessor Macro "USE_FBX" to both Debug/Release sections. (Build Settings)
 - Add Preprocessor Macro "USE_FBX" to both Debug/Release sections. (Build Settings)
 - Add the FBX include directory to Header Search Paths: (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)
 - 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
   * Example: libiconv.dylib, Cocoa.framework, SystemConfiguration.framework
 - Build gameplay-encoder
 - Build gameplay-encoder
 
 

+ 3 - 4
gameplay-encoder/gameplay-encoder.xcodeproj/xcshareddata/xcschemes/gameplay-encoder.xcscheme

@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <Scheme
-   LastUpgradeVersion = "0430"
    version = "1.3">
    version = "1.3">
    <BuildAction
    <BuildAction
       parallelizeBuildables = "YES"
       parallelizeBuildables = "YES"
@@ -23,8 +22,8 @@
       </BuildActionEntries>
       </BuildActionEntries>
    </BuildAction>
    </BuildAction>
    <TestAction
    <TestAction
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES"
       shouldUseLaunchSchemeArgsEnv = "YES"
       buildConfiguration = "Debug">
       buildConfiguration = "Debug">
       <Testables>
       <Testables>
@@ -41,7 +40,7 @@
    </TestAction>
    </TestAction>
    <LaunchAction
    <LaunchAction
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       useCustomWorkingDirectory = "NO"
       buildConfiguration = "Debug"
       buildConfiguration = "Debug"

+ 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()))
         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();
     _begin = clock();
     const char* nodeId = arguments.getNodeId();
     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
     // Load the collada document
     _collada = new DAE();
     _collada = new DAE();
@@ -239,7 +235,7 @@ void DAESceneEncoder::write(const std::string& filepath, const EncoderArguments&
     end("Open file");
     end("Open file");
     if (!_dom)
     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)
         if (_collada)
         {
         {
             delete _collada;
             delete _collada;
@@ -309,24 +305,32 @@ void DAESceneEncoder::write(const std::string& filepath, const EncoderArguments&
     loadAnimations(_dom);
     loadAnimations(_dom);
     end("loadAnimations");
     end("loadAnimations");
 
 
-    std::string dstFilename = dstPath;
-    dstFilename.append(1, '/');
-    dstFilename.append(getFilenameNoExt(filenameOnly));
-
     _gamePlayFile.adjust();
     _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
     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();
         begin();
-        _gamePlayFile.saveBinary(outFile);
+        if (!_gamePlayFile.saveBinary(outputFilePath))
+        {
+            fprintf(stderr,"Error writing binary file: %s\n", outputFilePath.c_str());
+        }
         end("save binary");
         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)
         for (size_t j = 0; j < visualSceneCount; ++j)
         {
         {
             domVisual_sceneRef visualScene = visualScenes.get(j);
             domVisual_sceneRef visualScene = visualScenes.get(j);
-            return visualScene.cast();
+            if (domVisual_scene* v = visualScene.cast())
+            {
+                return v;
+            }
         }
         }
     }
     }
     return NULL;
     return NULL;

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

@@ -24,26 +24,30 @@ EncoderArguments::EncoderArguments(size_t argc, const char** argv) :
 
 
     if (argc > 1)
     if (argc > 1)
     {
     {
-        size_t filePathIndex = argc - 1;
-        if (argv[filePathIndex])
-        {
-            _filePath.assign(getRealPath(argv[filePathIndex]));
-        }
-        
         // read the options
         // 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
     else
     {
     {
@@ -70,10 +74,42 @@ const char* EncoderArguments::getFilePathPointer() const
     return _filePath.c_str();
     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
 const std::string& EncoderArguments::getDAEOutputPath() const
@@ -134,7 +170,7 @@ bool EncoderArguments::fileExists() const
 
 
 void EncoderArguments::printUsage() 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,"Supported file extensions:\n");
     fprintf(stderr,"  .dae\t(COLLADA)\n");
     fprintf(stderr,"  .dae\t(COLLADA)\n");
     fprintf(stderr,"  .fbx\t(FBX)\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)
 std::string EncoderArguments::getRealPath(const std::string& filepath)
 {
 {
     char path[PATH_MAX + 1]; /* not sure about the "+ 1" */
     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.
      * 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>& getGroupAnimationNodeId() const;
     const std::vector<std::string>& getGroupAnimationAnimationId() const;
     const std::vector<std::string>& getGroupAnimationAnimationId() const;
@@ -92,6 +99,9 @@ public:
     const char* getNodeId() const;
     const char* getNodeId() const;
     unsigned int getFontSize() const;
     unsigned int getFontSize() const;
 
 
+
+    static std::string getRealPath(const std::string& filepath);
+
 private:
 private:
 
 
     /**
     /**
@@ -103,7 +113,13 @@ private:
      */
      */
     void readOption(const std::vector<std::string>& options, size_t *index);
     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.
      * Replaces all instance of oldChar with newChar in str.
@@ -113,6 +129,7 @@ private:
 private:
 private:
     
     
     std::string _filePath;
     std::string _filePath;
+    std::string _fileOutputPath;
     std::string _nodeId;
     std::string _nodeId;
     std::string _daeOutputPath;
     std::string _daeOutputPath;
 
 
@@ -129,6 +146,8 @@ private:
 
 
 };
 };
 
 
+void unittestsEncoderArguments();
+
 }
 }
 
 
 #endif
 #endif

ファイルの差分が大きいため隠しています
+ 208 - 169
gameplay-encoder/src/FBXSceneEncoder.cpp


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

@@ -3,6 +3,8 @@
 
 
 #ifdef USE_FBX
 #ifdef USE_FBX
 
 
+#define FBXSDK_NEW_API
+
 #include <iostream>
 #include <iostream>
 #include <list>
 #include <list>
 #include <vector>
 #include <vector>
@@ -65,7 +67,7 @@ private:
      * 
      * 
      * @param fbxScene The FBX scene to load.
      * @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.
      * Loads all of the animatiosn from the given FBX scene.
@@ -73,7 +75,7 @@ private:
      * @param fbxScene The scene to load animations from.
      * @param fbxScene The scene to load animations from.
      * @param arguments The command line arguments passed to the encoder.
      * @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.
      * 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 fbxNode The node to start loading animations from.
      * @param arguments The command line arguments passed to the encoder.
      * @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.
      * 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 fbxNode The node to load animation channels from.
      * @param animation The animation to add the channels to.
      * @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.
      * 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.
      * @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.
      * 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 fbxNode The FBX node to load from.
      * @param node The GamePlay node to add to.
      * @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.
      * 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 fbxNode The FBX node to load from.
      * @param node The GamePlay node to add to.
      * @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.
      * 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 fbxNode The FBX node to load from.
      * @param node The GamePlay node to add to.
      * @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.
      * 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 fbxMesh The FBX mesh to load the skin from.
      * @param model The model to add the skin to.
      * @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.
      * 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.
      * @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.
      * Loads the FbxMesh and returns a GamePlay mesh.
@@ -149,7 +151,7 @@ private:
      * 
      * 
      * @return The GamePlay mesh that was loaded from the FBX Mesh.
      * @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.
      * 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.
      * @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.
      * 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 meshId The ID of the FbxMesh to use as a key.
      * @param mesh The mesh to save.
      * @param mesh The mesh to save.
      */
      */
-    void saveMesh(size_t meshId, Mesh* mesh);
+    void saveMesh(FbxUInt64 meshId, Mesh* mesh);
     
     
     /**
     /**
      * Prints a message.
      * Prints a message.
@@ -181,14 +183,14 @@ private:
      * @param fbxNode The FBX node to get the transfrom data from
      * @param fbxNode The FBX node to get the transfrom data from
      * @param node The GamePlay Node to copy the transform to.
      * @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.
      * Recursively triangules the meshes starting from the given node.
      * 
      * 
      * @param fbxNode The node to start triangulating from.
      * @param fbxNode The node to start triangulating from.
      */
      */
-    static void triangulateRecursive(KFbxNode* fbxNode);
+    static void triangulateRecursive(FbxNode* fbxNode);
 
 
     /**
     /**
      * Prints a warning message.
      * 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)
      * 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.
      * 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;
     return __instance;
 }
 }
 
 
-void GPBFile::saveBinary(const std::string& filepath)
+bool GPBFile::saveBinary(const std::string& filepath)
 {
 {
     _file = fopen(filepath.c_str(), "w+b");
     _file = fopen(filepath.c_str(), "w+b");
+    if (!_file)
+    {
+        return false;
+    }
+    size_t n = 0;
 
 
     // identifier
     // identifier
     char identifier[] = { '«', 'G', 'P', 'B', '»', '\r', '\n', '\x1A', '\n' };
     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
     // 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
     // write refs
     _refTable.writeBinary(_file);
     _refTable.writeBinary(_file);
@@ -60,13 +77,24 @@ void GPBFile::saveBinary(const std::string& filepath)
     _refTable.updateOffsets(_file);
     _refTable.updateOffsets(_file);
     
     
     fclose(_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");
     _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
     // write refs
     _refTable.writeText(_file);
     _refTable.writeText(_file);
@@ -86,6 +114,7 @@ void GPBFile::saveText(const std::string& filepath)
     fprintf(_file, "</root>");
     fprintf(_file, "</root>");
 
 
     fclose(_file);
     fclose(_file);
+    return true;
 }
 }
 
 
 void GPBFile::add(Object* obj)
 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.
      * Saves the GPBFile as a binary file at filepath.
      *
      *
      * @param filepath The file name and path to save to.
      * @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.
      * Saves the GPBFile as a text file at filepath. Useful for debugging.
      *
      *
      * @param filepath The file name and path to save to.
      * @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 add(Object* obj);
     void addScene(Scene* scene);
     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());
             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 += "/heightmap_";
             heightmapFilename += getId();
             heightmapFilename += getId();
             heightmapFilename += ".png";
             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;
     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)
 bool equals(const std::string& a, const char* b)
 {
 {
     return (a.compare(b) == 0);
     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)
 bool equalsIgnoreCase(const std::string& a, const char* b)
 {
 {
     size_t bLength = strlen(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)
     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 "";
     return "";
 }
 }

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

@@ -4,19 +4,31 @@
 namespace gameplay
 namespace gameplay
 {
 {
 
 
+/**
+ * Returns true if the given string starts with the prefix.
+ */
 bool startsWith(const char* str, const char* prefix, bool ignoreCase = true);
 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 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.
  * 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 char* b);
+bool equals(const std::string& a, const std::string& b);
 
 
 /**
 /**
  * Returns true if the strings are equal. Case insensitive.
  * Returns true if the strings are equal. Case insensitive.
  */
  */
 bool equalsIgnoreCase(const std::string& a, const char* b);
 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 getFilenameFromFilePath(const std::string& filepath);
 
 
 std::string getFilenameNoExt(const std::string& filename);
 std::string getFilenameNoExt(const std::string& filename);

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

@@ -43,7 +43,7 @@
 
 
 /* Begin PBXFileReference section */
 /* 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>"; };
 		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-MacOSX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT-MacOSX.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; };
 		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>"; };
 		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; };
 		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>"; };
 		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; };
 		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>"; };
 		5B61611214CCC2200073B857 /* TEMPLATE_PROJECT-macosx.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "TEMPLATE_PROJECT-macosx.plist"; sourceTree = "<group>"; };
-		5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT-iOS.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>"; };
 		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; };
 		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; };
 		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 */ = {
 		42C932BD1491A0DB0098216A /* Products */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
-				42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX.app */,
-				5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-iOS.app */,
+				42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-macosx.app */,
+				5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-ios.app */,
 			);
 			);
 			name = Products;
 			name = Products;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -209,9 +209,9 @@
 /* End PBXGroup section */
 /* End PBXGroup section */
 
 
 /* Begin PBXNativeTarget section */
 /* Begin PBXNativeTarget section */
-		42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX */ = {
+		42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-macosx */ = {
 			isa = PBXNativeTarget;
 			isa = PBXNativeTarget;
-			buildConfigurationList = 42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-MacOSX" */;
+			buildConfigurationList = 42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-macosx" */;
 			buildPhases = (
 			buildPhases = (
 				42C932B81491A0DB0098216A /* Sources */,
 				42C932B81491A0DB0098216A /* Sources */,
 				42C932B91491A0DB0098216A /* Frameworks */,
 				42C932B91491A0DB0098216A /* Frameworks */,
@@ -223,28 +223,28 @@
 			);
 			);
 			dependencies = (
 			dependencies = (
 			);
 			);
-			name = "TEMPLATE_PROJECT-MacOSX";
+			name = "TEMPLATE_PROJECT-macosx";
 			productName = TEMPLATE_PROJECT;
 			productName = TEMPLATE_PROJECT;
-			productReference = 42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX.app */;
+			productReference = 42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-macosx.app */;
 			productType = "com.apple.product-type.application";
 			productType = "com.apple.product-type.application";
 		};
 		};
-		5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-iOS */ = {
+		5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-ios */ = {
 			isa = PBXNativeTarget;
 			isa = PBXNativeTarget;
-			buildConfigurationList = 5B61612914CCC24C0073B857 /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-iOS" */;
+			buildConfigurationList = 5B61612914CCC24C0073B857 /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-ios" */;
 			buildPhases = (
 			buildPhases = (
 				5B61611514CCC24C0073B857 /* Sources */,
 				5B61611514CCC24C0073B857 /* Sources */,
 				5B61611714CCC24C0073B857 /* Frameworks */,
 				5B61611714CCC24C0073B857 /* Frameworks */,
 				5B61612414CCC24C0073B857 /* ShellScript */,
 				5B61612414CCC24C0073B857 /* ShellScript */,
 				5B61612514CCC24C0073B857 /* Resources */,
 				5B61612514CCC24C0073B857 /* Resources */,
-				5BAF20A3152F2FCE003E2AC3 /* Copy Gameplay Reousrces Run Script */,
+				5BAF20A3152F2FCE003E2AC3 /* Copy Gameplay Resources Run Script */,
 			);
 			);
 			buildRules = (
 			buildRules = (
 			);
 			);
 			dependencies = (
 			dependencies = (
 			);
 			);
-			name = "TEMPLATE_PROJECT-iOS";
+			name = "TEMPLATE_PROJECT-ios";
 			productName = TEMPLATE_PROJECT;
 			productName = TEMPLATE_PROJECT;
-			productReference = 5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-iOS.app */;
+			productReference = 5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-ios.app */;
 			productType = "com.apple.product-type.application";
 			productType = "com.apple.product-type.application";
 		};
 		};
 /* End PBXNativeTarget section */
 /* End PBXNativeTarget section */
@@ -267,8 +267,8 @@
 			projectDirPath = "";
 			projectDirPath = "";
 			projectRoot = "";
 			projectRoot = "";
 			targets = (
 			targets = (
-				42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX */,
-				5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-iOS */,
+				42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-macosx */,
+				5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-ios */,
 			);
 			);
 		};
 		};
 /* End PBXProject section */
 /* End PBXProject section */
@@ -321,7 +321,7 @@
 			shellPath = /bin/sh;
 			shellPath = /bin/sh;
 			shellScript = "touch -cm ${SRCROOT}/res";
 			shellScript = "touch -cm ${SRCROOT}/res";
 		};
 		};
-		5BAF20A3152F2FCE003E2AC3 /* Copy Gameplay Reousrces Run Script */ = {
+		5BAF20A3152F2FCE003E2AC3 /* Copy Gameplay Resources Run Script */ = {
 			isa = PBXShellScriptBuildPhase;
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
@@ -333,7 +333,7 @@
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
 			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 */ = {
 		5BAF20D7152F30C3003E2AC3 /* Copy Gameplay Resources - Run Script */ = {
 			isa = PBXShellScriptBuildPhase;
 			isa = PBXShellScriptBuildPhase;
@@ -544,7 +544,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 			defaultConfigurationName = Release;
 		};
 		};
-		42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-MacOSX" */ = {
+		42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-macosx" */ = {
 			isa = XCConfigurationList;
 			isa = XCConfigurationList;
 			buildConfigurations = (
 			buildConfigurations = (
 				42C932DB1491A0DB0098216A /* Debug */,
 				42C932DB1491A0DB0098216A /* Debug */,
@@ -553,7 +553,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 			defaultConfigurationName = Release;
 		};
 		};
-		5B61612914CCC24C0073B857 /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-iOS" */ = {
+		5B61612914CCC24C0073B857 /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-ios" */ = {
 			isa = XCConfigurationList;
 			isa = XCConfigurationList;
 			buildConfigurations = (
 			buildConfigurations = (
 				5B61612A14CCC24C0073B857 /* Debug */,
 				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
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project
 # doxygen (www.doxygen.org) for a project
@@ -32,7 +32,7 @@ PROJECT_NAME           = gameplay
 # This could be handy for archiving the generated documentation or 
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 # 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 
 # 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 
 # for a project that appears at the top of each page and should give viewer 
@@ -205,13 +205,6 @@ TAB_SIZE               = 8
 
 
 ALIASES                = 
 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 
 # 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. 
 # 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 
 # For instance, some of the names that are used will be different. The list 
@@ -250,15 +243,6 @@ OPTIMIZE_OUTPUT_VHDL   = NO
 
 
 EXTENSION_MAPPING      = 
 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 
 # 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 
 # 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 
 # 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 
 # a logarithmic scale so increasing the size by one will roughly double the 
 # memory usage. The cache size is given by this formula: 
 # 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, 
 # 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
 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
 # Build related configuration options
 #---------------------------------------------------------------------------
 #---------------------------------------------------------------------------
@@ -372,11 +345,6 @@ EXTRACT_ALL            = NO
 
 
 EXTRACT_PRIVATE        = 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 
 # If the EXTRACT_STATIC tag is set to YES all static members of a file 
 # will be included in the documentation.
 # will be included in the documentation.
 
 
@@ -606,8 +574,7 @@ LAYOUT_FILE            =
 # .bib extension is automatically appended if omitted. Using this command 
 # .bib extension is automatically appended if omitted. Using this command 
 # requires the bibtex tool to be installed. See also 
 # requires the bibtex tool to be installed. See also 
 # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style 
 # 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         = 
 CITE_BIB_FILES         = 
 
 
@@ -698,15 +665,14 @@ FILE_PATTERNS          = *.h \
 
 
 RECURSIVE              = NO
 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 
 # 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. 
 # 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                = 
 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 
 # directories that are symbolic links (a Unix file system feature) are excluded 
 # from the input.
 # from the input.
 
 
@@ -891,7 +857,7 @@ HTML_FILE_EXTENSION    = .html
 # standard header. Note that when using a custom header you are responsible  
 # standard header. Note that when using a custom header you are responsible  
 # for the proper inclusion of any scripts and style sheets that doxygen 
 # for the proper inclusion of any scripts and style sheets that doxygen 
 # needs, which is dependent on the configuration options used. 
 # 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 
 # header.html footer.html stylesheet.css YourConfigFile" and then modify 
 # that header. Note that the header is subject to change so you typically 
 # 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 
 # 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 
 # 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 
 # 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 
 # 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        = 
 HTML_STYLESHEET        = 
 
 
@@ -924,7 +890,7 @@ HTML_STYLESHEET        =
 HTML_EXTRA_FILES       = 
 HTML_EXTRA_FILES       = 
 
 
 # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. 
 # 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, 
 # according to this color. Hue is specified as an angle on a colorwheel, 
 # see http://en.wikipedia.org/wiki/Hue for more information. 
 # see http://en.wikipedia.org/wiki/Hue for more information. 
 # For instance the value 0 represents red, 60 is yellow, 120 is green, 
 # 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
 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
 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 
 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 
 # structure should be generated to display hierarchical information. 
 # structure should be generated to display hierarchical information. 
 # If the tag value is set to YES, a side panel will be generated 
 # 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 
 # containing a tree-like index structure (just like the one that 
 # is generated for HTML Help). For this to work a browser that supports 
 # 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). 
 # 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
 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, 
 # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, 
 # and Class Hierarchy pages using a tree view instead of an ordered list.
 # 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 
 # (see http://www.mathjax.org) which uses client side Javascript for the 
 # rendering instead of using prerendered bitmaps. Use this if you do not 
 # 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 
 # 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.
 # configure the path to it using the MATHJAX_RELPATH option.
 
 
 USE_MATHJAX            = NO
 USE_MATHJAX            = NO
@@ -1191,10 +1153,10 @@ USE_MATHJAX            = NO
 # HTML output directory using the MATHJAX_RELPATH option. The destination 
 # HTML output directory using the MATHJAX_RELPATH option. The destination 
 # directory should contain the MathJax.js script. For instance, if the mathjax 
 # 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 
 # 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
 MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
 
 
@@ -1353,7 +1315,7 @@ COMPACT_RTF            = NO
 
 
 RTF_HYPERLINKS         = 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 
 # config file, i.e. a series of assignments. You only have to provide 
 # replacements, missing definitions are set to their default value.
 # replacements, missing definitions are set to their default value.
 
 
@@ -1542,16 +1504,20 @@ SKIP_FUNCTION_MACROS   = YES
 # Configuration::additions related to external references
 # 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 ... 
 #   TAGFILES = file1 file2 ... 
 # Adding location for the tag files is done as follows: 
 # Adding location for the tag files is done as follows: 
 #   TAGFILES = file1=loc1 "file2 = loc2" ... 
 #   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               = 
 TAGFILES               = 
 
 
@@ -1642,7 +1608,7 @@ DOT_FONTPATH           =
 # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
 # 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 
 # will generate a graph for each documented class showing the direct and 
 # indirect inheritance relations. Setting this tag to YES will force the 
 # 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
 CLASS_GRAPH            = YES
 
 
@@ -1664,15 +1630,6 @@ GROUP_GRAPHS           = YES
 
 
 UML_LOOK               = NO
 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 
 # If set to YES, the inheritance and collaboration graphs will show the 
 # relations between templates and their instances.
 # relations between templates and their instances.
 
 

+ 15 - 2
gameplay/.cproject

@@ -29,8 +29,8 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								</option>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.1968057343" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList"/>
-								<option id="com.qnx.qcc.option.compiler.ccoptions.1078137668" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
+								<option id="com.qnx.qcc.option.compiler.qccoptions.1968057343" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions"/>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.1078137668" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
 									<listOptionValue builtIn="false" value="-mfpu=neon"/>
 									<listOptionValue builtIn="false" value="-mfpu=neon"/>
 								</option>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.997142816" superClass="com.qnx.qcc.inputType.compiler"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.997142816" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -86,6 +86,9 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.1122311163" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
+									<listOptionValue builtIn="false" value="-mfpu=neon"/>
+								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.1380846613" superClass="com.qnx.qcc.inputType.compiler"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.1380846613" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.855139060" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
 							<tool id="com.qnx.qcc.tool.assembler.855139060" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -138,6 +141,9 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.1956270067" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
+									<listOptionValue builtIn="false" value="-mfpu=neon"/>
+								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.81809638" superClass="com.qnx.qcc.inputType.compiler"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.81809638" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.2145279747" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
 							<tool id="com.qnx.qcc.tool.assembler.2145279747" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -193,6 +199,9 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.47607907" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
+									<listOptionValue builtIn="false" value="-mfpu=neon"/>
+								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.2007171407" superClass="com.qnx.qcc.inputType.compiler"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.2007171407" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.1537562121" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
 							<tool id="com.qnx.qcc.tool.assembler.1537562121" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -246,6 +255,7 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.1432778691" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.1038720310" superClass="com.qnx.qcc.inputType.compiler"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.1038720310" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.521146732" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
 							<tool id="com.qnx.qcc.tool.assembler.521146732" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -299,6 +309,7 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.663337616" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.1961855927" superClass="com.qnx.qcc.inputType.compiler"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.1961855927" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.1089440729" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
 							<tool id="com.qnx.qcc.tool.assembler.1089440729" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -353,6 +364,7 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.346770186" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.1658185881" superClass="com.qnx.qcc.inputType.compiler"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.1658185881" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.746786008" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
 							<tool id="com.qnx.qcc.tool.assembler.746786008" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -411,4 +423,5 @@
 		<resource resourceType="PROJECT" workspacePath="/gameplay"/>
 		<resource resourceType="PROJECT" workspacePath="/gameplay"/>
 	</storageModule>
 	</storageModule>
 	<storageModule moduleId="com.qnx.tools.ide.qde.core.QNXProjectProperties"/>
 	<storageModule moduleId="com.qnx.tools.ide.qde.core.QNXProjectProperties"/>
+	<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
 </cproject>
 </cproject>

+ 1 - 1
gameplay/android/jni/Android.mk

@@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir)/../../src
 
 
 include $(CLEAR_VARS)
 include $(CLEAR_VARS)
 LOCAL_MODULE    := libgameplay
 LOCAL_MODULE    := libgameplay
-LOCAL_SRC_FILES := AbsoluteLayout.cpp Animation.cpp AnimationClip.cpp AnimationController.cpp AnimationTarget.cpp AnimationValue.cpp AudioBuffer.cpp AudioController.cpp AudioListener.cpp AudioSource.cpp BoundingBox.cpp BoundingSphere.cpp Bundle.cpp Button.cpp Camera.cpp CheckBox.cpp Container.cpp Control.cpp Curve.cpp DebugNew.cpp DepthStencilTarget.cpp Effect.cpp FileSystem.cpp FlowLayout.cpp Font.cpp Form.cpp FrameBuffer.cpp Frustum.cpp Game.cpp gameplay-main-android.cpp Image.cpp Joint.cpp Label.cpp Layout.cpp Light.cpp Material.cpp MaterialParameter.cpp Matrix.cpp Mesh.cpp MeshBatch.cpp MeshPart.cpp MeshSkin.cpp Model.cpp Node.cpp ParticleEmitter.cpp Pass.cpp PhysicsCharacter.cpp PhysicsCollisionObject.cpp PhysicsCollisionShape.cpp PhysicsConstraint.cpp PhysicsController.cpp PhysicsFixedConstraint.cpp PhysicsGenericConstraint.cpp PhysicsGhostObject.cpp PhysicsHingeConstraint.cpp PhysicsMotionState.cpp PhysicsRigidBody.cpp PhysicsSocketConstraint.cpp PhysicsSpringConstraint.cpp Plane.cpp PlatformAndroid.cpp Properties.cpp Quaternion.cpp RadioButton.cpp Ray.cpp Rectangle.cpp Ref.cpp RenderState.cpp RenderTarget.cpp Scene.cpp SceneLoader.cpp Slider.cpp SpriteBatch.cpp Technique.cpp TextBox.cpp Texture.cpp Theme.cpp ThemeStyle.cpp Transform.cpp Vector2.cpp Vector3.cpp Vector4.cpp VertexAttributeBinding.cpp VertexFormat.cpp VerticalLayout.cpp
+LOCAL_SRC_FILES := AbsoluteLayout.cpp Animation.cpp AnimationClip.cpp AnimationController.cpp AnimationTarget.cpp AnimationValue.cpp AudioBuffer.cpp AudioController.cpp AudioListener.cpp AudioSource.cpp BoundingBox.cpp BoundingSphere.cpp Bundle.cpp Button.cpp Camera.cpp CheckBox.cpp Container.cpp Control.cpp Curve.cpp DebugNew.cpp DepthStencilTarget.cpp Effect.cpp FileSystem.cpp FlowLayout.cpp Font.cpp Form.cpp FrameBuffer.cpp Frustum.cpp Game.cpp gameplay-main-android.cpp Image.cpp Joint.cpp Joystick.cpp Label.cpp Layout.cpp Light.cpp Material.cpp MaterialParameter.cpp Matrix.cpp Mesh.cpp MeshBatch.cpp MeshPart.cpp MeshSkin.cpp Model.cpp Node.cpp ParticleEmitter.cpp Pass.cpp PhysicsCharacter.cpp PhysicsCollisionObject.cpp PhysicsCollisionShape.cpp PhysicsConstraint.cpp PhysicsController.cpp PhysicsFixedConstraint.cpp PhysicsGenericConstraint.cpp PhysicsGhostObject.cpp PhysicsHingeConstraint.cpp PhysicsMotionState.cpp PhysicsRigidBody.cpp PhysicsSocketConstraint.cpp PhysicsSpringConstraint.cpp Plane.cpp PlatformAndroid.cpp Properties.cpp Quaternion.cpp RadioButton.cpp Ray.cpp Rectangle.cpp Ref.cpp RenderState.cpp RenderTarget.cpp Scene.cpp SceneLoader.cpp Slider.cpp SpriteBatch.cpp Technique.cpp TextBox.cpp Texture.cpp Theme.cpp ThemeStyle.cpp Transform.cpp Vector2.cpp Vector3.cpp Vector4.cpp VertexAttributeBinding.cpp VertexFormat.cpp VerticalLayout.cpp
 LOCAL_CFLAGS := -D__ANDROID__ -I"../../external-deps/bullet/include" -I"../../external-deps/libpng/include" -I"../../external-deps/oggvorbis/include" -I"../../external-deps/openal/include"
 LOCAL_CFLAGS := -D__ANDROID__ -I"../../external-deps/bullet/include" -I"../../external-deps/libpng/include" -I"../../external-deps/oggvorbis/include" -I"../../external-deps/openal/include"
 LOCAL_STATIC_LIBRARIES := android_native_app_glue
 LOCAL_STATIC_LIBRARIES := android_native_app_glue
 
 

+ 7 - 1
gameplay/gameplay.vcxproj

@@ -48,6 +48,7 @@
     <ClCompile Include="src\gameplay-main-win32.cpp" />
     <ClCompile Include="src\gameplay-main-win32.cpp" />
     <ClCompile Include="src\Image.cpp" />
     <ClCompile Include="src\Image.cpp" />
     <ClCompile Include="src\Joint.cpp" />
     <ClCompile Include="src\Joint.cpp" />
+    <ClCompile Include="src\Joystick.cpp" />
     <ClCompile Include="src\Label.cpp" />
     <ClCompile Include="src\Label.cpp" />
     <ClCompile Include="src\Layout.cpp" />
     <ClCompile Include="src\Layout.cpp" />
     <ClCompile Include="src\Light.cpp" />
     <ClCompile Include="src\Light.cpp" />
@@ -138,11 +139,13 @@
     <ClInclude Include="src\gameplay.h" />
     <ClInclude Include="src\gameplay.h" />
     <ClInclude Include="src\Image.h" />
     <ClInclude Include="src\Image.h" />
     <ClInclude Include="src\Joint.h" />
     <ClInclude Include="src\Joint.h" />
+    <ClInclude Include="src\Joystick.h" />
     <ClInclude Include="src\Keyboard.h" />
     <ClInclude Include="src\Keyboard.h" />
     <ClInclude Include="src\Label.h" />
     <ClInclude Include="src\Label.h" />
     <ClInclude Include="src\Layout.h" />
     <ClInclude Include="src\Layout.h" />
     <ClInclude Include="src\Light.h" />
     <ClInclude Include="src\Light.h" />
     <ClInclude Include="src\Material.h" />
     <ClInclude Include="src\Material.h" />
+    <ClInclude Include="src\MathUtil.h" />
     <ClInclude Include="src\MeshBatch.h" />
     <ClInclude Include="src\MeshBatch.h" />
     <ClInclude Include="src\Mouse.h" />
     <ClInclude Include="src\Mouse.h" />
     <ClInclude Include="src\Pass.h" />
     <ClInclude Include="src\Pass.h" />
@@ -230,6 +233,9 @@
     <None Include="src\gameplay-main-ios.mm" />
     <None Include="src\gameplay-main-ios.mm" />
     <None Include="src\gameplay-main-macosx.mm" />
     <None Include="src\gameplay-main-macosx.mm" />
     <None Include="src\Image.inl" />
     <None Include="src\Image.inl" />
+    <None Include="src\MathUtil.inl" />
+    <None Include="src\MathUtilNeon.inl" />
+    <None Include="src\Joystick.inl" />
     <None Include="src\Matrix.inl" />
     <None Include="src\Matrix.inl" />
     <None Include="src\MeshBatch.inl" />
     <None Include="src\MeshBatch.inl" />
     <None Include="src\Plane.inl" />
     <None Include="src\Plane.inl" />
@@ -359,4 +365,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
   </ImportGroup>
-</Project>
+</Project>

+ 19 - 1
gameplay/gameplay.vcxproj.filters

@@ -279,6 +279,9 @@
     <ClCompile Include="src\FlowLayout.cpp">
     <ClCompile Include="src\FlowLayout.cpp">
       <Filter>src</Filter>
       <Filter>src</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="src\Joystick.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
     <ClInclude Include="src\Animation.h">
@@ -554,6 +557,12 @@
     <ClInclude Include="src\FlowLayout.h">
     <ClInclude Include="src\FlowLayout.h">
       <Filter>src</Filter>
       <Filter>src</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="src\Joystick.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\MathUtil.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
     <None Include="res\shaders\bumped-specular.vsh">
@@ -652,6 +661,15 @@
     <None Include="res\logo_white.png">
     <None Include="res\logo_white.png">
       <Filter>res</Filter>
       <Filter>res</Filter>
     </None>
     </None>
+    <None Include="src\MathUtil.inl">
+      <Filter>src</Filter>
+    </None>
+    <None Include="src\MathUtilNeon.inl">
+      <Filter>src</Filter>
+    </None>
+    <None Include="src\Joystick.inl">
+      <Filter>src</Filter>
+    </None>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <None Include="src\PhysicsFixedConstraint.inl">
     <None Include="src\PhysicsFixedConstraint.inl">
@@ -697,4 +715,4 @@
       <Filter>src</Filter>
       <Filter>src</Filter>
     </None>
     </None>
   </ItemGroup>
   </ItemGroup>
-</Project>
+</Project>

+ 24 - 0
gameplay/gameplay.xcodeproj/project.pbxproj

@@ -18,6 +18,12 @@
 		422260D81537790F0011E3AB /* Bundle.h in Headers */ = {isa = PBXBuildFile; fileRef = 422260D51537790F0011E3AB /* Bundle.h */; };
 		422260D81537790F0011E3AB /* Bundle.h in Headers */ = {isa = PBXBuildFile; fileRef = 422260D51537790F0011E3AB /* Bundle.h */; };
 		422260D91537790F0011E3AB /* Bundle.h in Headers */ = {isa = PBXBuildFile; fileRef = 422260D51537790F0011E3AB /* Bundle.h */; };
 		422260D91537790F0011E3AB /* Bundle.h in Headers */ = {isa = PBXBuildFile; fileRef = 422260D51537790F0011E3AB /* Bundle.h */; };
 		4234D99E14686C52003031B3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4234D99D14686C52003031B3 /* Cocoa.framework */; };
 		4234D99E14686C52003031B3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4234D99D14686C52003031B3 /* Cocoa.framework */; };
+		4239DDEC157545A1005EA3F6 /* Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4239DDE9157545A1005EA3F6 /* Joystick.cpp */; };
+		4239DDED157545A1005EA3F6 /* Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4239DDE9157545A1005EA3F6 /* Joystick.cpp */; };
+		4239DDEE157545A1005EA3F6 /* Joystick.h in Headers */ = {isa = PBXBuildFile; fileRef = 4239DDEA157545A1005EA3F6 /* Joystick.h */; };
+		4239DDEF157545A1005EA3F6 /* Joystick.h in Headers */ = {isa = PBXBuildFile; fileRef = 4239DDEA157545A1005EA3F6 /* Joystick.h */; };
+		4239DDF4157545C1005EA3F6 /* MathUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4239DDF1157545C1005EA3F6 /* MathUtil.h */; };
+		4239DDF5157545C1005EA3F6 /* MathUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4239DDF1157545C1005EA3F6 /* MathUtil.h */; };
 		4251B131152D049B002F6199 /* ScreenDisplayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4251B12E152D049B002F6199 /* ScreenDisplayer.h */; };
 		4251B131152D049B002F6199 /* ScreenDisplayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4251B12E152D049B002F6199 /* ScreenDisplayer.h */; };
 		4251B132152D049B002F6199 /* ScreenDisplayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4251B12E152D049B002F6199 /* ScreenDisplayer.h */; };
 		4251B132152D049B002F6199 /* ScreenDisplayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4251B12E152D049B002F6199 /* ScreenDisplayer.h */; };
 		4251B133152D049B002F6199 /* ThemeStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4251B12F152D049B002F6199 /* ThemeStyle.cpp */; };
 		4251B133152D049B002F6199 /* ThemeStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4251B12F152D049B002F6199 /* ThemeStyle.cpp */; };
@@ -399,6 +405,12 @@
 		422260D51537790F0011E3AB /* Bundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Bundle.h; path = src/Bundle.h; sourceTree = SOURCE_ROOT; };
 		422260D51537790F0011E3AB /* Bundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Bundle.h; path = src/Bundle.h; sourceTree = SOURCE_ROOT; };
 		4234D99A14686C52003031B3 /* libgameplay.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgameplay.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		4234D99A14686C52003031B3 /* libgameplay.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgameplay.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		4234D99D14686C52003031B3 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
 		4234D99D14686C52003031B3 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+		4239DDE9157545A1005EA3F6 /* Joystick.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Joystick.cpp; path = src/Joystick.cpp; sourceTree = SOURCE_ROOT; };
+		4239DDEA157545A1005EA3F6 /* Joystick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Joystick.h; path = src/Joystick.h; sourceTree = SOURCE_ROOT; };
+		4239DDEB157545A1005EA3F6 /* Joystick.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Joystick.inl; path = src/Joystick.inl; sourceTree = SOURCE_ROOT; };
+		4239DDF1157545C1005EA3F6 /* MathUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MathUtil.h; path = src/MathUtil.h; sourceTree = SOURCE_ROOT; };
+		4239DDF2157545C1005EA3F6 /* MathUtil.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = MathUtil.inl; path = src/MathUtil.inl; sourceTree = SOURCE_ROOT; };
+		4239DDF3157545C1005EA3F6 /* MathUtilNeon.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = MathUtilNeon.inl; path = src/MathUtilNeon.inl; sourceTree = SOURCE_ROOT; };
 		4251B12E152D049B002F6199 /* ScreenDisplayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScreenDisplayer.h; path = src/ScreenDisplayer.h; sourceTree = SOURCE_ROOT; };
 		4251B12E152D049B002F6199 /* ScreenDisplayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScreenDisplayer.h; path = src/ScreenDisplayer.h; sourceTree = SOURCE_ROOT; };
 		4251B12F152D049B002F6199 /* ThemeStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThemeStyle.cpp; path = src/ThemeStyle.cpp; sourceTree = SOURCE_ROOT; };
 		4251B12F152D049B002F6199 /* ThemeStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThemeStyle.cpp; path = src/ThemeStyle.cpp; sourceTree = SOURCE_ROOT; };
 		4251B130152D049B002F6199 /* ThemeStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThemeStyle.h; path = src/ThemeStyle.h; sourceTree = SOURCE_ROOT; };
 		4251B130152D049B002F6199 /* ThemeStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThemeStyle.h; path = src/ThemeStyle.h; sourceTree = SOURCE_ROOT; };
@@ -761,6 +773,9 @@
 				4208DEE814A4079F00D3C511 /* Image.inl */,
 				4208DEE814A4079F00D3C511 /* Image.inl */,
 				42CD0DE4147D8FF50000361E /* Joint.cpp */,
 				42CD0DE4147D8FF50000361E /* Joint.cpp */,
 				42CD0DE5147D8FF50000361E /* Joint.h */,
 				42CD0DE5147D8FF50000361E /* Joint.h */,
+				4239DDE9157545A1005EA3F6 /* Joystick.cpp */,
+				4239DDEA157545A1005EA3F6 /* Joystick.h */,
+				4239DDEB157545A1005EA3F6 /* Joystick.inl */,
 				4208DEEB14A407B900D3C511 /* Keyboard.h */,
 				4208DEEB14A407B900D3C511 /* Keyboard.h */,
 				5BD52641150F822A004C9099 /* Label.cpp */,
 				5BD52641150F822A004C9099 /* Label.cpp */,
 				5BD52642150F822A004C9099 /* Label.h */,
 				5BD52642150F822A004C9099 /* Label.h */,
@@ -772,6 +787,9 @@
 				42CD0DE9147D8FF50000361E /* Material.h */,
 				42CD0DE9147D8FF50000361E /* Material.h */,
 				42CD0DEA147D8FF50000361E /* MaterialParameter.cpp */,
 				42CD0DEA147D8FF50000361E /* MaterialParameter.cpp */,
 				42CD0DEB147D8FF50000361E /* MaterialParameter.h */,
 				42CD0DEB147D8FF50000361E /* MaterialParameter.h */,
+				4239DDF1157545C1005EA3F6 /* MathUtil.h */,
+				4239DDF2157545C1005EA3F6 /* MathUtil.inl */,
+				4239DDF3157545C1005EA3F6 /* MathUtilNeon.inl */,
 				42CD0DEC147D8FF50000361E /* Matrix.cpp */,
 				42CD0DEC147D8FF50000361E /* Matrix.cpp */,
 				42CD0DED147D8FF50000361E /* Matrix.h */,
 				42CD0DED147D8FF50000361E /* Matrix.h */,
 				42CD0DEE147D8FF50000361E /* Matrix.inl */,
 				42CD0DEE147D8FF50000361E /* Matrix.inl */,
@@ -1064,6 +1082,8 @@
 				4251B135152D049B002F6199 /* ThemeStyle.h in Headers */,
 				4251B135152D049B002F6199 /* ThemeStyle.h in Headers */,
 				422260D81537790F0011E3AB /* Bundle.h in Headers */,
 				422260D81537790F0011E3AB /* Bundle.h in Headers */,
 				426878AE153F4BB300844500 /* FlowLayout.h in Headers */,
 				426878AE153F4BB300844500 /* FlowLayout.h in Headers */,
+				4239DDEE157545A1005EA3F6 /* Joystick.h in Headers */,
+				4239DDF4157545C1005EA3F6 /* MathUtil.h in Headers */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
@@ -1161,6 +1181,8 @@
 				4251B136152D049B002F6199 /* ThemeStyle.h in Headers */,
 				4251B136152D049B002F6199 /* ThemeStyle.h in Headers */,
 				422260D91537790F0011E3AB /* Bundle.h in Headers */,
 				422260D91537790F0011E3AB /* Bundle.h in Headers */,
 				426878AF153F4BB300844500 /* FlowLayout.h in Headers */,
 				426878AF153F4BB300844500 /* FlowLayout.h in Headers */,
+				4239DDEF157545A1005EA3F6 /* Joystick.h in Headers */,
+				4239DDF5157545C1005EA3F6 /* MathUtil.h in Headers */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
@@ -1317,6 +1339,7 @@
 				4271C08E15337C8200B89DA7 /* Layout.cpp in Sources */,
 				4271C08E15337C8200B89DA7 /* Layout.cpp in Sources */,
 				422260D61537790F0011E3AB /* Bundle.cpp in Sources */,
 				422260D61537790F0011E3AB /* Bundle.cpp in Sources */,
 				426878AC153F4BB300844500 /* FlowLayout.cpp in Sources */,
 				426878AC153F4BB300844500 /* FlowLayout.cpp in Sources */,
+				4239DDEC157545A1005EA3F6 /* Joystick.cpp in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
@@ -1409,6 +1432,7 @@
 				4271C08F15337C8200B89DA7 /* Layout.cpp in Sources */,
 				4271C08F15337C8200B89DA7 /* Layout.cpp in Sources */,
 				422260D71537790F0011E3AB /* Bundle.cpp in Sources */,
 				422260D71537790F0011E3AB /* Bundle.cpp in Sources */,
 				426878AD153F4BB300844500 /* FlowLayout.cpp in Sources */,
 				426878AD153F4BB300844500 /* FlowLayout.cpp in Sources */,
+				4239DDED157545A1005EA3F6 /* Joystick.cpp in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};

+ 2 - 2
gameplay/src/AbsoluteLayout.cpp

@@ -40,7 +40,7 @@ Layout::Type AbsoluteLayout::getType()
     return Layout::LAYOUT_ABSOLUTE;
     return Layout::LAYOUT_ABSOLUTE;
 }
 }
 
 
-void AbsoluteLayout::update(const Container* container)
+void AbsoluteLayout::update(const Container* container, const Vector2& offset)
 {
 {
     GP_ASSERT(container);
     GP_ASSERT(container);
 
 
@@ -53,7 +53,7 @@ void AbsoluteLayout::update(const Container* container)
         GP_ASSERT(control);
         GP_ASSERT(control);
 
 
         align(control, container);
         align(control, container);
-        control->update(container->getClip(), Vector2::zero());
+        control->update(container, offset);
     }
     }
 }
 }
 
 

+ 1 - 1
gameplay/src/AbsoluteLayout.h

@@ -41,7 +41,7 @@ protected:
      *
      *
      * @param container The container to update.
      * @param container The container to update.
      */
      */
-    void update(const Container* container);
+    void update(const Container* container, const Vector2& offset);
 
 
 private:
 private:
     
     

+ 3 - 1
gameplay/src/Base.h

@@ -191,7 +191,9 @@ extern void printError(const char* format, ...);
     #define glClearDepth glClearDepthf
     #define glClearDepth glClearDepthf
     #define OPENGL_ES
     #define OPENGL_ES
     #define USE_PVRTC
     #define USE_PVRTC
-	#define USE_NEON
+    #ifdef __ARM__
+        #define USE_NEON
+    #endif
 #elif __ANDROID__
 #elif __ANDROID__
 	#include <EGL/egl.h>
 	#include <EGL/egl.h>
     #include <GLES2/gl2.h>
     #include <GLES2/gl2.h>

+ 15 - 2
gameplay/src/Bundle.cpp

@@ -598,10 +598,14 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext, Node* nodeContext)
     if (sceneContext)
     if (sceneContext)
     {
     {
         node = sceneContext->findNode(id, true);
         node = sceneContext->findNode(id, true);
+        if (node)
+            node->addRef();
     }
     }
     else if (nodeContext)
     else if (nodeContext)
     {
     {
         node = nodeContext->findNode(id, true);
         node = nodeContext->findNode(id, true);
+        if (node)
+            node->addRef();
     }
     }
 
 
     if (node == NULL)
     if (node == NULL)
@@ -704,7 +708,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
             if (fseek(_file, sizeof(float) * 16, SEEK_CUR) != 0)
             if (fseek(_file, sizeof(float) * 16, SEEK_CUR) != 0)
             {
             {
                 GP_ERROR("Failed to skip over node transform for node '%s'.", id);
                 GP_ERROR("Failed to skip over node transform for node '%s'.", id);
-                return false;
+                return NULL;
             }
             }
             readString(_file);
             readString(_file);
 
 
@@ -724,6 +728,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
                 }
                 }
             }
             }
 
 
+            iter->second->addRef();
             return iter->second;
             return iter->second;
         }
         }
         else
         else
@@ -1108,6 +1113,7 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
                     Joint* joint = static_cast<Joint*>(n);
                     Joint* joint = static_cast<Joint*>(n);
                     joint->setInverseBindPose(skinData->inverseBindPoseMatrices[j]);
                     joint->setInverseBindPose(skinData->inverseBindPoseMatrices[j]);
                     skinData->skin->setJoint(joint, j);
                     skinData->skin->setJoint(joint, j);
+                    SAFE_RELEASE(joint);
                 }
                 }
             }
             }
         }
         }
@@ -1120,6 +1126,7 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
             GP_ASSERT(node);
             GP_ASSERT(node);
             Node* parent = node->getParent();
             Node* parent = node->getParent();
             
             
+            std::vector<Node*> loadedNodes;
             while (true)
             while (true)
             {
             {
                 if (parent)
                 if (parent)
@@ -1165,13 +1172,19 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
                     }
                     }
 
 
                     if (nodeID != rootJoint->getId())
                     if (nodeID != rootJoint->getId())
-                        loadNode(nodeID.c_str(), sceneContext, nodeContext);
+                        loadedNodes.push_back(loadNode(nodeID.c_str(), sceneContext, nodeContext));
 
 
                     break;
                     break;
                 }
                 }
             }
             }
 
 
             skinData->skin->setRootJoint(rootJoint);
             skinData->skin->setRootJoint(rootJoint);
+
+            // Release all the nodes that we loaded since the nodes are now owned by the mesh skin.
+            for (unsigned int i = 0; i < loadedNodes.size(); i++)
+            {
+                SAFE_RELEASE(loadedNodes[i]);
+            }
         }
         }
 
 
         // Remove the joint hierarchy from the scene since it is owned by the mesh skin.
         // Remove the joint hierarchy from the scene since it is owned by the mesh skin.

+ 2 - 2
gameplay/src/CheckBox.cpp

@@ -94,9 +94,9 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
     return Button::touchEvent(evt, x, y, contactIndex);
     return Button::touchEvent(evt, x, y, contactIndex);
 }
 }
 
 
-void CheckBox::update(const Rectangle& clip, const Vector2& offset)
+void CheckBox::update(const Control* container, const Vector2& offset)
 {
 {
-    Label::update(clip, offset);
+    Label::update(container, offset);
 
 
     Vector2 size;
     Vector2 size;
     if (_imageSize.isZero())
     if (_imageSize.isZero())

+ 2 - 2
gameplay/src/CheckBox.h

@@ -118,9 +118,9 @@ protected:
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this control's parent container.
+     * @param container This control's parent container.
      */
      */
-    void update(const Rectangle& clip, const Vector2& offset);
+    void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Draw the checkbox icon associated with this control.
      * Draw the checkbox icon associated with this control.

+ 191 - 97
gameplay/src/Container.cpp

@@ -10,31 +10,27 @@
 #include "RadioButton.h"
 #include "RadioButton.h"
 #include "Slider.h"
 #include "Slider.h"
 #include "TextBox.h"
 #include "TextBox.h"
+#include "Joystick.h"
 #include "Game.h"
 #include "Game.h"
 
 
 namespace gameplay
 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()
 Container::Container()
     : _layout(NULL), _scrollBarTopCap(NULL), _scrollBarVertical(NULL), _scrollBarBottomCap(NULL),
     : _layout(NULL), _scrollBarTopCap(NULL), _scrollBarVertical(NULL), _scrollBarBottomCap(NULL),
       _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
       _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
       _scroll(SCROLL_NONE), _scrollBarBounds(Rectangle::empty()), _scrollPosition(Vector2::zero()),
       _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)
 {
 {
 }
 }
 
 
@@ -49,7 +45,6 @@ Container::~Container()
     {
     {
         SAFE_RELEASE((*it));
         SAFE_RELEASE((*it));
     }
     }
-
     SAFE_RELEASE(_layout);
     SAFE_RELEASE(_layout);
 }
 }
 
 
@@ -83,7 +78,13 @@ Container* Container::create(Theme::Style* style, Properties* properties, Theme*
     Container* container = Container::create(getLayoutType(layoutString));
     Container* container = Container::create(getLayoutType(layoutString));
     container->initialize(style, properties);
     container->initialize(style, properties);
     container->_scroll = getScroll(properties->getString("scroll"));
     container->_scroll = getScroll(properties->getString("scroll"));
+    container->_scrollBarsAutoHide = properties->getBool("scrollBarsAutoHide");
+    if (container->_scrollBarsAutoHide)
+    {
+        container->_scrollBarOpacity = 0.0f;
+    }
     container->addControls(theme, properties);
     container->addControls(theme, properties);
+    container->_layout->update(container, container->_scrollPosition);
 
 
     return container;
     return container;
 }
 }
@@ -101,9 +102,16 @@ void Container::addControls(Theme* theme, Properties* properties)
 
 
         const char* controlStyleName = controlSpace->getString("style");
         const char* controlStyleName = controlSpace->getString("style");
         Theme::Style* controlStyle = NULL;
         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::string controlName(controlSpace->getNamespace());
         std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);
         std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);
@@ -135,6 +143,10 @@ void Container::addControls(Theme* theme, Properties* properties)
         {
         {
             control = TextBox::create(controlStyle, controlSpace);
             control = TextBox::create(controlStyle, controlSpace);
         }
         }
+        else if (controlName == "JOYSTICK")
+        {
+            control = Joystick::create(controlStyle, controlSpace);
+        }
         else
         else
         {
         {
             GP_ERROR("Failed to create control; unrecognized control name '%s'.", controlName.c_str());
             GP_ERROR("Failed to create control; unrecognized control name '%s'.", controlName.c_str());
@@ -154,6 +166,9 @@ void Container::addControls(Theme* theme, Properties* properties)
         // Get the next control.
         // Get the next control.
         controlSpace = properties->getNextNamespace();
         controlSpace = properties->getNextNamespace();
     }
     }
+
+    // Sort controls by Z-Order.
+    std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
 }
 }
 
 
 Layout* Container::getLayout()
 Layout* Container::getLayout()
@@ -260,6 +275,20 @@ Container::Scroll Container::getScroll() const
     return _scroll;
     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
 Animation* Container::getAnimation(const char* id) const
 {
 {
     std::vector<Control*>::const_iterator itr = _controls.begin();
     std::vector<Control*>::const_iterator itr = _controls.begin();
@@ -285,10 +314,10 @@ Animation* Container::getAnimation(const char* id) const
     return NULL;
     return NULL;
 }
 }
 
 
-void Container::update(const Rectangle& clip, const Vector2& offset)
+void Container::update(const Control* container, const Vector2& offset)
 {
 {
     // Update this container's viewport.
     // Update this container's viewport.
-    Control::update(clip, offset);
+    Control::update(container, offset);
 
 
     // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
     // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
     if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
     if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
@@ -297,6 +326,8 @@ void Container::update(const Rectangle& clip, const Vector2& offset)
         _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
         _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
         _scrollBarRightCap = getImage("scrollBarRightCap", _state);
         _scrollBarRightCap = getImage("scrollBarRightCap", _state);
 
 
+        GP_ASSERT(_scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap);
+
         _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
         _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
     }
     }
 
 
@@ -305,6 +336,8 @@ void Container::update(const Rectangle& clip, const Vector2& offset)
         _scrollBarTopCap = getImage("scrollBarTopCap", _state);
         _scrollBarTopCap = getImage("scrollBarTopCap", _state);
         _scrollBarVertical = getImage("verticalScrollBar", _state);
         _scrollBarVertical = getImage("verticalScrollBar", _state);
         _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
         _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
+
+        GP_ASSERT(_scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap);
         
         
         _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
         _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
     }
     }
@@ -313,15 +346,15 @@ void Container::update(const Rectangle& clip, const Vector2& offset)
     std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
     std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
 
 
     GP_ASSERT(_layout);
     GP_ASSERT(_layout);
-    _layout->update(this);
-
     if (_scroll != SCROLL_NONE)
     if (_scroll != SCROLL_NONE)
-        this->updateScroll(this);
+        updateScroll();
+    else
+        _layout->update(this, Vector2::zero());
 }
 }
 
 
-void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight)
+void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight)
 {
 {
-    if (_skin && needsClear)
+    if (needsClear)
     {
     {
         GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
         GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
         GL_ASSERT( glClearColor(0, 0, 0, 0) );
         GL_ASSERT( glClearColor(0, 0, 0, 0) );
@@ -332,6 +365,11 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
         GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
         GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
 
 
         needsClear = false;
         needsClear = false;
+        cleared = true;
+    }
+    else if (!cleared)
+    {
+        needsClear = true;
     }
     }
 
 
     spriteBatch->begin();
     spriteBatch->begin();
@@ -346,31 +384,34 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
         GP_ASSERT(control);
         GP_ASSERT(control);
         if (!needsClear || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
         if (!needsClear || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
         {
         {
-            control->draw(spriteBatch, _viewportClipBounds, needsClear, targetHeight);
+            control->draw(spriteBatch, _viewportClipBounds, needsClear, cleared, targetHeight);
             Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
             Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
         }
         }
     }
     }
 
 
-    if (_scroll != SCROLL_NONE)
+    if (_scroll != SCROLL_NONE && (_scrollBarOpacity > 0.0f))
     {
     {
         // Draw scroll bars.
         // Draw scroll bars.
         Rectangle clipRegion(_viewportClipBounds);
         Rectangle clipRegion(_viewportClipBounds);
 
 
         spriteBatch->begin();
         spriteBatch->begin();
 
 
-        if (_scrollBarBounds.height > 0 && (_scrolling || _velocity.y) && _scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap)
+        if (_scrollBarBounds.height > 0)
         {
         {
             const Rectangle& topRegion = _scrollBarTopCap->getRegion();
             const Rectangle& topRegion = _scrollBarTopCap->getRegion();
             const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
             const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
             Vector4 topColor = _scrollBarTopCap->getColor();
             Vector4 topColor = _scrollBarTopCap->getColor();
+            topColor.w *= _scrollBarOpacity * _opacity;
 
 
             const Rectangle& verticalRegion = _scrollBarVertical->getRegion();
             const Rectangle& verticalRegion = _scrollBarVertical->getRegion();
             const Theme::UVs& verticalUVs = _scrollBarVertical->getUVs();
             const Theme::UVs& verticalUVs = _scrollBarVertical->getUVs();
             Vector4 verticalColor = _scrollBarVertical->getColor();
             Vector4 verticalColor = _scrollBarVertical->getColor();
+            verticalColor.w *= _scrollBarOpacity * _opacity;
 
 
             const Rectangle& bottomRegion = _scrollBarBottomCap->getRegion();
             const Rectangle& bottomRegion = _scrollBarBottomCap->getRegion();
             const Theme::UVs& bottomUVs = _scrollBarBottomCap->getUVs();
             const Theme::UVs& bottomUVs = _scrollBarBottomCap->getUVs();
             Vector4 bottomColor = _scrollBarBottomCap->getColor();
             Vector4 bottomColor = _scrollBarBottomCap->getColor();
+            bottomColor.w *= _scrollBarOpacity * _opacity;
 
 
             clipRegion.width += verticalRegion.width;
             clipRegion.width += verticalRegion.width;
 
 
@@ -388,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);
             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) && _scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap)
+        if (_scrollBarBounds.width > 0)
         {
         {
             const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
             const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
             const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
             const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
             Vector4 leftColor = _scrollBarLeftCap->getColor();
             Vector4 leftColor = _scrollBarLeftCap->getColor();
+            leftColor.w *= _scrollBarOpacity * _opacity;
 
 
             const Rectangle& horizontalRegion = _scrollBarHorizontal->getRegion();
             const Rectangle& horizontalRegion = _scrollBarHorizontal->getRegion();
             const Theme::UVs& horizontalUVs = _scrollBarHorizontal->getUVs();
             const Theme::UVs& horizontalUVs = _scrollBarHorizontal->getUVs();
             Vector4 horizontalColor = _scrollBarHorizontal->getColor();
             Vector4 horizontalColor = _scrollBarHorizontal->getColor();
+            horizontalColor.w *= _scrollBarOpacity * _opacity;
 
 
             const Rectangle& rightRegion = _scrollBarRightCap->getRegion();
             const Rectangle& rightRegion = _scrollBarRightCap->getRegion();
             const Theme::UVs& rightUVs = _scrollBarRightCap->getUVs();
             const Theme::UVs& rightUVs = _scrollBarRightCap->getUVs();
             Vector4 rightColor = _scrollBarRightCap->getColor();
             Vector4 rightColor = _scrollBarRightCap->getColor();
+            rightColor.w *= _scrollBarOpacity * _opacity;
 
 
             clipRegion.height += horizontalRegion.height;
             clipRegion.height += horizontalRegion.height;
         
         
@@ -420,7 +464,7 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
 
 
         spriteBatch->end();
         spriteBatch->end();
 
 
-        if (_velocity.isZero())
+        if (_scrollingVelocity.isZero())
         {
         {
             _dirty = false;
             _dirty = false;
         }
         }
@@ -518,9 +562,8 @@ bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int con
         break;
         break;
     }
     }
 
 
-    if (!eventConsumed)
+    if (!eventConsumed && _scroll != SCROLL_NONE)
     {
     {
-        // Pass the event on to the layout.
         if (touchEventScroll(evt, x - xPos, y - yPos, contactIndex))
         if (touchEventScroll(evt, x - xPos, y - yPos, contactIndex))
         {
         {
             _dirty = true;
             _dirty = true;
@@ -586,24 +629,21 @@ Layout::Type Container::getLayoutType(const char* layoutString)
     }
     }
 }
 }
 
 
-void Container::updateScroll(const Container* container)
+void Container::updateScroll()
 {
 {
-    GP_ASSERT(container);
-
     // Update Time.
     // Update Time.
     static long lastFrameTime = Game::getGameTime();
     static long lastFrameTime = Game::getGameTime();
     long frameTime = Game::getGameTime();
     long frameTime = Game::getGameTime();
     long elapsedTime = (frameTime - lastFrameTime);
     long elapsedTime = (frameTime - lastFrameTime);
     lastFrameTime = frameTime;
     lastFrameTime = frameTime;
 
 
-    const Rectangle& containerBounds = container->getBounds();
-    const Theme::Border& containerBorder = container->getBorder(container->getState());
-    const Theme::Padding& containerPadding = container->getPadding();
+    const Theme::Border& containerBorder = getBorder(_state);
+    const Theme::Padding& containerPadding = getPadding();
 
 
     // Calculate total width and height.
     // Calculate total width and height.
     float totalWidth = 0;
     float totalWidth = 0;
     float totalHeight = 0;
     float totalHeight = 0;
-    std::vector<Control*> controls = container->getControls();
+    std::vector<Control*> controls = getControls();
     unsigned int controlsCount = controls.size();
     unsigned int controlsCount = controls.size();
     for (unsigned int i = 0; i < controlsCount; i++)
     for (unsigned int i = 0; i < controlsCount; i++)
     {
     {
@@ -625,53 +665,53 @@ void Container::updateScroll(const Container* container)
         }
         }
     }
     }
 
 
-    float vWidth = container->getImageRegion("verticalScrollBar", container->getState()).width;
-    float hHeight = container->getImageRegion("horizontalScrollBar", container->getState()).height;
-    float clipWidth = containerBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
-    float clipHeight = containerBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
+    float vWidth = getImageRegion("verticalScrollBar", _state).width;
+    float hHeight = getImageRegion("horizontalScrollBar", _state).height;
+    float clipWidth = _bounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
+    float clipHeight = _bounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
 
 
     // Apply and dampen inertia.
     // Apply and dampen inertia.
-    if (!_scrolling && !_velocity.isZero())
+    if (!_scrolling && !_scrollingVelocity.isZero())
     {
     {
         // Calculate the time passed since last update.
         // Calculate the time passed since last update.
         float elapsedSecs = (float)elapsedTime * 0.001f;
         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.
     // Stop scrolling when the far edge is reached.
     if (-_scrollPosition.x > totalWidth - clipWidth)
     if (-_scrollPosition.x > totalWidth - clipWidth)
     {
     {
         _scrollPosition.x = -(totalWidth - clipWidth);
         _scrollPosition.x = -(totalWidth - clipWidth);
-        _velocity.x = 0;
+        _scrollingVelocity.x = 0;
     }
     }
     
     
     if (-_scrollPosition.y > totalHeight - clipHeight)
     if (-_scrollPosition.y > totalHeight - clipHeight)
     {
     {
         _scrollPosition.y = -(totalHeight - clipHeight);
         _scrollPosition.y = -(totalHeight - clipHeight);
-        _velocity.y = 0;
+        _scrollingVelocity.y = 0;
     }
     }
 
 
     if (_scrollPosition.x > 0)
     if (_scrollPosition.x > 0)
     {
     {
         _scrollPosition.x = 0;
         _scrollPosition.x = 0;
-        _velocity.x = 0;
+        _scrollingVelocity.x = 0;
     }
     }
 
 
     if (_scrollPosition.y > 0)
     if (_scrollPosition.y > 0)
     {
     {
         _scrollPosition.y = 0;
         _scrollPosition.y = 0;
-        _velocity.y = 0;
+        _scrollingVelocity.y = 0;
     }
     }
 
 
     float scrollWidth = 0;
     float scrollWidth = 0;
@@ -686,12 +726,18 @@ void Container::updateScroll(const Container* container)
                          ((-_scrollPosition.y) / totalHeight) * clipHeight,
                          ((-_scrollPosition.y) / totalHeight) * clipHeight,
                          scrollWidth, scrollHeight);
                          scrollWidth, scrollHeight);
 
 
-    // Position controls within scroll area.
-    for (unsigned int i = 0; i < controlsCount; i++)
+    // If scroll velocity is 0 and scrollbars are not always visible, trigger fade-out animation.
+    if (!_scrolling && _scrollingVelocity.isZero() && _scrollBarsAutoHide && _scrollBarOpacity == 1.0f)
     {
     {
-        Control* control = controls.at(i);
-        control->update(container->getClip(), _scrollPosition);
+        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);
 }
 }
 
 
 bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
@@ -699,69 +745,76 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
     switch(evt)
     switch(evt)
     {
     {
     case Touch::TOUCH_PRESS:
     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;
         _scrolling = true;
-        _startTimeX = _startTimeY = 0;
-        break;
+        _scrollingStartTimeX = _scrollingStartTimeY = 0;
+        
+        if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
+        {
+            _scrollBarOpacityClip->stop();
+            _scrollBarOpacityClip = NULL;
+        }
+        _scrollBarOpacity = 1.0f;
+        return true;
 
 
     case Touch::TOUCH_MOVE:
     case Touch::TOUCH_MOVE:
         if (_scrolling)
         if (_scrolling)
         {
         {
             // Calculate the latest movement delta for the next update to use.
             // 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.x += vx;
             _scrollPosition.y += vy;
             _scrollPosition.y += vy;
-            _lastX = x;
-            _lastY = y;
+            _scrollingLastX = x;
+            _scrollingLastY = y;
 
 
             // If the user changes direction, reset the start time and position.
             // If the user changes direction, reset the start time and position.
             bool goingRight = (vx > 0);
             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);
             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;
             return true;
         }
         }
         break;
         break;
 
 
     case Touch::TOUCH_RELEASE:
     case Touch::TOUCH_RELEASE:
-        long timeSinceLastMove = Game::getAbsoluteTime() - _lastTime;
-        if (timeSinceLastMove > STOP_TIME)
+        _scrolling = false;
+        long timeSinceLastMove = Game::getAbsoluteTime() - _scrollingLastTime;
+        if (timeSinceLastMove > SCROLL_INERTIA_DELAY)
         {
         {
-            _velocity.set(0, 0);
-            _scrolling = false;
-            break;
+            _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;
         float elapsedSecsX = (float)timeTakenX * 0.001f;
-        long timeTakenY = Game::getAbsoluteTime() - _startTimeY;
+        long timeTakenY = Game::getAbsoluteTime() - _scrollingStartTimeY;
         float elapsedSecsY = (float)timeTakenY * 0.001f;
         float elapsedSecsY = (float)timeTakenY * 0.001f;
 
 
         float vx = dx;
         float vx = dx;
@@ -771,10 +824,9 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
         if (elapsedSecsY > 0)
         if (elapsedSecsY > 0)
             vy = (float)dy / elapsedSecsY;
             vy = (float)dy / elapsedSecsY;
 
 
-        _velocity.set(vx, vy);
+        _scrollingVelocity.set(vx, vy);
 
 
-        _scrolling = false;
-        break;
+        return true;
     }
     }
 
 
     return false;
     return false;
@@ -817,4 +869,46 @@ bool sortControlsByZOrder(Control* c1, Control* c2)
     return false;
     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;
+    }
+}
+
 }
 }

+ 92 - 42
gameplay/src/Container.h

@@ -25,7 +25,8 @@ namespace gameplay
          size        = <width, height>   // Size of the container, measured in pixels.
          size        = <width, height>   // Size of the container, measured in pixels.
          width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
          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'
          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.
          // All the nested controls within this container.
          container 
          container 
@@ -46,6 +47,11 @@ class Container : public Control
 {
 {
 public:
 public:
 
 
+    /**
+     * Constant used to auto-hide scrollbars.
+     */
+    static const int ANIMATE_SCROLLBAR_OPACITY = 8;
+
     /**
     /**
      * The definition for container scrolling.
      * The definition for container scrolling.
      */
      */
@@ -127,22 +133,53 @@ public:
     const std::vector<Control*>& getControls() const;
     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);
     void setScroll(Scroll scroll);
 
 
+    /**
+     * Gets the allowed scroll directions for this container.
+     *
+     * @return The allowed scroll directions for this container.
+     */
     Scroll getScroll() const;
     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;
     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:
 protected:
 
 
     /**
     /**
@@ -177,9 +214,9 @@ protected:
      * Updates each control within this container,
      * Updates each control within this container,
      * and positions them according to the container's layout.
      * and positions them according to the container's layout.
      *
      *
-     * @param clip The clipping rectangle of this container's parent container.
+     * @param container This container's parent container.
      */
      */
-    virtual void update(const Rectangle& clip, const Vector2& offset);
+    virtual void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Touch callback on touch events.  Controls return true if they consume the touch event.
      * Touch callback on touch events.  Controls return true if they consume the touch event.
@@ -229,15 +266,32 @@ protected:
      */
      */
     void addControls(Theme* theme, Properties* properties);
     void addControls(Theme* theme, Properties* properties);
 
 
-    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
+    /**
+     * Draws a sprite batch for the specified clipping rect 
+     */
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight);
 
 
     /**
     /**
      * Update scroll position and velocity.
      * Update scroll position and velocity.
      */
      */
-    void updateScroll(const Container* container);
+    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);
     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);
     static Scroll getScroll(const char* scroll);
 
 
     /**
     /**
@@ -250,6 +304,7 @@ protected:
      */
      */
     std::vector<Control*> _controls;
     std::vector<Control*> _controls;
 
 
+    // Images used to draw scrollbars.
     Theme::ThemeImage* _scrollBarTopCap;
     Theme::ThemeImage* _scrollBarTopCap;
     Theme::ThemeImage* _scrollBarVertical;
     Theme::ThemeImage* _scrollBarVertical;
     Theme::ThemeImage* _scrollBarBottomCap;
     Theme::ThemeImage* _scrollBarBottomCap;
@@ -259,49 +314,44 @@ protected:
 
 
     // Flag representing whether scrolling is enabled, and in which directions.
     // Flag representing whether scrolling is enabled, and in which directions.
     Scroll _scroll;
     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;
     Rectangle _scrollBarBounds;
-
     // How far this layout has been scrolled in each direction.
     // How far this layout has been scrolled in each direction.
     Vector2 _scrollPosition;
     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;
     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.
     // Speed to continue scrolling at after touch release.
-    Vector2 _velocity;
-
+    Vector2 _scrollingVelocity;
     // Friction dampens velocity.
     // 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:
 private:
 
 
     Container(const Container& copy);
     Container(const Container& copy);
 
 
+    AnimationClip* _scrollBarOpacityClip;
     int _zIndexDefault;
     int _zIndexDefault;
 };
 };
 
 

+ 11 - 5
gameplay/src/Control.cpp

@@ -731,8 +731,11 @@ void Control::notifyListeners(Listener::EventType eventType)
     }
     }
 }
 }
 
 
-void Control::update(const Rectangle& clip, const Vector2& offset)
+void Control::update(const Control* container, const Vector2& offset)
 {
 {
+    const Rectangle& clip = container->getClip();
+    const Rectangle& absoluteViewport = container->_viewportBounds;
+
     _clearBounds.set(_absoluteClipBounds);
     _clearBounds.set(_absoluteClipBounds);
 
 
     // Calculate the clipped bounds.
     // Calculate the clipped bounds.
@@ -774,8 +777,8 @@ void Control::update(const Rectangle& clip, const Vector2& offset)
     _clipBounds.set(x, y, width, height);
     _clipBounds.set(x, y, width, height);
 
 
     // Calculate the absolute bounds.
     // Calculate the absolute bounds.
-    x = _bounds.x + offset.x + clip.x;
-    y = _bounds.y + offset.y + clip.y;
+    x = _bounds.x + offset.x + absoluteViewport.x;
+    y = _bounds.y + offset.y + absoluteViewport.y;
     _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
     _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
 
 
     // Calculate the absolute viewport bounds.
     // Calculate the absolute viewport bounds.
@@ -899,7 +902,7 @@ void Control::drawText(const Rectangle& position)
 {
 {
 }
 }
 
 
-void Control::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight)
+void Control::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight)
 {
 {
     if (needsClear)
     if (needsClear)
     {
     {
@@ -961,8 +964,11 @@ Theme::ThemeImage* Control::getImage(const char* id, State state)
 {
 {
     Theme::Style::Overlay* overlay = getOverlay(state);
     Theme::Style::Overlay* overlay = getOverlay(state);
     GP_ASSERT(overlay);
     GP_ASSERT(overlay);
+    
     Theme::ImageList* imageList = overlay->getImageList();
     Theme::ImageList* imageList = overlay->getImageList();
-    GP_ASSERT(imageList);
+    if (!imageList)
+        return NULL;
+
     return imageList->getImage(id);
     return imageList->getImage(id);
 }
 }
 
 

+ 18 - 9
gameplay/src/Control.h

@@ -24,7 +24,6 @@ class Control : public Ref, public AnimationTarget
     friend class AbsoluteLayout;
     friend class AbsoluteLayout;
     friend class VerticalLayout;
     friend class VerticalLayout;
     friend class FlowLayout;
     friend class FlowLayout;
-    friend class ScrollLayout;
 
 
 public:
 public:
 
 
@@ -659,9 +658,19 @@ public:
      */
      */
     Theme::Style* getStyle() const;
     Theme::Style* getStyle() const;
 
 
+    /**
+     * Get this control's z-index.
+     *
+     * @return This control's z-index.
+     */
     int getZIndex() const;
     int getZIndex() const;
 
 
-    void setZIndex(int zOrder);
+    /**
+     * Set this control's z-index.
+     *
+     * @param zIndex The new z-index.
+     */
+    void setZIndex(int zIndex);
 
 
     /**
     /**
      * Add a listener to be notified of specific events affecting
      * Add a listener to be notified of specific events affecting
@@ -678,17 +687,17 @@ public:
     /**
     /**
      * @see AnimationTarget#getAnimationPropertyComponentCount
      * @see AnimationTarget#getAnimationPropertyComponentCount
      */
      */
-    unsigned int getAnimationPropertyComponentCount(int propertyId) const;
+    virtual unsigned int getAnimationPropertyComponentCount(int propertyId) const;
 
 
     /**
     /**
      * @see AnimationTarget#getAnimationProperty
      * @see AnimationTarget#getAnimationProperty
      */
      */
-    void getAnimationPropertyValue(int propertyId, AnimationValue* value);
+    virtual void getAnimationPropertyValue(int propertyId, AnimationValue* value);
 
 
     /**
     /**
      * @see AnimationTarget#setAnimationProperty
      * @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:
 protected:
 
 
@@ -739,10 +748,10 @@ protected:
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this control's parent container.
-     * @param offset Layout-computed positioning offset to add to the control's position.
+     * @param container This control's parent container.
+     * @param offset Positioning offset to add to the control's position.
      */
      */
-    virtual void update(const Rectangle& clip, const Vector2& offset);
+    virtual void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.
@@ -761,7 +770,7 @@ protected:
      */
      */
     virtual void drawText(const Rectangle& clip);
     virtual void drawText(const Rectangle& clip);
 
 
-    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight);
 
 
     /**
     /**
      * Initialize properties common to STATE_ALL Controls.
      * Initialize properties common to STATE_ALL Controls.

+ 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)
 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].time = time;
     _points[index].type = type;
     _points[index].type = type;

+ 3 - 2
gameplay/src/FileSystem.cpp

@@ -308,7 +308,8 @@ void createFileFromAsset(const char* path)
 
 
     GP_ASSERT(path);
     GP_ASSERT(path);
     std::string fullPath(__resourcePath);
     std::string fullPath(__resourcePath);
-    fullPath += FileSystem::resolvePath(path);
+    std::string resolvedPath = FileSystem::resolvePath(path);
+    fullPath += resolvedPath;
 
 
     std::string directoryPath = fullPath.substr(0, fullPath.rfind('/'));
     std::string directoryPath = fullPath.substr(0, fullPath.rfind('/'));
     struct stat s;
     struct stat s;
@@ -320,7 +321,7 @@ void createFileFromAsset(const char* path)
     // for each time the process (game) runs.
     // for each time the process (game) runs.
     if (upToDateAssets.find(fullPath) == upToDateAssets.end())
     if (upToDateAssets.find(fullPath) == upToDateAssets.end())
     {
     {
-        AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
+        AAsset* asset = AAssetManager_open(__assetManager, resolvedPath.c_str(), AASSET_MODE_RANDOM);
         if (asset)
         if (asset)
         {
         {
             const void* data = AAsset_getBuffer(asset);
             const void* data = AAsset_getBuffer(asset);

+ 2 - 5
gameplay/src/FlowLayout.cpp

@@ -39,7 +39,7 @@ Layout::Type FlowLayout::getType()
     return Layout::LAYOUT_FLOW;
     return Layout::LAYOUT_FLOW;
 }
 }
 
 
-void FlowLayout::update(const Container* container)
+void FlowLayout::update(const Container* container, const Vector2& offset)
 {
 {
     GP_ASSERT(container);
     GP_ASSERT(container);
     const Rectangle& containerBounds = container->getBounds();
     const Rectangle& containerBounds = container->getBounds();
@@ -78,10 +78,7 @@ void FlowLayout::update(const Container* container)
         yPosition = rowY + margin.top;
         yPosition = rowY + margin.top;
 
 
         control->setPosition(xPosition, yPosition);
         control->setPosition(xPosition, yPosition);
-        if (control->isDirty() || control->isContainer())
-        {
-            control->update(container->getClip(), Vector2::zero());
-        }
+        control->update(container, offset);
 
 
         xPosition += bounds.width + margin.right;
         xPosition += bounds.width + margin.right;
 
 

+ 1 - 1
gameplay/src/FlowLayout.h

@@ -34,7 +34,7 @@ protected:
      *
      *
      * @param container The container to update.
      * @param container The container to update.
      */
      */
-    void update(const Container* container);
+    void update(const Container* container, const Vector2& offset);
 
 
 private:
 private:
 
 

+ 1 - 2
gameplay/src/Font.cpp

@@ -187,7 +187,6 @@ Font::Text* Font::createText(const char* text, const Rectangle& area, const Vect
     bool wrap, bool rightToLeft, const Rectangle* clip)
     bool wrap, bool rightToLeft, const Rectangle* clip)
 {
 {
     GP_ASSERT(text);
     GP_ASSERT(text);
-    GP_ASSERT(clip);
     GP_ASSERT(_glyphs);
     GP_ASSERT(_glyphs);
     GP_ASSERT(_batch);
     GP_ASSERT(_batch);
 
 
@@ -628,7 +627,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         }
         }
 
 
         bool draw = true;
         bool draw = true;
-        if (yPos < area.y)
+        if (yPos < area.y - size)
         {
         {
             // Skip drawing until line break or wrap.
             // Skip drawing until line break or wrap.
             draw = false;
             draw = false;

+ 23 - 5
gameplay/src/Form.cpp

@@ -90,8 +90,19 @@ Form* Form::create(const char* url)
     Game* game = Game::getInstance();
     Game* game = Game::getInstance();
     Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
     Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
 
 
+    Theme::Style* style = NULL;
     const char* styleName = formProperties->getString("style");
     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
     // Alignment
     if ((form->_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
     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->_scroll = getScroll(formProperties->getString("scroll"));
+    form->_scrollBarsAutoHide = formProperties->getBool("scrollBarsAutoHide");
+    if (form->_scrollBarsAutoHide)
+    {
+        form->_scrollBarOpacity = 0.0f;
+    }
 
 
     // Add all the controls to the form.
     // Add all the controls to the form.
     form->addControls(theme, formProperties);
     form->addControls(theme, formProperties);
 
 
+    form->update();
+
     SAFE_DELETE(properties);
     SAFE_DELETE(properties);
 
 
     __forms.push_back(form);
     __forms.push_back(form);
@@ -417,10 +435,10 @@ void Form::update()
         }
         }
 
 
         GP_ASSERT(_layout);
         GP_ASSERT(_layout);
-        _layout->update(this);
-
         if (_scroll != SCROLL_NONE)
         if (_scroll != SCROLL_NONE)
-            this->updateScroll(this);
+            updateScroll();
+        else
+            _layout->update(this, Vector2::zero());
     }
     }
 }
 }
 
 
@@ -451,7 +469,7 @@ void Form::draw()
 
 
         GP_ASSERT(_theme);
         GP_ASSERT(_theme);
         _theme->setProjectionMatrix(_projectionMatrix);
         _theme->setProjectionMatrix(_projectionMatrix);
-        Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height), _skin == NULL, _bounds.height);
+        Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height), _skin != NULL, false, _bounds.height);
         _theme->setProjectionMatrix(_defaultProjectionMatrix);
         _theme->setProjectionMatrix(_defaultProjectionMatrix);
 
 
         // Rebind the default framebuffer and game viewport.
         // Rebind the default framebuffer and game viewport.

+ 9 - 1
gameplay/src/FrameBuffer.cpp

@@ -10,6 +10,7 @@ namespace gameplay
 
 
 static unsigned int __maxRenderTargets = 0;
 static unsigned int __maxRenderTargets = 0;
 static std::vector<FrameBuffer*> __frameBuffers;
 static std::vector<FrameBuffer*> __frameBuffers;
+static FrameBufferHandle __defaultHandle = 0;
 
 
 FrameBuffer::FrameBuffer(const char* id) :
 FrameBuffer::FrameBuffer(const char* id) :
     _id(id ? id : ""), _handle(0), _renderTargets(NULL), _depthStencilTarget(NULL)
     _id(id ? id : ""), _handle(0), _renderTargets(NULL), _depthStencilTarget(NULL)
@@ -41,6 +42,13 @@ FrameBuffer::~FrameBuffer()
     }
     }
 }
 }
 
 
+void FrameBuffer::initialize()
+{
+    GLint fbo;
+    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
+    __defaultHandle = (FrameBufferHandle)fbo;
+}
+
 FrameBuffer* FrameBuffer::create(const char* id)
 FrameBuffer* FrameBuffer::create(const char* id)
 {
 {
     // Create GL FBO resource.
     // Create GL FBO resource.
@@ -230,7 +238,7 @@ void FrameBuffer::bind()
 
 
 void FrameBuffer::bindDefault()
 void FrameBuffer::bindDefault()
 {
 {
-    GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, 0) );
+    GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, __defaultHandle) );
 }
 }
 
 
 }
 }

+ 4 - 0
gameplay/src/FrameBuffer.h

@@ -19,6 +19,8 @@ namespace gameplay
  */
  */
 class FrameBuffer : public Ref
 class FrameBuffer : public Ref
 {
 {
+    friend class Game;
+
 public:
 public:
 
 
     /**
     /**
@@ -118,6 +120,8 @@ private:
      */
      */
     ~FrameBuffer();
     ~FrameBuffer();
 
 
+    static void initialize();
+
     std::string _id;
     std::string _id;
     FrameBufferHandle _handle;
     FrameBufferHandle _handle;
     RenderTarget** _renderTargets;
     RenderTarget** _renderTargets;

+ 2 - 1
gameplay/src/Game.cpp

@@ -3,6 +3,7 @@
 #include "Platform.h"
 #include "Platform.h"
 #include "RenderState.h"
 #include "RenderState.h"
 #include "FileSystem.h"
 #include "FileSystem.h"
+#include "FrameBuffer.h"
 
 
 // Extern global variables
 // Extern global variables
 GLenum __gl_error_code = GL_NO_ERROR;
 GLenum __gl_error_code = GL_NO_ERROR;
@@ -92,8 +93,8 @@ bool Game::startup()
         return false;
         return false;
 
 
     setViewport(Rectangle(0.0f, 0.0f, (float)_width, (float)_height));
     setViewport(Rectangle(0.0f, 0.0f, (float)_width, (float)_height));
-
     RenderState::initialize();
     RenderState::initialize();
+    FrameBuffer::initialize();
 
 
     _animationController = new AnimationController();
     _animationController = new AnimationController();
     _animationController->initialize();
     _animationController->initialize();

+ 219 - 0
gameplay/src/Joystick.cpp

@@ -0,0 +1,219 @@
+#include "Base.h"
+#include "Joystick.h"
+
+#define INVALID_CONTACT_INDEX ((unsigned int)-1)
+
+namespace gameplay
+{
+
+Joystick::Joystick() : _contactIndex(INVALID_CONTACT_INDEX), _absolute(true)
+{
+}
+
+Joystick::Joystick(const Joystick& copy)
+{
+}
+
+Joystick::~Joystick()
+{
+}
+
+Joystick* Joystick::create(Theme::Style* style, Properties* properties)
+{
+    Joystick* joystick = new Joystick();
+    joystick->initialize(style, properties);
+    joystick->_consumeTouchEvents = false;
+
+    return joystick;
+}
+
+void Joystick::initialize(Theme::Style* style, Properties* properties)
+{
+    GP_ASSERT(properties);
+
+    Control::initialize(style, properties);
+
+    if (!properties->exists("radius"))
+    {
+        GP_ERROR("Failed to load joystick; required attribute 'radius' is missing.");
+        return;
+    }
+    _radius = properties->getFloat("radius");
+
+    Vector4 v;
+    if (properties->getVector4("region", &v))
+    {
+        _absolute = false;
+        _region = _bounds;
+        _bounds.x = v.x;
+        _bounds.y = v.y;
+        _bounds.width = v.z;
+        _bounds.height = v.w;
+    }
+}
+
+void Joystick::addListener(Control::Listener* listener, int eventFlags)
+{
+    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    {
+        GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
+    }
+
+    _consumeTouchEvents = true;
+
+    Control::addListener(listener, eventFlags);
+}
+
+bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned int contactIndex)
+{
+    switch (touchEvent)
+    {
+        case Touch::TOUCH_PRESS:
+        {
+            float dx = 0.0f;
+            float dy = 0.0f;
+
+            if (_absolute)
+            {
+                dx = x - _bounds.width * 0.5f;
+                dy = _bounds.height * 0.5f - y;
+            }
+            else
+            {
+                _region.x = x + _bounds.x - _region.width * 0.5f;
+                _region.y = y + _bounds.y - _region.height * 0.5f;
+            }
+
+            if ((dx >= -_radius && dx <= _radius) && (dy >= -_radius && dy <= _radius) && 
+                _contactIndex == INVALID_CONTACT_INDEX)
+            {
+                _contactIndex = contactIndex;
+                _displacement.set(0.0f, 0.0f);
+                
+                Vector2 value(0.0f, 0.0f);
+                if (_value != value)
+                {
+                    _value.set(value);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
+                }
+
+                _state = ACTIVE;
+            }
+        }
+        case Touch::TOUCH_MOVE:
+        {
+            if (_contactIndex == contactIndex)
+            {
+                float dx = x - ((!_absolute) ? _region.x - _bounds.x : 0.0f) - _region.width * 0.5f;
+                float dy = -(y - ((!_absolute) ? _region.y - _bounds.y : 0.0f) - _region.height * 0.5f);
+                if (((dx * dx) + (dy * dy)) <= (_radius * _radius))
+                {
+                    GP_ASSERT(_radius);
+                    Vector2 value(dx, dy);
+                    value.scale(1.0f / _radius);
+                    if (_value != value)
+                    {
+                        _value.set(value);
+                        notifyListeners(Control::Listener::VALUE_CHANGED);
+                    }
+                }
+                else
+                {
+                    Vector2 value(dx, dy);
+                    value.normalize();
+                    value.scale(_radius);
+                    value.normalize();
+                    if (_value != value)
+                    {
+                        _value.set(value);
+                        notifyListeners(Control::Listener::VALUE_CHANGED);
+                    }
+                }
+
+                _displacement.set(dx, dy);
+            }
+        }
+        break;
+        case Touch::TOUCH_RELEASE:
+        {
+            if (_contactIndex == contactIndex)
+            {
+                // Reset displacement and direction vectors.
+                _contactIndex = INVALID_CONTACT_INDEX;
+                _displacement.set(0.0f, 0.0f);
+                
+                Vector2 value(0.0f, 0.0f);
+                if (_value != value)
+                {
+                    _value.set(value);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
+                }
+
+                _state = NORMAL;
+            }
+        }
+        break;
+    }
+
+    return Control::touchEvent(touchEvent, x, y, contactIndex);
+}
+
+void Joystick::update(const Control* container, const Vector2& offset)
+{
+    Control::update(container, offset);
+}
+
+void Joystick::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
+{
+    GP_ASSERT(spriteBatch);
+    spriteBatch->begin();
+
+    // If the joystick is not absolute, then only draw if it is active.
+    if (_absolute || (!_absolute && _state == ACTIVE))
+    {
+        if (_absolute)
+            _region = _bounds;
+
+        // Draw the outer image.
+        Theme::ThemeImage* outer = getImage("outer", _state);
+        if (outer)
+        {
+            // Get the uvs and color and draw.
+            const Theme::UVs& uvs = outer->getUVs();
+            const Vector4& color = outer->getColor();
+            spriteBatch->draw(_region.x, _region.y, _region.width, _region.height, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
+        }
+
+        // Draw the inner image.
+        Theme::ThemeImage* inner = getImage("inner", _state);
+        if (inner)
+        {
+            Rectangle region = _region;
+
+            // Adjust position to reflect displacement.
+            if (((_displacement.x * _displacement.x) + (_displacement.y * _displacement.y)) <= (_radius * _radius))
+            {
+                region.x += _displacement.x;
+                region.y += -_displacement.y;
+            }
+            else
+            {
+                // Find the point on the joystick's circular bound where the
+                // vector intersects. This is the position of the inner image.
+                Vector2 delta = Vector2(_displacement.x, -_displacement.y);
+                delta.normalize();
+                delta.scale(_radius);
+                region.x += delta.x;
+                region.y += delta.y;
+            }
+        
+            // Get the uvs and color and draw.
+            const Theme::UVs& uvs = inner->getUVs();
+            const Vector4& color = inner->getColor();
+            spriteBatch->draw(region.x, region.y, _region.width, _region.height, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
+        }
+    }
+    spriteBatch->end();
+}
+
+}

+ 147 - 0
gameplay/src/Joystick.h

@@ -0,0 +1,147 @@
+#ifndef JOYSTICK_H_
+#define JOYSTICK_H_
+
+#include "Control.h"
+
+namespace gameplay
+{
+
+class Joystick : public Control
+{
+    friend class Container;
+
+public:
+    
+    /**
+     * Add a listener to be notified of specific events affecting
+     * this control.  Event types can be OR'ed together.
+     * E.g. To listen to touch-press and touch-release events,
+     * pass <code>Control::Listener::TOUCH | Control::Listener::RELEASE</code>
+     * as the second parameter.
+     *
+     * @param listener The listener to add.
+     * @param eventFlags The events to listen for.
+     */
+    void addListener(Control::Listener* listener, int eventFlags);
+
+    /**
+     * Retrieves the value (2-dimensional direction) of the joystick.
+     * 
+     * @return The value of the joystick.
+     */
+    inline const Vector2& getValue() const;
+
+    /**
+     * Sets the region within which the joystick will be spontaneously created on a user's touch.
+     * 
+     * Note: This does not actually enable spontaneous joystick creation on touch input.
+     * To enable (or disable) absolute position explicitly, use #setAbsolute.
+     * 
+     * @param region The region to use.
+     */
+    inline void setRegion(const Rectangle& region);
+
+    /**
+     * Gets the region within which the joystick will be spontaneously created on a user's touch.
+     * 
+     * Note: just because the returned region is not empty does not mean that it is necessarily
+     * being used. If absolute positioning is not enabled, then it will be used (to check if
+     * absolute positioning is enabled, call #isAbsolute).
+     * 
+     * @return The region within which the joystick will be spontaneously created on a user's touch.
+     */
+    inline const Rectangle& getRegion() const;
+
+    /**
+     * Sets whether absolute positioning is enabled or not.
+     * 
+     * @param absolute Whether absolute positioning should be enabled or not.
+     */
+    inline void setAbsolute(bool absolute);
+
+    /**
+     * Retrieves whether absolute positioning is enabled or not.
+     * 
+     * @return <code>true</code> if absolute positioning is enabled; <code>false</code> otherwise.
+     */
+    inline bool isAbsolute() const;
+
+protected:
+    
+    /**
+     * Constructor.
+     */
+    Joystick();
+
+    /**
+     * Destructor.
+     */
+    virtual ~Joystick();
+
+    /**
+     * Create a joystick with a given style and properties.
+     *
+     * @param style The style to apply to this joystick.
+     * @param properties The properties to set on this joystick.
+     *
+     * @return The new joystick.
+     */
+    static Joystick* create(Theme::Style* style, Properties* properties);
+
+    /**
+     * Initialize this joystick.
+     */
+    virtual void initialize(Theme::Style* style, Properties* properties);
+
+    /**
+     * Touch callback on touch events.  Controls return true if they consume the touch event.
+     *
+     * @param evt The touch event that occurred.
+     * @param x The x position of the touch in pixels. Left edge is zero.
+     * @param y The y position of the touch in pixels. Top edge is zero.
+     * @param contactIndex The order of occurrence for multiple touch contacts starting at zero.
+     *
+     * @return Whether the touch event was consumed by the control.
+     *
+     * @see Touch::TouchEvent
+     */
+    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+
+    /**
+     * Called when a control's properties change.  Updates this control's internal rendering
+     * properties, such as its text viewport.
+     *
+     * @param container This control's parent container.
+     * @param offset Positioning offset to add to the control's position.
+     */
+    void update(const Control* container, const Vector2& offset);
+
+    /**
+     * Draw the images associated with this control.
+     *
+     * @param spriteBatch The sprite batch containing this control's icons.
+     * @param clip The clipping rectangle of this control's parent container.
+     * @param offset Layout-computed positioning offset to add to the control's position.
+     */
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
+
+private:
+
+    /**
+     * Copy constructor.
+     */
+    Joystick(const Joystick& copy);
+
+    float _radius;
+    unsigned int _contactIndex;
+    bool _absolute;
+    Vector2 _displacement;
+    Vector2 _value;
+    Rectangle _region;
+};
+
+}
+
+#include "Joystick.inl"
+
+#endif

+ 34 - 0
gameplay/src/Joystick.inl

@@ -0,0 +1,34 @@
+#include "Joystick.h"
+
+namespace gameplay
+{
+
+inline const Vector2& Joystick::getValue() const
+{
+    return _value;
+}
+
+inline void Joystick::setRegion(const Rectangle& region)
+{
+    if (_region.isEmpty())
+        _region = _bounds;
+
+    _bounds = region;
+}
+
+inline const Rectangle& Joystick::getRegion() const
+{
+    return _bounds;
+}
+
+inline void Joystick::setAbsolute(bool absolute)
+{
+    _absolute = absolute;
+}
+
+inline bool Joystick::isAbsolute() const
+{
+    return _absolute;
+}
+
+}

+ 2 - 2
gameplay/src/Label.cpp

@@ -67,9 +67,9 @@ const char* Label::getText()
     return _text.c_str();
     return _text.c_str();
 }
 }
 
 
-void Label::update(const Rectangle& clip, const Vector2& offset)
+void Label::update(const Control* container, const Vector2& offset)
 {
 {
-    Control::update(clip, offset);
+    Control::update(container, offset);
 
 
     _textBounds.set(_viewportBounds);
     _textBounds.set(_viewportBounds);
 
 

+ 2 - 2
gameplay/src/Label.h

@@ -90,10 +90,10 @@ protected:
      * Called when a label's properties change. Updates this label's internal rendering
      * Called when a label's properties change. Updates this label's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this label's parent container.
+     * @param container This label's parent container.
      * @param offset The scroll offset of this label's parent container.
      * @param offset The scroll offset of this label's parent container.
      */
      */
-    void update(const Rectangle& clip, const Vector2& offset);
+    void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Draw this label's text.
      * Draw this label's text.

+ 2 - 1
gameplay/src/Layout.h

@@ -3,6 +3,7 @@
 
 
 #include "Ref.h"
 #include "Ref.h"
 #include "Touch.h"
 #include "Touch.h"
+#include "Vector2.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -68,7 +69,7 @@ protected:
      *
      *
      * @param container The container to update.
      * @param container The container to update.
      */
      */
-    virtual void update(const Container* container) = 0;
+    virtual void update(const Container* container, const Vector2& offset) = 0;
 
 
     /**
     /**
      * Align a control within a container.
      * Align a control within a container.

+ 49 - 0
gameplay/src/MathUtil.h

@@ -0,0 +1,49 @@
+#ifndef MATHUTIL_H_
+#define MATHUTIL_H_
+
+namespace gameplay
+{
+/**
+ * Math utility class. Used for internal math optimizations.
+ */
+class MathUtil
+{
+	friend class Matrix;
+	friend class Vector3;
+
+private:
+
+	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 transposeMatrix(const float* m, float* dst);
+
+	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

+ 177 - 0
gameplay/src/MathUtil.inl

@@ -0,0 +1,177 @@
+namespace gameplay
+{
+
+inline void MathUtil::addMatrix(const float* m, float scalar, float* dst)
+{
+	dst[0]  = m[0]  + scalar;
+	dst[1]  = m[1]  + scalar;
+	dst[2]  = m[2]  + scalar;
+	dst[3]  = m[3]  + scalar;
+	dst[4]  = m[4]  + scalar;
+	dst[5]  = m[5]  + scalar;
+	dst[6]  = m[6]  + scalar;
+	dst[7]  = m[7]  + scalar;
+	dst[8]  = m[8]  + scalar;
+	dst[9]  = m[9]  + scalar;
+	dst[10] = m[10] + scalar;
+	dst[11] = m[11] + scalar;
+	dst[12] = m[12] + scalar;
+	dst[13] = m[13] + scalar;
+	dst[14] = m[14] + scalar;
+	dst[15] = m[15] + scalar;
+}
+
+inline void MathUtil::addMatrix(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::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;
+	dst[1]  = m[1]  * scalar;
+	dst[2]  = m[2]  * scalar;
+	dst[3]  = m[3]  * scalar;
+	dst[4]  = m[4]  * scalar;
+	dst[5]  = m[5]  * scalar;
+	dst[6]  = m[6]  * scalar;
+	dst[7]  = m[7]  * scalar;
+	dst[8]  = m[8]  * scalar;
+	dst[9]  = m[9]  * scalar;
+	dst[10] = m[10] * scalar;
+	dst[11] = m[11] * scalar;
+	dst[12] = m[12] * scalar;
+	dst[13] = m[13] * scalar;
+	dst[14] = m[14] * scalar;
+	dst[15] = m[15] * scalar;
+}
+
+inline void MathUtil::multiplyMatrix(const float* m1, const float* m2, float* dst)
+{
+	// Support the case where m1 or m2 is the same array as dst.
+	float product[16];
+
+	product[0]  = m1[0] * m2[0]  + m1[4] * m2[1] + m1[8]   * m2[2]  + m1[12] * m2[3];
+	product[1]  = m1[1] * m2[0]  + m1[5] * m2[1] + m1[9]   * m2[2]  + m1[13] * m2[3];
+	product[2]  = m1[2] * m2[0]  + m1[6] * m2[1] + m1[10]  * m2[2]  + m1[14] * m2[3];
+	product[3]  = m1[3] * m2[0]  + m1[7] * m2[1] + m1[11]  * m2[2]  + m1[15] * m2[3];
+
+	product[4]  = m1[0] * m2[4]  + m1[4] * m2[5] + m1[8]   * m2[6]  + m1[12] * m2[7];
+	product[5]  = m1[1] * m2[4]  + m1[5] * m2[5] + m1[9]   * m2[6]  + m1[13] * m2[7];
+	product[6]  = m1[2] * m2[4]  + m1[6] * m2[5] + m1[10]  * m2[6]  + m1[14] * m2[7];
+	product[7]  = m1[3] * m2[4]  + m1[7] * m2[5] + m1[11]  * m2[6]  + m1[15] * m2[7];
+
+	product[8]  = m1[0] * m2[8]  + m1[4] * m2[9] + m1[8]   * m2[10] + m1[12] * m2[11];
+	product[9]  = m1[1] * m2[8]  + m1[5] * m2[9] + m1[9]   * m2[10] + m1[13] * m2[11];
+	product[10] = m1[2] * m2[8]  + m1[6] * m2[9] + m1[10]  * m2[10] + m1[14] * m2[11];
+	product[11] = m1[3] * m2[8]  + m1[7] * m2[9] + m1[11]  * m2[10] + m1[15] * m2[11];
+
+	product[12] = m1[0] * m2[12] + m1[4] * m2[13] + m1[8]  * m2[14] + m1[12] * m2[15];
+	product[13] = m1[1] * m2[12] + m1[5] * m2[13] + m1[9]  * m2[14] + m1[13] * m2[15];
+	product[14] = m1[2] * m2[12] + m1[6] * m2[13] + m1[10] * m2[14] + m1[14] * m2[15];
+	product[15] = m1[3] * m2[12] + m1[7] * m2[13] + m1[11] * m2[14] + m1[15] * m2[15];
+
+	memcpy(dst, product, MATRIX_SIZE);
+}
+
+inline void MathUtil::negateMatrix(const float* m, float* dst)
+{
+	dst[0]  = -m[0];
+	dst[1]  = -m[1];
+	dst[2]  = -m[2];
+	dst[3]  = -m[3];
+	dst[4]  = -m[4];
+	dst[5]  = -m[5];
+	dst[6]  = -m[6];
+	dst[7]  = -m[7];
+	dst[8]  = -m[8];
+	dst[9]  = -m[9];
+	dst[10] = -m[10];
+	dst[11] = -m[11];
+	dst[12] = -m[12];
+	dst[13] = -m[13];
+	dst[14] = -m[14];
+	dst[15] = -m[15];
+}
+
+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::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::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];
+	float y = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + v[3] * m[13];
+    float z = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + v[3] * m[14];
+    float w = v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + v[3] * m[15];
+
+    dst[0] = x;
+    dst[1] = y;
+	dst[2] = z;
+	dst[3] = w;
+}
+
+inline void MathUtil::crossVector3(const float* v1, const float* v2, float* dst)
+{
+	float x = (v1[1] * v2[2]) - (v1[2] * v2[1]);
+	float y = (v1[2] * v2[0]) - (v1[0] * v2[2]);
+	float z = (v1[0] * v2[1]) - (v1[1] * v2[0]);
+
+	dst[0] = x;
+	dst[1] = y;
+	dst[2] = z;
+}
+
+}
+
+

+ 229 - 0
gameplay/src/MathUtilNeon.inl

@@ -0,0 +1,229 @@
+namespace gameplay
+{
+
+inline void MathUtil::addMatrix(const float* m, float scalar, float* dst)
+{
+	asm volatile(
+		"vld1.32 {q0, q1}, [%1]! 	\n\t" // M[m0-m7]
+		"vld1.32 {q2, q3}, [%1] 	\n\t" // M[m8-m15]
+		"vld1.32 {d8[0]},  [%2] 	\n\t" // s
+		"vmov.f32 s17, s16          \n\t" // s
+		"vmov.f32 s18, s16          \n\t" // s
+		"vmov.f32 s19, s16          \n\t" // s
+
+		"vadd.f32 q8, q0, q4  		\n\t" // DST->M[m0-m3] = M[m0-m3] + s
+		"vadd.f32 q9, q1, q4 		\n\t" // DST->M[m4-m7] = M[m4-m7] + s
+		"vadd.f32 q10, q2, q4 		\n\t" // DST->M[m8-m11] = M[m8-m11] + s
+		"vadd.f32 q11, q3, q4 		\n\t" // DST->M[m12-m15] = M[m12-m15] + s
+
+		"vst1.32 {q8, q9}, [%0]!  	\n\t" // DST->M[m0-m7]
+		"vst1.32 {q10, q11}, [%0]   \n\t" // DST->M[m8-m15]
+		:
+		: "r"(dst), "r"(m), "r"(&scalar)
+		: "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11", "memory"
+	);
+}
+
+inline void MathUtil::addMatrix(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]
+
+		"vadd.f32   q12, q0, q8 		\n\t" // DST->M[m0-m3] = M1[m0-m3] + M2[m0-m3]
+		"vadd.f32   q13, q1, q9			\n\t" // DST->M[m4-m7] = M1[m4-m7] + M2[m4-m7]
+		"vadd.f32   q14, q2, q10		\n\t" // DST->M[m8-m11] = M1[m8-m11] + M2[m8-m11]
+		"vadd.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::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(
+		"vld1.32 	{d0[0]},	 	[%2]     	\n\t" // M[m0-m7]
+		"vld1.32	{q4-q5},  		[%1]!    	\n\t" // M[m8-m15]
+		"vld1.32	{q6-q7},  		[%1]		\n\t" // s
+
+		"vmul.f32 	q8, q4, d0[0]    			\n\t" // DST->M[m0-m3] = M[m0-m3] * s
+		"vmul.f32 	q9, q5, d0[0]    			\n\t" // DST->M[m4-m7] = M[m4-m7] * s
+		"vmul.f32 	q10, q6, d0[0]    			\n\t" // DST->M[m8-m11] = M[m8-m11] * s
+		"vmul.f32 	q11, q7, d0[0]   		 	\n\t" // DST->M[m12-m15] = M[m12-m15] * s
+
+		"vst1.32 	{q8-q9},   		[%0]! 		\n\t" // DST->M[m0-m7]
+		"vst1.32 	{q10-q11}, 		[%0]		\n\t" // DST->M[m8-m15]
+		:
+		: "r"(dst), "r"(m), "r"(&scalar)
+		: "q0", "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11", "memory"
+	);
+}
+
+inline void MathUtil::multiplyMatrix(const float* m1, const float* m2, float* dst)
+{
+	asm volatile(
+		"vld1.32	 {d16 - d19}, [%1]!	  \n\t"       // M1[m0-m7]
+		"vld1.32     {d20 - d23}, [%1]    \n\t"       // M1[m8-m15]
+		"vld1.32     {d0 - d3}, [%2]!     \n\t"       // M2[m0-m7]
+		"vld1.32     {d4 - d7}, [%2]      \n\t"       // M2[m8-m15]
+
+		"vmul.f32    q12, q8, d0[0]     \n\t"         // DST->M[m0-m3] = M1[m0-m3] * M2[m0]
+		"vmul.f32    q13, q8, d2[0]     \n\t"         // DST->M[m4-m7] = M1[m4-m7] * M2[m4]
+		"vmul.f32    q14, q8, d4[0]     \n\t"         // DST->M[m8-m11] = M1[m8-m11] * M2[m8]
+		"vmul.f32    q15, q8, d6[0]     \n\t"         // DST->M[m12-m15] = M1[m12-m15] * M2[m12]
+
+		"vmla.f32    q12, q9, d0[1]     \n\t"         // DST->M[m0-m3] += M1[m0-m3] * M2[m1]
+		"vmla.f32    q13, q9, d2[1]     \n\t"         // DST->M[m4-m7] += M1[m4-m7] * M2[m5]
+		"vmla.f32    q14, q9, d4[1]     \n\t"         // DST->M[m8-m11] += M1[m8-m11] * M2[m9]
+		"vmla.f32    q15, q9, d6[1]     \n\t"         // DST->M[m12-m15] += M1[m12-m15] * M2[m13]
+
+		"vmla.f32    q12, q10, d1[0]    \n\t"         // DST->M[m0-m3] += M1[m0-m3] * M2[m2]
+		"vmla.f32    q13, q10, d3[0]    \n\t"         // DST->M[m4-m7] += M1[m4-m7] * M2[m6]
+		"vmla.f32    q14, q10, d5[0]    \n\t"         // DST->M[m8-m11] += M1[m8-m11] * M2[m10]
+		"vmla.f32    q15, q10, d7[0]    \n\t"         // DST->M[m12-m15] += M1[m12-m15] * M2[m14]
+
+		"vmla.f32    q12, q11, d1[1]    \n\t"         // DST->M[m0-m3] += M1[m0-m3] * M2[m3]
+		"vmla.f32    q13, q11, d3[1]    \n\t"         // DST->M[m4-m7] += M1[m4-m7] * M2[m7]
+		"vmla.f32    q14, q11, d5[1]    \n\t"         // DST->M[m8-m11] += M1[m8-m11] * M2[m11]
+		"vmla.f32    q15, q11, d7[1]    \n\t"         // DST->M[m12-m15] += M1[m12-m15] * M2[m15]
+
+		"vst1.32    {d24 - d27}, [%0]!    \n\t"       // DST->M[m0-m7]
+		"vst1.32    {d28 - d31}, [%0]     \n\t"       // DST->M[m8-m15]
+
+		: // output
+		: "r"(dst), "r"(m1), "r"(m2) // input - note *value* of pointer doesn't change.
+		: "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
+	);
+}
+
+inline void MathUtil::negateMatrix(const float* m, float* dst)
+{
+	asm volatile(
+		"vld1.32 	{q0-q1},  [%1]! 	\n\t" // load m0-m7
+		"vld1.32 	{q2-q3},  [%1]   	\n\t" // load m8-m15
+
+		"vneg.f32 	q4, q0 				\n\t" // negate m0-m3
+		"vneg.f32 	q5, q1 				\n\t" // negate m4-m7
+		"vneg.f32 	q6, q2 				\n\t" // negate m8-m15
+		"vneg.f32 	q7, q3 				\n\t" // negate m8-m15
+
+		"vst1.32 	{q4-q5},  [%0]!		\n\t" // store m0-m7
+		"vst1.32 	{q6-q7},  [%0]		\n\t" // store m8-m15
+		:
+		: "r"(dst), "r"(m)
+		: "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory"
+	);
+}
+
+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::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]
+		"vld1.32	{d0[1]},    	[%2]	\n\t"	// V[y]
+		"vld1.32	{d1[0]},		[%3]	\n\t"	// V[z]
+		"vld1.32	{d1[1]},		[%4]	\n\t"	// V[w]
+		"vld1.32	{d18 - d21},	[%5]!	\n\t"	// M[m0-m7]
+		"vld1.32	{d22 - d25},	[%5]	\n\t"	// M[m8-m15]
+
+		"vmul.f32 q13,  q9, d0[0]			\n\t"	// DST->V = M[m0-m3] * V[x]
+		"vmla.f32 q13, q10, d0[1]      		\n\t"	// DST->V += M[m4-m7] * V[y]
+		"vmla.f32 q13, q11, d1[0]      		\n\t"	// DST->V += M[m8-m11] * V[z]
+		"vmla.f32 q13, q12, d1[1]      		\n\t"	// DST->V += M[m12-m15] * V[w]
+
+		"vst1.32 {d26}, [%0]!        		\n\t"	// DST->V[x, y]
+		"vst1.32 {d27[0]}, [%0]        		\n\t"	// DST->V[z]
+		:
+		: "r"(dst), "r"(&x), "r"(&y), "r"(&z), "r"(&w), "r"(m)
+		: "q0", "q9", "q10","q11", "q12", "q13", "memory"
+	);
+}
+
+inline void MathUtil::transformVector4(const float* m, const float* v, float* dst)
+{
+	asm volatile
+	(
+		"vld1.32	{d0, d1}, [%1]		\n\t"   // V[x, y, z, w]
+		"vld1.32    {d18 - d21}, [%2]!  \n\t"   // M[m0-m7]
+		"vld1.32    {d22 - d25}, [%2]  \n\t"    // M[m8-m15]
+
+		"vmul.f32   q13, q9, d0[0]      \n\t"   // DST->V = M[m0-m3] * V[x]
+		"vmla.f32   q13, q10, d0[1]     \n\t"   // DST->V = M[m4-m7] * V[y]
+		"vmla.f32   q13, q11, d1[0]     \n\t"   // DST->V = M[m8-m11] * V[z]
+		"vmla.f32   q13, q12, d1[1]     \n\t"   // DST->V = M[m12-m15] * V[w]
+
+		"vst1.32    {d26, d27}, [%0]    \n\t"   // DST->V
+		:
+		: "r"(dst), "r"(v), "r"(m)
+		: "q0", "q9", "q10","q11", "q12", "q13", "memory"
+	);
+}
+
+inline void MathUtil::crossVector3(const float* v1, const float* v2, float* dst)
+{
+	asm volatile(
+		"vld1.32 {d1[1]},  [%1] 		\n\t" //
+		"vld1.32 {d0},     [%2]         \n\t" //
+		"vmov.f32 s2, s1                \n\t" // q0 = (v1y, v1z, v1z, v1x)
+
+		"vld1.32 {d2[1]},  [%3]	    	\n\t" //
+		"vld1.32 {d3},     [%4]         \n\t" //
+		"vmov.f32 s4, s7          		\n\t" // q1 = (v2z, v2x, v2y, v2z)
+
+		"vmul.f32 d4, d0, d2  			\n\t" // x = v1y * v2z, y = v1z * v2x
+		"vmls.f32 d4, d1, d3  			\n\t" // x -= v1z * v2y, y-= v1x - v2z
+
+		"vmul.f32 d5, d3, d1[1]			\n\t" // z = v1x * v2y
+		"vmls.f32 d5, d0, d2[1]         \n\t" // z-= v1y * vx
+
+		"vst1.32 {d4}, 	  [%0]!    		\n\t" // V[x, y]
+		"vst1.32 {d5[0]}, [%0]     		\n\t" // V[z]
+		:
+		: "r"(dst), "r"(v1), "r"((v1+1)), "r"(v2), "r"((v2+1))
+		: "q0", "q1", "q2", "memory"
+	);
+}
+
+}

+ 10 - 321
gameplay/src/Matrix.cpp

@@ -1,8 +1,7 @@
 #include "Base.h"
 #include "Base.h"
 #include "Matrix.h"
 #include "Matrix.h"
 #include "Quaternion.h"
 #include "Quaternion.h"
-
-#define MATRIX_SIZE     ( sizeof(float) * 16 )
+#include "MathUtil.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -361,22 +360,7 @@ void Matrix::add(float scalar, Matrix* dst)
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
 
 
-    dst->m[0]  = m[0]  + scalar;
-    dst->m[1]  = m[1]  + scalar;
-    dst->m[2]  = m[2]  + scalar;
-    dst->m[3]  = m[3]  + scalar;
-    dst->m[4]  = m[4]  + scalar;
-    dst->m[5]  = m[5]  + scalar;
-    dst->m[6]  = m[6]  + scalar;
-    dst->m[7]  = m[7]  + scalar;
-    dst->m[8]  = m[8]  + scalar;
-    dst->m[9]  = m[9]  + scalar;
-    dst->m[10] = m[10] + scalar;
-    dst->m[11] = m[11] + scalar;
-    dst->m[12] = m[12] + scalar;
-    dst->m[13] = m[13] + scalar;
-    dst->m[14] = m[14] + scalar;
-    dst->m[15] = m[15] + scalar;
+    MathUtil::addMatrix(m, scalar, dst->m);
 }
 }
 
 
 void Matrix::add(const Matrix& m)
 void Matrix::add(const Matrix& m)
@@ -388,44 +372,7 @@ void Matrix::add(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
 
 
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32 	{q0, q1}, 	[%1]! 	\n\t"
-		"vld1.32 	{q2, q3}, 	[%1]! 	\n\t"
-    	"vld1.32 	{q8, q9}, 	[%2]! 	\n\t"
-		"vld1.32 	{q10, q11}, [%2]! 	\n\t"
-		"vadd.f32   q12, q0, q8 		\n\t"
-    	"vadd.f32   q13, q1, q9			\n\t"
-    	"vadd.f32   q14, q2, q10		\n\t"
-    	"vadd.f32   q15, q3, q11		\n\t"
-    	"vst1.32    {q12, q13}, [%0]!   \n\t"
-		"vst1.32    {q14, q15}, [%0]!   \n\t"
-		:
-        : "r"(dst->m), "r"(m1.m), "r"(m2.m)
-        : "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory"
-    );
-
-#else
-
-    dst->m[0]  = m1.m[0]  + m2.m[0];
-    dst->m[1]  = m1.m[1]  + m2.m[1];
-    dst->m[2]  = m1.m[2]  + m2.m[2];
-    dst->m[3]  = m1.m[3]  + m2.m[3];
-    dst->m[4]  = m1.m[4]  + m2.m[4];
-    dst->m[5]  = m1.m[5]  + m2.m[5];
-    dst->m[6]  = m1.m[6]  + m2.m[6];
-    dst->m[7]  = m1.m[7]  + m2.m[7];
-    dst->m[8]  = m1.m[8]  + m2.m[8];
-    dst->m[9]  = m1.m[9]  + m2.m[9];
-    dst->m[10] = m1.m[10] + m2.m[10];
-    dst->m[11] = m1.m[11] + m2.m[11];
-    dst->m[12] = m1.m[12] + m2.m[12];
-    dst->m[13] = m1.m[13] + m2.m[13];
-    dst->m[14] = m1.m[14] + m2.m[14];
-    dst->m[15] = m1.m[15] + m2.m[15];
-
-#endif
+    MathUtil::addMatrix(m1.m, m2.m, dst->m);
 }
 }
 
 
 bool Matrix::decompose(Vector3* scale, Quaternion* rotation, Vector3* translation) const
 bool Matrix::decompose(Vector3* scale, Quaternion* rotation, Vector3* translation) const
@@ -697,45 +644,7 @@ void Matrix::multiply(const Matrix& m, float scalar, Matrix* dst)
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
 
 
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32 	{d0[0]},	 	[%0]     	\n\t"
-    	"vld1.32	{q4-q5},  		[%1]!    	\n\t"
-		"vld1.32	{q6-q7},  		[%1]!		\n\t"
-
-    	"vmul.f32 	q8, q4, d0[0]    			\n\t"
-    	"vmul.f32 	q9, q5, d0[0]    			\n\t"
-		"vmul.f32 	q10, q6, d0[0]    			\n\t"
-		"vmul.f32 	q11, q7, d0[0]   		 	\n\t"
-
-    	"vst1.32 	{q8-q9},   		[%2]! 		\n\t"
-		"vst1.32 	{q10-q11}, 		[%2]!		\n\t"
-		:
-		: "r"(&scalar), "r"(m.m), "r"(dst->m)
-		: "q0", "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11", "memory"
-	);
-
-#else
-
-    dst->m[0]  = m.m[0]  * scalar;
-    dst->m[1]  = m.m[1]  * scalar;
-    dst->m[2]  = m.m[2]  * scalar;
-    dst->m[3]  = m.m[3]  * scalar;
-    dst->m[4]  = m.m[4]  * scalar;
-    dst->m[5]  = m.m[5]  * scalar;
-    dst->m[6]  = m.m[6]  * scalar;
-    dst->m[7]  = m.m[7]  * scalar;
-    dst->m[8]  = m.m[8]  * scalar;
-    dst->m[9]  = m.m[9]  * scalar;
-    dst->m[10] = m.m[10] * scalar;
-    dst->m[11] = m.m[11] * scalar;
-    dst->m[12] = m.m[12] * scalar;
-    dst->m[13] = m.m[13] * scalar;
-    dst->m[14] = m.m[14] * scalar;
-    dst->m[15] = m.m[15] * scalar;
-
-#endif
+    MathUtil::multiplyMatrix(m.m, scalar, dst->m);
 }
 }
 
 
 void Matrix::multiply(const Matrix& m)
 void Matrix::multiply(const Matrix& m)
@@ -747,71 +656,7 @@ void Matrix::multiply(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
 {
 	GP_ASSERT(dst);
 	GP_ASSERT(dst);
 
 
-#ifdef USE_NEON // if set, neon unit is present.
-
-    asm volatile
-    (
-        "vld1.32	 {d16 - d19}, [%1]!	\n\t"         // load first eight elements of matrix 0
-		"vld1.32     {d20 - d23}, [%1]!   \n\t"         // load second eight elements of matrix 0
-		"vld1.32     {d0 - d3}, [%2]!     \n\t"         // load first eight elements of matrix 1
-		"vld1.32     {d4 - d7}, [%2]!     \n\t"         // load second eight elements of matrix 1
-
-		"vmul.f32    q12, q8, d0[0]     \n\t"         // rslt col0  = (mat0 col0) * (mat1 col0 elt0)
-		"vmul.f32    q13, q8, d2[0]     \n\t"         // rslt col1  = (mat0 col0) * (mat1 col1 elt0)
-		"vmul.f32    q14, q8, d4[0]     \n\t"         // rslt col2  = (mat0 col0) * (mat1 col2 elt0)
-		"vmul.f32    q15, q8, d6[0]     \n\t"         // rslt col3  = (mat0 col0) * (mat1 col3 elt0)
-
-		"vmla.f32    q12, q9, d0[1]     \n\t"         // rslt col0 += (mat0 col1) * (mat1 col0 elt1)
-		"vmla.f32    q13, q9, d2[1]     \n\t"         // rslt col1 += (mat0 col1) * (mat1 col1 elt1)
-		"vmla.f32    q14, q9, d4[1]     \n\t"         // rslt col2 += (mat0 col1) * (mat1 col2 elt1)
-		"vmla.f32    q15, q9, d6[1]     \n\t"         // rslt col3 += (mat0 col1) * (mat1 col3 elt1)
-
-		"vmla.f32    q12, q10, d1[0]    \n\t"         // rslt col0 += (mat0 col2) * (mat1 col0 elt2)
-		"vmla.f32    q13, q10, d3[0]    \n\t"         // rslt col1 += (mat0 col2) * (mat1 col1 elt2)
-		"vmla.f32    q14, q10, d5[0]    \n\t"         // rslt col2 += (mat0 col2) * (mat1 col2 elt2)
-		"vmla.f32    q15, q10, d7[0]    \n\t"         // rslt col3 += (mat0 col2) * (mat1 col2 elt2)
-
-		"vmla.f32    q12, q11, d1[1]    \n\t"         // rslt col0 += (mat0 col3) * (mat1 col0 elt3)
-		"vmla.f32    q13, q11, d3[1]    \n\t"         // rslt col1 += (mat0 col3) * (mat1 col1 elt3)
-		"vmla.f32    q14, q11, d5[1]    \n\t"         // rslt col2 += (mat0 col3) * (mat1 col2 elt3)
-		"vmla.f32    q15, q11, d7[1]    \n\t"         // rslt col3 += (mat0 col3) * (mat1 col3 elt3)
-
-		"vst1.32    {d24 - d27}, [%0]!    \n\t"         // store first eight elements of result
-		"vst1.32    {d28 - d31}, [%0]!    \n\t"         // store second eight elements of result
-        
-        : // output
-        : "r"(dst->m), "r"(m1.m), "r"(m2.m) // input - note *value* of pointer doesn't change.
-        : "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
-	);
-
-#else
-
-    // Support the case where m1 or m2 is the same array as dst.
-    float product[16];
-
-    product[0]  = m1.m[0] * m2.m[0]  + m1.m[4] * m2.m[1] + m1.m[8]   * m2.m[2]  + m1.m[12] * m2.m[3];
-    product[1]  = m1.m[1] * m2.m[0]  + m1.m[5] * m2.m[1] + m1.m[9]   * m2.m[2]  + m1.m[13] * m2.m[3];
-    product[2]  = m1.m[2] * m2.m[0]  + m1.m[6] * m2.m[1] + m1.m[10]  * m2.m[2]  + m1.m[14] * m2.m[3];
-    product[3]  = m1.m[3] * m2.m[0]  + m1.m[7] * m2.m[1] + m1.m[11]  * m2.m[2]  + m1.m[15] * m2.m[3];
-
-    product[4]  = m1.m[0] * m2.m[4]  + m1.m[4] * m2.m[5] + m1.m[8]   * m2.m[6]  + m1.m[12] * m2.m[7];
-    product[5]  = m1.m[1] * m2.m[4]  + m1.m[5] * m2.m[5] + m1.m[9]   * m2.m[6]  + m1.m[13] * m2.m[7];
-    product[6]  = m1.m[2] * m2.m[4]  + m1.m[6] * m2.m[5] + m1.m[10]  * m2.m[6]  + m1.m[14] * m2.m[7];
-    product[7]  = m1.m[3] * m2.m[4]  + m1.m[7] * m2.m[5] + m1.m[11]  * m2.m[6]  + m1.m[15] * m2.m[7];
-
-    product[8]  = m1.m[0] * m2.m[8]  + m1.m[4] * m2.m[9] + m1.m[8]   * m2.m[10] + m1.m[12] * m2.m[11];
-    product[9]  = m1.m[1] * m2.m[8]  + m1.m[5] * m2.m[9] + m1.m[9]   * m2.m[10] + m1.m[13] * m2.m[11];
-    product[10] = m1.m[2] * m2.m[8]  + m1.m[6] * m2.m[9] + m1.m[10]  * m2.m[10] + m1.m[14] * m2.m[11];
-    product[11] = m1.m[3] * m2.m[8]  + m1.m[7] * m2.m[9] + m1.m[11]  * m2.m[10] + m1.m[15] * m2.m[11];
-
-    product[12] = m1.m[0] * m2.m[12] + m1.m[4] * m2.m[13] + m1.m[8]  * m2.m[14] + m1.m[12] * m2.m[15];
-    product[13] = m1.m[1] * m2.m[12] + m1.m[5] * m2.m[13] + m1.m[9]  * m2.m[14] + m1.m[13] * m2.m[15];
-    product[14] = m1.m[2] * m2.m[12] + m1.m[6] * m2.m[13] + m1.m[10] * m2.m[14] + m1.m[14] * m2.m[15];
-    product[15] = m1.m[3] * m2.m[12] + m1.m[7] * m2.m[13] + m1.m[11] * m2.m[14] + m1.m[15] * m2.m[15];
-
-    memcpy(dst->m, product, MATRIX_SIZE);
-
-#endif
+	MathUtil::multiplyMatrix(m1.m, m2.m, dst->m);
 }
 }
 
 
 void Matrix::negate()
 void Matrix::negate()
@@ -823,44 +668,7 @@ void Matrix::negate(Matrix* dst) const
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
 
 
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32 	{q0-q1},  [%1]! 	\n\t" // load m0-m7
-    	"vld1.32 	{q2-q3},  [%1]! 	\n\t" // load m8-m15
-
-    	"vneg.f32 	q4, q0 				\n\t" // negate m0-m3
-    	"vneg.f32 	q5, q1 				\n\t" // negate m4-m7
-		"vneg.f32 	q6, q2 				\n\t" // negate m8-m15
-		"vneg.f32 	q7, q3 				\n\t" // negate m8-m15
-
-    	"vst1.32 	{q4-q5},  [%0]!		\n\t" // store m0-m7
-    	"vst1.32 	{q6-q7},  [%0]!		\n\t" // store m8-m15
-    	:
-    	: "r"(dst->m), "r"(m)
-    	: "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory"
-    );
-
-#else
-
-    dst->m[0]  = -m[0];
-    dst->m[1]  = -m[1];
-    dst->m[2]  = -m[2];
-    dst->m[3]  = -m[3];
-    dst->m[4]  = -m[4];
-    dst->m[5]  = -m[5];
-    dst->m[6]  = -m[6];
-    dst->m[7]  = -m[7];
-    dst->m[8]  = -m[8];
-    dst->m[9]  = -m[9];
-    dst->m[10] = -m[10];
-    dst->m[11] = -m[11];
-    dst->m[12] = -m[12];
-    dst->m[13] = -m[13];
-    dst->m[14] = -m[14];
-    dst->m[15] = -m[15];
-
-#endif
+    MathUtil::negateMatrix(m, dst->m);
 }
 }
 
 
 void Matrix::rotate(const Quaternion& q)
 void Matrix::rotate(const Quaternion& q)
@@ -1005,45 +813,7 @@ void Matrix::subtract(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
 
 
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32 	{q0, q1}, 	[%1]! 	\n\t"
-		"vld1.32 	{q2, q3}, 	[%1]! 	\n\t"
-    	"vld1.32 	{q8, q9}, 	[%2]! 	\n\t"
-		"vld1.32 	{q10, q11}, [%2]! 	\n\t"
-		"vsub.f32   q12, q0, q8 		\n\t"
-    	"vsub.f32   q13, q1, q9			\n\t"
-    	"vsub.f32   q14, q2, q10		\n\t"
-    	"vsub.f32   q15, q3, q11		\n\t"
-    	"vst1.32    {q12, q13}, [%0]!   \n\t"
-		"vst1.32    {q14, q15}, [%0]!   \n\t"
-		:
-        : "r"(dst->m), "r"(m1.m), "r"(m2.m)
-        : "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory"
-    );
-
-
-#else
-
-    dst->m[0]  = m1.m[0]  - m2.m[0];
-    dst->m[1]  = m1.m[1]  - m2.m[1];
-    dst->m[2]  = m1.m[2]  - m2.m[2];
-    dst->m[3]  = m1.m[3]  - m2.m[3];
-    dst->m[4]  = m1.m[4]  - m2.m[4];
-    dst->m[5]  = m1.m[5]  - m2.m[5];
-    dst->m[6]  = m1.m[6]  - m2.m[6];
-    dst->m[7]  = m1.m[7]  - m2.m[7];
-    dst->m[8]  = m1.m[8]  - m2.m[8];
-    dst->m[9]  = m1.m[9]  - m2.m[9];
-    dst->m[10] = m1.m[10] - m2.m[10];
-    dst->m[11] = m1.m[11] - m2.m[11];
-    dst->m[12] = m1.m[12] - m2.m[12];
-    dst->m[13] = m1.m[13] - m2.m[13];
-    dst->m[14] = m1.m[14] - m2.m[14];
-    dst->m[15] = m1.m[15] - m2.m[15];
-
-#endif
+    MathUtil::subtractMatrix(m1.m, m2.m, dst->m);
 }
 }
 
 
 void Matrix::transformPoint(Vector3* point) const
 void Matrix::transformPoint(Vector3* point) const
@@ -1072,37 +842,7 @@ void Matrix::transformVector(float x, float y, float z, float w, Vector3* dst) c
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
 
 
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32	{d0[0]},		[%0]	\n\t"	// load x
-		"vld1.32	{d0[1]},    	[%1]	\n\t"	// load y
-		"vld1.32	{d1[0]},		[%2]	\n\t"	// load z
-		"vld1.32	{d1[1]},		[%3]	\n\t"	// load w
-		"vld1.32	{d18 - d21},	[%4]!	\n\t"	// load first 8 elements of matrix m0-m7
-		"vld1.32	{d22 - d25},	[%4]!	\n\t"	// load second 8 elements of matrix m8-m15
-
-    	"vmul.f32 q13,  q9, d0[0]			\n\t"	// Q5 =  (m0-m3)*x
-    	"vmla.f32 q13, q10, d0[1]      		\n\t"	// Q5 +=  (m4-m7)*y
-    	"vmla.f32 q13, q11, d1[0]      		\n\t"	// Q5 +=  (m8-m11)*z
-		"vmla.f32 q13, q12, d1[1]      		\n\t"	// Q5 +=  (m12-m15)*w
-
-    	"vst1.32 {d26[0]}, [%5]!        	\n\t"	// store dst->x
-		"vst1.32 {d26[1]}, [%5]!        	\n\t"	// store dst->y
-		"vst1.32 {d27[0]}, [%5]!        	\n\t"	// store dst->z
-		:
-    	: "r"(&x), "r"(&y), "r"(&z), "r"(&w), "r"(m), "r"(dst)
-        : "q0", "q9", "q10","q11", "q12", "q13", "memory"
-    );
-
-#else
-
-    dst->set(
-        x * m[0] + y * m[4] + z * m[8] + w * m[12],
-        x * m[1] + y * m[5] + z * m[9] + w * m[13],
-        x * m[2] + y * m[6] + z * m[10] + w * m[14]);
-
-#endif
+    MathUtil::transformVector4(m, x, y, z, w, (float*)dst);
 }
 }
 
 
 void Matrix::transformVector(Vector4* vector) const
 void Matrix::transformVector(Vector4* vector) const
@@ -1115,33 +855,7 @@ void Matrix::transformVector(const Vector4& vector, Vector4* dst) const
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
 
 
-#ifdef USE_NEON
-
-    asm volatile
-    (
-    		"vld1.32	{d0, d1}, [%1]		\n\t"   //Q0 = v (x, y, z, w)
-    		"vld1.32    {d18 - d21}, [%0]!  \n\t"   //Q1 = M (m0-m7)
-    		"vld1.32    {d22 - d25}, [%0]!  \n\t"   //Q2 = M (m8-m15)
-
-    		"vmul.f32   q13, q9, d0[0]      \n\t"   //Q5 =  Q0*Q0[0]
-    		"vmla.f32   q13, q10, d0[1]     \n\t"   //Q5 += Q1*Q0[1]
-    		"vmla.f32   q13, q11, d1[0]     \n\t"   //Q5 += Q2*Q0[2]
-    		"vmla.f32   q13, q12, d1[1]     \n\t"   //Q5 += Q3*Q0[3]
-    		"vst1.32    {d26, d27}, [%2]    \n\t"   //Q4 = m+12
-    		:
-    		: "r"(m), "r"(&vector), "r"(dst)
-    		: "q0", "q9", "q10","q11", "q12", "q13", "memory"
-    );
-
-#else
-
-    dst->set(
-        vector.x * m[0] + vector.y * m[4] + vector.z * m[8] + vector.w * m[12],
-        vector.x * m[1] + vector.y * m[5] + vector.z * m[9] + vector.w * m[13],
-        vector.x * m[2] + vector.y * m[6] + vector.z * m[10] + vector.w * m[14],
-        vector.x * m[3] + vector.y * m[7] + vector.z * m[11] + vector.w * m[15]);
-
-#endif
+    MathUtil::transformVector4(m, (const float*) &vector, (float*)dst);
 }
 }
 
 
 void Matrix::translate(float x, float y, float z)
 void Matrix::translate(float x, float y, float z)
@@ -1175,32 +889,7 @@ void Matrix::transpose(Matrix* dst) const
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
 
 
-#ifdef USE_NEON
-    
-    asm volatile(
-    	"vld4.32 {d0[0], d2[0], d4[0], d6[0]}, [%0]! \n\t"
-		"vld4.32 {d0[1], d2[1], d4[1], d6[1]}, [%0]! \n\t"
-		"vld4.32 {d1[0], d3[0], d5[0], d7[0]}, [%0]! \n\t"
-		"vld4.32 {d1[1], d3[1], d5[1], d7[1]}, [%0]! \n\t"
-
-		"vst1.32 {q0-q1}, [%1]! \n\t"
-		"vst1.32 {q2-q3}, [%1]! \n\t"
-    	:
-    	: "r"(this->m), "r"(dst->m)
-    	: "q0", "q1", "q2", "q3", "memory"
-    );
-
-#else
-
-    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->m, t, MATRIX_SIZE);
-
-#endif
+    MathUtil::transposeMatrix(m, dst->m);
 }
 }
 
 
 }
 }

+ 2 - 2
gameplay/src/Plane.cpp

@@ -63,7 +63,7 @@ void Plane::intersection(const Plane& p1, const Plane& p2, const Plane& p3, Vect
                 p1._normal.z * p3._normal.y) + p3._normal.x * (p1._normal.y * p2._normal.z - p1._normal.z * p2._normal.y);
                 p1._normal.z * p3._normal.y) + p3._normal.x * (p1._normal.y * p2._normal.z - p1._normal.z * p2._normal.y);
 
 
     // If the determinant is zero, then the planes do not all intersect.
     // If the determinant is zero, then the planes do not all intersect.
-    if (det == 0.0f)
+    if (fabs(det) <= MATH_EPSILON)
         return;
         return;
 
 
     // Create 3 points, one on each plane.
     // Create 3 points, one on each plane.
@@ -161,7 +161,7 @@ float Plane::intersects(const Frustum& frustum) const
 float Plane::intersects(const Plane& plane) const
 float Plane::intersects(const Plane& plane) const
 {
 {
     // Check if the planes intersect.
     // Check if the planes intersect.
-    if (!isParallel(plane))
+    if ((_normal.x == plane._normal.x && _normal.y == plane._normal.y && _normal.z == plane._normal.z) || !isParallel(plane))
     {
     {
         return Plane::INTERSECTS_INTERSECTING;
         return Plane::INTERSECTS_INTERSECTING;
     }
     }

+ 87 - 59
gameplay/src/Properties.cpp

@@ -6,6 +6,10 @@
 namespace gameplay
 namespace gameplay
 {
 {
 
 
+// Utility functions (shared with SceneLoader).
+void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath);
+Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
+
 Properties::Properties()
 Properties::Properties()
 {
 {
 }
 }
@@ -55,28 +59,11 @@ Properties* Properties::create(const char* url)
         return NULL;
         return NULL;
     }
     }
 
 
+    // Calculate the file and full namespace path from the specified url.
     std::string urlString = url;
     std::string urlString = url;
     std::string fileString;
     std::string fileString;
     std::vector<std::string> namespacePath;
     std::vector<std::string> namespacePath;
-
-    // If the url references a specific namespace within the file,
-    // calculate the full namespace path to the final namespace.
-    unsigned int loc = urlString.rfind("#");
-    if (loc != urlString.npos)
-    {
-        fileString = urlString.substr(0, loc);
-        std::string namespacePathString = urlString.substr(loc + 1);
-        while ((loc = namespacePathString.find("/")) != namespacePathString.npos)
-        {
-            namespacePath.push_back(namespacePathString.substr(0, loc));
-            namespacePathString = namespacePathString.substr(loc + 1);
-        }
-        namespacePath.push_back(namespacePathString);
-    }
-    else
-    {
-        fileString = url;
-    }
+    calculateNamespacePath(urlString, fileString, namespacePath);
 
 
     FILE* file = FileSystem::openFile(fileString.c_str(), "rb");
     FILE* file = FileSystem::openFile(fileString.c_str(), "rb");
     if (!file)
     if (!file)
@@ -86,52 +73,26 @@ Properties* Properties::create(const char* url)
     }
     }
 
 
     Properties* properties = new Properties(file);
     Properties* properties = new Properties(file);
-
     properties->resolveInheritance();
     properties->resolveInheritance();
-
     fclose(file);
     fclose(file);
 
 
-    // If the url references a specific namespace within the file,
-    // return the specified namespace or notify the user if it cannot be found.
-    Properties* originalProperties = properties;
-    if (namespacePath.size() > 0)
+    // Get the specified properties object.
+    Properties* p = getPropertiesFromNamespacePath(properties, namespacePath);
+    if (!p)
     {
     {
-        unsigned int size = namespacePath.size();
-        Properties* iter = properties->getNextNamespace();
-        for (unsigned int i = 0; i < size;)
-        {
-            while (true)
-            {
-                if (iter == NULL)
-                {
-                    GP_ERROR("Failed to load Properties object from URL '%s'.", url);
-                    return NULL;
-                }
-
-                if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
-                {
-                    if (i != size - 1)
-                    {
-                        properties = iter->getNextNamespace();
-                        iter = properties;
-                    }
-                    else
-                        properties = iter;
-
-                    i++;
-                    break;
-                }
-                
-                iter = properties->getNextNamespace();
-            }
-        }
+        GP_ERROR("Failed to load properties from url '%s'.", url);
+        return NULL;
+    }
 
 
-        properties = properties->clone();
-        SAFE_DELETE(originalProperties);
-        return properties;
+    // If the loaded properties object is not the root namespace,
+    // then we have to clone it and delete the root namespace
+    // so that we don't leak memory.
+    if (p != properties)
+    {
+        p = p->clone();
+        SAFE_DELETE(properties);
     }
     }
-    else
-        return properties;
+    return p;
 }
 }
 
 
 void Properties::readProperties(FILE* file)
 void Properties::readProperties(FILE* file)
@@ -1009,4 +970,71 @@ Properties* Properties::clone()
     return p;
     return p;
 }
 }
 
 
+void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath)
+{
+    // If the url references a specific namespace within the file,
+    // calculate the full namespace path to the final namespace.
+    unsigned int loc = urlString.rfind("#");
+    if (loc != urlString.npos)
+    {
+        fileString = urlString.substr(0, loc);
+        std::string namespacePathString = urlString.substr(loc + 1);
+        while ((loc = namespacePathString.find("/")) != namespacePathString.npos)
+        {
+            namespacePath.push_back(namespacePathString.substr(0, loc));
+            namespacePathString = namespacePathString.substr(loc + 1);
+        }
+        namespacePath.push_back(namespacePathString);
+    }
+    else
+    {
+        fileString = urlString;
+    }
+}
+
+Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath)
+{
+    // If the url references a specific namespace within the file,
+    // return the specified namespace or notify the user if it cannot be found.
+    Properties* originalProperties = properties;
+    if (namespacePath.size() > 0)
+    {
+        unsigned int size = namespacePath.size();
+        const char* tmp = namespacePath[0].c_str();
+        properties->rewind();
+        Properties* iter = properties->getNextNamespace();
+        for (unsigned int i = 0; i < size;)
+        {
+            while (true)
+            {
+                if (iter == NULL)
+                {
+                    GP_ERROR("Failed to load properties object from url.");
+                    return NULL;
+                }
+
+                if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
+                {
+                    if (i != size - 1)
+                    {
+                        properties = iter->getNextNamespace();
+                        iter = properties;
+                    }
+                    else
+                        properties = iter;
+
+                    i++;
+                    break;
+                }
+                
+                iter = properties->getNextNamespace();
+            }
+        }
+
+        return properties;
+    }
+    else
+        return properties;
+}
+
 }
 }

+ 2 - 2
gameplay/src/RadioButton.cpp

@@ -122,9 +122,9 @@ void RadioButton::clearSelected(const std::string& groupId)
     }
     }
 }
 }
 
 
-void RadioButton::update(const Rectangle& clip, const Vector2& offset)
+void RadioButton::update(const Control* container, const Vector2& offset)
 {
 {
-    Label::update(clip, offset);
+    Label::update(container, offset);
 
 
     Vector2 size;
     Vector2 size;
     if (_imageSize.isZero())
     if (_imageSize.isZero())

+ 2 - 2
gameplay/src/RadioButton.h

@@ -112,9 +112,9 @@ protected:
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this control's parent container.
+     * @param container This control's parent container.
      */
      */
-    void update(const Rectangle& clip, const Vector2& offset);
+    void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.

+ 38 - 7
gameplay/src/SceneLoader.cpp

@@ -6,6 +6,11 @@
 namespace gameplay
 namespace gameplay
 {
 {
 
 
+// Utility functions (shared with Properties).
+extern void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath);
+extern Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
+    
+std::map<std::string, Properties*> SceneLoader::_propertiesFromFile;
 std::map<std::string, Properties*> SceneLoader::_properties;
 std::map<std::string, Properties*> SceneLoader::_properties;
 std::vector<SceneLoader::SceneAnimation> SceneLoader::_animations;
 std::vector<SceneLoader::SceneAnimation> SceneLoader::_animations;
 std::vector<SceneLoader::SceneNode> SceneLoader::_sceneNodes;
 std::vector<SceneLoader::SceneNode> SceneLoader::_sceneNodes;
@@ -89,11 +94,11 @@ Scene* SceneLoader::load(const char* url)
         loadPhysics(physics, scene);
         loadPhysics(physics, scene);
 
 
     // Clean up all loaded properties objects.
     // Clean up all loaded properties objects.
-    std::map<std::string, Properties*>::iterator iter = _properties.begin();
-    for (; iter != _properties.end(); iter++)
+    _properties.clear();
+    std::map<std::string, Properties*>::iterator iter = _propertiesFromFile.begin();
+    for (; iter != _propertiesFromFile.end(); iter++)
     {
     {
-        if (iter->first.find(_path) == iter->first.npos)
-            SAFE_DELETE(iter->second);
+        SAFE_DELETE(iter->second);
     }
     }
 
 
     // Clean up the .scene file's properties object.
     // Clean up the .scene file's properties object.
@@ -827,10 +832,36 @@ void SceneLoader::loadReferencedFiles()
     {
     {
         if (iter->second == NULL)
         if (iter->second == NULL)
         {
         {
-            Properties* p = Properties::create(iter->first.c_str());
-            if (p == NULL)
-                GP_ERROR("Failed to load referenced file '%s'.", iter->first.c_str());
+            std::string fileString;
+            std::vector<std::string> namespacePath;
+            calculateNamespacePath(iter->first, fileString, namespacePath);
+
+            // Check if the referenced properties file has already been loaded.
+            Properties* properties = NULL;
+            std::map<std::string, Properties*>::iterator pffIter = _propertiesFromFile.find(fileString);
+            if (pffIter != _propertiesFromFile.end())
+            {
+                properties = pffIter->second;
+            }
+            else
+            {
+                properties = Properties::create(fileString.c_str());
+                if (properties == NULL)
+                {
+                    GP_ERROR("Failed to load referenced properties file '%s'.", fileString.c_str());
+                    continue;
+                }
+
+                // Add the properties object to the cache.
+                _propertiesFromFile.insert(std::make_pair(fileString, properties));
+            }
 
 
+            Properties* p = getPropertiesFromNamespacePath(properties, namespacePath);
+            if (!p)
+            {
+                GP_ERROR("Failed to load referenced properties from url '%s'.", iter->first.c_str());
+                continue;
+            }
             iter->second = p;
             iter->second = p;
         }
         }
     }
     }

+ 1 - 0
gameplay/src/SceneLoader.h

@@ -105,6 +105,7 @@ private:
     static void splitURL(const std::string& url, std::string* file, std::string* id);
     static void splitURL(const std::string& url, std::string* file, std::string* id);
     
     
     
     
+    static std::map<std::string, Properties*> _propertiesFromFile;      // Holds the properties object for a given file.
     static std::map<std::string, Properties*> _properties;              // Holds the properties object for a given URL.
     static std::map<std::string, Properties*> _properties;              // Holds the properties object for a given URL.
     static std::vector<SceneAnimation> _animations;                     // Holds the animations declared in the .scene file.
     static std::vector<SceneAnimation> _animations;                     // Holds the animations declared in the .scene file.
     static std::vector<SceneNode> _sceneNodes;                          // Holds all the nodes+properties declared in the .scene file.
     static std::vector<SceneNode> _sceneNodes;                          // Holds all the nodes+properties declared in the .scene file.

+ 2 - 2
gameplay/src/Slider.cpp

@@ -141,9 +141,9 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     return Control::touchEvent(evt, x, y, contactIndex);
     return Control::touchEvent(evt, x, y, contactIndex);
 }
 }
 
 
-void Slider::update(const Rectangle& clip, const Vector2& offset)
+void Slider::update(const Control* container, const Vector2& offset)
 {
 {
-    Label::update(clip, offset);
+    Label::update(container, offset);
 
 
     _minImage = getImage("minCap", _state);
     _minImage = getImage("minCap", _state);
     _maxImage = getImage("maxCap", _state);
     _maxImage = getImage("maxCap", _state);

+ 2 - 2
gameplay/src/Slider.h

@@ -153,10 +153,10 @@ protected:
      * Called when a slider's properties change. Updates this slider's internal rendering
      * Called when a slider's properties change. Updates this slider's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this slider's parent container.
+     * @param container This slider's parent container.
      * @param offset The scroll offset of this slider's parent container.
      * @param offset The scroll offset of this slider's parent container.
      */
      */
-    void update(const Rectangle& clip, const Vector2& offset);
+    void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * The minimum value for the Slider.
      * The minimum value for the Slider.

+ 2 - 2
gameplay/src/TextBox.cpp

@@ -291,9 +291,9 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
     _lastKeypress = key;
     _lastKeypress = key;
 }
 }
 
 
-void TextBox::update(const Rectangle& clip, const Vector2& offset)
+void TextBox::update(const Control* container, const Vector2& offset)
 {
 {
-    Label::update(clip, offset);
+    Label::update(container, offset);
 
 
     _fontSize = getFontSize(_state);
     _fontSize = getFontSize(_state);
     _caretImage = getImage("textCaret", _state);
     _caretImage = getImage("textCaret", _state);

+ 2 - 2
gameplay/src/TextBox.h

@@ -110,9 +110,9 @@ protected:
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this control's parent container.
+     * @param container This control's parent container.
      */
      */
-    void update(const Rectangle& clip, const Vector2& offset);
+    void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.

+ 11 - 0
gameplay/src/Texture.cpp

@@ -637,6 +637,7 @@ Texture* Texture::createCompressedDDS(const char* path)
             {
             {
                 GP_ERROR("Failed to close file '%s'.", path);
                 GP_ERROR("Failed to close file '%s'.", path);
             }
             }
+            SAFE_DELETE_ARRAY(mipLevels);
             return NULL;
             return NULL;
         }
         }
 
 
@@ -676,6 +677,7 @@ Texture* Texture::createCompressedDDS(const char* path)
         {
         {
             GP_ERROR("Failed to close file '%s'.", path);
             GP_ERROR("Failed to close file '%s'.", path);
         }
         }
+        SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
         return NULL;
     }
     }
     else if (header.ddspf.dwFlags == 0x41/*DDPF_RGB|DDPF_ALPHAPIXELS*/)
     else if (header.ddspf.dwFlags == 0x41/*DDPF_RGB|DDPF_ALPHAPIXELS*/)
@@ -687,6 +689,7 @@ Texture* Texture::createCompressedDDS(const char* path)
         {
         {
             GP_ERROR("Failed to close file '%s'.", path);
             GP_ERROR("Failed to close file '%s'.", path);
         }
         }
+        SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
         return NULL;
     }
     }
     else
     else
@@ -697,6 +700,7 @@ Texture* Texture::createCompressedDDS(const char* path)
         {
         {
             GP_ERROR("Failed to close file '%s'.", path);
             GP_ERROR("Failed to close file '%s'.", path);
         }
         }
+        SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
         return NULL;
     }
     }
     
     
@@ -731,8 +735,15 @@ Texture* Texture::createCompressedDDS(const char* path)
         {
         {
             GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, i, internalFormat, mipLevels[i].width, mipLevels[i].height, 0, format, GL_UNSIGNED_INT, mipLevels[i].data) );
             GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, i, internalFormat, mipLevels[i].width, mipLevels[i].height, 0, format, GL_UNSIGNED_INT, mipLevels[i].data) );
         }
         }
+        
+        // Clean up the texture data.
+        SAFE_DELETE_ARRAY(mipLevels[i].data);
     }
     }
     
     
+
+    // Clean up mip levels structure.
+    SAFE_DELETE_ARRAY(mipLevels);
+
     return texture;
     return texture;
 }
 }
 
 

+ 3 - 1
gameplay/src/ThemeStyle.cpp

@@ -85,7 +85,9 @@ Theme::Style::Overlay* Theme::Style::Overlay::create()
     return overlay;
     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 Theme;
     friend class Control;
     friend class Control;
+    friend class Container;
+    friend class Form;
 
 
 private:
 private:
 
 
@@ -45,6 +47,8 @@ private:
         friend class Theme;
         friend class Theme;
         friend class Theme::Style;
         friend class Theme::Style;
         friend class Control;
         friend class Control;
+        friend class Container;
+        friend class Form;
 
 
     private:
     private:
 
 

+ 3 - 12
gameplay/src/Vector3.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "Base.h"
 #include "Vector3.h"
 #include "Vector3.h"
+#include "MathUtil.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -165,24 +166,14 @@ void Vector3::clamp(const Vector3& v, const Vector3& min, const Vector3& max, Ve
 
 
 void Vector3::cross(const Vector3& v)
 void Vector3::cross(const Vector3& v)
 {
 {
-    float tx = (y * v.z) - (z * v.y);
-    float ty = (z * v.x) - (x * v.z);
-    float tz = (x * v.y) - (y * v.x);
-    x = tx;
-    y = ty;
-    z = tz;
+    cross(*this, v, this);
 }
 }
 
 
 void Vector3::cross(const Vector3& v1, const Vector3& v2, Vector3* dst)
 void Vector3::cross(const Vector3& v1, const Vector3& v2, Vector3* dst)
 {
 {
     GP_ASSERT(dst);
     GP_ASSERT(dst);
 
 
-    float x = (v1.y * v2.z) - (v1.z * v2.y);
-    float y = (v1.z * v2.x) - (v1.x * v2.z);
-    float z = (v1.x * v2.y) - (v1.y * v2.x);
-    dst->x = x;
-    dst->y = y;
-    dst->z = z;
+    MathUtil::crossVector3((const float*)&v1, (const float*)&v2, (float*)dst);
 }
 }
 
 
 float Vector3::distance(const Vector3& v) const
 float Vector3::distance(const Vector3& v) const

+ 3 - 3
gameplay/src/VerticalLayout.cpp

@@ -43,7 +43,7 @@ Layout::Type VerticalLayout::getType()
     return Layout::LAYOUT_VERTICAL;
     return Layout::LAYOUT_VERTICAL;
 }
 }
 
 
-void VerticalLayout::update(const Container* container)
+void VerticalLayout::update(const Container* container, const Vector2& offset)
 {
 {
     GP_ASSERT(container);
     GP_ASSERT(container);
 
 
@@ -73,7 +73,7 @@ void VerticalLayout::update(const Container* container)
     {
     {
         Control* control = controls.at(i);
         Control* control = controls.at(i);
         GP_ASSERT(control);
         GP_ASSERT(control);
-            
+
         align(control, container);
         align(control, container);
 
 
         const Rectangle& bounds = control->getBounds();
         const Rectangle& bounds = control->getBounds();
@@ -82,7 +82,7 @@ void VerticalLayout::update(const Container* container)
         yPosition += margin.top;
         yPosition += margin.top;
 
 
         control->setPosition(margin.left, yPosition);
         control->setPosition(margin.left, yPosition);
-        control->update(container->getClip(), Vector2::zero());
+        control->update(container, offset);
 
 
         yPosition += bounds.height + margin.bottom;
         yPosition += bounds.height + margin.bottom;
 
 

+ 1 - 1
gameplay/src/VerticalLayout.h

@@ -67,7 +67,7 @@ protected:
      *
      *
      * @param container The container to update.
      * @param container The container to update.
      */
      */
-    void update(const Container* container);
+    void update(const Container* container, const Vector2& offset);
 
 
     /**
     /**
      * Flag determining whether this layout will start laying out controls from the bottom of the container.
      * Flag determining whether this layout will start laying out controls from the bottom of the container.

+ 1 - 0
gameplay/src/gameplay.h

@@ -87,4 +87,5 @@
 #include "Layout.h"
 #include "Layout.h"
 #include "AbsoluteLayout.h"
 #include "AbsoluteLayout.h"
 #include "VerticalLayout.h"
 #include "VerticalLayout.h"
+#include "Joystick.h"
 
 

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません