Przeglądaj źródła

Merge remote-tracking branch 'upstream/next'

Joilnen Leite 13 lat temu
rodzic
commit
33cd2ae992
100 zmienionych plików z 3382 dodań i 1192 usunięć
  1. 1 0
      .gitignore
  2. 0 1
      .kateconfig
  3. 18 1
      CHANGES.md
  4. 23 15
      README.md
  5. 0 8
      cmake/README.md
  6. 1 1
      gameplay-encoder/src/Scene.h
  7. 2 2
      gameplay-luagen/gameplay-luagen.vcxproj
  8. 31 31
      gameplay-template/android/template.AndroidManifest.xml
  9. 3 4
      gameplay-template/android/template.build.xml
  10. 4 4
      gameplay-template/gameplay-template.vcxproj
  11. 1 1
      gameplay-template/res/box.material
  12. 2 2
      gameplay-template/res/colored.vert
  13. 1 1
      gameplay.doxyfile
  14. 11 7
      gameplay/.cproject
  15. 1 0
      gameplay/gameplay.vcxproj
  16. 3 0
      gameplay/gameplay.vcxproj.filters
  17. 7 1
      gameplay/gameplay.xcodeproj/project.pbxproj
  18. 2 2
      gameplay/res/shaders/colored.vert
  19. 2 2
      gameplay/res/shaders/lib/attributes-skinning.vert
  20. 1 4
      gameplay/res/shaders/lib/lighting-point.vert
  21. 9 5
      gameplay/res/shaders/textured-bumped.frag
  22. 2 2
      gameplay/res/shaders/textured-bumped.vert
  23. 11 7
      gameplay/res/shaders/textured-unlit.frag
  24. 4 0
      gameplay/res/shaders/textured.frag
  25. 2 2
      gameplay/res/shaders/textured.vert
  26. 1 1
      gameplay/src/Animation.cpp
  27. 81 55
      gameplay/src/AudioBuffer.cpp
  28. 3 2
      gameplay/src/AudioBuffer.h
  29. 1 0
      gameplay/src/Base.h
  30. 84 102
      gameplay/src/Bundle.cpp
  31. 1 1
      gameplay/src/Bundle.h
  32. 10 8
      gameplay/src/Container.cpp
  33. 8 8
      gameplay/src/Container.h
  34. 67 13
      gameplay/src/Control.cpp
  35. 42 12
      gameplay/src/Control.h
  36. 106 3
      gameplay/src/DebugNew.h
  37. 8 8
      gameplay/src/Effect.cpp
  38. 441 33
      gameplay/src/FileSystem.cpp
  39. 28 0
      gameplay/src/FileSystem.h
  40. 4 4
      gameplay/src/Font.cpp
  41. 9 5
      gameplay/src/Font.h
  42. 16 7
      gameplay/src/Form.cpp
  43. 5 0
      gameplay/src/Form.h
  44. 1 11
      gameplay/src/FrameBuffer.cpp
  45. 2 2
      gameplay/src/Frustum.h
  46. 12 12
      gameplay/src/Game.cpp
  47. 10 4
      gameplay/src/Game.h
  48. 6 0
      gameplay/src/Game.inl
  49. 3 3
      gameplay/src/Gamepad.h
  50. 13 28
      gameplay/src/Image.cpp
  51. 0 2
      gameplay/src/Joystick.h
  52. 11 2
      gameplay/src/Layout.cpp
  53. 2 3
      gameplay/src/MathUtil.cpp
  54. 72 3
      gameplay/src/Matrix.cpp
  55. 63 31
      gameplay/src/Matrix.h
  56. 78 35
      gameplay/src/Model.cpp
  57. 12 0
      gameplay/src/Node.cpp
  58. 5 5
      gameplay/src/Node.h
  59. 1 2
      gameplay/src/ParticleEmitter.cpp
  60. 0 6
      gameplay/src/Pass.h
  61. 2 2
      gameplay/src/PhysicsCollisionObject.cpp
  62. 1 1
      gameplay/src/PhysicsCollisionObject.h
  63. 15 9
      gameplay/src/PhysicsController.cpp
  64. 1 1
      gameplay/src/PhysicsController.h
  65. 4 4
      gameplay/src/PhysicsGenericConstraint.cpp
  66. 2 2
      gameplay/src/PhysicsHingeConstraint.cpp
  67. 4 4
      gameplay/src/PhysicsSocketConstraint.cpp
  68. 2 2
      gameplay/src/PhysicsSpringConstraint.cpp
  69. 1 1
      gameplay/src/PhysicsVehicle.cpp
  70. 9 0
      gameplay/src/Platform.h
  71. 194 61
      gameplay/src/PlatformAndroid.cpp
  72. 28 20
      gameplay/src/PlatformBlackBerry.cpp
  73. 331 55
      gameplay/src/PlatformLinux.cpp
  74. 205 39
      gameplay/src/PlatformMacOSX.mm
  75. 44 12
      gameplay/src/PlatformWindows.cpp
  76. 141 2
      gameplay/src/PlatformiOS.mm
  77. 48 35
      gameplay/src/Properties.cpp
  78. 5 4
      gameplay/src/Properties.h
  79. 5 5
      gameplay/src/RenderState.h
  80. 2 2
      gameplay/src/Scene.h
  81. 47 23
      gameplay/src/ScriptController.cpp
  82. 107 17
      gameplay/src/ScriptController.h
  83. 8 7
      gameplay/src/SpriteBatch.cpp
  84. 167 0
      gameplay/src/Stream.h
  85. 2 3
      gameplay/src/TextBox.cpp
  86. 21 89
      gameplay/src/Texture.cpp
  87. 3 2
      gameplay/src/Texture.h
  88. 3 1
      gameplay/src/Transform.h
  89. 1 1
      gameplay/src/Vector2.cpp
  90. 1 1
      gameplay/src/Vector2.h
  91. 4 1
      gameplay/src/Vector3.cpp
  92. 1 1
      gameplay/src/Vector4.cpp
  93. 1 1
      gameplay/src/Vector4.h
  94. 153 66
      gameplay/src/lua/lua_Button.cpp
  95. 4 2
      gameplay/src/lua/lua_Button.h
  96. 153 66
      gameplay/src/lua/lua_CheckBox.cpp
  97. 4 2
      gameplay/src/lua/lua_CheckBox.h
  98. 153 66
      gameplay/src/lua/lua_Container.cpp
  99. 4 2
      gameplay/src/lua/lua_Container.h
  100. 153 66
      gameplay/src/lua/lua_Control.cpp

+ 1 - 0
.gitignore

@@ -10,6 +10,7 @@ Thumbs.db
 /.metadata
 /169.254.0.1
 /ipch
+/build
 /cmake
 /Debug
 /Release

+ 0 - 1
.kateconfig

@@ -1 +0,0 @@
-kate: space-indent on; tab-width 4; indent-width 4; replace-tabs on; show-tabs: on; replace-tabs-save: off; replace-trailing-space-save: off;

+ 18 - 1
CHANGES.md

@@ -1,9 +1,26 @@
+## v1.6.0
+- Adds file Stream interface for reading/writing files instead of using fread/fwrite. 
+- Adds additional gameplay-tests for billboards and forms.
+- Adds support for launching the browser via launchURL(const char*).
+- Adds physics support for setLinearFactor and setAngularFactor  on rigid bodies.
+- Adds option for fullscreen without width/height config to use native desktop resolution.
+- Adds Linux support for OpenAL PulseAudio back-end.
+- Adds support for latest Bullet Physics 2.81 with NEON optimizations for mobile targets.
+- Adds Windows 7 64-bit support.
+- Fixes to external-deps to reduce the size of the libraries on Windows.
+- Fixes for Android to no longer need to copy files to the SD card before reading them. None of the Android samples require an SD card.
+- Fixes for animation of opacity on UI and fonts.
+- Fixes in UI for removing controls and also setVisible(bool)
+- Fixes for UI controls missing on MacOSX.
+- Fixes for setting UI alignment programmatically.
+- Fixes for directional and spotlight shaders.
+
 ## v1.5.0
 
 - Linux support. (tested on Ubuntu 12)
 - CMake support for makefile generation for Linux.
 - CodeBlocks 10 IDE support for Linux.
-- Gamepad controllers support for desktops.
+- Gamepad API support for desktops.
 - Touch gesture support for tap, swipe and pinch.
 - Vehicle physics support via new PhysicsVehicle and PhysicsVehicleWheel classes.
 - Adds new racer sample (sample06-racer).

+ 23 - 15
README.md

@@ -1,29 +1,37 @@
-## gameplay v1.5.0
-An open-source, cross-platform 3D native C++ game framework making it easy to learn and write mobile and desktop games. 
+## gameplay v1.6.0
+
+GamePlay3D is an open-source, cross-platform 3D native C++ game framework making it easy to learn and write mobile and desktop games. 
+
+<img align="right" src="https://raw.github.com/wiki/blackberry/GamePlay/img/logo.png" alt="gameplay" />
+
+- [Website](http://www.gameplay3d.org/)
+- [Forums](http://www.gameplay3d.org/forums/)
+- [Wiki](https://github.com/blackberry/GamePlay/wiki)
+- [API Reference](http://www.gameplay3d.org/api.php)
+- [Documentation](http://www.gameplay3d.org/docs.php)
 
 ## Supported Mobile Platforms
-- BlackBerry 10 and PlayBook 2.0 (using BlackBerry Native SDK)
-- Apple iOS 5.1 (using Apple XCode 4.3.2)
-- Google Android 2.3+ (using Google Android NDK, SDK API level 9+)
+- BlackBerry 10 and PlayBook (using [BlackBerry Native SDK](http://developer.blackberry.com/native/))
+- Apple iOS 5 (using Apple XCode 4)
+- Google Android 2.3+ (using Google Android NDK)
 
 ## Supported Desktop Platforms
 - Microsoft Windows 7 (using Microsoft Visual Studio 2010)
-- Apple MacOS X (using Apple XCode 4.3.2)
+- Apple MacOS X (using Apple XCode 4)
 - Linux (using CMake or CodeBlocks 10)
 
 ## Roadmap for 'next' branch
-- AI Pathfinding
-- Terrain and Water
-- Asset Pipeline improvements
+- [Version 1.7.0 Milestone](https://github.com/blackberry/GamePlay/issues?milestone=4)
 
 ## License
-The project is open sourced under the Apache 2.0 license.
+The project is open sourced under the [Apache 2.0 license](http://www.tldrlegal.com/license/apache-license-2.0-%28apache-2.0%29).
+
+## Bug Reporting
+Please log bugs under [Issues](https://github.com/blackberry/GamePlay/issues) on github.
+If you are unsure if your problem is a bug then post on the [Help Forum](http://www.gameplay3d.org/forums/viewforum.php?f=3).
 
-## Bug Reporting and Feature Requests
-If you find a bug in a Sample, or have an enhancement request, simply file an 
-[Issue](https://github.com/blackberry/GamePlay/issues) and send a message (via github messages) 
-to the Committers to the project to let them know that you have filed 
-an [Issue](https://github.com/blackberry/GamePlay/issues).
+## Feature Requests
+Please post feature requests on the [Feature Request Forum](http://www.gameplay3d.org/forums/viewforum.php?f=4). Approved feature requests will be added to the github issues list. 
 
 ## Disclaimer
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 

+ 0 - 8
cmake/README.md

@@ -1,8 +0,0 @@
-## Building with CMake
-Run the following command lines from this directory:
-* cmake ..
-* make
-
-
-
-

+ 1 - 1
gameplay-encoder/src/Scene.h

@@ -59,7 +59,7 @@ private:
 
     /**
      * Recursively calculates the ambient color of the scene starting at the given node.
-     * The ambient light color is added to the givne float array.
+     * The ambient light color is added to the given float array.
      * 
      * @param node The node in this scene to traverse from.
      * @param values Pointer to 3 floats that contains the calculated ambient color.

+ 2 - 2
gameplay-luagen/gameplay-luagen.vcxproj

@@ -78,7 +78,7 @@
     <Link>
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalLibraryDirectories>..\external-deps\tinyxml2\lib\windows;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\external-deps\tinyxml2\lib\windows\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalDependencies>tinyxml2.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
     <PreBuildEvent>
@@ -110,7 +110,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalLibraryDirectories>..\external-deps\tinyxml2\lib\windows;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\external-deps\tinyxml2\lib\windows\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalDependencies>tinyxml2.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
     <PreBuildEvent>

+ 31 - 31
gameplay-template/android/template.AndroidManifest.xml

@@ -1,32 +1,32 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="TEMPLATE_UUID"
-        android:versionCode="1"
-        android:versionName="1.0">
-
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-        
-    <!-- This is the platform API where the app was introduced. -->
-    <uses-sdk android:minSdkVersion="9" />
-	<uses-feature android:glEsVersion="0x00020000"/>
-
-    <application android:icon="@drawable/icon" android:label="@string/app_name" android:hasCode="true">
-
-        <!-- Our activity is the built-in NativeActivity framework class.
-             This will take care of integrating with our NDK code. -->
-        <activity android:name="android.app.NativeActivity"
-                android:label="@string/app_name"
-                android:configChanges="orientation|keyboardHidden"
-				android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
-				android:screenOrientation="landscape">
-            <!-- Tell NativeActivity the name of or .so -->
-            <meta-data android:name="android.app.lib_name"
-                    android:value="TEMPLATE_PROJECT" />
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="TEMPLATE_UUID"
+        android:versionCode="1"
+        android:versionName="1.0">
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+        
+    <!-- This is the platform API where the app was introduced. -->
+    <uses-sdk android:minSdkVersion="9" />
+	<uses-feature android:glEsVersion="0x00020000"/>
+
+    <application android:icon="@drawable/icon" android:label="@string/app_name" android:hasCode="true">
+
+        <!-- Our activity is the built-in NativeActivity framework class.
+             This will take care of integrating with our NDK code. -->
+        <activity android:name="android.app.NativeActivity"
+                android:label="@string/app_name"
+                android:configChanges="orientation|keyboardHidden"
+				android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+				android:screenOrientation="landscape">
+            <!-- Tell NativeActivity the name of or .so -->
+            <meta-data android:name="android.app.lib_name"
+                    android:value="TEMPLATE_PROJECT" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
 </manifest> 

+ 3 - 4
gameplay-template/android/template.build.xml

@@ -62,10 +62,9 @@
        If this is not done in place, override ${out.dex.input.absolute.dir} */
        -->
     <target name="-post-compile">
-        <copy file="../res/box.gpb" tofile="assets/res/box.gpb"/>
-        <copy file="../res/box.material" tofile="assets/res/box.material"/>
-        <copy file="../res/colored.vert" tofile="assets/res/colored.vert"/>
-        <copy file="../res/colored.frag" tofile="assets/res/colored.frag"/>
+        <copy todir="assets/res">
+            <fileset dir="../res" />
+        </copy>
     </target>
 
     <!-- Import the actual build file.

+ 4 - 4
gameplay-template/gameplay-template.vcxproj

@@ -198,7 +198,7 @@
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>lua.lib;OpenAL32.lib;OpenGL32.lib;GLU32.lib;glew32.lib;libpng14.lib;zlib.lib;gameplay.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;BulletDynamics.lib;BulletCollision.lib;LinearMath.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>GAMEPLAY_PATH/external-deps/lua/lib/windows;GAMEPLAY_PATH/external-deps/bullet/lib/windows;GAMEPLAY_PATH/external-deps/openal/lib/windows;GAMEPLAY_PATH/external-deps/oggvorbis/lib/windows;GAMEPLAY_PATH/external-deps/glew/lib/windows;GAMEPLAY_PATH/external-deps/libpng/lib/windows;GAMEPLAY_PATH/external-deps/zlib/lib/windows;GAMEPLAY_PATH/gameplay/$(Configuration)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>GAMEPLAY_PATH/external-deps/lua/lib/windows/x86;GAMEPLAY_PATH/external-deps/bullet/lib/windows/x86;GAMEPLAY_PATH/external-deps/openal/lib/windows/x86;GAMEPLAY_PATH/external-deps/oggvorbis/lib/windows/x86;GAMEPLAY_PATH/external-deps/glew/lib/windows/x86;GAMEPLAY_PATH/external-deps/libpng/lib/windows/x86;GAMEPLAY_PATH/external-deps/zlib/lib/windows/x86;GAMEPLAY_PATH/gameplay/windows/x86/$(Configuration)</AdditionalLibraryDirectories>
     </Link>
     <PostBuildEvent>
       <Command>
@@ -230,7 +230,7 @@
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>lua.lib;OpenAL32.lib;OpenGL32.lib;GLU32.lib;glew32.lib;libpng14.lib;zlib.lib;gameplay.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;BulletDynamics.lib;BulletCollision.lib;LinearMath.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>GAMEPLAY_PATH/external-deps/lua/lib/windows;GAMEPLAY_PATH/external-deps/bullet/lib/windows;GAMEPLAY_PATH/external-deps/openal/lib/windows;GAMEPLAY_PATH/external-deps/oggvorbis/lib/windows;GAMEPLAY_PATH/external-deps/glew/lib/windows;GAMEPLAY_PATH/external-deps/libpng/lib/windows;GAMEPLAY_PATH/external-deps/zlib/lib/windows;GAMEPLAY_PATH/gameplay/$(Configuration)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>GAMEPLAY_PATH/external-deps/lua/lib/windows/x64;GAMEPLAY_PATH/external-deps/bullet/lib/windows/x64;GAMEPLAY_PATH/external-deps/openal/lib/windows/x64;GAMEPLAY_PATH/external-deps/oggvorbis/lib/windows/x64;GAMEPLAY_PATH/external-deps/glew/lib/windows/x64;GAMEPLAY_PATH/external-deps/libpng/lib/windows/x64;GAMEPLAY_PATH/external-deps/zlib/lib/windows/x64;GAMEPLAY_PATH/gameplay/windows/x64/$(Configuration)</AdditionalLibraryDirectories>
     </Link>
     <PostBuildEvent>
       <Command>
@@ -262,7 +262,7 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalDependencies>lua.lib;OpenAL32.lib;OpenGL32.lib;GLU32.lib;glew32.lib;libpng14.lib;zlib.lib;gameplay.lib;BulletDynamics.lib;BulletCollision.lib;LinearMath.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>GAMEPLAY_PATH/external-deps/lua/lib/windows;GAMEPLAY_PATH/external-deps/bullet/lib/windows;GAMEPLAY_PATH/external-deps/openal/lib/windows;GAMEPLAY_PATH/external-deps/oggvorbis/lib/windows;GAMEPLAY_PATH/external-deps/glew/lib/windows;GAMEPLAY_PATH/external-deps/libpng/lib/windows;GAMEPLAY_PATH/external-deps/zlib/lib/windows;GAMEPLAY_PATH/gameplay/$(Configuration)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>GAMEPLAY_PATH/external-deps/lua/lib/windows/x86;GAMEPLAY_PATH/external-deps/bullet/lib/windows/x86;GAMEPLAY_PATH/external-deps/openal/lib/windows/x86;GAMEPLAY_PATH/external-deps/oggvorbis/lib/windows/x86;GAMEPLAY_PATH/external-deps/glew/lib/windows/x86;GAMEPLAY_PATH/external-deps/libpng/lib/windows/x86;GAMEPLAY_PATH/external-deps/zlib/lib/windows/x86;GAMEPLAY_PATH/gameplay/windows/x86/$(Configuration)</AdditionalLibraryDirectories>
     </Link>
     <PostBuildEvent>
       <Command>
@@ -294,7 +294,7 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalDependencies>lua.lib;OpenAL32.lib;OpenGL32.lib;GLU32.lib;glew32.lib;libpng14.lib;zlib.lib;gameplay.lib;BulletDynamics.lib;BulletCollision.lib;LinearMath.lib;libogg.lib;libvorbis.lib;libvorbisfile.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>GAMEPLAY_PATH/external-deps/lua/lib/windows;GAMEPLAY_PATH/external-deps/bullet/lib/windows;GAMEPLAY_PATH/external-deps/openal/lib/windows;GAMEPLAY_PATH/external-deps/oggvorbis/lib/windows;GAMEPLAY_PATH/external-deps/glew/lib/windows;GAMEPLAY_PATH/external-deps/libpng/lib/windows;GAMEPLAY_PATH/external-deps/zlib/lib/windows;GAMEPLAY_PATH/gameplay/$(Configuration)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>GAMEPLAY_PATH/external-deps/lua/lib/windows/x64;GAMEPLAY_PATH/external-deps/bullet/lib/windows/x64;GAMEPLAY_PATH/external-deps/openal/lib/windows/x64;GAMEPLAY_PATH/external-deps/oggvorbis/lib/windows/x64;GAMEPLAY_PATH/external-deps/glew/lib/windows/x64;GAMEPLAY_PATH/external-deps/libpng/lib/windows/x64;GAMEPLAY_PATH/external-deps/zlib/lib/windows/x64;GAMEPLAY_PATH/gameplay/windows/x64/$(Configuration)</AdditionalLibraryDirectories>
     </Link>
     <PostBuildEvent>
       <Command>

+ 1 - 1
gameplay-template/res/box.material

@@ -10,7 +10,7 @@ material box
             
             // uniforms
             u_worldViewProjectionMatrix = WORLD_VIEW_PROJECTION_MATRIX
-            u_inverseTransposeWorldViewMatrix = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX
+            u_inverseTransposeWorldMatrix = INVERSE_TRANSPOSE_WORLD_MATRIX
             u_diffuseColor = 1.0, 1.0, 1.0, 1.0
 
             // render state

+ 2 - 2
gameplay-template/res/colored.vert

@@ -4,7 +4,7 @@ attribute vec3 a_normal;                            // Vertex Normal (x, y, z)
 
 // Uniforms
 uniform mat4 u_worldViewProjectionMatrix;           // Matrix to transform a position to clip space.
-uniform mat4 u_inverseTransposeWorldViewMatrix;     // Matrix to transform a normal to view space.
+uniform mat4 u_inverseTransposeWorldMatrix;         // Matrix to transform a normal to view space.
 
 // Outputs
 varying vec3 v_normalVector;                        // Normal vector in view space.
@@ -19,6 +19,6 @@ void main()
     gl_Position = u_worldViewProjectionMatrix * position;
 
     // Transform normal to view space.
-    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz);
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldMatrix[0].xyz, u_inverseTransposeWorldMatrix[1].xyz, u_inverseTransposeWorldMatrix[2].xyz);
     v_normalVector = inverseTransposeWorldViewMatrix * normal;
 }

+ 1 - 1
gameplay.doxyfile

@@ -32,7 +32,7 @@ PROJECT_NAME           = gameplay
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 
-PROJECT_NUMBER         = 1.5.0
+PROJECT_NUMBER         = 1.6.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description 
 # for a project that appears at the top of each page and should give viewer 

+ 11 - 7
gameplay/.cproject

@@ -23,6 +23,7 @@
 								<option id="com.qnx.qcc.option.compiler.security.311918799" name="Enhanced Security (-fstack-protector-all)" superClass="com.qnx.qcc.option.compiler.security" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.defines.1481323494" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
+									<listOptionValue builtIn="false" value="BT_USE_NEON"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.2133604142" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/lua/include&quot;"/>
@@ -80,10 +81,11 @@
 							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.1117051584" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
 							<builder buildPath="${workspace_loc:/gameplay/Device-Release}" id="cdt.managedbuild.target.gnu.builder.base.1199322737" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
 							<tool id="com.qnx.qcc.tool.compiler.1345567866" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
-								<option id="com.qnx.qcc.option.compiler.optlevel.1056793982" name="Optimization Level" superClass="com.qnx.qcc.option.compiler.optlevel" value="com.qnx.qcc.option.compiler.optlevel.2" valueType="enumerated"/>
+								<option id="com.qnx.qcc.option.compiler.optlevel.1056793982" name="Optimization Level" superClass="com.qnx.qcc.option.compiler.optlevel" value="com.qnx.qcc.option.compiler.optlevel.1" valueType="enumerated"/>
 								<option id="com.qnx.qcc.option.compiler.security.324540233" name="Enhanced Security (-fstack-protector-all)" superClass="com.qnx.qcc.option.compiler.security" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.defines.398688299" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
+									<listOptionValue builtIn="false" value="BT_USE_NEON"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1670164593" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
@@ -95,7 +97,7 @@
 								<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>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.1770609643" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
+								<option id="com.qnx.qcc.option.compiler.qccoptions.1770609643" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
 									<listOptionValue builtIn="false" value="-Wno-psabi"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.1380846613" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -144,6 +146,7 @@
 								<option id="com.qnx.qcc.option.compiler.security.1649809766" name="Enhanced Security (-fstack-protector-all)" superClass="com.qnx.qcc.option.compiler.security" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.defines.276653249" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
+									<listOptionValue builtIn="false" value="BY_USE_NEON"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1503059677" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
@@ -155,7 +158,7 @@
 								<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>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.1366354884" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
+								<option id="com.qnx.qcc.option.compiler.qccoptions.1366354884" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
 									<listOptionValue builtIn="false" value="-Wno-psabi"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.81809638" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -207,6 +210,7 @@
 								<option id="com.qnx.qcc.option.compiler.security.1227516972" name="Enhanced Security (-fstack-protector-all)" superClass="com.qnx.qcc.option.compiler.security" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.defines.374283024" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
+									<listOptionValue builtIn="false" value="BT_USE_NEON"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1769677874" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
@@ -218,7 +222,7 @@
 								<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>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.146547607" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
+								<option id="com.qnx.qcc.option.compiler.qccoptions.146547607" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
 									<listOptionValue builtIn="false" value="-Wno-psabi"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.2007171407" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -277,7 +281,7 @@
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.ccoptions.1432778691" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions"/>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.245518255" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
+								<option id="com.qnx.qcc.option.compiler.qccoptions.245518255" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
 									<listOptionValue builtIn="false" value="-Wno-psabi"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.1038720310" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -336,7 +340,7 @@
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.ccoptions.663337616" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions"/>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.288926109" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
+								<option id="com.qnx.qcc.option.compiler.qccoptions.288926109" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
 									<listOptionValue builtIn="false" value="-Wno-psabi"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.1961855927" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -396,7 +400,7 @@
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.ccoptions.346770186" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions"/>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.1085566269" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
+								<option id="com.qnx.qcc.option.compiler.qccoptions.1085566269" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
 									<listOptionValue builtIn="false" value="-Wno-psabi"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.1658185881" superClass="com.qnx.qcc.inputType.compiler"/>

+ 1 - 0
gameplay/gameplay.vcxproj

@@ -573,6 +573,7 @@
     <ClInclude Include="src\ScriptTarget.h" />
     <ClInclude Include="src\Slider.h" />
     <ClInclude Include="src\SpriteBatch.h" />
+    <ClInclude Include="src\Stream.h" />
     <ClInclude Include="src\Technique.h" />
     <ClInclude Include="src\TextBox.h" />
     <ClInclude Include="src\Texture.h" />

+ 3 - 0
gameplay/gameplay.vcxproj.filters

@@ -1622,6 +1622,9 @@
     <ClInclude Include="src\lua\lua_LoggerLevel.h">
       <Filter>lua</Filter>
     </ClInclude>
+    <ClInclude Include="src\Stream.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\Game.inl">

+ 7 - 1
gameplay/gameplay.xcodeproj/project.pbxproj

@@ -1701,6 +1701,8 @@
 		5BD52674150F8258004C9099 /* PhysicsCollisionObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BD5266D150F8257004C9099 /* PhysicsCollisionObject.cpp */; };
 		5BD52675150F8258004C9099 /* PhysicsCollisionObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BD5266E150F8258004C9099 /* PhysicsCollisionObject.h */; };
 		5BD52676150F8258004C9099 /* PhysicsCollisionObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BD5266E150F8258004C9099 /* PhysicsCollisionObject.h */; };
+		9FC6EE731665304F00F39955 /* Stream.h in Headers */ = {isa = PBXBuildFile; fileRef = 9FC6EE721665304F00F39955 /* Stream.h */; };
+		9FC6EE741665304F00F39955 /* Stream.h in Headers */ = {isa = PBXBuildFile; fileRef = 9FC6EE721665304F00F39955 /* Stream.h */; };
 		B67EC8EB161DFC8E000B4D12 /* lua_Logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B67EC8E7161DFC8E000B4D12 /* lua_Logger.cpp */; };
 		B67EC8EC161DFC8E000B4D12 /* lua_Logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B67EC8E7161DFC8E000B4D12 /* lua_Logger.cpp */; };
 		B67EC8ED161DFC8E000B4D12 /* lua_Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = B67EC8E8161DFC8E000B4D12 /* lua_Logger.h */; };
@@ -1715,7 +1717,7 @@
 		B67EC8F9161DFCA8000B4D12 /* Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = B67EC8F5161DFCA8000B4D12 /* Logger.h */; };
 		F1616ABC1614E24B008DD8B7 /* MathUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F1616ABB1614E24B008DD8B7 /* MathUtil.cpp */; };
 		F1616ABD1614E24B008DD8B7 /* MathUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F1616ABB1614E24B008DD8B7 /* MathUtil.cpp */; };
-		F18024A51627000D001BFF87 /* gameplay-ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = F18024A31627000D001BFF87 /* gameplay-main-ios.mm */; };
+		F18024A51627000D001BFF87 /* gameplay-main-ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = F18024A31627000D001BFF87 /* gameplay-main-ios.mm */; };
 		F18024A61627000D001BFF87 /* gameplay-main-ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = F18024A31627000D001BFF87 /* gameplay-main-ios.mm */; };
 		F18024A71627000D001BFF87 /* gameplay-main-macosx.mm in Sources */ = {isa = PBXBuildFile; fileRef = F18024A41627000D001BFF87 /* gameplay-main-macosx.mm */; };
 		F18024A81627000D001BFF87 /* gameplay-main-macosx.mm in Sources */ = {isa = PBXBuildFile; fileRef = F18024A41627000D001BFF87 /* gameplay-main-macosx.mm */; };
@@ -2610,6 +2612,7 @@
 		5BD5266C150F8257004C9099 /* PhysicsCharacter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhysicsCharacter.h; path = src/PhysicsCharacter.h; sourceTree = SOURCE_ROOT; };
 		5BD5266D150F8257004C9099 /* PhysicsCollisionObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PhysicsCollisionObject.cpp; path = src/PhysicsCollisionObject.cpp; sourceTree = SOURCE_ROOT; };
 		5BD5266E150F8258004C9099 /* PhysicsCollisionObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhysicsCollisionObject.h; path = src/PhysicsCollisionObject.h; sourceTree = SOURCE_ROOT; };
+		9FC6EE721665304F00F39955 /* Stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Stream.h; path = src/Stream.h; sourceTree = SOURCE_ROOT; };
 		B67EC8E7161DFC8E000B4D12 /* lua_Logger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lua_Logger.cpp; sourceTree = "<group>"; };
 		B67EC8E8161DFC8E000B4D12 /* lua_Logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lua_Logger.h; sourceTree = "<group>"; };
 		B67EC8E9161DFC8E000B4D12 /* lua_LoggerLevel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lua_LoggerLevel.cpp; sourceTree = "<group>"; };
@@ -2889,6 +2892,7 @@
 				42CD0E2F147D8FF50000361E /* SpriteBatch.cpp */,
 				42CD0E30147D8FF50000361E /* SpriteBatch.h */,
 				42CD0E31147D8FF50000361E /* Technique.cpp */,
+				9FC6EE721665304F00F39955 /* Stream.h */,
 				42CD0E32147D8FF50000361E /* Technique.h */,
 				42CD0E33147D8FF50000361E /* Texture.cpp */,
 				42CD0E34147D8FF50000361E /* Texture.h */,
@@ -4083,6 +4087,7 @@
 				B67EC8ED161DFC8E000B4D12 /* lua_Logger.h in Headers */,
 				B67EC8F1161DFC8E000B4D12 /* lua_LoggerLevel.h in Headers */,
 				B67EC8F8161DFCA8000B4D12 /* Logger.h in Headers */,
+				9FC6EE731665304F00F39955 /* Stream.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -4513,6 +4518,7 @@
 				B67EC8EE161DFC8E000B4D12 /* lua_Logger.h in Headers */,
 				B67EC8F2161DFC8E000B4D12 /* lua_LoggerLevel.h in Headers */,
 				B67EC8F9161DFCA8000B4D12 /* Logger.h in Headers */,
+				9FC6EE741665304F00F39955 /* Stream.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 2 - 2
gameplay/res/shaders/colored.vert

@@ -14,7 +14,7 @@ varying vec3 v_color;										// Output Vertex Color
 
 // Uniforms
 uniform mat4 u_worldViewProjectionMatrix;					// Matrix to transform a position to clip space.
-uniform mat4 u_inverseTransposeWorldViewMatrix;				// Matrix to transform a normal to view space.
+uniform mat4 u_inverseTransposeWorldMatrix;				    // Matrix to transform a normal to view space.
 #if defined(SKINNING)
 uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];		// Array of 4x3 matrices
 #endif
@@ -68,7 +68,7 @@ void main()
     gl_Position = u_worldViewProjectionMatrix * position;
 
     // Transform normal to view space.
-    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz);
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldMatrix[0].xyz, u_inverseTransposeWorldMatrix[1].xyz, u_inverseTransposeWorldMatrix[2].xyz);
     v_normalVector = inverseTransposeWorldViewMatrix * normal;
 
     // Apply light.

+ 2 - 2
gameplay/res/shaders/lib/attributes-skinning.vert

@@ -37,6 +37,8 @@ vec4 getPosition()
     return _skinnedPosition;    
 }
 
+#if defined(LIGHTING)
+
 void skinTangentSpaceVector(vec3 vector, float blendWeight, int matrixIndex)
 {
     vec3 tmp;
@@ -70,8 +72,6 @@ vec3 getTangentSpaceVector(vec3 vector)
     return _skinnedNormal;
 }
 
-#if defined(LIGHTING)
-
 vec3 getNormal()
 {
     return getTangentSpaceVector(a_normal);

+ 1 - 4
gameplay/res/shaders/lib/lighting-point.vert

@@ -34,14 +34,11 @@ void applyLight(vec4 position)
     // Compute the light direction.
     vec3 lightDirection = u_pointLightPosition - positionWorldViewSpace.xyz;
    
-    vec4 vertexToPointLightDirection;
-    vertexToPointLightDirection.xyz = lightDirection;
-   
     // Attenuation
     v_pointLightAttenuation = 1.0 - dot(lightDirection * u_pointLightRangeInverse, lightDirection * u_pointLightRangeInverse);
 
     // Output light direction.
-    v_vertexToPointLightDirection =  vertexToPointLightDirection;
+    v_vertexToPointLightDirection =  lightDirection;
    
     #if defined (SPECULAR)
    

+ 9 - 5
gameplay/res/shaders/textured-bumped.frag

@@ -56,13 +56,17 @@ void main()
 
     // Light the pixel
     gl_FragColor.a = _baseColor.a;
+    #if defined(TEXTURE_DISCARD_ALPHA)
+    if (gl_FragColor.a < 0.5)
+        discard;
+    #endif
     gl_FragColor.rgb = getLitPixel();
 
-	// Global color modulation
-	#if defined(MODULATE_COLOR)
-	gl_FragColor *= u_modulateColor;
-	#endif
-	#if defined(MODULATE_ALPHA)
+    // Global color modulation
+    #if defined(MODULATE_COLOR)
+    gl_FragColor *= u_modulateColor;
+    #endif
+    #if defined(MODULATE_ALPHA)
     gl_FragColor.a *= u_modulateAlpha;
     #endif
 }

+ 2 - 2
gameplay/res/shaders/textured-bumped.vert

@@ -14,7 +14,7 @@ attribute vec4 a_blendIndices;								// Vertex blend index int u_matrixPalette
 
 // Uniforms
 uniform mat4 u_worldViewProjectionMatrix;					// Matrix to transform a position to clip space
-uniform mat4 u_inverseTransposeWorldViewMatrix;				// Matrix to transform a normal to view space
+uniform mat4 u_inverseTransposeWorldMatrix;				    // Matrix to transform a normal to view space
 #if defined(SKINNING)
 uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];		// Array of 4x3 matrices
 #endif
@@ -80,7 +80,7 @@ void main()
     gl_Position = u_worldViewProjectionMatrix * position;
 
     // Transform the normal, tangent and binormals to view space.
-    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz);
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldMatrix[0].xyz, u_inverseTransposeWorldMatrix[1].xyz, u_inverseTransposeWorldMatrix[2].xyz);
     vec3 normalVector = normalize(inverseTransposeWorldViewMatrix * normal);
     
     // Create a transform to convert a vector to tangent space.

+ 11 - 7
gameplay/res/shaders/textured-unlit.frag

@@ -25,19 +25,23 @@ void main()
 {
     // Sample the texture for the color
     gl_FragColor = texture2D(u_diffuseTexture, v_texCoord0);
-	#if defined(TEXTURE_LIGHTMAP)
+    #if defined(TEXTURE_DISCARD_ALPHA)
+    if (gl_FragColor.a < 0.5)
+        discard;
+    #endif
+    #if defined(TEXTURE_LIGHTMAP)
     #if defined(TEXCOORD1)
     vec4 lightColor = texture2D(u_lightmapTexture, v_texCoord1);
     #else
     vec4 lightColor = texture2D(u_lightmapTexture, v_texCoord0);
     #endif
     gl_FragColor.rgb *= lightColor.rgb;
-	#endif
-	// Global color modulation
-	#if defined(MODULATE_COLOR)
-	gl_FragColor *= u_modulateColor;
-	#endif
-	#if defined(MODULATE_ALPHA)
+    #endif
+    // Global color modulation
+    #if defined(MODULATE_COLOR)
+    gl_FragColor *= u_modulateColor;
+    #endif
+    #if defined(MODULATE_ALPHA)
     gl_FragColor.a *= u_modulateAlpha;
     #endif
 }

+ 4 - 0
gameplay/res/shaders/textured.frag

@@ -55,6 +55,10 @@ void main()
 
     // Light the pixel
     gl_FragColor.a = _baseColor.a;
+    #if defined(TEXTURE_DISCARD_ALPHA)
+    if (gl_FragColor.a < 0.5)
+        discard;
+    #endif
     gl_FragColor.rgb = getLitPixel();
 	
 	// Global color modulation

+ 2 - 2
gameplay/res/shaders/textured.vert

@@ -11,7 +11,7 @@ attribute vec4 a_blendIndices;								// Vertex blend index int u_matrixPalette
 
 // Uniforms
 uniform mat4 u_worldViewProjectionMatrix;					// Matrix to transform a position to clip space
-uniform mat4 u_inverseTransposeWorldViewMatrix;				// Matrix to transform a normal to view space
+uniform mat4 u_inverseTransposeWorldMatrix;				    // Matrix to transform a normal to view space
 #if defined(SKINNING)
 uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];		// Array of 4x3 matrices
 #endif
@@ -70,7 +70,7 @@ void main()
     gl_Position = u_worldViewProjectionMatrix * position;
 
     // Transform normal to view space.
-    mat3 normalMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz);
+    mat3 normalMatrix = mat3(u_inverseTransposeWorldMatrix[0].xyz, u_inverseTransposeWorldMatrix[1].xyz, u_inverseTransposeWorldMatrix[2].xyz);
     v_normalVector = normalMatrix * normal;
 
     // Apply light.

+ 1 - 1
gameplay/src/Animation.cpp

@@ -18,7 +18,7 @@ namespace gameplay
 Animation::Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, unsigned int type)
     : _controller(Game::getInstance()->getAnimationController()), _id(id), _duration(0L), _defaultClip(NULL), _clips(NULL)
 {
-    createChannel(target, propertyId, keyCount, keyTimes, keyValues, type);
+    createChannel(target, propertyId, keyCount, keyTimes, keyValues, type);
 
     // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
     release();

+ 81 - 55
gameplay/src/AudioBuffer.cpp

@@ -8,6 +8,36 @@ namespace gameplay
 // Audio buffer cache
 static std::vector<AudioBuffer*> __buffers;
 
+// Callbacks for loading an ogg file using Stream
+static size_t readStream(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    return stream->read(ptr, size, nmemb);
+}
+
+static int seekStream(void *datasource, ogg_int64_t offset, int whence)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    return !stream->seek(offset, whence);
+}
+
+static int closeStream(void *datasource)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    stream->close();
+    return 0;
+}
+
+static long tellStream(void *datasource)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    return stream->position();
+}
+
 AudioBuffer::AudioBuffer(const char* path, ALuint buffer)
     : _filePath(path), _alBuffer(buffer)
 {
@@ -63,8 +93,8 @@ AudioBuffer* AudioBuffer::create(const char* path)
     }
     
     // Load sound file.
-    FILE* file = FileSystem::openFile(path, "rb");
-    if (!file)
+    std::auto_ptr<Stream> stream(FileSystem::open(path));
+    if (stream.get() == NULL || !stream->canRead())
     {
         GP_ERROR("Failed to load audio file %s.", path);
         goto cleanup;
@@ -72,7 +102,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
     
     // Read the file header
     char header[12];
-    if (fread(header, 1, 12, file) != 12)
+    if (stream->read(header, 1, 12) != 12)
     {
         GP_ERROR("Invalid header for audio file %s.", path);
         goto cleanup;
@@ -81,7 +111,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
     // Check the file format
     if (memcmp(header, "RIFF", 4) == 0)
     {
-        if (!AudioBuffer::loadWav(file, alBuffer))
+        if (!AudioBuffer::loadWav(stream.get(), alBuffer))
         {
             GP_ERROR("Invalid wave file: %s", path);
             goto cleanup;
@@ -89,7 +119,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
     }
     else if (memcmp(header, "OggS", 4) == 0)
     {
-        if (!AudioBuffer::loadOgg(file, alBuffer))
+        if (!AudioBuffer::loadOgg(stream.get(), alBuffer))
         {
             GP_ERROR("Invalid ogg file: %s", path);
             goto cleanup;
@@ -101,10 +131,6 @@ AudioBuffer* AudioBuffer::create(const char* path)
         goto cleanup;
     }
 
-    //NOTE: loadOgg actually sets this null, so it is expected
-    if (file)    
-        fclose(file);
-
     buffer = new AudioBuffer(path, alBuffer);
 
     // Add the buffer to the cache.
@@ -113,34 +139,32 @@ AudioBuffer* AudioBuffer::create(const char* path)
     return buffer;
     
 cleanup:
-    
-    if (file)
-        fclose(file);
     if (alBuffer)
         AL_CHECK( alDeleteBuffers(1, &alBuffer) );
     return NULL;
 }
 
-bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
+bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
 {
-    GP_ASSERT(file);
-    unsigned char stream[12];
+    GP_ASSERT(stream);
+
+    unsigned char data[12];
     
     // Verify the wave fmt magic value meaning format.
-    if (fread(stream, 1, 8, file) != 8 || memcmp(stream, "fmt ", 4) != 0 )
+    if (stream->read(data, 1, 8) != 8 || memcmp(data, "fmt ", 4) != 0 )
     {
         GP_ERROR("Failed to verify the magic value for the wave file format.");
         return false;
     }
     
     unsigned int section_size;
-    section_size  = stream[7]<<24;
-    section_size |= stream[6]<<16;
-    section_size |= stream[5]<<8;
-    section_size |= stream[4];
+    section_size  = data[7]<<24;
+    section_size |= data[6]<<16;
+    section_size |= data[5]<<8;
+    section_size |= data[4];
 
     // Check for a valid pcm format.
-    if (fread(stream, 1, 2, file) != 2 || stream[1] != 0 || stream[0] != 1)
+    if (stream->read(data, 1, 2) != 2 || data[1] != 0 || data[0] != 1)
     {
         GP_ERROR("Unsupported audio file format (must be a valid PCM format).");
         return false;
@@ -148,31 +172,31 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     
     // Get the channel count (16-bit little-endian).
     int channels;
-    if (fread(stream, 1, 2, file) != 2)
+    if (stream->read(data, 1, 2) != 2)
     {
         GP_ERROR("Failed to read the wave file's channel count.");
         return false;
     }
-    channels  = stream[1]<<8;
-    channels |= stream[0];
+    channels  = data[1]<<8;
+    channels |= data[0];
     
     // Get the sample frequency (32-bit little-endian).
     ALuint frequency;
-    if (fread(stream, 1, 4, file) != 4)
+    if (stream->read(data, 1, 4) != 4)
     {
         GP_ERROR("Failed to read the wave file's sample frequency.");
         return false;
     }
 
-    frequency  = stream[3]<<24;
-    frequency |= stream[2]<<16;
-    frequency |= stream[1]<<8;
-    frequency |= stream[0];
+    frequency  = data[3]<<24;
+    frequency |= data[2]<<16;
+    frequency |= data[1]<<8;
+    frequency |= data[0];
     
     // The next 6 bytes hold the block size and bytes-per-second. 
     // We don't need that info, so just read and ignore it. 
     // We could use this later if we need to know the duration.
-    if (fread(stream, 1, 6, file) != 6)
+    if (stream->read(data, 1, 6) != 6)
     {
         GP_ERROR("Failed to read past the wave file's block size and bytes-per-second.");
         return false;
@@ -180,13 +204,13 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     
     // Get the bit depth (16-bit little-endian).
     int bits;
-    if (fread(stream, 1, 2, file) != 2)
+    if (stream->read(data, 1, 2) != 2)
     {
         GP_ERROR("Failed to read the wave file's bit depth.");
         return false;
     }
-    bits  = stream[1]<<8;
-    bits |= stream[0];
+    bits  = data[1]<<8;
+    bits |= data[0];
     
     // Now convert the given channel count and bit depth into an OpenAL format. 
     ALuint format = 0;
@@ -216,7 +240,7 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
         unsigned int length = section_size - 16;
 
         // Extension size is 2 bytes.
-        if (fread(stream, 1, length, file) != length)
+        if (stream->read(data, 1, length) != length)
         {
             GP_ERROR("Failed to read extension size from wave file.");
             return false;
@@ -227,32 +251,32 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     while (true)
     {
         // Check if we are at the end of the file without reading the data.
-        if (feof(file))
+        if (stream->eof())
         {
             GP_ERROR("Failed to load wave file; file appears to have no data.");
             return false;
         }
 
         // Read in the type of the next section of the file.
-        if (fread(stream, 1, 4, file) != 4)
+        if (stream->read(data, 1, 4) != 4)
         {
             GP_ERROR("Failed to read next section type from wave file.");
             return false;
         }
 
         // Data chunk.
-        if (memcmp(stream, "data", 4) == 0)
+        if (memcmp(data, "data", 4) == 0)
         {
             // Read how much data is remaining and buffer it up.
             unsigned int dataSize;
-            if (fread(&dataSize, sizeof(int), 1, file) != 1)
+            if (stream->read(&dataSize, sizeof(int), 1) != 1)
             {
                 GP_ERROR("Failed to read size of data section from wave file.");
                 return false;
             }
 
             char* data = new char[dataSize];
-            if (fread(data, sizeof(char), dataSize, file) != dataSize)
+            if (stream->read(data, sizeof(char), dataSize) != dataSize)
             {
                 GP_ERROR("Failed to load wave file; file is missing data.");
                 SAFE_DELETE_ARRAY(data);
@@ -281,22 +305,22 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
         {
             // Store the name of the chunk so we can report errors informatively.
             char chunk[5] = { 0 };
-            memcpy(chunk, stream, 4);
+            memcpy(chunk, data, 4);
 
             // Read the chunk size.
-            if (fread(stream, 1, 4, file) != 4)
+            if (stream->read(data, 1, 4) != 4)
             {
                 GP_ERROR("Failed to read size of '%s' chunk from wave file.", chunk);
                 return false;
             }
 
-            section_size  = stream[3]<<24;
-            section_size |= stream[2]<<16;
-            section_size |= stream[1]<<8;
-            section_size |= stream[0];
+            section_size  = data[3]<<24;
+            section_size |= data[2]<<16;
+            section_size |= data[1]<<8;
+            section_size |= data[0];
 
             // Seek past the chunk.
-            if (fseek(file, section_size, SEEK_CUR) != 0)
+            if (stream->seek(section_size, SEEK_CUR) == false)
             {
                 GP_ERROR("Failed to seek past '%s' chunk in wave file.", chunk);
                 return false;
@@ -304,10 +328,10 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
         }
     }
 }
-    
-bool AudioBuffer::loadOgg(FILE*& file, ALuint buffer)
+
+bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)
 {
-    GP_ASSERT(file);
+    GP_ASSERT(stream);
 
     OggVorbis_File ogg_file;
     vorbis_info* info;
@@ -316,11 +340,16 @@ bool AudioBuffer::loadOgg(FILE*& file, ALuint buffer)
     int section;
     long size = 0;
 
-    rewind(file);
+    stream->rewind();
 
-    if ((result = ov_open(file, &ogg_file, NULL, 0)) < 0)
+    ov_callbacks callbacks;
+    callbacks.read_func = readStream;
+    callbacks.seek_func = seekStream;
+    callbacks.close_func = closeStream;
+    callbacks.tell_func = tellStream;
+
+    if ((result = ov_open_callbacks(stream, &ogg_file, NULL, 0, callbacks)) < 0)
     {
-        fclose(file);
         GP_ERROR("Failed to open ogg file.");
         return false;
     }
@@ -367,9 +396,6 @@ bool AudioBuffer::loadOgg(FILE*& file, ALuint buffer)
     SAFE_DELETE_ARRAY(data);
     ov_clear(&ogg_file);
 
-    // ov_clear actually closes the file pointer as well.
-    file = 0;
-
     return true;
 }
 

+ 3 - 2
gameplay/src/AudioBuffer.h

@@ -2,6 +2,7 @@
 #define AUDIOBUFFER_H_
 
 #include "Ref.h"
+#include "Stream.h"
 
 namespace gameplay
 {
@@ -43,9 +44,9 @@ private:
      */
     static AudioBuffer* create(const char* path);
     
-    static bool loadWav(FILE* file, ALuint buffer);
+    static bool loadWav(Stream* stream, ALuint buffer);
     
-    static bool loadOgg(FILE*& file, ALuint buffer);
+    static bool loadOgg(Stream* stream, ALuint buffer);
 
     std::string _filePath;
     ALuint _alBuffer;

+ 1 - 0
gameplay/src/Base.h

@@ -21,6 +21,7 @@
 #include <set>
 #include <stack>
 #include <map>
+#include <queue>
 #include <algorithm>
 #include <limits>
 #include <functional>

+ 84 - 102
gameplay/src/Bundle.cpp

@@ -32,7 +32,7 @@ namespace gameplay
 static std::vector<Bundle*> __bundleCache;
 
 Bundle::Bundle(const char* path) :
-    _path(path), _referenceCount(0), _references(NULL), _file(NULL), _trackedNodes(NULL)
+    _path(path), _referenceCount(0), _references(NULL), _stream(NULL), _trackedNodes(NULL)
 {
 }
 
@@ -49,10 +49,9 @@ Bundle::~Bundle()
 
     SAFE_DELETE_ARRAY(_references);
 
-    if (_file)
+    if (_stream)
     {
-        fclose(_file);
-        _file = NULL;
+        SAFE_DELETE(_stream);
     }
 }
 
@@ -61,7 +60,7 @@ bool Bundle::readArray(unsigned int* length, T** ptr)
 {
     GP_ASSERT(length);
     GP_ASSERT(ptr);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     if (!read(length))
     {
@@ -71,7 +70,7 @@ bool Bundle::readArray(unsigned int* length, T** ptr)
     if (*length > 0)
     {
         *ptr = new T[*length];
-        if (fread(*ptr, sizeof(T), *length, _file) != *length)
+        if (_stream->read(*ptr, sizeof(T), *length) != *length)
         {
             GP_ERROR("Failed to read an array of data from bundle (into an array).");
             SAFE_DELETE_ARRAY(*ptr);
@@ -85,7 +84,7 @@ template <class T>
 bool Bundle::readArray(unsigned int* length, std::vector<T>* values)
 {
     GP_ASSERT(length);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     if (!read(length))
     {
@@ -95,7 +94,7 @@ bool Bundle::readArray(unsigned int* length, std::vector<T>* values)
     if (*length > 0 && values)
     {
         values->resize(*length);
-        if (fread(&(*values)[0], sizeof(T), *length, _file) != *length)
+        if (_stream->read(&(*values)[0], sizeof(T), *length) != *length)
         {
             GP_ERROR("Failed to read an array of data from bundle (into a std::vector).");
             return false;
@@ -108,7 +107,7 @@ template <class T>
 bool Bundle::readArray(unsigned int* length, std::vector<T>* values, unsigned int readSize)
 {
     GP_ASSERT(length);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
     GP_ASSERT(sizeof(T) >= readSize);
 
     if (!read(length))
@@ -119,7 +118,7 @@ bool Bundle::readArray(unsigned int* length, std::vector<T>* values, unsigned in
     if (*length > 0 && values)
     {
         values->resize(*length);
-        if (fread(&(*values)[0], readSize, *length, _file) != *length)
+        if (_stream->read(&(*values)[0], readSize, *length) != *length)
         {
             GP_ERROR("Failed to read an array of data from bundle (into a std::vector with a specified single element read size).");
             return false;
@@ -128,12 +127,12 @@ bool Bundle::readArray(unsigned int* length, std::vector<T>* values, unsigned in
     return true;
 }
 
-static std::string readString(FILE* fp)
+static std::string readString(Stream* stream)
 {
-    GP_ASSERT(fp);
+    GP_ASSERT(stream);
 
     unsigned int length;
-    if (fread(&length, 4, 1, fp) != 1)
+    if (stream->read(&length, 4, 1) != 1)
     {
         GP_ERROR("Failed to read the length of a string from a bundle.");
         return std::string();
@@ -146,7 +145,7 @@ static std::string readString(FILE* fp)
     if (length > 0)
     {
         str.resize(length);
-        if (fread(&str[0], 1, length, fp) != length)
+        if (stream->read(&str[0], 1, length) != length)
         {
             GP_ERROR("Failed to read string from bundle.");
             return std::string();
@@ -173,8 +172,8 @@ Bundle* Bundle::create(const char* path)
     }
 
     // Open the bundle.
-    FILE* fp = FileSystem::openFile(path, "rb");
-    if (!fp)
+    Stream* stream = FileSystem::open(path);
+    if (!stream)
     {
         GP_ERROR("Failed to open file '%s'.", path);
         return NULL;
@@ -182,46 +181,34 @@ Bundle* Bundle::create(const char* path)
 
     // Read the GPB header info.
     char sig[9];
-    if (fread(sig, 1, 9, fp) != 9 || memcmp(sig, "\xABGPB\xBB\r\n\x1A\n", 9) != 0)
+    if (stream->read(sig, 1, 9) != 9 || memcmp(sig, "\xABGPB\xBB\r\n\x1A\n", 9) != 0)
     {
+        SAFE_DELETE(stream);
         GP_ERROR("Invalid GPB header for bundle '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
     // Read version.
     unsigned char ver[2];
-    if (fread(ver, 1, 2, fp) != 2)
+    if (stream->read(ver, 1, 2) != 2)
     {
+        SAFE_DELETE(stream);
         GP_ERROR("Failed to read GPB version for bundle '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
     if (ver[0] != BUNDLE_VERSION_MAJOR || ver[1] != BUNDLE_VERSION_MINOR)
     {
+        SAFE_DELETE(stream);
         GP_ERROR("Unsupported version (%d.%d) for bundle '%s' (expected %d.%d).", (int)ver[0], (int)ver[1], path, BUNDLE_VERSION_MAJOR, BUNDLE_VERSION_MINOR);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
     // Read ref table.
     unsigned int refCount;
-    if (fread(&refCount, 4, 1, fp) != 1)
+    if (stream->read(&refCount, 4, 1) != 1)
     {
+        SAFE_DELETE(stream);
         GP_ERROR("Failed to read ref table for bundle '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -229,15 +216,12 @@ Bundle* Bundle::create(const char* path)
     Reference* refs = new Reference[refCount];
     for (unsigned int i = 0; i < refCount; ++i)
     {
-        if ((refs[i].id = readString(fp)).empty() ||
-            fread(&refs[i].type, 4, 1, fp) != 1 ||
-            fread(&refs[i].offset, 4, 1, fp) != 1)
+        if ((refs[i].id = readString(stream)).empty() ||
+            stream->read(&refs[i].type, 4, 1) != 1 ||
+            stream->read(&refs[i].offset, 4, 1) != 1)
         {
+            SAFE_DELETE(stream);
             GP_ERROR("Failed to read ref number %d for bundle '%s'.", i, path);
-            if (fclose(fp) != 0)
-            {
-                GP_ERROR("Failed to close file '%s'.", path);
-            }
             SAFE_DELETE_ARRAY(refs);
             return NULL;
         }
@@ -247,7 +231,7 @@ Bundle* Bundle::create(const char* path)
     Bundle* bundle = new Bundle(path);
     bundle->_referenceCount = refCount;
     bundle->_references = refs;
-    bundle->_file = fp;
+    bundle->_stream = stream;
 
     return bundle;
 }
@@ -281,8 +265,8 @@ void Bundle::clearLoadSession()
 
 const char* Bundle::getIdFromOffset() const
 {
-    GP_ASSERT(_file);
-    return getIdFromOffset((unsigned int) ftell(_file));
+    GP_ASSERT(_stream);
+    return getIdFromOffset((unsigned int) _stream->position());
 }
 
 const char* Bundle::getIdFromOffset(unsigned int offset) const
@@ -318,8 +302,8 @@ Bundle::Reference* Bundle::seekTo(const char* id, unsigned int type)
     }
 
     // Seek to the offset of this object.
-    GP_ASSERT(_file);
-    if (fseek(_file, ref->offset, SEEK_SET) != 0)
+    GP_ASSERT(_stream);
+    if (_stream->seek(ref->offset, SEEK_SET) == false)
     {
         GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", id, _path.c_str());
         return NULL;
@@ -331,7 +315,7 @@ Bundle::Reference* Bundle::seekTo(const char* id, unsigned int type)
 Bundle::Reference* Bundle::seekToFirstType(unsigned int type)
 {
     GP_ASSERT(_references);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     for (unsigned int i = 0; i < _referenceCount; ++i)
     {
@@ -339,7 +323,7 @@ Bundle::Reference* Bundle::seekToFirstType(unsigned int type)
         if (ref->type == type)
         {
             // Found a match.
-            if (fseek(_file, ref->offset, SEEK_SET) != 0)
+            if (_stream->seek(ref->offset, SEEK_SET) == false)
             {
                 GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
                 return NULL;
@@ -352,22 +336,22 @@ Bundle::Reference* Bundle::seekToFirstType(unsigned int type)
 
 bool Bundle::read(unsigned int* ptr)
 {
-    return fread(ptr, sizeof(unsigned int), 1, _file) == 1;
+    return _stream->read(ptr, sizeof(unsigned int), 1) == 1;
 }
 
 bool Bundle::read(unsigned char* ptr)
 {
-    return fread(ptr, sizeof(unsigned char), 1, _file) == 1;
+    return _stream->read(ptr, sizeof(unsigned char), 1) == 1;
 }
 
 bool Bundle::read(float* ptr)
 {
-    return fread(ptr, sizeof(float), 1, _file) == 1;
+    return _stream->read(ptr, sizeof(float), 1) == 1;
 }
 
 bool Bundle::readMatrix(float* m)
 {
-    return fread(m, sizeof(float), 16, _file) == 16;
+    return _stream->read(m, sizeof(float), 16) == 16;
 }
 
 Scene* Bundle::loadScene(const char* id)
@@ -419,7 +403,7 @@ Scene* Bundle::loadScene(const char* id)
         }
     }
     // Read active camera.
-    std::string xref = readString(_file);
+    std::string xref = readString(_stream);
     if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
     {
         Node* node = scene->findNode(xref.c_str() + 1, true);
@@ -453,14 +437,14 @@ Scene* Bundle::loadScene(const char* id)
 
     // Parse animations.
     GP_ASSERT(_references);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
     for (unsigned int i = 0; i < _referenceCount; ++i)
     {
         Reference* ref = &_references[i];
         if (ref->type == BUNDLE_TYPE_ANIMATIONS)
         {
             // Found a match.
-            if (fseek(_file, ref->offset, SEEK_SET) != 0)
+            if (_stream->seek(ref->offset, SEEK_SET) == false)
             {
                 GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
                 return NULL;
@@ -483,7 +467,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
 {
     GP_ASSERT(id);
     GP_ASSERT(_references);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     clearLoadSession();
 
@@ -499,7 +483,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
         Reference* ref = &_references[i];
         if (ref->type == BUNDLE_TYPE_ANIMATIONS)
         {
-            if (fseek(_file, ref->offset, SEEK_SET) != 0)
+            if (_stream->seek(ref->offset, SEEK_SET) == false)
             {
                 GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
                 SAFE_DELETE(_trackedNodes);
@@ -517,7 +501,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
 
             for (unsigned int j = 0; j < animationCount; j++)
             {
-                const std::string id = readString(_file);
+                const std::string id = readString(_stream);
 
                 // Read the number of animation channels in this animation.
                 unsigned int animationChannelCount;
@@ -532,7 +516,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
                 for (unsigned int k = 0; k < animationChannelCount; k++)
                 {
                     // Read target id.
-                    std::string targetId = readString(_file);
+                    std::string targetId = readString(_stream);
                     if (targetId.empty())
                     {
                         GP_ERROR("Failed to read target id for animation '%s'.", id.c_str());
@@ -627,7 +611,7 @@ bool Bundle::skipNode()
 {
     const char* id = getIdFromOffset();
     GP_ASSERT(id);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     // Skip the node's type.
     unsigned int nodeType;
@@ -638,12 +622,12 @@ bool Bundle::skipNode()
     }
 
     // Skip over the node's transform and parent ID.
-    if (fseek(_file, sizeof(float) * 16, SEEK_CUR) != 0)
+    if (_stream->seek(sizeof(float) * 16, SEEK_CUR) == false)
     {
         GP_ERROR("Failed to skip over node transform for node '%s'.", id);
         return false;
     }
-    readString(_file);
+    readString(_stream);
 
     // Skip over the node's children.
     unsigned int childrenCount;
@@ -673,7 +657,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
 {
     const char* id = getIdFromOffset();
     GP_ASSERT(id);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     // If we are tracking nodes and it's not in the set yet, add it.
     if (_trackedNodes)
@@ -725,7 +709,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
 
     // Read transform.
     float transform[16];
-    if (fread(transform, sizeof(float), 16, _file) != 16)
+    if (_stream->read(transform, sizeof(float), 16) != 16)
     {
         GP_ERROR("Failed to read transform for node '%s'.", id);
         SAFE_RELEASE(node);
@@ -734,7 +718,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
     setTransform(transform, node);
 
     // Skip the parent ID.
-    readString(_file);
+    readString(_stream);
 
     // Read children.
     unsigned int childrenCount;
@@ -952,12 +936,10 @@ Light* Bundle::readLight()
 
 Model* Bundle::readModel(const char* nodeId)
 {
-    // Read mesh.
-    Mesh* mesh = NULL;
-    std::string xref = readString(_file);
+    std::string xref = readString(_stream);
     if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
     {
-        mesh = loadMesh(xref.c_str() + 1, nodeId);
+        Mesh* mesh = loadMesh(xref.c_str() + 1, nodeId);
         if (mesh)
         {
             Model* model = Model::create(mesh);
@@ -1035,7 +1017,7 @@ MeshSkin* Bundle::readMeshSkin()
     // Read joint xref strings for all joints in the list.
     for (unsigned int i = 0; i < jointCount; i++)
     {
-        skinData->joints.push_back(readString(_file));
+        skinData->joints.push_back(readString(_stream));
     }
 
     // Read bind poses.
@@ -1072,7 +1054,7 @@ MeshSkin* Bundle::readMeshSkin()
 
 void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
 {
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     for (size_t i = 0, skinCount = _meshSkins.size(); i < skinCount; ++i)
     {
@@ -1144,12 +1126,12 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
                         seekTo(nodeId.c_str(), ref->type);
 
                         // Skip over the node type (1 unsigned int) and transform (16 floats) and read the parent id.
-                        if (fseek(_file, sizeof(unsigned int) + sizeof(float)*16, SEEK_CUR) != 0)
+                        if (_stream->seek(sizeof(unsigned int) + sizeof(float)*16, SEEK_CUR) == false)
                         {
                             GP_ERROR("Failed to skip over node type and transform for node '%s' in bundle '%s'.", nodeId.c_str(), _path.c_str());
                             return;
                         }
-                        std::string parentID = readString(_file);
+                        std::string parentID = readString(_stream);
 
                         if (!parentID.empty())
                             nodeId = parentID;
@@ -1185,7 +1167,7 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
 
 void Bundle::readAnimation(Scene* scene)
 {
-    const std::string animationId = readString(_file);
+    const std::string animationId = readString(_stream);
 
     // Read the number of animation channels in this animation.
     unsigned int animationChannelCount;
@@ -1223,7 +1205,7 @@ Animation* Bundle::readAnimationChannel(Scene* scene, Animation* animation, cons
     GP_ASSERT(animationId);
 
     // Read target id.
-    std::string targetId = readString(_file);
+    std::string targetId = readString(_stream);
     if (targetId.empty())
     {
         GP_ERROR("Failed to read target id for animation '%s'.", animationId);
@@ -1331,11 +1313,11 @@ Mesh* Bundle::loadMesh(const char* id)
 
 Mesh* Bundle::loadMesh(const char* id, const char* nodeId)
 {
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
     GP_ASSERT(id);
 
     // Save the file position.
-    long position = ftell(_file);
+    long position = _stream->position();
     if (position == -1L)
     {
         GP_ERROR("Failed to save the current file position before loading mesh '%s'.", id);
@@ -1395,7 +1377,7 @@ Mesh* Bundle::loadMesh(const char* id, const char* nodeId)
     SAFE_DELETE(meshData);
 
     // Restore file pointer.
-    if (fseek(_file, position, SEEK_SET) != 0)
+    if (_stream->seek(position, SEEK_SET) == false)
     {
         GP_ERROR("Failed to restore file pointer after loading mesh '%s'.", id);
         return NULL;
@@ -1408,7 +1390,7 @@ Bundle::MeshData* Bundle::readMeshData()
 {
     // Read vertex format/elements.
     unsigned int vertexElementCount;
-    if (fread(&vertexElementCount, 4, 1, _file) != 1)
+    if (_stream->read(&vertexElementCount, 4, 1) != 1)
     {
         GP_ERROR("Failed to load vertex element count.");
         return NULL;
@@ -1423,13 +1405,13 @@ Bundle::MeshData* Bundle::readMeshData()
     for (unsigned int i = 0; i < vertexElementCount; ++i)
     {
         unsigned int vUsage, vSize;
-        if (fread(&vUsage, 4, 1, _file) != 1)
+        if (_stream->read(&vUsage, 4, 1) != 1)
         {
             GP_ERROR("Failed to load vertex usage.");
             SAFE_DELETE_ARRAY(vertexElements);
             return NULL;
         }
-        if (fread(&vSize, 4, 1, _file) != 1)
+        if (_stream->read(&vSize, 4, 1) != 1)
         {
             GP_ERROR("Failed to load vertex size.");
             SAFE_DELETE_ARRAY(vertexElements);
@@ -1445,7 +1427,7 @@ Bundle::MeshData* Bundle::readMeshData()
 
     // Read vertex data.
     unsigned int vertexByteCount;
-    if (fread(&vertexByteCount, 4, 1, _file) != 1)
+    if (_stream->read(&vertexByteCount, 4, 1) != 1)
     {
         GP_ERROR("Failed to load vertex byte count.");
         SAFE_DELETE(meshData);
@@ -1461,7 +1443,7 @@ Bundle::MeshData* Bundle::readMeshData()
     GP_ASSERT(meshData->vertexFormat.getVertexSize());
     meshData->vertexCount = vertexByteCount / meshData->vertexFormat.getVertexSize();
     meshData->vertexData = new unsigned char[vertexByteCount];
-    if (fread(meshData->vertexData, 1, vertexByteCount, _file) != vertexByteCount)
+    if (_stream->read(meshData->vertexData, 1, vertexByteCount) != vertexByteCount)
     {
         GP_ERROR("Failed to load vertex data.");
         SAFE_DELETE(meshData);
@@ -1469,13 +1451,13 @@ Bundle::MeshData* Bundle::readMeshData()
     }
 
     // Read mesh bounds (bounding box and bounding sphere).
-    if (fread(&meshData->boundingBox.min.x, 4, 3, _file) != 3 || fread(&meshData->boundingBox.max.x, 4, 3, _file) != 3)
+    if (_stream->read(&meshData->boundingBox.min.x, 4, 3) != 3 || _stream->read(&meshData->boundingBox.max.x, 4, 3) != 3)
     {
         GP_ERROR("Failed to load mesh bounding box.");
         SAFE_DELETE(meshData);
         return NULL;
     }
-    if (fread(&meshData->boundingSphere.center.x, 4, 3, _file) != 3 || fread(&meshData->boundingSphere.radius, 4, 1, _file) != 1)
+    if (_stream->read(&meshData->boundingSphere.center.x, 4, 3) != 3 || _stream->read(&meshData->boundingSphere.radius, 4, 1) != 1)
     {
         GP_ERROR("Failed to load mesh bounding sphere.");
         SAFE_DELETE(meshData);
@@ -1484,7 +1466,7 @@ Bundle::MeshData* Bundle::readMeshData()
 
     // Read mesh parts.
     unsigned int meshPartCount;
-    if (fread(&meshPartCount, 4, 1, _file) != 1)
+    if (_stream->read(&meshPartCount, 4, 1) != 1)
     {
         GP_ERROR("Failed to load mesh part count.");
         SAFE_DELETE(meshData);
@@ -1494,19 +1476,19 @@ Bundle::MeshData* Bundle::readMeshData()
     {
         // Read primitive type, index format and index count.
         unsigned int pType, iFormat, iByteCount;
-        if (fread(&pType, 4, 1, _file) != 1)
+        if (_stream->read(&pType, 4, 1) != 1)
         {
             GP_ERROR("Failed to load primitive type for mesh part with index %d.", i);
             SAFE_DELETE(meshData);
             return NULL;
         }
-        if (fread(&iFormat, 4, 1, _file) != 1)
+        if (_stream->read(&iFormat, 4, 1) != 1)
         {
             GP_ERROR("Failed to load index format for mesh part with index %d.", i);
             SAFE_DELETE(meshData);
             return NULL;
         }
-        if (fread(&iByteCount, 4, 1, _file) != 1)
+        if (_stream->read(&iByteCount, 4, 1) != 1)
         {
             GP_ERROR("Failed to load index byte count for mesh part with index %d.", i);
             SAFE_DELETE(meshData);
@@ -1540,7 +1522,7 @@ Bundle::MeshData* Bundle::readMeshData()
         partData->indexCount = iByteCount / indexSize;
 
         partData->indexData = new unsigned char[iByteCount];
-        if (fread(partData->indexData, 1, iByteCount, _file) != iByteCount)
+        if (_stream->read(partData->indexData, 1, iByteCount) != iByteCount)
         {
             GP_ERROR("Failed to read index data for mesh part with index %d.", i);
             SAFE_DELETE(meshData);
@@ -1601,7 +1583,7 @@ Bundle::MeshData* Bundle::readMeshData(const char* url)
 Font* Bundle::loadFont(const char* id)
 {
     GP_ASSERT(id);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     // Seek to the specified font.
     Reference* ref = seekTo(id, BUNDLE_TYPE_FONT);
@@ -1612,7 +1594,7 @@ Font* Bundle::loadFont(const char* id)
     }
 
     // Read font family.
-    std::string family = readString(_file);
+    std::string family = readString(_stream);
     if (family.empty())
     {
         GP_ERROR("Failed to read font family for font '%s'.", id);
@@ -1621,23 +1603,23 @@ Font* Bundle::loadFont(const char* id)
 
     // Read font style and size.
     unsigned int style, size;
-    if (fread(&style, 4, 1, _file) != 1)
+    if (_stream->read(&style, 4, 1) != 1)
     {
         GP_ERROR("Failed to read style for font '%s'.", id);
         return NULL;
     }
-    if (fread(&size, 4, 1, _file) != 1)
+    if (_stream->read(&size, 4, 1) != 1)
     {
         GP_ERROR("Failed to read size for font '%s'.", id);
         return NULL;
     }
 
     // Read character set.
-    std::string charset = readString(_file);
+    std::string charset = readString(_stream);
 
     // Read font glyphs.
     unsigned int glyphCount;
-    if (fread(&glyphCount, 4, 1, _file) != 1)
+    if (_stream->read(&glyphCount, 4, 1) != 1)
     {
         GP_ERROR("Failed to read glyph count for font '%s'.", id);
         return NULL;
@@ -1649,7 +1631,7 @@ Font* Bundle::loadFont(const char* id)
     }
 
     Font::Glyph* glyphs = new Font::Glyph[glyphCount];
-    if (fread(glyphs, sizeof(Font::Glyph), glyphCount, _file) != glyphCount)
+    if (_stream->read(glyphs, sizeof(Font::Glyph), glyphCount) != glyphCount)
     {
         GP_ERROR("Failed to read glyphs for font '%s'.", id);
         SAFE_DELETE_ARRAY(glyphs);
@@ -1658,19 +1640,19 @@ Font* Bundle::loadFont(const char* id)
 
     // Read texture attributes.
     unsigned int width, height, textureByteCount;
-    if (fread(&width, 4, 1, _file) != 1)
+    if (_stream->read(&width, 4, 1) != 1)
     {
         GP_ERROR("Failed to read texture width for font '%s'.", id);
         SAFE_DELETE_ARRAY(glyphs);
         return NULL;
     }
-    if (fread(&height, 4, 1, _file) != 1)
+    if (_stream->read(&height, 4, 1) != 1)
     {
         GP_ERROR("Failed to read texture height for font '%s'.", id);
         SAFE_DELETE_ARRAY(glyphs);
         return NULL;
     }
-    if (fread(&textureByteCount, 4, 1, _file) != 1)
+    if (_stream->read(&textureByteCount, 4, 1) != 1)
     {
         GP_ERROR("Failed to read texture byte count for font '%s'.", id);
         SAFE_DELETE_ARRAY(glyphs);
@@ -1685,7 +1667,7 @@ Font* Bundle::loadFont(const char* id)
 
     // Read texture data.
     unsigned char* textureData = new unsigned char[textureByteCount];
-    if (fread(textureData, 1, textureByteCount, _file) != textureByteCount)
+    if (_stream->read(textureData, 1, textureByteCount) != textureByteCount)
     {
         GP_ERROR("Failed to read texture data for font '%s'.", id);
         SAFE_DELETE_ARRAY(glyphs);

+ 1 - 1
gameplay/src/Bundle.h

@@ -429,7 +429,7 @@ private:
     std::string _path;
     unsigned int _referenceCount;
     Reference* _references;
-    FILE* _file;
+    Stream* _stream;
 
     std::vector<MeshSkinData*> _meshSkins;
     std::map<std::string, Node*>* _trackedNodes;

+ 10 - 8
gameplay/src/Container.cpp

@@ -278,6 +278,7 @@ void Container::removeControl(const char* id)
         Control* c = *it;
         if (strcmp(id, c->getId()) == 0)
         {
+            c->_parent = NULL;
             SAFE_RELEASE(c);
             _controls.erase(it);
             return;
@@ -293,6 +294,7 @@ void Container::removeControl(Control* control)
     {
         if (*it == control)
         {
+            control->_parent = NULL;
             SAFE_RELEASE(control);
             _controls.erase(it);
             return;
@@ -1107,11 +1109,11 @@ bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
         }
         break;
     case Touch::TOUCH_RELEASE:
-		if (eventConsumed)
-		{
-			if (_contactIndices > 0)
-				_contactIndices--;
-		}
+        if (eventConsumed)
+        {
+            if (_contactIndices > 0)
+                _contactIndices--;
+        }
         break;
     }
 
@@ -1126,10 +1128,10 @@ bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
 
     release();
     if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
-		y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
-    	return (_consumeInputEvents | eventConsumed);
+        y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
+        return (_consumeInputEvents | eventConsumed);
     else
-    	return eventConsumed;
+        return eventConsumed;
 }
 
 Container::Scroll Container::getScroll(const char* scroll)

+ 8 - 8
gameplay/src/Container.h

@@ -319,7 +319,7 @@ protected:
     void addControls(Theme* theme, Properties* properties);
 
     /**
-     * Draws a sprite batch for the specified clipping rect .
+     * Draws a sprite batch for the specified clipping rect.
      *
      * @param spriteBatch The sprite batch to use.
      * @param clip The clipping rectangle.
@@ -444,13 +444,13 @@ protected:
      */
     bool _scrolling;
     /** 
-	 * First scrolling touch x position
-	 */
-	int _scrollingVeryFirstX;
-	/**
-	 * First scrolling touch y position
-	 */
-	int _scrollingVeryFirstY;
+     * First scrolling touch x position
+     */
+    int _scrollingVeryFirstX;
+    /**
+     * First scrolling touch y position
+     */
+    int _scrollingVeryFirstY;
     /**
      * First scrolling touch x position since last change in direction.
      */ 

+ 67 - 13
gameplay/src/Control.cpp

@@ -7,7 +7,7 @@ namespace gameplay
 
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
-    _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT), _autoWidth(false), _autoHeight(false), _listeners(NULL),
+    _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false), _autoWidth(false), _autoHeight(false), _listeners(NULL), _visible(true),
     _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(0), _parent(NULL), _styleOverridden(false), _skin(NULL)
 {
     addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
@@ -17,7 +17,7 @@ Control::~Control()
 {
     if (_listeners)
     {
-        for (std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
+        for (std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); ++itr)
         {
             std::list<Listener*>* list = itr->second;
             SAFE_DELETE(list);
@@ -208,6 +208,8 @@ float Control::getHeight() const
 void Control::setAlignment(Alignment alignment)
 {
     _alignment = alignment;
+    _isAlignmentSet = true;
+    _dirty = true;
 }
 
 Control::Alignment Control::getAlignment() const
@@ -243,6 +245,27 @@ bool Control::getAutoHeight() const
     return _autoHeight;
 }
 
+void Control::setVisible(bool visible)
+{
+    if (visible && !_visible)
+    {
+        setEnabled(true);
+        _visible = true;
+        _dirty = true;
+    }
+    else if (!visible && _visible)
+    {
+        setEnabled(false);
+        _visible = false;
+        _dirty = true;
+    }
+}
+
+bool Control::isVisible() const
+{
+    return _visible;
+}
+
 void Control::setOpacity(float opacity, unsigned char states)
 {
     overrideStyle();
@@ -596,19 +619,21 @@ Control::State Control::getState() const
     return _state;
 }
 
-void Control::disable()
+void Control::setEnabled(bool enabled)
 {
-    _state = DISABLED;
-    _dirty = true;
-}
-
-void Control::enable()
-{
-    _state = NORMAL;
-    _dirty = true;
+	if (enabled && _state == Control::DISABLED)
+	{
+		_state = Control::NORMAL;
+		_dirty = true;
+	}
+	else if (!enabled && _state != Control::DISABLED)
+	{
+		_state = Control::DISABLED;
+		_dirty = true;
+	}
 }
 
-bool Control::isEnabled()
+bool Control::isEnabled() const
 {
     return _state != DISABLED;
 }
@@ -694,6 +719,29 @@ void Control::addListener(Control::Listener* listener, int eventFlags)
     }
 }
 
+void Control::removeListener(Control::Listener* listener)
+{
+    if (_listeners == NULL || listener == NULL)
+        return;
+
+    for (std::map<Listener::EventType, std::list<Listener*>*>::iterator itr = _listeners->begin(); itr != _listeners->end();)
+    {
+        itr->second->remove(listener);
+
+        if(itr->second->empty())
+        {
+            std::list<Listener*>* list = itr->second;
+            _listeners->erase(itr++);
+            SAFE_DELETE(list);
+        }
+        else
+            ++itr;
+    }
+
+    if (_listeners->empty())
+        SAFE_DELETE(_listeners);
+}
+
 void Control::addSpecificListener(Control::Listener* listener, Listener::EventType eventType)
 {
     GP_ASSERT(listener);
@@ -809,7 +857,7 @@ void Control::notifyListeners(Listener::EventType eventType)
         if (itr != _listeners->end())
         {
             std::list<Listener*>* listenerList = itr->second;
-            for (std::list<Listener*>::iterator listenerItr = listenerList->begin(); listenerItr != listenerList->end(); listenerItr++)
+            for (std::list<Listener*>::iterator listenerItr = listenerList->begin(); listenerItr != listenerList->end(); ++listenerItr)
             {
                 GP_ASSERT(*listenerItr);
                 (*listenerItr)->controlEvent(this, eventType);
@@ -995,6 +1043,9 @@ void Control::drawText(const Rectangle& position)
 
 void Control::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight)
 {
+    if (!_visible)
+        return;
+
     if (needsClear)
     {
         GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
@@ -1114,6 +1165,8 @@ void Control::getAnimationPropertyValue(int propertyId, AnimationValue* value)
         value->setFloat(0, _bounds.height);
         break;
     case ANIMATE_OPACITY:
+        value->setFloat(0, _opacity);
+        break;
     default:
         break;
     }
@@ -1152,6 +1205,7 @@ void Control::setAnimationPropertyValue(int propertyId, AnimationValue* value, f
         _dirty = true;
         break;
     case ANIMATE_OPACITY:
+        setOpacity(Curve::lerp(blendWeight, _opacity, value->getFloat(0)));
         _dirty = true;
         break;
     }

+ 42 - 12
gameplay/src/Control.h

@@ -590,6 +590,22 @@ public:
      */
     bool getTextRightToLeft(State state = NORMAL) const;
 
+    /**
+     * Sets the visibility of a control.
+     *
+     * This is a quick way to hide a control without having to remove it from a form.
+     *
+     * @param visible true if the control is visible and enabled; false if not-visible and disabled.
+     */
+    void setVisible(bool visible);
+
+    /**
+     * Get the visibility of a control.
+     *
+     * @return true if the control is visible; false if not visible.
+     */
+    bool isVisible() const;
+
     /**
      * Set the opacity of this control.
      *
@@ -636,22 +652,19 @@ public:
      */
     State getState() const;
 
-    /**
-     * Disable this control.
-     */
-    void disable();
-
-    /**
-     * Enable this control.
-     */
-    void enable();
+	/**
+	 * Enables/Disables a control. 
+	 *
+	 * @param enabled true if the control is enabled; false if disabled.
+	 */
+	void setEnabled(bool enabled);
 
     /**
      * Get whether this control is currently enabled.
      *
      * @return Whether this control is currently enabled.
      */
-    bool isEnabled();
+    bool isEnabled() const;
 
     /**
      * Set whether this control consumes input events,
@@ -725,7 +738,7 @@ public:
     virtual const char* getType() const;
 
     /**
-     * Add a listener to be notified of specific events affecting
+     * Adds 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>
@@ -736,6 +749,13 @@ public:
      */
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
+    /**
+     * Removes a listener from this control.
+     * 
+     * @param listener The listener to remove.
+     */
+    virtual void removeListener(Control::Listener* listener);
+
     /**
      * @see AnimationTarget::getAnimationPropertyComponentCount
      */
@@ -847,7 +867,7 @@ protected:
     virtual void drawText(const Rectangle& clip);
 
     /**
-     * Draws a sprite batch for the specified clipping rect .
+     * Draws a sprite batch for the specified clipping rect.
      *
      * @param spriteBatch The sprite batch to use.
      * @param clip The clipping rectangle.
@@ -965,6 +985,11 @@ protected:
      * The Control's Alignment
      */
     Alignment _alignment;
+
+    /**
+     * Whether the Control's alignment has been set programmatically.
+     */
+    bool _isAlignmentSet;
     
     /**
      * Whether the Control's width is auto-sized.
@@ -986,6 +1011,11 @@ protected:
      */
     Theme::Style* _style;
 
+    /**
+     * The control is not visible and _state become DISABLED if false.
+     */
+    bool _visible;
+
     /**
      * The current opacity of the control.
      */

+ 106 - 3
gameplay/src/DebugNew.h

@@ -52,7 +52,7 @@ template<typename T> T* bullet_new()
 #endif
 }
 
-template<typename T, typename T1> T* bullet_new(T1 t1)
+template<typename T, typename T1> T* bullet_new(const T1& t1)
 {
 #ifdef GAMEPLAY_MEM_LEAK_DETECTION 
 #undef new 
@@ -64,7 +64,7 @@ template<typename T, typename T1> T* bullet_new(T1 t1)
 #endif
 }
 
-template<typename T, typename T1, typename T2> T* bullet_new(T1 t1, T2 t2)
+template<typename T, typename T1, typename T2> T* bullet_new(const T1& t1, const T2& t2)
 {
 #ifdef GAMEPLAY_MEM_LEAK_DETECTION
 #undef new
@@ -76,8 +76,111 @@ template<typename T, typename T1, typename T2> T* bullet_new(T1 t1, T2 t2)
 #endif
 }
 
+template<typename T, typename T1, typename T2> T* bullet_new(T1& t1, const T2& t2)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3> 
+T* bullet_new(const T1& t1, const T2& t2, const T3& t3)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3> 
+T* bullet_new(T1& t1, const T2& t2, const T3& t3)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3, typename T4> 
+T* bullet_new(const T1& t1, const T2& t2, const T3& t3, const T4& t4)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3, t4);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3, t4);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3, typename T4> 
+T* bullet_new(T1& t1, const T2& t2, const T3& t3, const T4& t4)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3, t4);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3, t4);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3, typename T4> 
+T* bullet_new(T1& t1, T2& t2, const T3& t3, const T4& t4)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3, t4);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3, t4);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3, typename T4, typename T5> 
+T* bullet_new(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3, t4, t5);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3, t4, t5);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3, typename T4, typename T5> 
+T* bullet_new(T1& t1, T2& t2, const T3& t3, const T4& t4, const T5& t5)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3, t4, t5);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3, t4, t5);
+#endif
+}
+
 template<typename T, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9> 
-T* bullet_new(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9)
+T* bullet_new(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7, const T8& t8, const T9& t9)
 {
 #ifdef GAMEPLAY_MEM_LEAK_DETECTION
 #undef new

+ 8 - 8
gameplay/src/Effect.cpp

@@ -21,7 +21,7 @@ Effect::~Effect()
     __effectCache.erase(_id);
 
     // Free uniforms.
-    for (std::map<std::string, Uniform*>::iterator itr = _uniforms.begin(); itr != _uniforms.end(); itr++)
+    for (std::map<std::string, Uniform*>::iterator itr = _uniforms.begin(); itr != _uniforms.end(); ++itr)
     {
         SAFE_DELETE(itr->second);
     }
@@ -126,9 +126,8 @@ static void replaceIncludes(const char* filepath, const char* source, std::strin
     std::string str = source;
     size_t lastPos = 0;
     size_t headPos = 0;
-    size_t tailPos = 0;
     size_t fileLen = str.length();
-    tailPos = fileLen;
+    size_t tailPos = fileLen;
     while (headPos < fileLen)
     {
         lastPos = headPos;
@@ -200,10 +199,11 @@ static void writeShaderToErrorFile(const char* filePath, const char* source)
 {
     std::string path = filePath;
     path += ".err";
-    FILE* file = FileSystem::openFile(path.c_str(), "wb");
-    int err = ferror(file);
-    fwrite(source, 1, strlen(source), file);
-    fclose(file);
+    std::auto_ptr<Stream> stream(FileSystem::open(path.c_str(), FileSystem::WRITE));
+    if (stream.get() != NULL && stream->canWrite())
+    {
+        stream->write(source, 1, strlen(source));
+    }
 }
 
 Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, const char* fshPath, const char* fshSource, const char* defines)
@@ -454,7 +454,7 @@ Uniform* Effect::getUniform(const char* name) const
 Uniform* Effect::getUniform(unsigned int index) const
 {
     unsigned int i = 0;
-    for (std::map<std::string, Uniform*>::const_iterator itr = _uniforms.begin(); itr != _uniforms.end(); itr++, i++)
+    for (std::map<std::string, Uniform*>::const_iterator itr = _uniforms.begin(); itr != _uniforms.end(); ++itr, ++i)
     {
         if (i == index)
         {

+ 441 - 33
gameplay/src/FileSystem.cpp

@@ -1,6 +1,7 @@
 #include "Base.h"
 #include "FileSystem.h"
 #include "Properties.h"
+#include "Stream.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -28,7 +29,7 @@ namespace gameplay
 #ifdef __ANDROID__
 #include <unistd.h>
 
-void makepath(std::string path, int mode)
+static void makepath(std::string path, int mode)
 {
     std::vector<std::string> dirs;
     while (path.length() > 0)
@@ -63,12 +64,100 @@ void makepath(std::string path, int mode)
     
     return;
 }
+
+/**
+ * Returns true if the file exists in the android read-only asset directory.
+ */
+static bool androidFileExists(const char* filePath)
+{
+    AAsset* asset = AAssetManager_open(__assetManager, filePath, AASSET_MODE_RANDOM);
+    if (asset)
+    {
+        int lenght = AAsset_getLength(asset);
+        AAsset_close(asset);
+        return length > 0;
+    }
+    return false;
+}
+
 #endif
 
 /** @script{ignore} */
 static std::string __resourcePath("./");
 static std::map<std::string, std::string> __aliases;
 
+/**
+ * 
+ * @script{ignore}
+ */
+class FileStream : public Stream
+{
+public:
+    friend class FileSystem;
+    
+    ~FileStream();
+    virtual bool canRead();
+    virtual bool canWrite();
+    virtual bool canSeek();
+    virtual void close();
+    virtual size_t read(void* ptr, size_t size, size_t count);
+    virtual char* readLine(char* str, int num);
+    virtual size_t write(const void* ptr, size_t size, size_t count);
+    virtual bool eof();
+    virtual size_t length();
+    virtual long int position();
+    virtual bool seek(long int offset, int origin);
+    virtual bool rewind();
+
+    static FileStream* create(const char* filePath, const char* mode);
+
+private:
+    FileStream(FILE* file);
+
+private:
+    FILE* _file;
+    bool _canRead;
+    bool _canWrite;
+};
+
+#ifdef __ANDROID__
+
+/**
+ * 
+ * @script{ignore}
+ */
+class FileStreamAndroid : public Stream
+{
+public:
+    friend class FileSystem;
+    
+    ~FileStreamAndroid();
+    virtual bool canRead();
+    virtual bool canWrite();
+    virtual bool canSeek();
+    virtual void close();
+    virtual size_t read(void* ptr, size_t size, size_t count);
+    virtual char* readLine(char* str, int num);
+    virtual size_t write(const void* ptr, size_t size, size_t count);
+    virtual bool eof();
+    virtual size_t length();
+    virtual long int position();
+    virtual bool seek(long int offset, int origin);
+    virtual bool rewind();
+
+    static FileStreamAndroid* create(const char* filePath, const char* mode);
+
+private:
+    FileStreamAndroid(AAsset* asset);
+
+private:
+    AAsset* _asset;
+};
+
+#endif
+
+/////////////////////////////
+
 FileSystem::FileSystem()
 {
 }
@@ -201,11 +290,16 @@ bool FileSystem::fileExists(const char* filePath)
 {
     GP_ASSERT(filePath);
 
+#ifdef __ANDROID__
+    if (androidFileExists(filePath))
+    {
+        return true;
+    }
+#endif
+
     std::string fullPath(__resourcePath);
     fullPath += resolvePath(filePath);
 
-    createFileFromAsset(filePath);
-
     gp_stat_struct s;
 
 #ifdef WIN32
@@ -230,15 +324,72 @@ bool FileSystem::fileExists(const char* filePath)
 #endif
 }
 
-FILE* FileSystem::openFile(const char* path, const char* mode)
+Stream* FileSystem::open(const char* path, size_t mode)
 {
-    GP_ASSERT(path);
-    GP_ASSERT(mode);
+    char modeStr[] = "rb";
+    if ((mode & WRITE) != 0)
+        modeStr[0] = 'w';
+#ifdef __ANDROID__
+    if ((mode & WRITE) != 0)
+    {
+        // Open a file on the SD card
+        std::string fullPath(__resourcePath);
+        fullPath += resolvePath(path);
 
+        size_t index = fullPath.rfind('/');
+        if (index != std::string::npos)
+        {
+            std::string directoryPath = fullPath.substr(0, index);
+            struct stat s;
+            if (stat(directoryPath.c_str(), &s) != 0)
+                makepath(directoryPath, 0777);
+        }
+        return FileStream::create(fullPath.c_str(), modeStr);
+    }
+    else
+    {
+        // Open a file in the read-only asset directory
+        return FileStreamAndroid::create(resolvePath(path), modeStr);
+    }
+#else
     std::string fullPath(__resourcePath);
     fullPath += resolvePath(path);
+    
+#ifdef WIN32
+    gp_stat_struct s;
+    if (stat(fullPath.c_str(), &s) != 0 && (mode & WRITE) == 0)
+    {
+        fullPath = __resourcePath;
+        fullPath += "../../gameplay/";
+        fullPath += path;
+        
+        int result = stat(fullPath.c_str(), &s);
+        if (result != 0)
+        {
+            fullPath = __resourcePath;
+            fullPath += "../gameplay/";
+            fullPath += path;
+            if (stat(fullPath.c_str(), &s) != 0)
+            {
+                return NULL;
+            }
+        }
+    }
+#endif
+    FileStream* stream = FileStream::create(fullPath.c_str(), modeStr);
+    return stream;
+#endif
+}
 
-    createFileFromAsset(path);
+FILE* FileSystem::openFile(const char* filePath, const char* mode)
+{
+    GP_ASSERT(filePath);
+    GP_ASSERT(mode);
+
+    std::string fullPath(__resourcePath);
+    fullPath += resolvePath(filePath);
+
+    createFileFromAsset(filePath);
     
     FILE* fp = fopen(fullPath.c_str(), mode);
     
@@ -247,14 +398,14 @@ FILE* FileSystem::openFile(const char* path, const char* mode)
     {
         fullPath = __resourcePath;
         fullPath += "../../gameplay/";
-        fullPath += path;
+        fullPath += filePath;
         
         fp = fopen(fullPath.c_str(), mode);
         if (!fp)
         {
             fullPath = __resourcePath;
             fullPath += "../gameplay/";
-            fullPath += path;
+            fullPath += filePath;
             fp = fopen(fullPath.c_str(), mode);
         }
     }
@@ -268,32 +419,20 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     GP_ASSERT(filePath);
 
     // Open file for reading.
-    FILE* file = openFile(filePath, "rb");
-    if (file == NULL)
+    std::auto_ptr<Stream> stream(open(filePath));
+    if (stream.get() == NULL)
     {
         GP_ERROR("Failed to load file: %s", filePath);
         return NULL;
     }
-
-    // Obtain file length.
-    if (fseek(file, 0, SEEK_END) != 0)
-    {
-        GP_ERROR("Failed to seek to the end of the file '%s' to obtain the file length.", filePath);
-        return NULL;
-    }
-    int size = (int)ftell(file);
-    if (fseek(file, 0, SEEK_SET) != 0)
-    {
-        GP_ERROR("Failed to seek to beginning of the file '%s' to begin reading in the entire file.", filePath);
-        return NULL;
-    }
+    size_t size = stream->length();
 
     // Read entire file contents.
     char* buffer = new char[size + 1];
-    int read = (int)fread(buffer, 1, size, file);
+    size_t read = stream->read(buffer, 1, size);
     if (read != size)
     {
-        GP_ERROR("Failed to read complete contents of file '%s' (amount read vs. file size: %d < %d).", filePath, (int)read, (int)size);
+        GP_ERROR("Failed to read complete contents of file '%s' (amount read vs. file size: %u < %u).", filePath, read, size);
         SAFE_DELETE_ARRAY(buffer);
         return NULL;
     }
@@ -301,15 +440,9 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     // Force the character buffer to be NULL-terminated.
     buffer[size] = '\0';
 
-    // Close file and return.
-    if (fclose(file) != 0)
-    {
-        GP_ERROR("Failed to close file '%s'.", filePath);
-    }
-
     if (fileSize)
     {
-        *fileSize = size; 
+        *fileSize = (int)size; 
     }
     return buffer;
 }
@@ -383,4 +516,279 @@ void FileSystem::createFileFromAsset(const char* path)
 #endif
 }
 
+//////////////////
+
+FileStream::FileStream(FILE* file)
+    : _file(file), _canRead(false), _canWrite(false)
+{
+    
+}
+
+FileStream::~FileStream()
+{
+    if (_file)
+    {
+        close();
+    }
+}
+
+FileStream* FileStream::create(const char* filePath, const char* mode)
+{
+    FILE* file = fopen(filePath, mode);
+    if (file)
+    {
+        FileStream* stream = new FileStream(file);
+        const char* s = mode;
+        while (s != NULL && *s != '\0')
+        {
+            if (*s == 'r')
+                stream->_canRead = true;
+            else if (*s == 'w')
+                stream->_canWrite = true;
+            ++s;
+        }
+
+        return stream;
+    }
+    return NULL;
+}
+
+bool FileStream::canRead()
+{
+    return _file && _canRead;
+}
+
+bool FileStream::canWrite()
+{
+    return _file && _canWrite;
+}
+
+bool FileStream::canSeek()
+{
+    return _file != NULL;
+}
+
+void FileStream::close()
+{
+    if (_file)
+        fclose(_file);
+    _file = NULL;
+}
+
+size_t FileStream::read(void* ptr, size_t size, size_t count)
+{
+    if (!_file)
+        return 0;
+    return fread(ptr, size, count, _file);
+}
+
+char* FileStream::readLine(char* str, int num)
+{
+    if (!_file)
+        return 0;
+    return fgets(str, num, _file);
+}
+
+size_t FileStream::write(const void* ptr, size_t size, size_t count)
+{
+    if (!_file)
+        return 0;
+    return fwrite(ptr, size, count, _file);
+}
+
+bool FileStream::eof()
+{
+    if (!_file || feof(_file))
+        return true;
+    return ((size_t)position()) >= length();
+}
+
+size_t FileStream::length()
+{
+    size_t len = 0;
+    if (canSeek())
+    {
+        long int pos = position();
+        if (seek(0, SEEK_END))
+        {
+            len = position();
+        }
+        seek(pos, SEEK_SET);
+    }
+    return len;
+}
+
+long int FileStream::position()
+{
+    if (!_file)
+        return -1;
+    return ftell(_file);
+}
+
+bool FileStream::seek(long int offset, int origin)
+{
+    if (!_file)
+        return false;
+    return fseek(_file, offset, origin) == 0;
+}
+
+bool FileStream::rewind()
+{
+    if (canSeek())
+    {
+        ::rewind(_file);
+        return true;
+    }
+    return false;
+}
+
+////////////////////////////////
+
+#ifdef __ANDROID__
+
+FileStreamAndroid::FileStreamAndroid(AAsset* asset)
+    : _asset(asset)
+{
+}
+
+FileStreamAndroid::~FileStreamAndroid()
+{
+    if (_asset)
+        close();
+}
+
+FileStreamAndroid* FileStreamAndroid::create(const char* filePath, const char* mode)
+{
+    AAsset* asset = AAssetManager_open(__assetManager, filePath, AASSET_MODE_RANDOM);
+    if (asset)
+    {
+        FileStreamAndroid* stream = new FileStreamAndroid(asset);
+        return stream;
+    }
+    return NULL;
+}
+
+bool FileStreamAndroid::canRead()
+{
+    return true;
+}
+
+bool FileStreamAndroid::canWrite()
+{
+    return false;
+}
+
+bool FileStreamAndroid::canSeek()
+{
+    return true;
+}
+
+void FileStreamAndroid::close()
+{
+    if (_asset)
+        AAsset_close(_asset);
+    _asset = NULL;
+}
+
+size_t FileStreamAndroid::read(void* ptr, size_t size, size_t count)
+{
+    int result = AAsset_read(_asset, ptr, size * count);
+    return result > 0 ? ((size_t)result) / size : 0;
+}
+
+char* FileStreamAndroid::readLine(char* str, int num)
+{
+    if (num <= 0)
+        return NULL;
+    char c = 0;
+    size_t maxCharsToRead = num - 1;
+    for (size_t i = 0; i < maxCharsToRead; ++i)
+    {
+        size_t result = read(&c, 1, 1);
+        if (result != 1)
+        {
+            str[i] = '\0';
+            break;
+        }
+        if (c == '\n')
+        {
+            str[i] = c;
+            str[i + 1] = '\0';
+            break;
+        }
+        else if(c == '\r')
+        {
+            str[i] = c;
+            // next may be '\n'
+            size_t pos = position();
+
+            char nextChar = 0;
+            if (read(&nextChar, 1, 1) != 1)
+            {
+                // no more characters
+                str[i + 1] = '\0';
+                break;
+            }
+            if (nextChar == '\n')
+            {
+                if (i == maxCharsToRead - 1)
+                {
+                    str[i + 1] = '\0';
+                    break;
+                }
+                else
+                {
+                    str[i + 1] = nextChar;
+                    str[i + 2] = '\0';
+                    break;
+                }
+            }
+            else
+            {
+                seek(pos, SEEK_SET);
+                str[i + 1] = '\0';
+                break;
+            }
+        }
+        str[i] = c;
+    }
+    return str; // what if first read failed?
+}
+
+size_t FileStreamAndroid::write(const void* ptr, size_t size, size_t count)
+{
+    return 0;
+}
+
+bool FileStreamAndroid::eof()
+{
+    return position() >= length();
+}
+
+size_t FileStreamAndroid::length()
+{
+    return (size_t)AAsset_getLength(_asset);
+}
+
+long int FileStreamAndroid::position()
+{
+    return AAsset_getLength(_asset) - AAsset_getRemainingLength(_asset);
+}
+
+bool FileStreamAndroid::seek(long int offset, int origin)
+{
+    return AAsset_seek(_asset, offset, origin) != -1;
+}
+
+bool FileStreamAndroid::rewind()
+{
+    if (canSeek())
+    {
+        return AAsset_seek(_asset, 0, SEEK_SET) != -1;
+    }
+    return false;
+}
+
+#endif
+
 }

+ 28 - 0
gameplay/src/FileSystem.h

@@ -1,6 +1,8 @@
 #ifndef FILESYSTEM_H_
 #define FILESYSTEM_H_
 
+#include "Stream.h"
+
 namespace gameplay
 {
 
@@ -13,6 +15,16 @@ class FileSystem
 {
 public:
 
+    /**
+     * Mode flags for opening a stream.
+     * @script{ignore}
+     */
+    enum StreamMode
+    {
+        READ = 1,
+        WRITE = 2
+    };
+
     /**
      * Destructor.
      */
@@ -107,6 +119,22 @@ public:
      */
     static bool fileExists(const char* filePath);
 
+    /**
+     * Opens a byte stream for the given resource path.
+     *
+     * If <code>path</code> is a file path, the file at the specified location is opened relative to the currently set
+     * resource path.
+     *
+     * @param path The path to the resource to be opened, relative to the currently set resource path.
+     * @param mode The mode used to open the file.
+     * 
+     * @return A stream that can be used to read or write to the file depending on the mode.
+     *         Returns NULL if there was an error. (Request mode not supported).
+     * 
+     * @script{ignore}
+     */
+    static Stream* open(const char* path, size_t mode = READ);
+
     /**
      * Opens the specified file.
      *

+ 4 - 4
gameplay/src/Font.cpp

@@ -30,7 +30,7 @@
     "void main()\n" \
     "{\n" \
         "gl_FragColor = v_color;\n" \
-        "gl_FragColor.a = texture2D(u_texture, v_texCoord).a;\n" \
+        "gl_FragColor.a = texture2D(u_texture, v_texCoord).a * v_color.a;\n" \
     "}"
 
 namespace gameplay
@@ -272,7 +272,7 @@ Font::Text* Font::createText(const char* text, const Rectangle& area, const Vect
         }
 
         bool draw = true;
-        if (yPos < area.y)
+        if (yPos < static_cast<int>(area.y))
         {
             // Skip drawing until line break or wrap.
             draw = false;
@@ -490,7 +490,7 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
 
         GP_ASSERT(_glyphs);
         GP_ASSERT(_batch);
-        for (size_t i = startIndex; i < length && i >= 0; i += iteration)
+        for (size_t i = startIndex; i < length; i += (size_t)iteration)
         {
             char c = 0;
             if (rightToLeft)
@@ -629,7 +629,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         }
 
         bool draw = true;
-        if (yPos < area.y - size)
+        if (yPos < static_cast<int>(area.y - size))
         {
             // Skip drawing until line break or wrap.
             draw = false;

+ 9 - 5
gameplay/src/Font.h

@@ -69,17 +69,21 @@ public:
          */
         ~Text();
 
-        /**
-         * Hidden copy assignment operator.
-         */
-        Text& operator=(const Text&);
-
         /**
          * Get the string that will be drawn from this Text object.
          */
         const char* getText();
 
     private:
+        /**
+         * Hidden copy constructor.
+         */
+        Text(const Text&); 
+        /**
+         * Hidden copy assignment operator.
+         */
+        Text& operator=(const Text&);
+        
         std::string _text;
         unsigned int _vertexCount;
         SpriteBatch::SpriteVertex* _vertices;

+ 16 - 7
gameplay/src/Form.cpp

@@ -99,6 +99,8 @@ Form* Form::create(const char* id, Theme::Style* style, Layout::Type layoutType)
     Game* game = Game::getInstance();
     Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
 
+    form->updateBounds();
+
     __forms.push_back(form);
 
     return form;
@@ -200,6 +202,8 @@ Form* Form::create(const char* url)
     form->addControls(theme, formProperties);
 
     SAFE_DELETE(properties);
+    
+    form->updateBounds();
 
     __forms.push_back(form);
 
@@ -209,7 +213,7 @@ Form* Form::create(const char* url)
 Form* Form::getForm(const char* id)
 {
     std::vector<Form*>::const_iterator it;
-    for (it = __forms.begin(); it < __forms.end(); it++)
+    for (it = __forms.begin(); it < __forms.end(); ++it)
     {
         Form* f = *it;
         GP_ASSERT(f);
@@ -403,6 +407,11 @@ void Form::setNode(Node* node)
 }
 
 void Form::update(float elapsedTime)
+{
+    updateBounds();
+}
+
+void Form::updateBounds()
 {
     if (isDirty())
     {
@@ -552,7 +561,8 @@ void Form::draw()
 
         GP_ASSERT(_theme);
         _theme->setProjectionMatrix(_projectionMatrix);
-        Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height), _skin != NULL, false, _bounds.height);
+        Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height),
+                        true/*WAS _skin!=NULL*/, false, _bounds.height);
         _theme->setProjectionMatrix(_defaultProjectionMatrix);
 
         // Rebind the default framebuffer and game viewport.
@@ -710,13 +720,12 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
 bool Form::projectPoint(int x, int y, Vector3* point)
 {
     Scene* scene = _node->getScene();
-    GP_ASSERT(scene);
-    Camera* camera = scene->getActiveCamera();
+    Camera* camera;
 
-    if (camera)
+    if (scene && (camera = scene->getActiveCamera()))
     {
         // Get info about the form's position.
-        Matrix m = _node->getMatrix();
+        Matrix m = _node->getWorldMatrix();
         Vector3 min(0, 0, 0);
         m.transformPoint(&min);
 
@@ -731,7 +740,7 @@ bool Form::projectPoint(int x, int y, Vector3* point)
         // by the quad's forward vector and one of its points to the plane defined by the same vector and the origin.
         const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
         const float d = -(a*min.x) - (b*min.y) - (c*min.z);
-        const float distance = abs(d) /  sqrt(a*a + b*b + c*c);
+        const float distance = fabs(d) /  sqrt(a*a + b*b + c*c);
         Plane plane(normal, -distance);
 
         // Check for collision with plane.

+ 5 - 0
gameplay/src/Form.h

@@ -183,6 +183,11 @@ private:
      */
     void initializeQuad(Mesh* mesh);
 
+    /**
+     * Update this form's bounds.
+     */
+    void updateBounds();
+
     /**
      * Propagate touch events to enabled forms.
      *

+ 1 - 11
gameplay/src/FrameBuffer.cpp

@@ -84,12 +84,6 @@ FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned in
     RenderTarget* renderTarget = NULL;
     if (width > 0 && height > 0)
     {
-        if (!isPowerOfTwo(width) | !isPowerOfTwo(height))
-        {
-            GP_ERROR("Failed to create render target for frame buffer. Width and Height must be a power of 2.");
-            return NULL;
-        }
-
         // Create a default RenderTarget with same ID.
         renderTarget = RenderTarget::create(id, width, height);
         if (renderTarget == NULL)
@@ -126,7 +120,7 @@ FrameBuffer* FrameBuffer::getFrameBuffer(const char* id)
 
     // Search the vector for a matching ID.
     std::vector<FrameBuffer*>::const_iterator it;
-    for (it = __frameBuffers.begin(); it < __frameBuffers.end(); it++)
+    for (it = __frameBuffers.begin(); it < __frameBuffers.end(); ++it)
     {
         FrameBuffer* fb = *it;
         GP_ASSERT(fb);
@@ -266,9 +260,5 @@ void FrameBuffer::bindDefault()
     __currentHandle = __defaultHandle;
 }
 
-bool FrameBuffer::isPowerOfTwo(unsigned int value)
-{
-    return (value != 0) && ((value & (value - 1)) == 0);
-}
 
 }

+ 2 - 2
gameplay/src/Frustum.h

@@ -114,7 +114,7 @@ public:
     void getCorners(Vector3* corners) const;
 
     /**
-     * Tests whether this frustum instersects the specified point.
+     * Tests whether this frustum intersects the specified point.
      *
      * @param point The point to test intersection with.
      *
@@ -123,7 +123,7 @@ public:
     bool intersects(const Vector3& point) const;
 
     /**
-     * Tests whether this frustum instersects the specified point.
+     * Tests whether this frustum intersects the specified point.
      *
      * @param x The x coordinate.
      * @param y The y coordinate.

+ 12 - 12
gameplay/src/Game.cpp

@@ -246,18 +246,18 @@ void Game::resume()
         --_pausedCount;
 
         if (_pausedCount == 0)
-		{
-			GP_ASSERT(_animationController);
-			GP_ASSERT(_audioController);
-			GP_ASSERT(_physicsController);
-			GP_ASSERT(_aiController);
-			_state = RUNNING;
-			_pausedTimeTotal += Platform::getAbsoluteTime() - _pausedTimeLast;
-			_animationController->resume();
-			_audioController->resume();
-			_physicsController->resume();
-			_aiController->resume();
-		}
+        {
+            GP_ASSERT(_animationController);
+            GP_ASSERT(_audioController);
+            GP_ASSERT(_physicsController);
+            GP_ASSERT(_aiController);
+            _state = RUNNING;
+            _pausedTimeTotal += Platform::getAbsoluteTime() - _pausedTimeLast;
+            _animationController->resume();
+            _audioController->resume();
+            _physicsController->resume();
+            _aiController->resume();
+        }
     }
 }
 

+ 10 - 4
gameplay/src/Game.h

@@ -1,8 +1,6 @@
 #ifndef GAME_H_
 #define GAME_H_
 
-#include <queue>
-
 #include "Keyboard.h"
 #include "Mouse.h"
 #include "Touch.h"
@@ -17,7 +15,6 @@
 #include "Vector4.h"
 #include "TimeListener.h"
 
-
 namespace gameplay
 {
 
@@ -466,7 +463,7 @@ public:
     inline Gamepad* getGamepad(unsigned int index) const;
 
     /**
-     * Sets muli-touch is to be enabled/disabled. Default is disabled.
+     * Sets multi-touch is to be enabled/disabled. Default is disabled.
      *
      * @param enabled true sets multi-touch is enabled, false to be disabled.
      */
@@ -517,6 +514,15 @@ public:
      */
     void schedule(float timeOffset, const char* function);
 
+    /**
+     * Opens an URL in an external browser, if available.
+     *
+     * @param url URL to be opened.
+     *
+     * @return True if URL was opened successfully, false otherwise.
+     */
+    bool launchURL(const char *url) const;
+
 protected:
 
     /**

+ 6 - 0
gameplay/src/Game.inl

@@ -140,4 +140,10 @@ inline Gamepad* Game::getGamepad(unsigned int index) const
     else
         return NULL;
 }
+
+inline bool Game::launchURL(const char* url) const
+{
+    return Platform::launchURL(url);
+}
+
 }

+ 3 - 3
gameplay/src/Gamepad.h

@@ -33,8 +33,8 @@ public:
      */
     enum ButtonState
     {
-        BUTTON_PRESSED = gameplay::Button::Listener::PRESS, 
-        BUTTON_RELEASED = gameplay::Button::Listener::RELEASE
+        BUTTON_PRESSED = gameplay::Control::Listener::PRESS, 
+        BUTTON_RELEASED = gameplay::Control::Listener::RELEASE
     };
 
     /**
@@ -78,7 +78,7 @@ public:
      * Returns the specified joystick's value as a Vector2.
      *
      * @param joystickId The index of the joystick to get the value for.
-     * @param outValues The current x-axis and y-asix value displacement of the joystick.
+     * @param outValues The current x-axis and y-axis value displacement of the joystick.
      */
     void getJoystickAxisValues(unsigned int joystickId, Vector2* outValues) const;
 

+ 13 - 28
gameplay/src/Image.cpp

@@ -4,14 +4,23 @@
 
 namespace gameplay
 {
+// Callback for reading a png image using Stream
+static void readStream(png_structp png, png_bytep data, png_size_t length)
+{
+    Stream* stream = reinterpret_cast<Stream*>(png_get_io_ptr(png));
+    if (stream == NULL || stream->read(data, 1, length) != length)
+    {
+        png_error(png, "Error reading PNG.");
+    }
+}
 
 Image* Image::create(const char* path)
 {
     GP_ASSERT(path);
 
     // Open the file.
-    FILE* fp = FileSystem::openFile(path, "rb");
-    if (fp == NULL)
+    std::auto_ptr<Stream> stream(FileSystem::open(path));
+    if (stream.get() == NULL || !stream->canRead())
     {
         GP_ERROR("Failed to open image file '%s'.", path);
         return NULL;
@@ -19,13 +28,9 @@ Image* Image::create(const char* path)
 
     // Verify PNG signature.
     unsigned char sig[8];
-    if (fread(sig, 1, 8, fp) != 8 || png_sig_cmp(sig, 0, 8) != 0)
+    if (stream->read(sig, 1, 8) != 8 || png_sig_cmp(sig, 0, 8) != 0)
     {
         GP_ERROR("Failed to load file '%s'; not a valid PNG.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close image file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -34,10 +39,6 @@ Image* Image::create(const char* path)
     if (png == NULL)
     {
         GP_ERROR("Failed to create PNG structure for reading PNG file '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close image file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -46,10 +47,6 @@ Image* Image::create(const char* path)
     if (info == NULL)
     {
         GP_ERROR("Failed to create PNG info structure for PNG file '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close image file '%s'.", path);
-        }
         png_destroy_read_struct(&png, NULL, NULL);
         return NULL;
     }
@@ -58,16 +55,12 @@ Image* Image::create(const char* path)
     if (setjmp(png_jmpbuf(png)))
     {
         GP_ERROR("Failed to set up error handling for reading PNG file '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close image file '%s'.", path);
-        }
         png_destroy_read_struct(&png, &info, NULL);
         return NULL;
     }
 
     // Initialize file io.
-    png_init_io(png, fp);
+    png_set_read_fn(png, stream.get(), readStream);
 
     // Indicate that we already read the first 8 bytes (signature).
     png_set_sig_bytes(png, 8);
@@ -92,10 +85,6 @@ Image* Image::create(const char* path)
 
     default:
         GP_ERROR("Unsupported PNG color type (%d) for image file '%s'.", (int)colorType, path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close image file '%s'.", path);
-        }
         png_destroy_read_struct(&png, &info, NULL);
         return NULL;
     }
@@ -114,10 +103,6 @@ Image* Image::create(const char* path)
 
     // Clean up.
     png_destroy_read_struct(&png, &info, NULL);
-    if (fclose(fp) != 0)
-    {
-        GP_ERROR("Failed to close image file '%s'.", path);
-    }
 
     return image;
 }

+ 0 - 2
gameplay/src/Joystick.h

@@ -18,10 +18,8 @@ namespace gameplay
         size        = <width, height>           // Size of the Control, measured in pixels.
         radius      = <float>                   // The value of the left- / bottom-most point on the slider.
         consumeEvents = <bool>                  // Whether the slider propagates input events to the Game's input event handler. Default is true.
-        
     }
  @endverbatim
- *
  */
 class Joystick : public Control
 {

+ 11 - 2
gameplay/src/Layout.cpp

@@ -12,8 +12,9 @@ void Layout::align(Control* control, const Container* container)
     GP_ASSERT(container);
 
     if (control->_alignment != Control::ALIGN_TOP_LEFT ||
+        control->_isAlignmentSet ||
         control->_autoWidth || control->_autoHeight)
-    {
+    {
         Rectangle controlBounds = control->getBounds();
         const Theme::Margin& controlMargin = control->getMargin();
         const Rectangle& containerBounds = container->getBounds();
@@ -35,7 +36,7 @@ void Layout::align(Control* control, const Container* container)
             clipHeight = containerBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom;
         }
 
-        if (control->_autoWidth)
+        if (control->_autoWidth)
         {
             controlBounds.width = clipWidth - controlMargin.left - controlMargin.right;
         }
@@ -54,6 +55,10 @@ void Layout::align(Control* control, const Container* container)
         {
             controlBounds.y = clipHeight * 0.5f - controlBounds.height * 0.5f;
         }
+        else if ((control->_alignment & Control::ALIGN_TOP) == Control::ALIGN_TOP)
+        {
+            controlBounds.y = 0;
+        }
 
         // Horizontal alignment
         if ((control->_alignment & Control::ALIGN_RIGHT) == Control::ALIGN_RIGHT)
@@ -64,6 +69,10 @@ void Layout::align(Control* control, const Container* container)
         {
             controlBounds.x = clipWidth * 0.5f - controlBounds.width * 0.5f;
         }
+        else if ((control->_alignment & Control::ALIGN_LEFT) == Control::ALIGN_LEFT)
+        {
+            controlBounds.x = 0;
+        }
 
         control->setBounds(controlBounds);
     }

+ 2 - 3
gameplay/src/MathUtil.cpp

@@ -17,11 +17,10 @@ void MathUtil::smooth(float* x, float target, float elapsedTime, float responseT
 void MathUtil::smooth(float* x, float target, float elapsedTime, float riseTime, float fallTime)
 {
     GP_ASSERT(x);
-
-    float delta;
+    
     if (elapsedTime > 0)
     {
-        delta = target - *x;
+        float delta = target - *x;
         *x += delta * elapsedTime / (elapsedTime + (delta > 0 ? riseTime : fallTime));
     }
 }

+ 72 - 3
gameplay/src/Matrix.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "Matrix.h"
+#include "Plane.h"
 #include "Quaternion.h"
 #include "MathUtil.h"
 
@@ -19,7 +20,8 @@ Matrix::Matrix()
     *this = Matrix::identity();
 }
 
-Matrix::Matrix(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44)
+Matrix::Matrix(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24,
+               float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44)
 {
     set(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44);
 }
@@ -60,7 +62,8 @@ const Matrix& Matrix::zero()
 
 void Matrix::createLookAt(const Vector3& eyePosition, const Vector3& targetPosition, const Vector3& up, Matrix* dst)
 {
-    createLookAt(eyePosition.x, eyePosition.y, eyePosition.z, targetPosition.x, targetPosition.y, targetPosition.z, up.x, up.y, up.z, dst);
+    createLookAt(eyePosition.x, eyePosition.y, eyePosition.z, targetPosition.x, targetPosition.y, targetPosition.z,
+                 up.x, up.y, up.z, dst);
 }
 
 void Matrix::createLookAt(float eyePositionX, float eyePositionY, float eyePositionZ,
@@ -162,6 +165,71 @@ void Matrix::createOrthographicOffCenter(float left, float right, float bottom,
     dst->m[14] = (-(zFarPlane + zNearPlane)) * f_n;
     dst->m[15] = 1.0f;
 }
+    
+void Matrix::createBillboard(const Vector3& objectPosition, const Vector3& cameraPosition,
+                             const Vector3& cameraUpVector, Matrix* dst)
+{
+    createBillboardHelper(objectPosition, cameraPosition, cameraUpVector, NULL, dst);
+}
+
+void Matrix::createBillboard(const Vector3& objectPosition, const Vector3& cameraPosition,
+                             const Vector3& cameraUpVector, const Vector3& cameraForwardVector,
+                             Matrix* dst)
+{
+    createBillboardHelper(objectPosition, cameraPosition, cameraUpVector, &cameraForwardVector, dst);
+}
+
+void Matrix::createBillboardHelper(const Vector3& objectPosition, const Vector3& cameraPosition,
+                                   const Vector3& cameraUpVector, const Vector3* cameraForwardVector,
+                                   Matrix* dst)
+{
+    Vector3 delta(objectPosition, cameraPosition);
+    bool isSufficientDelta = delta.lengthSquared() > MATH_EPSILON;
+
+    dst->setIdentity();
+    dst->m[3] = objectPosition.x;
+    dst->m[7] = objectPosition.y;
+    dst->m[11] = objectPosition.z;
+
+    // As per the contracts for the 2 variants of createBillboard, we need
+    // either a safe default or a sufficient distance between object and camera.
+    if (cameraForwardVector || isSufficientDelta)
+    {
+        Vector3 target = isSufficientDelta ? cameraPosition : (objectPosition - *cameraForwardVector);
+
+        // A billboard is the inverse of a lookAt rotation
+        Matrix lookAt;
+        createLookAt(objectPosition, target, cameraUpVector, &lookAt);
+        dst->m[0] = lookAt.m[0];
+        dst->m[1] = lookAt.m[4];
+        dst->m[2] = lookAt.m[8];
+        dst->m[4] = lookAt.m[1];
+        dst->m[5] = lookAt.m[5];
+        dst->m[6] = lookAt.m[9];
+        dst->m[8] = lookAt.m[2];
+        dst->m[9] = lookAt.m[6];
+        dst->m[10] = lookAt.m[10];
+    }
+}
+    
+void Matrix::createReflection(const Plane& plane, Matrix* dst)
+{
+    Vector3 normal(plane.getNormal());
+    float k = -2.0f * plane.getDistance();
+
+    dst->setIdentity();
+
+    dst->m[0] -= 2.0f * normal.x * normal.x;
+    dst->m[5] -= 2.0f * normal.y * normal.y;
+    dst->m[10] -= 2.0f * normal.z * normal.z;
+    dst->m[1] = dst->m[4] = -2.0f * normal.x * normal.y;
+    dst->m[2] = dst->m[8] = -2.0f * normal.x * normal.z;
+    dst->m[6] = dst->m[9] = -2.0f * normal.y * normal.z;
+    
+    dst->m[3] = k * normal.x;
+    dst->m[7] = k * normal.y;
+    dst->m[11] = k * normal.z;
+}
 
 void Matrix::createScale(const Vector3& scale, Matrix* dst)
 {
@@ -763,7 +831,8 @@ void Matrix::scale(const Vector3& s, Matrix* dst) const
     scale(s.x, s.y, s.z, dst);
 }
 
-void Matrix::set(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44)
+void Matrix::set(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24,
+                 float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44)
 {
     m[0]  = m11;
     m[1]  = m21;

+ 63 - 31
gameplay/src/Matrix.h

@@ -7,6 +7,8 @@
 namespace gameplay
 {
 
+class Plane;
+
 /**
  * Defines a 4 x 4 floating point matrix representing a 3D transformation.
  *
@@ -77,8 +79,8 @@ public:
      * @param m43 The third element of the fourth row.
      * @param m44 The fourth element of the fourth row.
      */
-    Matrix(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31,
-           float m32, float m33, float m34, float m41, float m42, float m43, float m44);
+    Matrix(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24,
+           float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44);
 
     /**
      * Creates a matrix initialized to the specified column-major array.
@@ -149,8 +151,9 @@ public:
      * @param upZ The up vector z-coordinate value.
      * @param dst A matrix to store the result in.
      */
-    static void createLookAt(float eyePositionX, float eyePositionY, float eyePositionZ, float targetCenterX,
-                             float targetCenterY, float targetCenterZ, float upX, float upY, float upZ, Matrix* dst);
+    static void createLookAt(float eyePositionX, float eyePositionY, float eyePositionZ,
+                             float targetCenterX, float targetCenterY, float targetCenterZ,
+                             float upX, float upY, float upZ, Matrix* dst);
 
     /**
      * Builds a perspective projection matrix based on a field of view and returns by value.
@@ -207,31 +210,54 @@ public:
      * @param zFarPlane The maximum z-value of the view volume.
      * @param dst A matrix to store the result in.
      */
-    static void createOrthographicOffCenter(float left, float right, float bottom, float top, float zNearPlane,
-                                            float zFarPlane, Matrix* dst);
-
-//    /*
-//     * Creates a spherical billboard that rotates around a specified object position.
-//     *
-//     * This method computes the facing direction of the billboard from the object position
-//     * and camera position. When the object and camera positions are too close, the matrix
-//     * will not be accurate. To avoid this problem, the method uses the optional camera
-//     * forward vector if the positions are too close.
-//     *
-//     * @param objectPosition The position of the object the billboard will rotate around.
-//     * @param cameraPosition The position of the camera.
-//     * @param cameraUpVector The up vector of the camera.
-//     * @param dst A matrix to store the result in.
-//     */
-//    static void createBillboard(const Vector3& objectPosition, const Vector3& cameraPosition, const Vector3& cameraUpVector, Matrix* dst);
-//
-//    /*
-//     * Fills in an existing Matrix so that it reflects the coordinate system about a specified Plane.
-//     *
-//     * @param plane The Plane about which to create a reflection.
-//     * @param dst A matrix to store the result in.
-//     */
-//    static void createReflection(const Plane& plane, Matrix* dst);
+    static void createOrthographicOffCenter(float left, float right, float bottom, float top,
+                                            float zNearPlane, float zFarPlane, Matrix* dst);
+
+    /*
+     * Creates a spherical billboard that rotates around a specified object position.
+     *
+     * This method computes the facing direction of the billboard from the object position
+     * and camera position. When the object and camera positions are too close, the matrix
+     * will not be accurate. To avoid this problem, this method defaults to the identity
+     * rotation if the positions are too close. (See the other overload of createBillboard
+     * for an alternative approach).
+     *
+     * @param objectPosition The position of the object the billboard will rotate around.
+     * @param cameraPosition The position of the camera.
+     * @param cameraUpVector The up vector of the camera.
+     * @param dst A matrix to store the result in.
+     */
+    static void createBillboard(const Vector3& objectPosition, const Vector3& cameraPosition,
+                                const Vector3& cameraUpVector, Matrix* dst);
+
+    /*
+     * Creates a spherical billboard that rotates around a specified object position with
+     * provision for a safe default orientation.
+     *
+     * This method computes the facing direction of the billboard from the object position
+     * and camera position. When the object and camera positions are too close, the matrix
+     * will not be accurate. To avoid this problem, this method uses the specified camera
+     * forward vector if the positions are too close. (See the other overload of createBillboard
+     * for an alternative approach).
+     *
+     * @param objectPosition The position of the object the billboard will rotate around.
+     * @param cameraPosition The position of the camera.
+     * @param cameraUpVector The up vector of the camera.
+     * @param cameraForwardVector The forward vector of the camera, used if the positions
+     *                            are too close.
+     * @param dst A matrix to store the result in.
+     */
+    static void createBillboard(const Vector3& objectPosition, const Vector3& cameraPosition,
+                                const Vector3& cameraUpVector, const Vector3& cameraForwardVector,
+                                Matrix* dst);
+
+    /*
+     * Fills in an existing Matrix so that it reflects the coordinate system about a specified Plane.
+     *
+     * @param plane The Plane about which to create a reflection.
+     * @param dst A matrix to store the result in.
+     */
+    static void createReflection(const Plane& plane, Matrix* dst);
 
     /**
      * Creates a scale matrix.
@@ -664,8 +690,8 @@ public:
      * @param m43 The third element of the fourth row.
      * @param m44 The fourth element of the fourth row.
      */
-    void set(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31,
-             float m32, float m33, float m34, float m41, float m42, float m43, float m44);
+    void set(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24,
+             float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44);
 
     /**
      * Sets the values of this matrix to those in the specified column-major array.
@@ -885,6 +911,12 @@ public:
      * @return This matrix, after the multiplication occurs.
      */
     inline Matrix& operator*=(const Matrix& m);
+    
+private:
+
+    static void createBillboardHelper(const Vector3& objectPosition, const Vector3& cameraPosition,
+                                      const Vector3& cameraUpVector, const Vector3* cameraForwardVector,
+                                      Matrix* dst);
 };
 
 /**

+ 78 - 35
gameplay/src/Model.cpp

@@ -253,6 +253,82 @@ void Model::setNode(Node* node)
     }
 }
 
+static bool drawWireframe(Mesh* mesh)
+{
+    switch (mesh->getPrimitiveType())
+    {
+    case Mesh::TRIANGLES:
+        {
+            unsigned int vertexCount = mesh->getVertexCount();
+            for (unsigned int i = 0; i < vertexCount; i += 3)
+            {
+                GL_ASSERT( glDrawArrays(GL_LINE_LOOP, i, 3) );
+            }
+        }
+        return true;
+
+    case Mesh::TRIANGLE_STRIP:
+        {
+            unsigned int vertexCount = mesh->getVertexCount();
+            for (unsigned int i = 2; i < vertexCount; ++i)
+            {
+                GL_ASSERT( glDrawArrays(GL_LINE_LOOP, i-2, 3) );
+            }
+        }
+        return true;
+
+    default:
+        // not supported
+        return false;
+    }
+}
+
+static bool drawWireframe(MeshPart* part)
+{
+    unsigned int indexCount = part->getIndexCount();
+    unsigned int indexSize = 0;
+    switch (part->getIndexFormat())
+    {
+    case Mesh::INDEX8:
+        indexSize = 1;
+        break;
+    case Mesh::INDEX16:
+        indexSize = 2;
+        break;
+    case Mesh::INDEX32:
+        indexSize = 4;
+        break;
+    default:
+        GP_ERROR("Unsupported index format (%d).", part->getIndexFormat());
+        return false;
+    }
+
+    switch (part->getPrimitiveType())
+    {
+    case Mesh::TRIANGLES:
+        {
+            for (unsigned int i = 0; i < indexCount; i += 3)
+            {
+                GL_ASSERT( glDrawElements(GL_LINE_LOOP, 3, part->getIndexFormat(), ((const GLvoid*)(i*indexSize))) );
+            }
+        }
+        return true;
+
+    case Mesh::TRIANGLE_STRIP:
+        {
+            for (unsigned int i = 2; i < indexCount; ++i)
+            {
+                GL_ASSERT( glDrawElements(GL_LINE_LOOP, 3, part->getIndexFormat(), ((const GLvoid*)((i-2)*indexSize))) );
+            }
+        }
+        return true;
+
+    default:
+        // not supported
+        return false;
+    }
+}
+
 void Model::draw(bool wireframe)
 {
     GP_ASSERT(_mesh);
@@ -272,15 +348,7 @@ void Model::draw(bool wireframe)
                 GP_ASSERT(pass);
                 pass->bind();
                 GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) );
-                if (wireframe && (_mesh->getPrimitiveType() == Mesh::TRIANGLES || _mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP))
-                {
-                    unsigned int vertexCount = _mesh->getVertexCount();
-                    for (unsigned int j = 0; j < vertexCount; j += 3)
-                    {
-                        GL_ASSERT( glDrawArrays(GL_LINE_LOOP, j, 3) );
-                    }
-                }
-                else
+                if (!wireframe || !drawWireframe(_mesh))
                 {
                     GL_ASSERT( glDrawArrays(_mesh->getPrimitiveType(), 0, _mesh->getVertexCount()) );
                 }
@@ -308,32 +376,7 @@ void Model::draw(bool wireframe)
                     GP_ASSERT(pass);
                     pass->bind();
                     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, part->_indexBuffer) );
-                    if (wireframe && (_mesh->getPrimitiveType() == Mesh::TRIANGLES || _mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP))
-                    {
-                        unsigned int indexCount = part->getIndexCount();
-                        unsigned int indexSize = 0;
-                        switch (part->getIndexFormat())
-                        {
-                        case Mesh::INDEX8:
-                            indexSize = 1;
-                            break;
-                        case Mesh::INDEX16:
-                            indexSize = 2;
-                            break;
-                        case Mesh::INDEX32:
-                            indexSize = 4;
-                            break;
-                        default:
-                            GP_ERROR("Unsupported index format (%d).", part->getIndexFormat());
-                            continue;
-                        }
-
-                        for (unsigned int k = 0; k < indexCount; k += 3)
-                        {
-                            GL_ASSERT( glDrawElements(GL_LINE_LOOP, 3, part->getIndexFormat(), ((const GLvoid*)(k*indexSize))) );
-                        }
-                    }
-                    else
+                    if (!wireframe || !drawWireframe(part))
                     {
                         GL_ASSERT( glDrawElements(part->getPrimitiveType(), part->getIndexCount(), part->getIndexFormat(), 0) );
                     }

+ 12 - 0
gameplay/src/Node.cpp

@@ -337,6 +337,18 @@ unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool rec
     
     unsigned int count = 0;
 
+    // If the node has a model with a mesh skin, search the skin's hierarchy as well.
+    Node* rootNode = NULL;
+    if (_model != NULL && _model->getSkin() != NULL && (rootNode = _model->getSkin()->_rootNode) != NULL)
+    {
+        if ((exactMatch && rootNode->_id == id) || (!exactMatch && rootNode->_id.find(id) == 0))
+        {
+            nodes.push_back(rootNode);
+            ++count;
+        }
+        count += rootNode->findNodes(id, nodes, true, exactMatch);
+    }
+
     // Search immediate children first.
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
     {

+ 5 - 5
gameplay/src/Node.h

@@ -334,7 +334,7 @@ public:
     /**
      * Returns the forward vector of the Node in view space.
      *
-     * @return The forwward vector in view space.
+     * @return The forward vector in view space.
      */
     Vector3 getForwardVectorView() const;
 
@@ -505,11 +505,11 @@ public:
      * entities in a game that still require collision events, such as volumetric triggers, 
      * power-ups, etc.
      *
-     * Characters are an extention of ghost objects which provide a number of additional features
+     * Characters are an extension of ghost objects which provide a number of additional features
      * for animating and moving characters within a game. Characters are represented as ghost
      * objects instead of rigid bodies to allow more direct control over character movement,
      * since attempting to model a physics character with a simulated rigid body usually results
-     * in unresponse and unpredictable character movement. Unlike normal ghost objects,
+     * in unresponsive and unpredictable character movement. Unlike normal ghost objects,
      * characters to react to other characters and rigid bodies in the world. Characters react
      * to gravity and collide (and respond) with rigid bodies to allow them to walk on the ground,
      * slide along walls and walk up/down slopes and stairs.
@@ -517,12 +517,12 @@ public:
      * @param type The type of the collision object to set; to disable the physics
      *        collision object, pass PhysicsCollisionObject::NONE.
      * @param shape Definition of a physics collision shape to be used for this collision object.
-     *        Use the static shape methods on the PhysicsCollisionShape class to specificy a shape
+     *        Use the static shape methods on the PhysicsCollisionShape class to specify a shape
      *        definition, such as PhysicsCollisionShape::box().
      * @param rigidBodyParameters If type is PhysicsCollisionObject::RIGID_BODY or
      *        PhysicsCollisionObject::VEHICLE, this must point to a valid rigid body
      *        parameters object containing information about the rigid body;
-     *        otherwise, this parmater may be NULL.
+     *        otherwise, this parameter may be NULL.
      */
     PhysicsCollisionObject* setCollisionObject(PhysicsCollisionObject::Type type, const PhysicsCollisionShape::Definition& shape = PhysicsCollisionShape::box(), 
                                                PhysicsRigidBody::Parameters* rigidBodyParameters = NULL);

+ 1 - 2
gameplay/src/ParticleEmitter.cpp

@@ -48,8 +48,7 @@ ParticleEmitter::~ParticleEmitter()
 
 ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlending textureBlending, unsigned int particleCountMax)
 {
-    Texture* texture = NULL;
-    texture = Texture::create(textureFile, false);
+    Texture* texture = Texture::create(textureFile, false);
 
     if (!texture)
     {

+ 0 - 6
gameplay/src/Pass.h

@@ -35,12 +35,6 @@ public:
      */
     Effect* getEffect() const;
 
-    /**
-     * Get a list of properties to be auto-bound.
-     * @script{ignore}
-     */
-    const std::vector<std::string>* getAutoBindProperties() const;
-
     /**
      * Sets a vertex attribute binding for this pass.
      *

+ 2 - 2
gameplay/src/PhysicsCollisionObject.cpp

@@ -17,7 +17,7 @@ struct CollidesWithCallback : public btCollisionWorld::ContactResultCallback
     /**
      * Called with each contact. Needed to implement collidesWith(PhysicsCollisionObject*).
      */
-    btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, const btCollisionObject* b, int partIdB, int indexB)
+    btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* a, int partIdA, int indexA, const btCollisionObjectWrapper* b, int partIdB, int indexB)
     {
         result = true;
         return 0.0f;
@@ -252,8 +252,8 @@ void PhysicsCollisionObject::PhysicsMotionState::updateTransformFromNode() const
 }
 
 PhysicsCollisionObject::ScriptListener::ScriptListener(const char* url)
+    : url(url)
 {
-    this->url = url;
     function = Game::getInstance()->getScriptController()->loadUrl(url);
 }
 

+ 1 - 1
gameplay/src/PhysicsCollisionObject.h

@@ -182,7 +182,7 @@ public:
     bool isDynamic() const;
 
     /**
-     * Check if th collision object is enabled.
+     * Check if the collision object is enabled.
      *
      * @return true if the collision object is enabled.
      */

+ 15 - 9
gameplay/src/PhysicsController.cpp

@@ -6,7 +6,13 @@
 #include "MeshPart.h"
 #include "Bundle.h"
 
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+#endif
 #include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h"
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#define new DEBUG_NEW
+#endif
 
 // The initial capacity of the Bullet debug drawer's vertex batch.
 #define INITIAL_CAPACITY 280
@@ -347,14 +353,14 @@ bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3&
     return false;
 }
 
-btScalar PhysicsController::CollisionCallback::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, 
-    const btCollisionObject* b, int partIdB, int indexB)
+btScalar PhysicsController::CollisionCallback::addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* a, int partIdA, int indexA, 
+    const btCollisionObjectWrapper* b, int partIdB, int indexB)
 {
     GP_ASSERT(_pc);
 
     // Get pointers to the PhysicsCollisionObject objects.
-    PhysicsCollisionObject* objectA = _pc->getCollisionObject(a);
-    PhysicsCollisionObject* objectB = _pc->getCollisionObject(b);
+    PhysicsCollisionObject* objectA = _pc->getCollisionObject(a->m_collisionObject);
+    PhysicsCollisionObject* objectB = _pc->getCollisionObject(b->m_collisionObject);
 
     // If the given collision object pair has collided in the past, then
     // we notify the listeners only if the pair was not colliding
@@ -422,13 +428,13 @@ btScalar PhysicsController::CollisionCallback::addSingleResult(btManifoldPoint&
 
 void PhysicsController::initialize()
 {
-    _collisionConfiguration = new btDefaultCollisionConfiguration();
-    _dispatcher = new btCollisionDispatcher(_collisionConfiguration);
-    _overlappingPairCache = new btDbvtBroadphase();
-    _solver = new btSequentialImpulseConstraintSolver();
+    _collisionConfiguration = bullet_new<btDefaultCollisionConfiguration>();
+    _dispatcher = bullet_new<btCollisionDispatcher>(_collisionConfiguration);
+    _overlappingPairCache = bullet_new<btDbvtBroadphase>();
+    _solver = bullet_new<btSequentialImpulseConstraintSolver>();
 
     // Create the world.
-    _world = new btDiscreteDynamicsWorld(_dispatcher, _overlappingPairCache, _solver, _collisionConfiguration);
+    _world = bullet_new<btDiscreteDynamicsWorld>(_dispatcher, _overlappingPairCache, _solver, _collisionConfiguration);
     _world->setGravity(BV(_gravity));
 
     // Register ghost pair callback so bullet detects collisions with ghost objects (used for character collisions).

+ 1 - 1
gameplay/src/PhysicsController.h

@@ -345,7 +345,7 @@ private:
         /**
             * Internal function used for Bullet integration (do not use or override).
             */
-        btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, const btCollisionObject* b, int partIdB, int indexB);    
+        btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* a, int partIdA, int indexA, const btCollisionObjectWrapper* b, int partIdB, int indexB);    
 
     private:
         PhysicsController* _pc;

+ 4 - 4
gameplay/src/PhysicsGenericConstraint.cpp

@@ -23,11 +23,11 @@ PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, PhysicsR
     {
         GP_ASSERT(b->_body && b->getNode());
         Vector3 origin = centerOfMassMidpoint(a->getNode(), b->getNode());
-        _constraint = new btGeneric6DofConstraint(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
+        _constraint = bullet_new<btGeneric6DofConstraint>(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
     }
     else
     {
-        _constraint = new btGeneric6DofConstraint(*a->_body, btTransform::getIdentity(), true);
+        _constraint = bullet_new<btGeneric6DofConstraint>(*a->_body, btTransform::getIdentity(), true);
     }
 }
 
@@ -53,12 +53,12 @@ PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, const Qu
 
         btTransform frameInA(BQ(rotationOffsetA), BV(tA));
         btTransform frameInB(BQ(rotationOffsetB), BV(tB));
-        _constraint = new btGeneric6DofConstraint(*a->_body, *b->_body, frameInA, frameInB, true);
+        _constraint = bullet_new<btGeneric6DofConstraint>(*a->_body, *b->_body, frameInA, frameInB, true);
     }
     else
     {
         btTransform frameInA(BQ(rotationOffsetA), BV(tA));
-        _constraint = new btGeneric6DofConstraint(*a->_body, frameInA, true);
+        _constraint = bullet_new<btGeneric6DofConstraint>(*a->_body, frameInA, true);
     }
 }
 

+ 2 - 2
gameplay/src/PhysicsHingeConstraint.cpp

@@ -34,12 +34,12 @@ PhysicsHingeConstraint::PhysicsHingeConstraint(PhysicsRigidBody* a, const Quater
 
         btTransform frameInA(BQ(rotationOffsetA), BV(tA));
         btTransform frameInB(BQ(rotationOffsetB), BV(tB));
-        _constraint = new btHingeConstraint(*a->_body, *b->_body, frameInA, frameInB);
+        _constraint = bullet_new<btHingeConstraint>(*a->_body, *b->_body, frameInA, frameInB);
     }
     else
     {
         btTransform frameInA(BQ(rotationOffsetA), BV(tA));
-        _constraint = new btHingeConstraint(*a->_body, frameInA);
+        _constraint = bullet_new<btHingeConstraint>(*a->_body, frameInA);
     }
 }
 

+ 4 - 4
gameplay/src/PhysicsSocketConstraint.cpp

@@ -16,11 +16,11 @@ PhysicsSocketConstraint::PhysicsSocketConstraint(PhysicsRigidBody* a, PhysicsRig
         btTransform frameInA = getTransformOffset(a->getNode(), origin);
         btTransform frameInB = getTransformOffset(b->getNode(), origin);
 
-        _constraint = new btPoint2PointConstraint(*a->_body, *b->_body, frameInA.getOrigin(), frameInB.getOrigin());
+        _constraint = bullet_new<btPoint2PointConstraint>(*a->_body, *b->_body, frameInA.getOrigin(), frameInB.getOrigin());
     }
     else
     {
-        _constraint = new btPoint2PointConstraint(*a->_body, btVector3(0.0f, 0.0f, 0.0f));
+        _constraint = bullet_new<btPoint2PointConstraint>(*a->_body, btVector3(0.0f, 0.0f, 0.0f));
     }
 }
 
@@ -44,11 +44,11 @@ PhysicsSocketConstraint::PhysicsSocketConstraint(PhysicsRigidBody* a, const Vect
         b->getNode()->getWorldMatrix().getScale(&sB);
         Vector3 tB(translationOffsetB.x * sB.x, translationOffsetB.y * sB.y, translationOffsetB.z * sB.z);
 
-        _constraint = new btPoint2PointConstraint(*a->_body, *b->_body, BV(tA), BV(tB));
+        _constraint = bullet_new<btPoint2PointConstraint>(*a->_body, *b->_body, BV(tA), BV(tB));
     }
     else
     {
-        _constraint = new btPoint2PointConstraint(*a->_body, BV(tA));
+        _constraint = bullet_new<btPoint2PointConstraint>(*a->_body, BV(tA));
     }
 }
 

+ 2 - 2
gameplay/src/PhysicsSpringConstraint.cpp

@@ -16,7 +16,7 @@ PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, PhysicsRig
     _b = b;
 
     Vector3 origin = centerOfMassMidpoint(a->getNode(), b->getNode());
-    _constraint = new btGeneric6DofSpringConstraint(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
+    _constraint = bullet_new<btGeneric6DofSpringConstraint>(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
 }
 
 PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
@@ -40,7 +40,7 @@ PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, const Quat
 
     btTransform frameInA(BQ(rotationOffsetA), BV(tA));
     btTransform frameInB(BQ(rotationOffsetB), BV(tB));
-    _constraint = new btGeneric6DofSpringConstraint(*a->_body, *b->_body, frameInA, frameInB, true);
+    _constraint = bullet_new<btGeneric6DofSpringConstraint>(*a->_body, *b->_body, frameInA, frameInB, true);
 }
 
 PhysicsSpringConstraint::~PhysicsSpringConstraint()

+ 1 - 1
gameplay/src/PhysicsVehicle.cpp

@@ -184,7 +184,7 @@ void PhysicsVehicle::initialize()
     btRigidBody* body = static_cast<btRigidBody*>(_rigidBody->getCollisionObject());
     btDynamicsWorld* dynamicsWorld = Game::getInstance()->getPhysicsController()->_world;
     _vehicleRaycaster = new VehicleNotMeRaycaster(dynamicsWorld, body);
-    _vehicle = new btRaycastVehicle(_vehicleTuning, body, _vehicleRaycaster);
+    _vehicle = bullet_new<btRaycastVehicle>(_vehicleTuning, body, _vehicleRaycaster);
     body->setActivationState(DISABLE_DEACTIVATION);
     dynamicsWorld->addVehicle(_vehicle);
     _vehicle->setCoordinateSystem(0, 1, 2);

+ 9 - 0
gameplay/src/Platform.h

@@ -361,6 +361,15 @@ public:
      * @see Mouse::MouseEvent
      */
     static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
+
+    /**
+     * Opens an URL in an external browser, if available.
+     *
+     * @param url URL to be opened.
+     *
+     * @return True if URL was opened successfully, false otherwise.
+     */
+    static bool launchURL(const char* url);
     
 private:
 

+ 194 - 61
gameplay/src/PlatformAndroid.cpp

@@ -105,25 +105,68 @@ static EGLenum checkErrorEGL(const char* msg)
     return error;
 }
 
+static int getRotation()
+{
+    jint rotation;
+
+    // Get the android application's activity.
+    ANativeActivity* activity = __state->activity;
+    JavaVM* jvm = __state->activity->vm;
+    JNIEnv* env = NULL;
+    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
+    jint res = jvm->AttachCurrentThread(&env, NULL);
+    if (res == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment when entering message pump.");
+        return -1; 
+    }
+    GP_ASSERT(env);
+
+    jclass clsContext = env->FindClass("android/content/Context");
+    GP_ASSERT(clsContext != NULL);
+    jmethodID getSystemService = env->GetMethodID(clsContext, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+    GP_ASSERT(getSystemService != NULL);
+    jfieldID WINDOW_SERVICE_ID = env->GetStaticFieldID(clsContext, "WINDOW_SERVICE", "Ljava/lang/String;");
+    GP_ASSERT(WINDOW_SERVICE_ID != NULL);
+    jstring WINDOW_SERVICE = (jstring) env->GetStaticObjectField(clsContext, WINDOW_SERVICE_ID);
+    GP_ASSERT(WINDOW_SERVICE != NULL);
+    jobject windowManager = env->CallObjectMethod(activity->clazz, getSystemService, WINDOW_SERVICE);
+    GP_ASSERT(windowManager != NULL);
+    jclass clsWindowManager = env->FindClass("android/view/WindowManager");
+    GP_ASSERT(clsWindowManager != NULL);
+    jmethodID getDefaultDisplay = env->GetMethodID(clsWindowManager, "getDefaultDisplay", "()Landroid/view/Display;");
+    GP_ASSERT(getDefaultDisplay != NULL);
+    jobject defaultDisplay = env->CallObjectMethod(windowManager, getDefaultDisplay);
+    GP_ASSERT(defaultDisplay != NULL);
+    jclass clsDisplay = env->FindClass("android/view/Display");
+    GP_ASSERT(clsDisplay != NULL);
+    jmethodID getRotation = env->GetMethodID(clsDisplay, "getRotation", "()I");
+    GP_ASSERT(getRotation != NULL)
+    rotation =  env->CallIntMethod(defaultDisplay, getRotation);
+
+    return rotation;
+}
+
+
 // Initialized EGL resources.
 static bool initEGL()
 {
-	int samples = 0;
-	Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
-	if (config)
-	{
-		samples = std::max(config->getInt("samples"), 0);
-	}
+    int samples = 0;
+    Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
+    if (config)
+    {
+        samples = std::max(config->getInt("samples"), 0);
+    }
 
     // Hard-coded to 32-bit/OpenGL ES 2.0.
     // NOTE: EGL_SAMPLE_BUFFERS, EGL_SAMPLES and EGL_DEPTH_SIZE MUST remain at the beginning of the attribute list
     // since they are expected to be at indices 0-5 in config fallback code later.
-	// EGL_DEPTH_SIZE is also expected to
+    // EGL_DEPTH_SIZE is also expected to
     EGLint eglConfigAttrs[] =
     {
-		EGL_SAMPLE_BUFFERS,     samples > 0 ? 1 : 0,
-		EGL_SAMPLES,            samples,
-		EGL_DEPTH_SIZE,         24,
+        EGL_SAMPLE_BUFFERS,     samples > 0 ? 1 : 0,
+        EGL_SAMPLES,            samples,
+        EGL_DEPTH_SIZE,         24,
         EGL_RED_SIZE,           8,
         EGL_GREEN_SIZE,         8,
         EGL_BLUE_SIZE,          8,
@@ -163,51 +206,51 @@ static bool initEGL()
             goto error;
         }
 
-		// Try both 24 and 16-bit depth sizes since some hardware (i.e. Tegra) does not support 24-bit depth
-		bool validConfig = false;
-		EGLint depthSizes[] = { 24, 16 };
-		for (unsigned int i = 0; i < 2; ++i)
-		{
-			eglConfigAttrs[1] = samples > 0 ? 1 : 0;
-			eglConfigAttrs[3] = samples;
-			eglConfigAttrs[5] = depthSizes[i];
-
-			if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
-			{
-				validConfig = true;
-				break;
-			}
-
-			if (samples)
-			{
-				// Try lowering the MSAA sample size until we find a supported config
-				int sampleCount = samples;
-				while (sampleCount)
-				{
-					GP_WARN("No EGL config found for depth_size=%d and samples=%d. Trying samples=%d instead.", depthSizes[i], sampleCount, sampleCount / 2);
-					sampleCount /= 2;
-					eglConfigAttrs[1] = sampleCount > 0 ? 1 : 0;
-					eglConfigAttrs[3] = sampleCount;
-					if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
-					{
-						validConfig = true;
-						break;
-					}
-				}
-				if (validConfig)
-					break;
-			}
-			else
-			{
-				GP_WARN("No EGL config found for depth_size=%d.", depthSizes[i]);
-			}
-		}
-
-		if (!validConfig)
-		{
-			checkErrorEGL("eglChooseConfig");
-			goto error;
-		}
+        // Try both 24 and 16-bit depth sizes since some hardware (i.e. Tegra) does not support 24-bit depth
+        bool validConfig = false;
+        EGLint depthSizes[] = { 24, 16 };
+        for (unsigned int i = 0; i < 2; ++i)
+        {
+            eglConfigAttrs[1] = samples > 0 ? 1 : 0;
+            eglConfigAttrs[3] = samples;
+            eglConfigAttrs[5] = depthSizes[i];
+
+            if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
+            {
+                validConfig = true;
+                break;
+            }
+
+            if (samples)
+            {
+                // Try lowering the MSAA sample size until we find a supported config
+                int sampleCount = samples;
+                while (sampleCount)
+                {
+                    GP_WARN("No EGL config found for depth_size=%d and samples=%d. Trying samples=%d instead.", depthSizes[i], sampleCount, sampleCount / 2);
+                    sampleCount /= 2;
+                    eglConfigAttrs[1] = sampleCount > 0 ? 1 : 0;
+                    eglConfigAttrs[3] = sampleCount;
+                    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
+                    {
+                        validConfig = true;
+                        break;
+                    }
+                }
+                if (validConfig)
+                    break;
+            }
+            else
+            {
+                GP_WARN("No EGL config found for depth_size=%d.", depthSizes[i]);
+            }
+        }
+
+        if (!validConfig)
+        {
+            checkErrorEGL("eglChooseConfig");
+            goto error;
+        }
 
         __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
         if (__eglContext == EGL_NO_CONTEXT)
@@ -241,8 +284,7 @@ static bool initEGL()
     eglQuerySurface(__eglDisplay, __eglSurface, EGL_WIDTH, &__width);
     eglQuerySurface(__eglDisplay, __eglSurface, EGL_HEIGHT, &__height);
 
-    if (__width < __height)
-        __orientationAngle = 0;
+    __orientationAngle = getRotation() * 90;
     
     // Set vsync.
     eglSwapInterval(__eglDisplay, WINDOW_VSYNC ? 1 : 0);
@@ -1107,15 +1149,24 @@ void Platform::getAccelerometerValues(float* pitch, float* roll)
     
     // By default, android accelerometer values are oriented to the portrait mode.
     // flipping the x and y to get the desired landscape mode values.
-    if (__orientationAngle == 90)
+    switch (__orientationAngle)
     {
+    case 90:
         tx = -__sensorEvent.acceleration.y;
         ty = __sensorEvent.acceleration.x;
-    }
-    else
-    {
+        break;
+    case 180:
+        tx = -__sensorEvent.acceleration.x;
+        ty = -__sensorEvent.acceleration.y;
+        break;
+    case 270:
+        tx = __sensorEvent.acceleration.y;
+        ty = -__sensorEvent.acceleration.x;
+        break;
+    default:
         tx = __sensorEvent.acceleration.x;
         ty = __sensorEvent.acceleration.y;
+        break;
     }
     tz = __sensorEvent.acceleration.z;
 
@@ -1307,6 +1358,88 @@ float Platform::getGamepadTriggerValue(unsigned int gamepadHandle, unsigned int
     return 0.0f;
 }
 
+bool Platform::launchURL(const char *url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    bool result = true;
+
+    android_app* state = __state;
+    GP_ASSERT(state && state->activity && state->activity->vm);
+    JavaVM* jvm = state->activity->vm;
+    JNIEnv* env = NULL;
+    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
+    jint r = jvm->AttachCurrentThread(&env, NULL);
+    if (r == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment to display keyboard.");
+        return false;
+    }
+    GP_ASSERT(env);
+
+    jclass classActivity = env->FindClass("android/app/NativeActivity");
+    jclass classIntent = env->FindClass("android/content/Intent");
+    jclass classUri = env->FindClass("android/net/Uri");
+
+    GP_ASSERT(classActivity && classIntent && classUri);
+
+    // Get static field ID Intent.ACTION_VIEW
+    jfieldID fieldActionView = env->GetStaticFieldID(classIntent, "ACTION_VIEW", "Ljava/lang/String;");
+    GP_ASSERT(fieldActionView);
+
+    // Get string value of Intent.ACTION_VIEW, we'll need that to pass to Intent's constructor later on
+    jstring paramActionView = (jstring)env->GetStaticObjectField(classIntent, fieldActionView);
+    GP_ASSERT(paramActionView);
+
+    // Get method ID Uri.parse, will be needed to parse the url given into Uri object
+    jmethodID methodUriParse = env->GetStaticMethodID(classUri, "parse","(Ljava/lang/String;)Landroid/net/Uri;");
+    GP_ASSERT(methodUriParse);
+
+    // Get method ID Activity.startActivity, so we can start the appropriate activity for the View action of our Uri
+    jmethodID methodActivityStartActivity = env->GetMethodID(classActivity, "startActivity","(Landroid/content/Intent;)V");
+    GP_ASSERT(methodActivityStartActivity);
+
+    // Get method ID Intent constructor, the one that takes action and uri (String;Uri)
+    jmethodID methodIntentInit = env->GetMethodID(classIntent, "<init>","(Ljava/lang/String;Landroid/net/Uri;)V");
+    GP_ASSERT(methodIntentInit);
+
+    // Convert our url to Java's string and parse it to Uri
+    jstring paramUrlString = env->NewStringUTF(url);
+    jobject paramUri = env->CallStaticObjectMethod(classUri, methodUriParse, paramUrlString);
+    GP_ASSERT(paramUri);
+
+    // Create Intent with Intent.ACTION_VIEW and parsed Uri arguments
+    jobject paramIntent = env->NewObject(classIntent, methodIntentInit, paramActionView, paramUri);
+    GP_ASSERT(paramIntent);
+
+    // Launch NativeActivity.startActivity with our intent to view the url! state->activity->clazz holds
+    // our NativeActivity object
+    env->CallVoidMethod(state->activity->clazz, methodActivityStartActivity, paramIntent);
+
+    /* startActivity may throw a ActivitNotFoundException if, well, activity is not found.
+       Example: http://<url> is passed to the intent but there is no browser installed in the system
+       we need to handle it. */
+    jobject exception = env->ExceptionOccurred();
+
+    // We're not lucky here
+    if (exception)
+    {
+        // Print out the exception data to logcat
+        env->ExceptionDescribe();
+
+        // Exception needs to be cleared
+        env->ExceptionClear();
+
+        // Launching the url failed
+        result = false;
+    }
+
+    // See you Space Cowboy
+    jvm->DetachCurrentThread();
+    return result;
+}
+
 }
 
 #endif

+ 28 - 20
gameplay/src/PlatformBlackBerry.cpp

@@ -776,26 +776,26 @@ Platform* Platform::create(Game* game, void* attachToWindow)
 
     if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) != EGL_TRUE || eglConfigCount == 0)
     {
-    	bool success = false;
-    	while (samples)
-    	{
-    		// Try lowering the MSAA sample count until we find a supported config
-    		GP_WARN("Failed to find a valid EGL configuration with EGL samples=%d. Trying samples=%d instead.", samples, samples/2);
-    		samples /= 2;
-    		eglConfigAttrs[1] = samples > 0 ? 1 : 0;
-    		eglConfigAttrs[3] = samples;
-    		if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
-    		{
-    			success = true;
-    			break;
-    		}
-    	}
-
-    	if (!success)
-    	{
-			checkErrorEGL("eglChooseConfig");
-			goto error;
-    	}
+        bool success = false;
+        while (samples)
+        {
+            // Try lowering the MSAA sample count until we find a supported config
+            GP_WARN("Failed to find a valid EGL configuration with EGL samples=%d. Trying samples=%d instead.", samples, samples/2);
+            samples /= 2;
+            eglConfigAttrs[1] = samples > 0 ? 1 : 0;
+            eglConfigAttrs[3] = samples;
+            if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
+            {
+                success = true;
+                break;
+            }
+        }
+
+        if (!success)
+        {
+            checkErrorEGL("eglChooseConfig");
+            goto error;
+        }
     }
 
     __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
@@ -1424,6 +1424,14 @@ float Platform::getGamepadTriggerValue(unsigned int gamepadHandle, unsigned int
     return 0.0f;
 }
 
+bool Platform::launchURL(const char* url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    return navigator_invoke(url, NULL) == BPS_SUCCESS;
+}
+
 }
 
 #endif

+ 331 - 55
gameplay/src/PlatformLinux.cpp

@@ -24,12 +24,16 @@ static double __timeAbsolute;
 static bool __vsync = WINDOW_VSYNC;
 static float __pitch;
 static float __roll;
+static bool __mouseCaptured = false;
+static float __mouseCapturePointX = 0;
+static float __mouseCapturePointY = 0;
 static bool __cursorVisible = true;
 static Display* __display;
 static Window   __window;
 static int __windowSize[2];
 static GLXContext __context;
 static Window __attachToWindow;
+static Atom __atomWmDeleteWindow;
 
 namespace gameplay
 {
@@ -355,6 +359,124 @@ static Keyboard::Key getKey(KeySym sym)
     }
 }
 
+/**
+ * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
+ */
+static int getUnicode(Keyboard::Key key)
+{
+
+    switch (key)
+    {
+    case Keyboard::KEY_BACKSPACE:
+        return 0x0008;
+    case Keyboard::KEY_TAB:
+        return 0x0009;
+    case Keyboard::KEY_RETURN:
+    case Keyboard::KEY_KP_ENTER:
+        return 0x000A;
+    case Keyboard::KEY_ESCAPE:
+        return 0x001B;
+    case Keyboard::KEY_SPACE:
+    case Keyboard::KEY_EXCLAM:
+    case Keyboard::KEY_QUOTE:
+    case Keyboard::KEY_NUMBER:
+    case Keyboard::KEY_DOLLAR:
+    case Keyboard::KEY_PERCENT:
+    case Keyboard::KEY_CIRCUMFLEX:
+    case Keyboard::KEY_AMPERSAND:
+    case Keyboard::KEY_APOSTROPHE:
+    case Keyboard::KEY_LEFT_PARENTHESIS:
+    case Keyboard::KEY_RIGHT_PARENTHESIS:
+    case Keyboard::KEY_ASTERISK:
+    case Keyboard::KEY_PLUS:
+    case Keyboard::KEY_COMMA:
+    case Keyboard::KEY_MINUS:
+    case Keyboard::KEY_PERIOD:
+    case Keyboard::KEY_SLASH:
+    case Keyboard::KEY_ZERO:
+    case Keyboard::KEY_ONE:
+    case Keyboard::KEY_TWO:
+    case Keyboard::KEY_THREE:
+    case Keyboard::KEY_FOUR:
+    case Keyboard::KEY_FIVE:
+    case Keyboard::KEY_SIX:
+    case Keyboard::KEY_SEVEN:
+    case Keyboard::KEY_EIGHT:
+    case Keyboard::KEY_NINE:
+    case Keyboard::KEY_COLON:
+    case Keyboard::KEY_SEMICOLON:
+    case Keyboard::KEY_LESS_THAN:
+    case Keyboard::KEY_EQUAL:
+    case Keyboard::KEY_GREATER_THAN:
+    case Keyboard::KEY_QUESTION:
+    case Keyboard::KEY_AT:
+    case Keyboard::KEY_CAPITAL_A:
+    case Keyboard::KEY_CAPITAL_B:
+    case Keyboard::KEY_CAPITAL_C:
+    case Keyboard::KEY_CAPITAL_D:
+    case Keyboard::KEY_CAPITAL_E:
+    case Keyboard::KEY_CAPITAL_F:
+    case Keyboard::KEY_CAPITAL_G:
+    case Keyboard::KEY_CAPITAL_H:
+    case Keyboard::KEY_CAPITAL_I:
+    case Keyboard::KEY_CAPITAL_J:
+    case Keyboard::KEY_CAPITAL_K:
+    case Keyboard::KEY_CAPITAL_L:
+    case Keyboard::KEY_CAPITAL_M:
+    case Keyboard::KEY_CAPITAL_N:
+    case Keyboard::KEY_CAPITAL_O:
+    case Keyboard::KEY_CAPITAL_P:
+    case Keyboard::KEY_CAPITAL_Q:
+    case Keyboard::KEY_CAPITAL_R:
+    case Keyboard::KEY_CAPITAL_S:
+    case Keyboard::KEY_CAPITAL_T:
+    case Keyboard::KEY_CAPITAL_U:
+    case Keyboard::KEY_CAPITAL_V:
+    case Keyboard::KEY_CAPITAL_W:
+    case Keyboard::KEY_CAPITAL_X:
+    case Keyboard::KEY_CAPITAL_Y:
+    case Keyboard::KEY_CAPITAL_Z:
+    case Keyboard::KEY_LEFT_BRACKET:
+    case Keyboard::KEY_BACK_SLASH:
+    case Keyboard::KEY_RIGHT_BRACKET:
+    case Keyboard::KEY_UNDERSCORE:
+    case Keyboard::KEY_GRAVE:
+    case Keyboard::KEY_A:
+    case Keyboard::KEY_B:
+    case Keyboard::KEY_C:
+    case Keyboard::KEY_D:
+    case Keyboard::KEY_E:
+    case Keyboard::KEY_F:
+    case Keyboard::KEY_G:
+    case Keyboard::KEY_H:
+    case Keyboard::KEY_I:
+    case Keyboard::KEY_J:
+    case Keyboard::KEY_K:
+    case Keyboard::KEY_L:
+    case Keyboard::KEY_M:
+    case Keyboard::KEY_N:
+    case Keyboard::KEY_O:
+    case Keyboard::KEY_P:
+    case Keyboard::KEY_Q:
+    case Keyboard::KEY_R:
+    case Keyboard::KEY_S:
+    case Keyboard::KEY_T:
+    case Keyboard::KEY_U:
+    case Keyboard::KEY_V:
+    case Keyboard::KEY_W:
+    case Keyboard::KEY_X:
+    case Keyboard::KEY_Y:
+    case Keyboard::KEY_Z:
+    case Keyboard::KEY_LEFT_BRACE:
+    case Keyboard::KEY_BAR:
+    case Keyboard::KEY_RIGHT_BRACE:
+    case Keyboard::KEY_TILDE:
+        return key;
+    default:
+        return 0;
+    }
+}
+
 extern void print(const char* format, ...)
 {
     GP_ASSERT(format);
@@ -380,19 +502,52 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     __attachToWindow = (Window)attachToWindow;
     FileSystem::setResourcePath("./");
     Platform* platform = new Platform(game);
-
-    // Get the display and initialize.
+    
+    // Get the display and initialize
     __display = XOpenDisplay(NULL);
     if (__display == NULL)
     {
         perror("XOpenDisplay");
         return NULL;
     }
+     
+    // Get the window configuration values
+    const char *title = NULL;
+    int __x = 0, __y = 0, __width = 1280, __height = 800;
+    bool fullscreen = false;
+    if (game->getConfig())
+    {
+        Properties* config = game->getConfig()->getNamespace("window", true);
+        if (config)
+        {
+            // Read window title.
+            title = config->getString("title");
+
+            // Read window rect.
+            int x = config->getInt("x");
+            int y = config->getInt("y");
+            int width = config->getInt("width");
+            int height = config->getInt("height");
+            fullscreen = config->getBool("fullscreen");
+
+            if (fullscreen && width == 0 && height == 0)
+            {
+                // Use the screen resolution if fullscreen is true but width and height were not set in the config
+                int screen_num = DefaultScreen(__display);
+                width = DisplayWidth(__display, screen_num);
+                height = DisplayHeight(__display, screen_num);
+            }
+            if (x != 0) __x = x;
+            if (y != 0) __y = y;
+            if (width != 0) __width = width;
+            if (height != 0) __height = height;
+        }
+    }
 
     // GLX version
     GLint majorGLX, minorGLX = 0;
     glXQueryVersion(__display, &majorGLX, &minorGLX);
-    if(majorGLX == 1 && minorGLX < 2)
+    if (majorGLX == 1 && minorGLX < 2)
     {
         perror("GLX 1.2 or greater is required.");
         XCloseDisplay(__display);
@@ -411,25 +566,25 @@ Platform* Platform::create(Game* game, void* attachToWindow)
 
     // Get the configs
     int configAttribs[] = 
-    { 
+    {
         GLX_RENDER_TYPE,    GLX_RGBA_BIT,
         GLX_DRAWABLE_TYPE,  GLX_WINDOW_BIT,
         GLX_X_RENDERABLE,   True,
-        GLX_DEPTH_SIZE,     24, 
+        GLX_DEPTH_SIZE,     24,
         GLX_STENCIL_SIZE,   8,
         GLX_RED_SIZE,       8,
         GLX_GREEN_SIZE,     8,
         GLX_BLUE_SIZE,      8,
         GLX_DOUBLEBUFFER,   True,
-        0 
+        0
     };
     GLXFBConfig* configs;
     int configCount = 0;
     configs = glXChooseFBConfig(__display, DefaultScreen(__display), configAttribs, &configCount);
-    if( configCount == 0 || configs == 0 )    
-    {    
+    if ( configCount == 0 || configs == 0 )
+    {
         perror( "glXChooseFBConfig" );
-        return NULL;    
+        return NULL;
     }
 
     // Create the windows
@@ -439,8 +594,8 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     XSetWindowAttributes winAttribs;
     long eventMask;
     eventMask = ExposureMask | VisibilityChangeMask | StructureNotifyMask |
-                KeyPressMask | KeyReleaseMask | PointerMotionMask | 
-                ButtonPressMask | ButtonReleaseMask | 
+                KeyPressMask | KeyReleaseMask | PointerMotionMask |
+                ButtonPressMask | ButtonReleaseMask |
                 EnterWindowMask | LeaveWindowMask;
     winAttribs.event_mask = eventMask;
     winAttribs.border_pixel = 0;
@@ -450,15 +605,40 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     GLint winMask;
     winMask = CWBorderPixel | CWBitGravity | CWEventMask| CWColormap;
    
-    __window = XCreateWindow(__display, DefaultRootWindow(__display), 0, 0, 1280, 720, 0, 
+    __window = XCreateWindow(__display, DefaultRootWindow(__display), __x, __y, __width, __height, 0,
                             visualInfo->depth, InputOutput, visualInfo->visual, winMask,
-                            &winAttribs); 
-    
+                            &winAttribs);
+
+    // Tell the window manager that it should send the delete window notification through ClientMessage
+    __atomWmDeleteWindow = XInternAtom(__display, "WM_DELETE_WINDOW", False);
+    XSetWMProtocols(__display, __window, &__atomWmDeleteWindow, 1);
+
     XMapWindow(__display, __window);
-    XStoreName(__display, __window, "");
+
+    // Send fullscreen atom message to the window; most window managers respect WM_STATE messages
+    // Note: fullscreen mode will use native desktop resolution and won't care about width/height specified
+    if (fullscreen)
+    {
+        XEvent xev;
+        Atom atomWm_state = XInternAtom(__display, "_NET_WM_STATE", False);
+        Atom atomFullscreen = XInternAtom(__display, "_NET_WM_STATE_FULLSCREEN", False);
+
+        memset(&xev, 0, sizeof(xev));
+        xev.type = ClientMessage;
+        xev.xclient.window = __window;
+        xev.xclient.message_type = atomWm_state;
+        xev.xclient.format = 32;
+        xev.xclient.data.l[0] = 1;
+        xev.xclient.data.l[1] = atomFullscreen;
+        xev.xclient.data.l[2] = 0;
+
+        XSendEvent(__display, DefaultRootWindow(__display), false, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
+    }
+    
+    XStoreName(__display, __window, title ? title : "");
 
     __context = glXCreateContext(__display, visualInfo, NULL, True);
-    if(!__context)
+    if (!__context)
     {
         perror("glXCreateContext");
         return NULL;
@@ -468,7 +648,7 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     // Use OpenGL 2.x with GLEW
     glewExperimental = GL_TRUE;
     GLenum glewStatus = glewInit();
-    if(glewStatus != GLEW_OK)
+    if (glewStatus != GLEW_OK)
     {
         perror("glewInit");
         return NULL;
@@ -508,14 +688,14 @@ double timespec2millis(struct timespec *a)
     return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
 }
 
-void updateWindowSize()    
-{    
-    GP_ASSERT(__display);    
+void updateWindowSize()
+{
+    GP_ASSERT(__display);
     GP_ASSERT(__window);
-    XWindowAttributes windowAttrs;    
-    XGetWindowAttributes(__display, __window, &windowAttrs);      
-    __windowSize[0] = windowAttrs.width;    
-    __windowSize[1] = windowAttrs.height;    
+    XWindowAttributes windowAttrs;
+    XGetWindowAttributes(__display, __window, &windowAttrs);
+    __windowSize[0] = windowAttrs.width;
+    __windowSize[1] = windowAttrs.height;
 }
 
 int Platform::enterMessagePump()
@@ -540,7 +720,7 @@ int Platform::enterMessagePump()
     // Run the game.
     _game->run();
 
-    // Setup select for message handling (to allow non-blocking)    
+    // Setup select for message handling (to allow non-blocking)
     int x11_fd = ConnectionNumber(__display);
     
     pollfd xpolls[1];
@@ -550,15 +730,23 @@ int Platform::enterMessagePump()
     // Message loop.
     while (true)
     {
-        int ret = poll( xpolls, 1, 16 );
-
-        // handle all pending events in one block 
-        while (ret && XPending(__display))
+        poll( xpolls, 1, 16 );
+        // handle all pending events in one block
+        while (XPending(__display))
         {
            XNextEvent(__display, &evt);
         
-            switch (evt.type) 
+            switch (evt.type)
             {
+            case ClientMessage:
+                {
+                    // Handle destroy window message correctly
+                    if (evt.xclient.data.l[0] == __atomWmDeleteWindow)
+                    {
+                        _game->exit();
+                    }
+                }
+                break;
             case DestroyNotify :
                 {
                     cleanupX11();
@@ -566,7 +754,7 @@ int Platform::enterMessagePump()
                 }
                 break;
 
-            case Expose: 
+            case Expose:
                 {
                     updateWindowSize();
                 }
@@ -574,9 +762,25 @@ int Platform::enterMessagePump()
 
             case KeyPress:
                 {
-                    KeySym sym = XLookupKeysym(&evt.xkey, 0);
+                    KeySym sym = XLookupKeysym(&evt.xkey, (evt.xkey.state & shiftDown) ? 1 : 0);
+
+
+                    //TempSym needed because XConvertCase operates on two keysyms: One lower and the other upper, we are only interested in the upper case
+                    KeySym tempSym;
+                    if (capsOn && !shiftDown)
+                        XConvertCase(sym,  &tempSym, &sym);
+
                     Keyboard::Key key = getKey(sym);
                     gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_PRESS, key);
+
+                    if (key == Keyboard::KEY_CAPS_LOCK)
+                        capsOn = !capsOn;
+                    if (key == Keyboard::KEY_SHIFT)
+                        shiftDown = true;
+
+                    if (int character = getUnicode(key))
+                        gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, character);
+
                 }
                 break;
 
@@ -584,10 +788,10 @@ int Platform::enterMessagePump()
                 {
                     //detect and drop repeating keystrokes (no other way to do this using the event interface)
                     XEvent next;
-                    if( XPending(__display) )
+                    if ( XPending(__display) )
                     {
                         XPeekEvent(__display,&next);
-                        if( next.type == KeyPress 
+                        if ( next.type == KeyPress
                             && next.xkey.time == evt.xkey.time
                             && next.xkey.keycode == evt.xkey.keycode )
                         {
@@ -599,13 +803,16 @@ int Platform::enterMessagePump()
                     KeySym sym = XLookupKeysym(&evt.xkey, 0);
                     Keyboard::Key key = getKey(sym);
                     gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_RELEASE, key);
+
+                    if (key == Keyboard::KEY_SHIFT)
+                        shiftDown = false;
                 }
                 break;
 
             case ButtonPress:
                 {
                     gameplay::Mouse::MouseEvent mouseEvt;
-                    switch(evt.xbutton.button)
+                    switch (evt.xbutton.button)
                     {
                         case 1:
                             mouseEvt = gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON;
@@ -618,8 +825,8 @@ int Platform::enterMessagePump()
                             break;
                         case 4:
                         case 5:
-                            gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_WHEEL, 
-                                                                   evt.xbutton.x, evt.xbutton.y, 
+                            gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_WHEEL,
+                                                                   evt.xbutton.x, evt.xbutton.y,
                                                                    evt.xbutton.button == Button4 ? 1 : -1);
                             break;
                         default:
@@ -635,7 +842,7 @@ int Platform::enterMessagePump()
             case ButtonRelease:
                 {
                     gameplay::Mouse::MouseEvent mouseEvt;
-                    switch(evt.xbutton.button)
+                    switch (evt.xbutton.button)
                     {
                         case 1:
                             mouseEvt = gameplay::Mouse::MOUSE_RELEASE_LEFT_BUTTON;
@@ -658,25 +865,45 @@ int Platform::enterMessagePump()
         
             case MotionNotify:
                 {
-                    if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_MOVE, evt.xmotion.x, evt.xmotion.y, 0))
+                    int x = evt.xmotion.x;
+                    int y = evt.xmotion.y;
+
+                    if (__mouseCaptured)
+                    {
+                        if (x == __mouseCapturePointX && y == __mouseCapturePointY)
+                        {
+                            // Discard the first MotionNotify following capture
+                            // since it contains bogus x,y data.
+                            break;
+                        }
+
+                        // Convert to deltas
+                        x -= __mouseCapturePointX;
+                        y -= __mouseCapturePointY;
+
+                        // Warp mouse back to center of screen.
+                        XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
+                    }
+
+                    if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
                     {
                         if (evt.xmotion.state & Button1Mask)
                         {
-                            gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, evt.xmotion.x, evt.xmotion.y, 0);
+                            gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, x, y, 0);
                         }
                         else if (evt.xmotion.state & Button3Mask)
                         {
                             // Update the pitch and roll by adding the scaled deltas.
-                            __roll += (float)(evt.xbutton.x - lx) * ACCELEROMETER_X_FACTOR;
-                            __pitch += -(float)(evt.xbutton.y - ly) * ACCELEROMETER_Y_FACTOR;
+                            __roll += (float)(x - lx) * ACCELEROMETER_X_FACTOR;
+                            __pitch += -(float)(y - ly) * ACCELEROMETER_Y_FACTOR;
 
                             // Clamp the values to the valid range.
                             __roll = max(min(__roll, 90.0f), -90.0f);
                             __pitch = max(min(__pitch, 90.0f), -90.0f);
 
                             // Update the last X/Y values.
-                            lx = evt.xbutton.x;
-                            ly = evt.xbutton.y;
+                            lx = x;
+                            ly = y;
                         }
                     }
                 }
@@ -688,7 +915,13 @@ int Platform::enterMessagePump()
         }
 
         if (_game)
+        {
+            // Game state will be uninitialized if game was closed through Game::exit()
+            if (_game->getState() == Game::UNINITIALIZED)
+                break;
+            
             _game->frame();
+        }
 
         glXSwapBuffers(__display, __window);
     }
@@ -698,15 +931,15 @@ int Platform::enterMessagePump()
     return 0;
 }
     
-void Platform::signalShutdown() 
-{ 
+void Platform::signalShutdown()
+{
     // nothing to do  
 }
-
-bool Platform::canExit()
-{
-    return true;
-}
+
+bool Platform::canExit()
+{
+    return true;
+}
     
 unsigned int Platform::getDisplayWidth()
 {
@@ -782,13 +1015,31 @@ bool Platform::hasMouse()
 
 void Platform::setMouseCaptured(bool captured)
 {
-    // TODO
+    if (captured != __mouseCaptured)
+    {
+        if (captured)
+        {
+            // Hide the cursor and warp it to the center of the screen
+            __mouseCapturePointX = getDisplayWidth() / 2;
+            __mouseCapturePointY = getDisplayHeight() / 2;
+
+            setCursorVisible(false);
+            XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
+        }
+        else
+        {
+            // Restore cursor
+            XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
+            setCursorVisible(true);
+        }
+
+        __mouseCaptured = captured;
+    }
 }
 
 bool Platform::isMouseCaptured()
 {
-    // TODO
-    return false;
+    return __mouseCaptured;
 }
 
 void Platform::setCursorVisible(bool visible)
@@ -797,7 +1048,17 @@ void Platform::setCursorVisible(bool visible)
     {
         if (visible)
         {
-            XDefineCursor(__display, __window, None);
+            Cursor invisibleCursor;
+            Pixmap bitmapNoData;
+            XColor black;
+            static char noData[] = {0, 0, 0, 0, 0, 0, 0, 0};
+            black.red = black.green = black.blue = 0;
+            bitmapNoData = XCreateBitmapFromData(__display, __window, noData, 8, 8);
+            invisibleCursor = XCreatePixmapCursor(__display, bitmapNoData, bitmapNoData, &black, &black, 0, 0);
+
+            XDefineCursor(__display, __window, invisibleCursor);
+            XFreeCursor(__display, invisibleCursor);
+            XFreePixmap(__display, bitmapNoData);
         }
         else
         {
@@ -929,6 +1190,21 @@ float Platform::getGamepadTriggerValue(unsigned int gamepadHandle, unsigned int
     return 0.0f;
 }
 
+bool Platform::launchURL(const char* url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    int len = strlen(url);
+    
+    char* cmd = new char[11 + len];
+    sprintf(cmd, "xdg-open %s", url);
+    int r = system(cmd);
+    SAFE_DELETE_ARRAY(cmd);
+    
+    return (r == 0);
+}
+
 }
 
 #endif

+ 205 - 39
gameplay/src/PlatformMacOSX.mm

@@ -48,6 +48,7 @@ static char* __title = NULL;
 static bool __fullscreen = false;
 static void* __attachToWindow = NULL;
 static bool __mouseCaptured = false;
+static bool __mouseCapturedFirstPass = false;
 static CGPoint __mouseCapturePoint;
 static bool __cursorVisible = true;
 static View* __view = NULL;
@@ -582,7 +583,7 @@ double getMachTimeInMilliseconds()
 - (void)hidValueAvailable:(IOHIDValueRef)value
 {
     IOHIDElementRef element = IOHIDValueGetElement(value);
-	IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
+    IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
     
     if(IOHIDValueGetLength(value) > 4) return; // saftey precaution for PS3 cotroller
     CFIndex integerValue = IOHIDValueGetIntegerValue(value);
@@ -848,8 +849,17 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     
+    float y;
     if (__mouseCaptured)
     {
+        if (__mouseCapturedFirstPass)
+        {
+            // Discard the first mouseMoved event following transition into capture
+            // since it contains bogus x,y data.
+            __mouseCapturedFirstPass = false;
+            return;
+        }
+
         point.x = [event deltaX];
         point.y = [event deltaY];
 
@@ -859,9 +869,14 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
         centerPoint.x = rect.origin.x + (rect.size.width / 2);
         centerPoint.y = rect.origin.y + (rect.size.height / 2);
         CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
+        y = point.y;
+    }
+    else
+    {
+        y = __height - point.y;
     }
     
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, y, 0);
 }
 
 - (void) mouseDragged: (NSEvent*) event
@@ -961,6 +976,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 int getKey(unsigned short keyCode, unsigned int modifierFlags)
 {
     __shiftDown = (modifierFlags & NSShiftKeyMask);
+    unsigned int caps = (__shiftDown ? 1 : 0) ^ ((modifierFlags & NSAlphaShiftKeyMask) ? 1 : 0);
     switch(keyCode)
     {
         case 0x69:
@@ -1098,63 +1114,181 @@ int getKey(unsigned short keyCode, unsigned int modifierFlags)
             return __shiftDown ? Keyboard::KEY_QUOTE : Keyboard::KEY_APOSTROPHE;
             
         case 0x00:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
+            return caps ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
         case 0x0B:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
+            return caps ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
         case 0x08:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
+            return caps ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
         case 0x02:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
+            return caps ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
         case 0x0E:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
+            return caps ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
         case 0x03:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
+            return caps ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
         case 0x05:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
+            return caps ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
         case 0x04:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
+            return caps ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
         case 0x22:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
+            return caps ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
         case 0x26:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
+            return caps ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
         case 0x28:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
+            return caps ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
         case 0x25:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
+            return caps ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
         case 0x2E:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
+            return caps ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
         case 0x2D:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
+            return caps ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
         case 0x1F:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
+            return caps ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
         case 0x23:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
+            return caps ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
         case 0x0C:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
+            return caps ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
         case 0x0F:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
+            return caps ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
         case 0x01:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
+            return caps ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
         case 0x11:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
+            return caps ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
         case 0x20:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
+            return caps ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
         case 0x09:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
+            return caps ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
         case 0x0D:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
+            return caps ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
         case 0x07:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
+            return caps ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
         case 0x10:
-            return __shiftDown ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
+            return caps ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
         case 0x06:
-            return __shiftDown ? Keyboard::KEY_CAPITAL_Z : Keyboard::KEY_Z;
+            return caps ? Keyboard::KEY_CAPITAL_Z : Keyboard::KEY_Z;
 
         default:
             return Keyboard::KEY_NONE;
     }
 }
 
+/**
+ * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
+ */
+int getUnicode(int key)
+{
+    
+    switch (key)
+    {
+        case Keyboard::KEY_BACKSPACE:
+            return 0x0008;
+        case Keyboard::KEY_TAB:
+            return 0x0009;
+        case Keyboard::KEY_RETURN:
+        case Keyboard::KEY_KP_ENTER:
+            return 0x000A;
+        case Keyboard::KEY_ESCAPE:
+            return 0x001B;
+        case Keyboard::KEY_SPACE:
+        case Keyboard::KEY_EXCLAM:
+        case Keyboard::KEY_QUOTE:
+        case Keyboard::KEY_NUMBER:
+        case Keyboard::KEY_DOLLAR:
+        case Keyboard::KEY_PERCENT:
+        case Keyboard::KEY_CIRCUMFLEX:
+        case Keyboard::KEY_AMPERSAND:
+        case Keyboard::KEY_APOSTROPHE:
+        case Keyboard::KEY_LEFT_PARENTHESIS:
+        case Keyboard::KEY_RIGHT_PARENTHESIS:
+        case Keyboard::KEY_ASTERISK:
+        case Keyboard::KEY_PLUS:
+        case Keyboard::KEY_COMMA:
+        case Keyboard::KEY_MINUS:
+        case Keyboard::KEY_PERIOD:
+        case Keyboard::KEY_SLASH:
+        case Keyboard::KEY_ZERO:
+        case Keyboard::KEY_ONE:
+        case Keyboard::KEY_TWO:
+        case Keyboard::KEY_THREE:
+        case Keyboard::KEY_FOUR:
+        case Keyboard::KEY_FIVE:
+        case Keyboard::KEY_SIX:
+        case Keyboard::KEY_SEVEN:
+        case Keyboard::KEY_EIGHT:
+        case Keyboard::KEY_NINE:
+        case Keyboard::KEY_COLON:
+        case Keyboard::KEY_SEMICOLON:
+        case Keyboard::KEY_LESS_THAN:
+        case Keyboard::KEY_EQUAL:
+        case Keyboard::KEY_GREATER_THAN:
+        case Keyboard::KEY_QUESTION:
+        case Keyboard::KEY_AT:
+        case Keyboard::KEY_CAPITAL_A:
+        case Keyboard::KEY_CAPITAL_B:
+        case Keyboard::KEY_CAPITAL_C:
+        case Keyboard::KEY_CAPITAL_D:
+        case Keyboard::KEY_CAPITAL_E:
+        case Keyboard::KEY_CAPITAL_F:
+        case Keyboard::KEY_CAPITAL_G:
+        case Keyboard::KEY_CAPITAL_H:
+        case Keyboard::KEY_CAPITAL_I:
+        case Keyboard::KEY_CAPITAL_J:
+        case Keyboard::KEY_CAPITAL_K:
+        case Keyboard::KEY_CAPITAL_L:
+        case Keyboard::KEY_CAPITAL_M:
+        case Keyboard::KEY_CAPITAL_N:
+        case Keyboard::KEY_CAPITAL_O:
+        case Keyboard::KEY_CAPITAL_P:
+        case Keyboard::KEY_CAPITAL_Q:
+        case Keyboard::KEY_CAPITAL_R:
+        case Keyboard::KEY_CAPITAL_S:
+        case Keyboard::KEY_CAPITAL_T:
+        case Keyboard::KEY_CAPITAL_U:
+        case Keyboard::KEY_CAPITAL_V:
+        case Keyboard::KEY_CAPITAL_W:
+        case Keyboard::KEY_CAPITAL_X:
+        case Keyboard::KEY_CAPITAL_Y:
+        case Keyboard::KEY_CAPITAL_Z:
+        case Keyboard::KEY_LEFT_BRACKET:
+        case Keyboard::KEY_BACK_SLASH:
+        case Keyboard::KEY_RIGHT_BRACKET:
+        case Keyboard::KEY_UNDERSCORE:
+        case Keyboard::KEY_GRAVE:
+        case Keyboard::KEY_A:
+        case Keyboard::KEY_B:
+        case Keyboard::KEY_C:
+        case Keyboard::KEY_D:
+        case Keyboard::KEY_E:
+        case Keyboard::KEY_F:
+        case Keyboard::KEY_G:
+        case Keyboard::KEY_H:
+        case Keyboard::KEY_I:
+        case Keyboard::KEY_J:
+        case Keyboard::KEY_K:
+        case Keyboard::KEY_L:
+        case Keyboard::KEY_M:
+        case Keyboard::KEY_N:
+        case Keyboard::KEY_O:
+        case Keyboard::KEY_P:
+        case Keyboard::KEY_Q:
+        case Keyboard::KEY_R:
+        case Keyboard::KEY_S:
+        case Keyboard::KEY_T:
+        case Keyboard::KEY_U:
+        case Keyboard::KEY_V:
+        case Keyboard::KEY_W:
+        case Keyboard::KEY_X:
+        case Keyboard::KEY_Y:
+        case Keyboard::KEY_Z:
+        case Keyboard::KEY_LEFT_BRACE:
+        case Keyboard::KEY_BAR:
+        case Keyboard::KEY_RIGHT_BRACE:
+        case Keyboard::KEY_TILDE:
+            return key;
+        default:
+            return 0;
+    }
+}
+
 - (void)flagsChanged: (NSEvent*)event
 {
     unsigned int keyCode = [event keyCode];
@@ -1195,7 +1329,14 @@ int getKey(unsigned short keyCode, unsigned int modifierFlags)
 {
     if ([event isARepeat] == NO)
     {
-        gameplay::Platform::keyEventInternal(Keyboard::KEY_PRESS, getKey([event keyCode], [event modifierFlags]));
+        int key = getKey([event keyCode], [event modifierFlags]);
+        gameplay::Platform::keyEventInternal(Keyboard::KEY_PRESS, key);
+        
+        int character = getUnicode(key);
+        if (character)
+        {
+            gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
+        }
     }
 }
 
@@ -1335,6 +1476,12 @@ int Platform::enterMessagePump()
 
             // Read fullscreen state.
             __fullscreen = config->getBool("fullscreen");
+            if (__fullscreen && width == 0 && height == 0)
+            {
+                CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID());
+                __width = CGRectGetWidth(mainMonitor);
+                __height = CGRectGetHeight(mainMonitor);
+            }
         }
     }
 
@@ -1477,6 +1624,7 @@ void Platform::setMouseCaptured(bool captured)
         if (captured)
         {
             [NSCursor hide];
+            __mouseCapturedFirstPass = true;
         }
         else
         {   
@@ -1816,25 +1964,25 @@ CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, U
 
 CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
 {
-	CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
-	if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
+    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
+    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
     {
-		return NULL;
-	}
+        return NULL;
+    }
     return (CFStringRef)typeRef;
 }
 
 int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
 {
-	CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
-	if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
+    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
+    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
     {
-		return 0;
-	}
+        return 0;
+    }
     
     int value;
-	CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
-	return value;
+    CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
+    return value;
 }
 
 static void hidDeviceDiscoveredCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inIOHIDDeviceRef) 
@@ -1881,4 +2029,22 @@ static void hidDeviceValueAvailableCallback(void* inContext, IOReturn inResult,
         CFRelease(valueRef); // Don't forget to release our HID value reference
     } while (1);
 }
+
+bool Platform::launchURL(const char *url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    CFURLRef urlRef = CFURLCreateWithBytes(
+        NULL,
+        (UInt8*)url,
+        strlen(url),
+        kCFStringEncodingASCII,
+        NULL
+    );
+    const OSStatus err = LSOpenCFURLRef(urlRef, 0);
+    CFRelease(urlRef);
+    return (err == noErr);
+}
+
 #endif

+ 44 - 12
gameplay/src/PlatformWindows.cpp

@@ -9,6 +9,7 @@
 #include "ScriptController.h"
 #include <GL/wglew.h>
 #include <windowsx.h>
+#include <shellapi.h>
 #ifdef USE_XINPUT
 #include <XInput.h>
 #endif
@@ -404,7 +405,7 @@ static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
     }
 }
 
-void UpdateCapture(LPARAM lParam)
+static void UpdateCapture(LPARAM lParam)
 {
     if ((lParam & MK_LBUTTON) || (lParam & MK_MBUTTON) || (lParam & MK_RBUTTON))
         SetCapture(__hwnd);
@@ -412,13 +413,27 @@ void UpdateCapture(LPARAM lParam)
         ReleaseCapture();
 }
 
-void WarpMouse(int clientX, int clientY)
+static void WarpMouse(int clientX, int clientY)
 {
     POINT p = { clientX, clientY };
     ClientToScreen(__hwnd, &p);
     SetCursorPos(p.x, p.y);
 }
 
+
+/**
+ * Gets the width and height of the screen in pixels.
+ */
+static void getDesktopResolution(int& width, int& height)
+{
+   RECT desktop;
+   const HWND hDesktop = GetDesktopWindow();
+   // Get the size of screen to the variable desktop
+   GetWindowRect(hDesktop, &desktop);
+   width = desktop.right;
+   height = desktop.bottom;
+}
+
 LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     static gameplay::Game* game = gameplay::Game::getInstance();
@@ -871,6 +886,11 @@ Platform* Platform::create(Game* game, void* attachToWindow)
                 SAFE_DELETE_ARRAY(wtitle);
             }
 
+            // Read fullscreen state.
+            params.fullscreen = config->getBool("fullscreen");
+            // Read multisampling state.
+            params.samples = config->getInt("samples");
+
             // Read window rect.
             int x = config->getInt("x");
             if (x != 0)
@@ -879,17 +899,15 @@ Platform* Platform::create(Game* game, void* attachToWindow)
             if (y != 0)
                 params.rect.top = y;
             int width = config->getInt("width");
+            int height = config->getInt("height");
+
+            if (width == 0 && height == 0 && params.fullscreen)
+                getDesktopResolution(width, height);
+
             if (width != 0)
                 params.rect.right = params.rect.left + width;
-            int height = config->getInt("height");
             if (height != 0)
                 params.rect.bottom = params.rect.top + height;
-
-            // Read fullscreen state.
-            params.fullscreen = config->getBool("fullscreen");
-
-            // Read multisampling state.
-            params.samples = config->getInt("samples");
         }
     }
 
@@ -960,9 +978,9 @@ Platform* Platform::create(Game* game, void* attachToWindow)
             DEVMODE dm;
             memset(&dm, 0, sizeof(dm));
             dm.dmSize= sizeof(dm);
-            dm.dmPelsWidth	= width;
-            dm.dmPelsHeight	= height;
-            dm.dmBitsPerPel	= DEFAULT_COLOR_BUFFER_SIZE;
+            dm.dmPelsWidth  = width;
+            dm.dmPelsHeight = height;
+            dm.dmBitsPerPel = DEFAULT_COLOR_BUFFER_SIZE;
             dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
 
             // Try to set selected mode and get results. NOTE: CDS_FULLSCREEN gets rid of start bar.
@@ -1454,6 +1472,20 @@ bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheel
     }
 }
 
+bool Platform::launchURL(const char* url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+ 
+    // Success when result code > 32
+    int len = MultiByteToWideChar(CP_ACP, 0, url, -1, NULL, 0);
+    wchar_t* wurl = new wchar_t[len];
+    MultiByteToWideChar(CP_ACP, 0, url, -1, wurl, len);
+    int r = (int)ShellExecute(NULL, NULL, wurl, NULL, NULL, SW_SHOWNORMAL);
+    SAFE_DELETE_ARRAY(wurl);
+    return (r > 32);
+}
+
 }
 
 #endif

+ 141 - 2
gameplay/src/PlatformiOS.mm

@@ -48,6 +48,7 @@ static float __roll;
 double getMachTimeInMilliseconds();
 
 int getKey(unichar keyCode);
+int getUnicode(int key);
 
 @interface View : UIView <UIKeyInput>
 {
@@ -482,13 +483,21 @@ int getKey(unichar keyCode);
     assert([text length] == 1);
     unichar c = [text characterAtIndex:0];
     int key = getKey(c);
-    Platform::keyEventInternal(Keyboard::KEY_PRESS, key);    
-    Platform::keyEventInternal(Keyboard::KEY_RELEASE, key);    
+    Platform::keyEventInternal(Keyboard::KEY_PRESS, key);
+
+    int character = getUnicode(key);
+    if (character)
+    {
+        Platform::keyEventInternal(Keyboard::KEY_CHAR, /*character*/c);
+    }
+    
+    Platform::keyEventInternal(Keyboard::KEY_RELEASE, key);
 }
 
 - (void)deleteBackward 
 {
     Platform::keyEventInternal(Keyboard::KEY_PRESS, Keyboard::KEY_BACKSPACE);    
+    Platform::keyEventInternal(Keyboard::KEY_CHAR, getUnicode(Keyboard::KEY_BACKSPACE));
     Platform::keyEventInternal(Keyboard::KEY_RELEASE, Keyboard::KEY_BACKSPACE);    
 }
 
@@ -845,6 +854,11 @@ int getKey(unichar keyCode)
 {
     switch(keyCode) 
     {
+        case 0x0A:
+            return Keyboard::KEY_RETURN;
+        case 0x20:
+            return Keyboard::KEY_SPACE;
+            
         case 0x30:
             return Keyboard::KEY_ZERO;
         case 0x31:
@@ -1056,6 +1070,123 @@ int getKey(unichar keyCode)
     return Keyboard::KEY_NONE;
 }
 
+/**
+ * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
+ */
+int getUnicode(int key)
+{
+    
+    switch (key)
+    {
+        case Keyboard::KEY_BACKSPACE:
+            return 0x0008;
+        case Keyboard::KEY_TAB:
+            return 0x0009;
+        case Keyboard::KEY_RETURN:
+        case Keyboard::KEY_KP_ENTER:
+            return 0x000A;
+        case Keyboard::KEY_ESCAPE:
+            return 0x001B;
+        case Keyboard::KEY_SPACE:
+        case Keyboard::KEY_EXCLAM:
+        case Keyboard::KEY_QUOTE:
+        case Keyboard::KEY_NUMBER:
+        case Keyboard::KEY_DOLLAR:
+        case Keyboard::KEY_PERCENT:
+        case Keyboard::KEY_CIRCUMFLEX:
+        case Keyboard::KEY_AMPERSAND:
+        case Keyboard::KEY_APOSTROPHE:
+        case Keyboard::KEY_LEFT_PARENTHESIS:
+        case Keyboard::KEY_RIGHT_PARENTHESIS:
+        case Keyboard::KEY_ASTERISK:
+        case Keyboard::KEY_PLUS:
+        case Keyboard::KEY_COMMA:
+        case Keyboard::KEY_MINUS:
+        case Keyboard::KEY_PERIOD:
+        case Keyboard::KEY_SLASH:
+        case Keyboard::KEY_ZERO:
+        case Keyboard::KEY_ONE:
+        case Keyboard::KEY_TWO:
+        case Keyboard::KEY_THREE:
+        case Keyboard::KEY_FOUR:
+        case Keyboard::KEY_FIVE:
+        case Keyboard::KEY_SIX:
+        case Keyboard::KEY_SEVEN:
+        case Keyboard::KEY_EIGHT:
+        case Keyboard::KEY_NINE:
+        case Keyboard::KEY_COLON:
+        case Keyboard::KEY_SEMICOLON:
+        case Keyboard::KEY_LESS_THAN:
+        case Keyboard::KEY_EQUAL:
+        case Keyboard::KEY_GREATER_THAN:
+        case Keyboard::KEY_QUESTION:
+        case Keyboard::KEY_AT:
+        case Keyboard::KEY_CAPITAL_A:
+        case Keyboard::KEY_CAPITAL_B:
+        case Keyboard::KEY_CAPITAL_C:
+        case Keyboard::KEY_CAPITAL_D:
+        case Keyboard::KEY_CAPITAL_E:
+        case Keyboard::KEY_CAPITAL_F:
+        case Keyboard::KEY_CAPITAL_G:
+        case Keyboard::KEY_CAPITAL_H:
+        case Keyboard::KEY_CAPITAL_I:
+        case Keyboard::KEY_CAPITAL_J:
+        case Keyboard::KEY_CAPITAL_K:
+        case Keyboard::KEY_CAPITAL_L:
+        case Keyboard::KEY_CAPITAL_M:
+        case Keyboard::KEY_CAPITAL_N:
+        case Keyboard::KEY_CAPITAL_O:
+        case Keyboard::KEY_CAPITAL_P:
+        case Keyboard::KEY_CAPITAL_Q:
+        case Keyboard::KEY_CAPITAL_R:
+        case Keyboard::KEY_CAPITAL_S:
+        case Keyboard::KEY_CAPITAL_T:
+        case Keyboard::KEY_CAPITAL_U:
+        case Keyboard::KEY_CAPITAL_V:
+        case Keyboard::KEY_CAPITAL_W:
+        case Keyboard::KEY_CAPITAL_X:
+        case Keyboard::KEY_CAPITAL_Y:
+        case Keyboard::KEY_CAPITAL_Z:
+        case Keyboard::KEY_LEFT_BRACKET:
+        case Keyboard::KEY_BACK_SLASH:
+        case Keyboard::KEY_RIGHT_BRACKET:
+        case Keyboard::KEY_UNDERSCORE:
+        case Keyboard::KEY_GRAVE:
+        case Keyboard::KEY_A:
+        case Keyboard::KEY_B:
+        case Keyboard::KEY_C:
+        case Keyboard::KEY_D:
+        case Keyboard::KEY_E:
+        case Keyboard::KEY_F:
+        case Keyboard::KEY_G:
+        case Keyboard::KEY_H:
+        case Keyboard::KEY_I:
+        case Keyboard::KEY_J:
+        case Keyboard::KEY_K:
+        case Keyboard::KEY_L:
+        case Keyboard::KEY_M:
+        case Keyboard::KEY_N:
+        case Keyboard::KEY_O:
+        case Keyboard::KEY_P:
+        case Keyboard::KEY_Q:
+        case Keyboard::KEY_R:
+        case Keyboard::KEY_S:
+        case Keyboard::KEY_T:
+        case Keyboard::KEY_U:
+        case Keyboard::KEY_V:
+        case Keyboard::KEY_W:
+        case Keyboard::KEY_X:
+        case Keyboard::KEY_Y:
+        case Keyboard::KEY_Z:
+        case Keyboard::KEY_LEFT_BRACE:
+        case Keyboard::KEY_BAR:
+        case Keyboard::KEY_RIGHT_BRACE:
+        case Keyboard::KEY_TILDE:
+            return key;
+        default:
+            return 0;
+    }
+}
 
 namespace gameplay
 {
@@ -1319,6 +1450,14 @@ float Platform::getGamepadTriggerValue(unsigned int gamepadHandle, unsigned int
 {
     return 0.0f;
 }
+
+bool Platform::launchURL(const char *url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    return [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithUTF8String: url]]];
+}
     
 }
 

+ 48 - 35
gameplay/src/Properties.cpp

@@ -6,6 +6,19 @@
 namespace gameplay
 {
 
+/**
+ * Reads the next character from the stream. Returns EOF if the end of the stream is reached.
+ */
+static signed char readChar(Stream* stream)
+{
+    if (stream->eof())
+        return EOF;
+    signed char c;
+    if (stream->read(&c, 1, 1) != 1)
+        return EOF;
+    return c;
+}
+
 // Utility functions (shared with SceneLoader).
 /** @script{ignore} */
 void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath);
@@ -29,13 +42,14 @@ Properties::Properties(const Properties& copy)
     rewind();
 }
 
-Properties::Properties(FILE* file)
+
+Properties::Properties(Stream* stream)
 {
-    readProperties(file);
+    readProperties(stream);
     rewind();
 }
 
-Properties::Properties(FILE* file, const char* name, const char* id, const char* parentID) : _namespace(name)
+Properties::Properties(Stream* stream, const char* name, const char* id, const char* parentID) : _namespace(name)
 {
     if (id)
     {
@@ -45,7 +59,7 @@ Properties::Properties(FILE* file, const char* name, const char* id, const char*
     {
         _parentID = parentID;
     }
-    readProperties(file);
+    readProperties(stream);
     rewind();
 }
 
@@ -63,16 +77,16 @@ Properties* Properties::create(const char* url)
     std::vector<std::string> namespacePath;
     calculateNamespacePath(urlString, fileString, namespacePath);
 
-    FILE* file = FileSystem::openFile(fileString.c_str(), "rb");
-    if (!file)
+    std::auto_ptr<Stream> stream(FileSystem::open(fileString.c_str()));
+    if (stream.get() == NULL)
     {
         GP_ERROR("Failed to open file '%s'.", fileString.c_str());
         return NULL;
     }
 
-    Properties* properties = new Properties(file);
+    Properties* properties = new Properties(stream.get());
     properties->resolveInheritance();
-    fclose(file);
+    stream->close();
 
     // Get the specified properties object.
     Properties* p = getPropertiesFromNamespacePath(properties, namespacePath);
@@ -93,9 +107,9 @@ Properties* Properties::create(const char* url)
     return p;
 }
 
-void Properties::readProperties(FILE* file)
+void Properties::readProperties(Stream* stream)
 {
-    GP_ASSERT(file);
+    GP_ASSERT(stream);
 
     char line[2048];
     int c;
@@ -108,14 +122,14 @@ void Properties::readProperties(FILE* file)
 
     while (true)
     {
-        skipWhiteSpace(file);
+        skipWhiteSpace(stream);
 
         // Stop when we have reached the end of the file.
-        if (feof(file))
+        if (stream->eof())
             break;
 
         // Read the next line.
-        rc = fgets(line, 2048, file);
+        rc = stream->readLine(line, 2048);
         if (rc == NULL)
         {
             GP_ERROR("Error reading line from file.");
@@ -213,20 +227,20 @@ void Properties::readProperties(FILE* file)
                     // If the namespace ends on this line, seek back to right before the '}' character.
                     if (rccc && rccc == lineEnd)
                     {
-                        if (fseek(file, -1, SEEK_CUR) != 0)
+                        if (stream->seek(-1, SEEK_CUR) == false)
                         {
                             GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                             return;
                         }
-                        while (fgetc(file) != '}')
+                        while (readChar(stream) != '}')
                         {
-                            if (fseek(file, -2, SEEK_CUR) != 0)
+                            if (stream->seek(-2, SEEK_CUR) == false)
                             {
                                 GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                                 return;
                             }
                         }
-                        if (fseek(file, -1, SEEK_CUR) != 0)
+                        if (stream->seek(-1, SEEK_CUR) == false)
                         {
                             GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                             return;
@@ -234,13 +248,13 @@ void Properties::readProperties(FILE* file)
                     }
 
                     // New namespace without an ID.
-                    Properties* space = new Properties(file, name, NULL, parentID);
+                    Properties* space = new Properties(stream, name, NULL, parentID);
                     _namespaces.push_back(space);
 
                     // If the namespace ends on this line, seek to right after the '}' character.
                     if (rccc && rccc == lineEnd)
                     {
-                        if (fseek(file, 1, SEEK_CUR) != 0)
+                        if (stream->seek(1, SEEK_CUR) == false)
                         {
                             GP_ERROR("Failed to seek to immediately after a '}' character in properties file.");
                             return;
@@ -255,20 +269,20 @@ void Properties::readProperties(FILE* file)
                         // If the namespace ends on this line, seek back to right before the '}' character.
                         if (rccc && rccc == lineEnd)
                         {
-                            if (fseek(file, -1, SEEK_CUR) != 0)
+                            if (stream->seek(-1, SEEK_CUR) == false)
                             {
                                 GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                                 return;
                             }
-                            while (fgetc(file) != '}')
+                            while (readChar(stream) != '}')
                             {
-                                if (fseek(file, -2, SEEK_CUR) != 0)
+                                if (stream->seek(-2, SEEK_CUR) == false)
                                 {
                                     GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                                     return;
                                 }
                             }
-                            if (fseek(file, -1, SEEK_CUR) != 0)
+                            if (stream->seek(-1, SEEK_CUR) == false)
                             {
                                 GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                                 return;
@@ -276,13 +290,13 @@ void Properties::readProperties(FILE* file)
                         }
 
                         // Create new namespace.
-                        Properties* space = new Properties(file, name, value, parentID);
+                        Properties* space = new Properties(stream, name, value, parentID);
                         _namespaces.push_back(space);
 
                         // If the namespace ends on this line, seek to right after the '}' character.
                         if (rccc && rccc == lineEnd)
                         {
-                            if (fseek(file, 1, SEEK_CUR) != 0)
+                            if (stream->seek(1, SEEK_CUR) == false)
                             {
                                 GP_ERROR("Failed to seek to immediately after a '}' character in properties file.");
                                 return;
@@ -292,18 +306,18 @@ void Properties::readProperties(FILE* file)
                     else
                     {
                         // Find out if the next line starts with "{"
-                        skipWhiteSpace(file);
-                        c = fgetc(file);
+                        skipWhiteSpace(stream);
+                        c = readChar(stream);
                         if (c == '{')
                         {
                             // Create new namespace.
-                            Properties* space = new Properties(file, name, value, parentID);
+                            Properties* space = new Properties(stream, name, value, parentID);
                             _namespaces.push_back(space);
                         }
                         else
                         {
                             // Back up from fgetc()
-                            if (fseek(file, -1, SEEK_CUR) != 0)
+                            if (stream->seek(-1, SEEK_CUR) == false)
                                 GP_ERROR("Failed to seek backwards a single character after testing if the next line starts with '{'.");
 
                             // Store "name value" as a name/value pair, or even just "name".
@@ -331,20 +345,19 @@ Properties::~Properties()
     }
 }
 
-void Properties::skipWhiteSpace(FILE* file)
+void Properties::skipWhiteSpace(Stream* stream)
 {
-    int c;
-
+    signed char c;
     do
     {
-        c = fgetc(file);
-    } while (isspace(c));
+        c = readChar(stream);
+    } while (isspace(c) && c != EOF);
 
     // If we are not at the end of the file, then since we found a
     // non-whitespace character, we put the cursor back in front of it.
     if (c != EOF)
     {
-        if (fseek(file, -1, SEEK_CUR) != 0)
+        if (stream->seek(-1, SEEK_CUR) == false)
         {
             GP_ERROR("Failed to seek backwards one character after skipping whitespace.");
         }

+ 5 - 4
gameplay/src/Properties.h

@@ -4,6 +4,7 @@
 #include "Base.h"
 #include "Matrix.h"
 #include "Vector2.h"
+#include "Stream.h"
 
 namespace gameplay
 {
@@ -383,17 +384,17 @@ private:
      * Constructors.
      */
     Properties();
-    Properties(FILE* file);
+    Properties(Stream* stream);
     Properties(const Properties& copy);
 
     /**
      * Constructor. Read from the beginning of namespace specified
      */
-    Properties(FILE* file, const char* name, const char* id = NULL, const char* parentID = NULL);
+    Properties(Stream* stream, const char* name, const char* id = NULL, const char* parentID = NULL);
 
-    void readProperties(FILE* file);
+    void readProperties(Stream* stream);
 
-    void skipWhiteSpace(FILE* file);
+    void skipWhiteSpace(Stream* stream);
 
     char* trimWhiteSpace(char* str);
 

+ 5 - 5
gameplay/src/RenderState.h

@@ -92,7 +92,7 @@ public:
      *
      * Functions matching this callback signature can be registered via the 
      * RenderState::registerAutoBindingResolver method to extend or override the set
-     * of built-in material paramter auto bindings.
+     * of built-in material parameter auto bindings.
      *
      * @param autoBinding Name of the auto binding to resolve.
      * @param node Node that is bound to the material of the specified parameter.
@@ -309,11 +309,11 @@ public:
     /**
      * Registers a custom auto binding resolver.
      *
-     * Implementing a custom auto binding reolver allows the set of built-in parameter auto
+     * Implementing a custom auto binding resolver allows the set of built-in parameter auto
      * bindings to be extended or overridden. Any parameter auto binding that is set on a
      * material will be forwarded to any custom auto binding resolvers, in the order in which
      * they are registered. If a registered resolver returns true (specifying that it handles
-     * the specified autoBinding), no further code will be exeucted for that autoBinding.
+     * the specified autoBinding), no further code will be executed for that autoBinding.
      * This allows auto binding resolvers to not only implement new/custom binding strings,
      * but it also lets them override existing/built-in ones. For this reason, you should
      * ensure that you ONLY return true if you explicitly handle a custom auto binding; return
@@ -324,7 +324,7 @@ public:
      * Model that belongs to a Node. The resolver is NOT called each frame or each time
      * the RenderState is bound. Therefore, when implementing custom auto bindings for values
      * that change over time, the you should bind a method pointer onto the passed in
-     * MaterialParaemter using the MaterialParameter::bindValue mehtod. This way, the bound 
+     * MaterialParaemter using the MaterialParameter::bindValue method. This way, the bound
      * method will be called each frame to set an updated value into the MaterialParameter.
      *
      * If no registered resolvers explicitly handle an auto binding, the binding will attempt
@@ -436,7 +436,7 @@ protected:
     RenderState* _parent;
 
     /**
-     * Map of custom auto binding resolverss.
+     * Map of custom auto binding resolvers.
      */
     static std::vector<ResolveAutoBindingCallback> _customAutoBindingResolvers;
 };

+ 2 - 2
gameplay/src/Scene.h

@@ -74,7 +74,7 @@ public:
      * @param id The ID of the node to find.
      * @param nodes Vector of nodes to be populated with matches.
      * @param recursive true if a recursive search should be performed, false otherwise.
-     * @param exactMatch true if only nodes whos ID exactly matches the specified ID are returned,
+     * @param exactMatch true if only nodes who's ID exactly matches the specified ID are returned,
      *      or false if nodes that start with the given ID are returned.
      * 
      * @return The number of matches found.
@@ -212,7 +212,7 @@ public:
     /**
      * Draws debugging information (bounding volumes, etc.) for the scene.
      *
-     * @param debugFlags Bitwise combination of debug flags from mthe DebugFlags 
+     * @param debugFlags Bitwise combination of debug flags from the DebugFlags
      *        enumeration, specifying which debugging information to draw.
      */
     void drawDebug(unsigned int debugFlags);

+ 47 - 23
gameplay/src/ScriptController.cpp

@@ -372,76 +372,100 @@ std::string ScriptController::loadUrl(const char* url)
     return id;
 }
 
-bool ScriptController::getBool(const char* name)
+bool ScriptController::getBool(const char* name, bool defaultValue)
 {
     lua_getglobal(_lua, name);
-    return ScriptUtil::luaCheckBool(_lua, -1);
+    bool b = lua_isboolean(_lua, -1) ? ScriptUtil::luaCheckBool(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return b;
 }
 
-char ScriptController::getChar(const char* name)
+char ScriptController::getChar(const char* name, char defaultValue)
 {
     lua_getglobal(_lua, name);
-    return (char)luaL_checkint(_lua, -1);
+    char c = lua_isnumber(_lua, -1) ?  (char)luaL_checkint(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return c;
 }
 
-short ScriptController::getShort(const char* name)
+short ScriptController::getShort(const char* name, short defaultValue)
 {
     lua_getglobal(_lua, name);
-    return (short)luaL_checkint(_lua, -1);
+    short n = lua_isnumber(_lua, -1) ? (short)luaL_checkint(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return n;
 }
 
-int ScriptController::getInt(const char* name)
+int ScriptController::getInt(const char* name, int defaultValue)
 {
     lua_getglobal(_lua, name);
-    return luaL_checkint(_lua, -1);
+    int n = lua_isnumber(_lua, -1) ? luaL_checkint(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return n;
 }
 
-long ScriptController::getLong(const char* name)
+long ScriptController::getLong(const char* name, long defaultValue)
 {
     lua_getglobal(_lua, name);
-    return luaL_checklong(_lua, -1);
+    long n = lua_isnumber(_lua, -1) ? luaL_checklong(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return n;
 }
 
-unsigned char ScriptController::getUnsignedChar(const char* name)
+unsigned char ScriptController::getUnsignedChar(const char* name, unsigned char defaultValue)
 {
     lua_getglobal(_lua, name);
-    return (unsigned char)luaL_checkunsigned(_lua, -1);
+    unsigned char c = lua_isnumber(_lua, -1) ? (unsigned char)luaL_checkunsigned(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return c;
 }
 
-unsigned short ScriptController::getUnsignedShort(const char* name)
+unsigned short ScriptController::getUnsignedShort(const char* name, unsigned short defaultValue)
 {
     lua_getglobal(_lua, name);
-    return (unsigned short)luaL_checkunsigned(_lua, -1);
+    unsigned short n = lua_isnumber(_lua, -1) ? (unsigned short)luaL_checkunsigned(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return n;
 }
 
-unsigned int ScriptController::getUnsignedInt(const char* name)
+unsigned int ScriptController::getUnsignedInt(const char* name, unsigned int defaultValue)
 {
     lua_getglobal(_lua, name);
-    return (unsigned int)luaL_checkunsigned(_lua, -1);
+    unsigned int n = lua_isnumber(_lua, -1) ? (unsigned int)luaL_checkunsigned(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return n;
 }
 
-unsigned long ScriptController::getUnsignedLong(const char* name)
+unsigned long ScriptController::getUnsignedLong(const char* name, unsigned long defaultValue)
 {
     lua_getglobal(_lua, name);
-    return (unsigned long)luaL_checkunsigned(_lua, -1);
+    unsigned long n = lua_isnumber(_lua, -1) ? (unsigned long)luaL_checkunsigned(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return n;
 }
 
-float ScriptController::getFloat(const char* name)
+float ScriptController::getFloat(const char* name, float defaultValue)
 {
     lua_getglobal(_lua, name);
-    return (float)luaL_checknumber(_lua, -1);
+    float f = lua_isnumber(_lua, -1) ? (float)luaL_checknumber(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return f;
 }
 
-double ScriptController::getDouble(const char* name)
+double ScriptController::getDouble(const char* name, double defaultValue)
 {
     lua_getglobal(_lua, name);
-    return (double)luaL_checknumber(_lua, -1);
+    double n = lua_isnumber(_lua, -1) ? (double)luaL_checknumber(_lua, -1) : defaultValue;
+    lua_pop(_lua, 1);
+    return n;
 }
 
 const char* ScriptController::getString(const char* name)
 {
     lua_getglobal(_lua, name);
-    return luaL_checkstring(_lua, -1);
+    const char* s = lua_isstring(_lua, -1) ? luaL_checkstring(_lua, -1) : NULL;
+    lua_pop(_lua, 1);
+    return s;
 }
 
 void ScriptController::setBool(const char* name, bool v)

+ 107 - 17
gameplay/src/ScriptController.h

@@ -1,5 +1,5 @@
-#ifndef SCRIPTCONTROLLER_H
-#define SCRIPTCONTROLLER_H
+#ifndef SCRIPTCONTROLLER_H_
+#define SCRIPTCONTROLLER_H_
 
 #include "Base.h"
 #include "Game.h"
@@ -19,6 +19,7 @@ namespace ScriptUtil
 
 /**
  * Represents a C++ object from within Lua.
+ * 
  * @script{ignore}
  */
 struct LuaObject
@@ -32,6 +33,7 @@ struct LuaObject
 /**
  * Stores a Lua parameter of an array/pointer type that is passed from Lua to C.
  * Handles automatic cleanup of any temporary memory associated with the array.
+ * 
  * @script{ignore}
  */
 template <typename T>
@@ -55,7 +57,7 @@ public:
     LuaArray(int count);
 
     /**
-     * Copy construcotr.
+     * Copy constructor.
      */
     LuaArray(const LuaArray<T>& copy);
 
@@ -102,6 +104,7 @@ private:
  * 
  * @param name The name of the library from within Lua.
  * @param functions The library function mapping (Lua function names to C++ functions).
+ * 
  * @script{ignore}
  */
 void registerLibrary(const char* name, const luaL_Reg* functions);
@@ -112,6 +115,7 @@ void registerLibrary(const char* name, const luaL_Reg* functions);
  * @param name The name of the constant (what the user would use from Lua).
  * @param value The constant's value.
  * @param scopePath The list of containing classes, going inward from the most outer class.
+ * 
  * @script{ignore}
  */
 void registerConstantBool(const std::string& name, bool value, const std::vector<std::string>& scopePath);
@@ -122,6 +126,7 @@ void registerConstantBool(const std::string& name, bool value, const std::vector
  * @param name The name of the constant (what the user would use from Lua).
  * @param value The constant's value.
  * @param scopePath The list of containing classes, going inward from the most outer class.
+ * 
  * @script{ignore}
  */
 void registerConstantNumber(const std::string& name, double value, const std::vector<std::string>& scopePath);
@@ -132,6 +137,7 @@ void registerConstantNumber(const std::string& name, double value, const std::ve
  * @param name The name of the constant (what the user would use from Lua).
  * @param value The constant's value.
  * @param scopePath The list of containing classes, going inward from the most outer class.
+ * 
  * @script{ignore}
  */
 void registerConstantString(const std::string& name, const std::string& value, const std::vector<std::string>& scopePath);
@@ -145,6 +151,7 @@ void registerConstantString(const std::string& name, const std::string& value, c
  * @param deleteFunction The function to call that destroys an instance of the class.
  * @param statics The library function mapping for all the static functions (Lua function names to C++ functions).
  * @param scopePath For an inner class, this is a list of its containing classes, going inward from the most outer class.
+ * 
  * @script{ignore}
  */
 void registerClass(const char* name, const luaL_Reg* members, lua_CFunction newFunction, lua_CFunction deleteFunction, const luaL_Reg* statics,
@@ -155,6 +162,7 @@ void registerClass(const char* name, const luaL_Reg* members, lua_CFunction newF
  * 
  * @param luaFunction The name of the function from within Lua.
  * @param cppFunction The C++ function pointer.
+ * 
  * @script{ignore}
  */
 void registerFunction(const char* luaFunction, lua_CFunction cppFunction);
@@ -164,6 +172,7 @@ void registerFunction(const char* luaFunction, lua_CFunction cppFunction);
  * 
  * @param base The base class of the inheritance pair.
  * @param derived The derived class of the inheritance pair.
+ * 
  * @script{ignore}
  */
 void setGlobalHierarchyPair(const std::string& base, const std::string& derived);
@@ -172,6 +181,7 @@ void setGlobalHierarchyPair(const std::string& base, const std::string& derived)
  * Adds the given function as a string-from-enumerated value conversion function.
  * 
  * @param stringFromEnum The pointer to the string-from-enum conversion function.
+ * 
  * @script{ignore}
  */
 void addStringFromEnumConversionFunction(luaStringEnumConversionFunction stringFromEnum);
@@ -180,7 +190,9 @@ void addStringFromEnumConversionFunction(luaStringEnumConversionFunction stringF
  * Gets a pointer to a bool (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
  * 
  * @param index The stack index.
+ * 
  * @return The pointer.
+ * 
  * @script{ignore}
  */
 LuaArray<bool> getBoolPointer(int index);
@@ -189,7 +201,9 @@ LuaArray<bool> getBoolPointer(int index);
  * Gets a pointer to a short (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
  * 
  * @param index The stack index.
+ * 
  * @return The pointer.
+ * 
  * @script{ignore}
  */
 LuaArray<short> getShortPointer(int index);
@@ -198,7 +212,9 @@ LuaArray<short> getShortPointer(int index);
  * Gets a pointer to an int (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
  * 
  * @param index The stack index.
+ * 
  * @return The pointer.
+ * 
  * @script{ignore}
  */
 LuaArray<int> getIntPointer(int index);
@@ -207,7 +223,9 @@ LuaArray<int> getIntPointer(int index);
  * Gets a pointer to a long (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
  * 
  * @param index The stack index.
+ * 
  * @return The pointer.
+ * 
  * @script{ignore}
  */
 LuaArray<long> getLongPointer(int index);
@@ -216,7 +234,9 @@ LuaArray<long> getLongPointer(int index);
  * Gets a pointer to an unsigned char (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
  * 
  * @param index The stack index.
+ * 
  * @return The pointer.
+ * 
  * @script{ignore}
  */
 LuaArray<unsigned char> getUnsignedCharPointer(int index);
@@ -225,7 +245,9 @@ LuaArray<unsigned char> getUnsignedCharPointer(int index);
  * Gets a pointer to an unsigned short (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
  * 
  * @param index The stack index.
+ * 
  * @return The pointer.
+ * 
  * @script{ignore}
  */
 LuaArray<unsigned short> getUnsignedShortPointer(int index);
@@ -234,7 +256,9 @@ LuaArray<unsigned short> getUnsignedShortPointer(int index);
  * Gets a pointer to an unsigned int (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
  * 
  * @param index The stack index.
+ * 
  * @return The pointer.
+ * 
  * @script{ignore}
  */
 LuaArray<unsigned int> getUnsignedIntPointer(int index);
@@ -243,7 +267,9 @@ LuaArray<unsigned int> getUnsignedIntPointer(int index);
  * Gets a pointer to an unsigned long (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
  * 
  * @param index The stack index.
+ * 
  * @return The pointer.
+ * 
  * @script{ignore}
  */
 LuaArray<unsigned long> getUnsignedLongPointer(int index);
@@ -252,7 +278,9 @@ LuaArray<unsigned long> getUnsignedLongPointer(int index);
  * Gets a pointer to a float (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
  * 
  * @param index The stack index.
+ * 
  * @return The pointer.
+ * 
  * @script{ignore}
  */
 LuaArray<float> getFloatPointer(int index);
@@ -261,7 +289,9 @@ LuaArray<float> getFloatPointer(int index);
  * Gets a pointer to a double (as an array-use SAFE_DELETE_ARRAY to clean up) for the given stack index.
  * 
  * @param index The stack index.
+ * 
  * @return The pointer.
+ * 
  * @script{ignore}
  */
 LuaArray<double> getDoublePointer(int index);
@@ -275,8 +305,10 @@ LuaArray<double> getDoublePointer(int index);
  *      are retrieving is actually a reference or by-value parameter).
  * @param success An out parameter that is set to true if the Lua parameter was successfully
  *      converted to a valid object, or false if it was unable to perform a valid conversion.
+ * 
  * @return The object pointer or <code>NULL</code> if the data at the stack index
  *      is not an object or if the object is not derived from the given type.
+ * 
  * @script{ignore}
  */
 template <typename T>
@@ -287,7 +319,9 @@ LuaArray<T> getObjectPointer(int index, const char* type, bool nonNull, bool* su
  * 
  * @param index The stack index.
  * @param isStdString Whether the string being retrieved is a std::string object or not.
+ * 
  * @return The string or <code>NULL</code>.
+ * 
  * @script{ignore}
  */
 const char* getString(int index, bool isStdString);
@@ -297,14 +331,15 @@ const char* getString(int index, bool isStdString);
  * 
  * @param state The Lua state.
  * @param n The stack index.
+ * 
  * @return The boolean (if successful; otherwise it logs an error).
+ * 
  * @script{ignore}
  */
 bool luaCheckBool(lua_State* state, int n);
 
 }
 
-
 /**
  * Controls and manages all scripts.
  */
@@ -326,6 +361,7 @@ public:
      * Given a URL, loads the referenced file and returns the referenced function name.
      * 
      * @param url The url to load.
+     * 
      * @return The function that the URL references.
      */
     std::string loadUrl(const char* url);
@@ -334,6 +370,7 @@ public:
      * Calls the specified no-parameter Lua function.
      * 
      * @param func The name of the function to call.
+     * 
      * @return The return value of the executed Lua function.
      */
     template<typename T> T executeFunction(const char* func);
@@ -358,6 +395,7 @@ public:
      *      - 'p' - pointer
      *      - '<object-type>' - a <b>pointer</b> to an object of the given type (where the qualified type name is enclosed by angle brackets).
      *      - '[enum-type]' - an enumerated value of the given type (where the qualified type name is enclosed by square brackets).
+     * 
      * @return The return value of the executed Lua function.
      */
     template<typename T> T executeFunction(const char* func, const char* args, ...);
@@ -383,6 +421,7 @@ public:
      *      - '<object-type>' - a <b>pointer</b> to an object of the given type (where the qualified type name is enclosed by angle brackets).
      *      - '[enum-type]' - an enumerated value of the given type (where the qualified type name is enclosed by square brackets).
      * @param list The variable argument list containing the function's parameters.
+     * 
      * @return The return value of the executed Lua function.
      */
     template<typename T> T executeFunction(const char* func, const char* args, va_list* list);
@@ -391,106 +430,141 @@ public:
      * Gets the global boolean script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a bool.
+     * 
      * @return The global boolean script variable.
+     * 
      * @script{ignore}
      */
-    bool getBool(const char* name);
+    bool getBool(const char* name, bool defaultValue = false);
 
     /**
      * Gets the global char script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a number.
+     * 
      * @return The global char script variable.
+     * 
      * @script{ignore}
      */
-    char getChar(const char* name);
+    char getChar(const char* name, char defaultValue = 0);
 
     /**
      * Gets the global short script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a number.
+     * 
      * @return The global short script variable.
+     * 
      * @script{ignore}
      */
-    short getShort(const char* name);
+    short getShort(const char* name, short defaultValue = 0);
 
     /**
      * Gets the global int script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a number.
+     * 
      * @return The global int script variable.
+     * 
      * @script{ignore}
      */
-    int getInt(const char* name);
+    int getInt(const char* name, int defaultValue = 0);
 
     /**
      * Gets the global long script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a number.
+     * 
      * @return The global long script variable.
+     * 
      * @script{ignore}
      */
-    long getLong(const char* name);
+    long getLong(const char* name, long defaultValue = 0);
 
     /**
      * Gets the global unsigned char script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a number.
+     * 
      * @return The global unsigned char script variable.
+     * 
      * @script{ignore}
      */
-    unsigned char getUnsignedChar(const char* name);
+    unsigned char getUnsignedChar(const char* name, unsigned char defaultValue = 0);
 
     /**
      * Gets the global unsigned short script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a number.
+     * 
      * @return The global unsigned short script variable.
+     * 
      * @script{ignore}
      */
-    unsigned short getUnsignedShort(const char* name);
+    unsigned short getUnsignedShort(const char* name, unsigned short defaultValue = 0);
 
     /**
      * Gets the global unsigned int script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a number.
+     * 
      * @return The global unsigned int script variable.
+     * 
      * @script{ignore}
      */
-    unsigned int getUnsignedInt(const char* name);
+    unsigned int getUnsignedInt(const char* name, unsigned int defaultValue = 0);
 
     /**
      * Gets the global unsigned long script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a number.
+     * 
      * @return The global unsigned long script variable.
+     * 
      * @script{ignore}
      */
-    unsigned long getUnsignedLong(const char* name);
+    unsigned long getUnsignedLong(const char* name, unsigned long defaultValue = 0);
 
     /**
      * Gets the global float script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a number.
+     * 
      * @return The global float script variable.
+     * 
      * @script{ignore}
      */
-    float getFloat(const char* name);
+    float getFloat(const char* name, float defaultValue = 0);
 
     /**
      * Gets the global double script variable with the given name.
      * 
      * @param name The name of the variable.
+     * @param defaultValue The default value to return if the variable is not a number.
+     * 
      * @return The global double script variable.
+     * 
      * @script{ignore}
      */
-    double getDouble(const char* name);
+    double getDouble(const char* name, double defaultValue = 0);
 
     /**
-     * Gets the global string script variable with the given name.
+     * Gets the global string variable with the given name.
      * 
      * @param name The name of the variable.
-     * @return The global string script variable.
+     * 
+     * @return The string variable or NULL if the variable is not a string.
+     * 
      * @script{ignore}
      */
     const char* getString(const char* name);
@@ -500,7 +574,9 @@ public:
      * 
      * @param type The type of the variable in Lua.
      * @param name The name of the variable.
+     * 
      * @return The global pointer script variable.
+     * 
      * @script{ignore}
      */
     template<typename T>T* getObjectPointer(const char* type, const char* name);
@@ -510,6 +586,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The boolean value.
+     * 
      * @script{ignore}
      */
     void setBool(const char* name, bool v);
@@ -519,6 +596,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The char value.
+     * 
      * @script{ignore}
      */
     void setChar(const char* name, char v);
@@ -528,6 +606,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The short value.
+     * 
      * @script{ignore}
      */
     void setShort(const char* name, short v);
@@ -537,6 +616,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The int value.
+     * 
      * @script{ignore}
      */
     void setInt(const char* name, int v);
@@ -546,6 +626,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The long value.
+     * 
      * @script{ignore}
      */
     void setLong(const char* name, long v);
@@ -555,6 +636,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The unsigned char value.
+     * 
      * @script{ignore}
      */
     void setUnsignedChar(const char* name, unsigned char v);
@@ -564,6 +646,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The unsigned short value.
+     * 
      * @script{ignore}
      */
     void setUnsignedShort(const char* name, unsigned short v);
@@ -573,6 +656,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The unsigned int value.
+     * 
      * @script{ignore}
      */
     void setUnsignedInt(const char* name, unsigned int v);
@@ -582,6 +666,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The unsigned long value.
+     * 
      * @script{ignore}
      */
     void setUnsignedLong(const char* name, unsigned long v);
@@ -591,6 +676,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The float value.
+     * 
      * @script{ignore}
      */
     void setFloat(const char* name, float v);
@@ -600,6 +686,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The double value.
+     * 
      * @script{ignore}
      */
     void setDouble(const char* name, double v);
@@ -609,6 +696,7 @@ public:
      * 
      * @param name The name of the script variable.
      * @param v The string value.
+     * 
      * @script{ignore}
      */
     void setString(const char* name, const char* v);
@@ -619,6 +707,7 @@ public:
      * @param type The type of the script variable.
      * @param name The name of the variable.
      * @param v The pointer value.
+     * 
      * @script{ignore}
      */
     template<typename T>void setObjectPointer(const char* type, const char* name, T* v);
@@ -790,6 +879,7 @@ private:
      * or to ScriptController::INVALID_CALLBACK if there is no valid conversion.
      * 
      * @param name The name of the callback.
+     * 
      * @return The corresponding callback enumeration value.
      */
     static ScriptController::ScriptCallback toCallback(const char* name);

+ 8 - 7
gameplay/src/SpriteBatch.cpp

@@ -73,12 +73,12 @@ SpriteBatch::~SpriteBatch()
 SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsigned int initialCapacity)
 {
     Texture* texture = Texture::create(texturePath);
-    SpriteBatch* batch = SpriteBatch::create(texture);
+    SpriteBatch* batch = SpriteBatch::create(texture, effect, initialCapacity);
     SAFE_RELEASE(texture);
     return batch;
 }
 
-SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int initialCapacity)
+SpriteBatch* SpriteBatch::create(Texture* texture,  Effect* effect, unsigned int initialCapacity)
 {
     GP_ASSERT(texture != NULL);
 
@@ -154,10 +154,11 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     batch->_textureWidthRatio = 1.0f / (float)texture->getWidth();
     batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
 
-    // Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
-    Game* game = Game::getInstance();
-    Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &batch->_projectionMatrix);
-    material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getProjectionMatrix);
+	// Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
+	Game* game = Game::getInstance();
+	Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &batch->_projectionMatrix);
+	material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getProjectionMatrix);
+	
     return batch;
 }
 
@@ -185,7 +186,7 @@ void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2&
     float u2 = u1 + _textureWidthRatio * src.width;
     float v2 = v1 - _textureHeightRatio * src.height;
 
-    draw(dst.x, dst.y, dst.z, scale.x, scale.y, u2, v2, u1, v1, color);
+    draw(dst.x, dst.y, dst.z, scale.x, scale.y, u1, v1, u2, v2, color);
 }
 
 void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2& scale, const Vector4& color,

+ 167 - 0
gameplay/src/Stream.h

@@ -0,0 +1,167 @@
+#ifndef Stream_H_
+#define Stream_H_
+
+namespace gameplay
+{
+
+/**
+ * Stream is an interface for reading and writing a sequence of bytes.
+ * 
+ * Use FileSystem::open() to create a stream.
+ * 
+ * @script{ignore}
+ */
+class Stream
+{
+public:
+
+    /**
+     * Destructor. The stream should be closed when it is destroyed.
+     */
+    virtual ~Stream() {};
+
+    /**
+     * Returns true if this stream can perform read operations.
+     * 
+     * @return True if the stream can read, false otherwise.
+     */
+    virtual bool canRead() = 0;
+
+    /**
+     * Returns true if this stream can perform write operations.
+     * 
+     * @return True if the stream can write, false otherwise.
+     */
+    virtual bool canWrite() = 0;
+
+    /**
+     * Returns true if this stream can seek.
+     * 
+     * @return True if the stream can seek, false otherwise.
+     */
+    virtual bool canSeek() = 0;
+
+    /**
+     * Closes this stream.
+     */
+    virtual void close() = 0;
+    
+    /**
+     * Reads an array of <code>count</code> elements, each of size <code>size</code>.
+     * 
+     * \code
+     * int numbers[3];
+     * if (stream->read(numbers, sizeof(int), 3) != 3)
+     *     print("Error reading from file");
+     * \endcode
+     * 
+     * @param ptr   The pointer to the memory to copy into.
+     *              The available size should be at least (<code>size * count</code>) bytes.
+     * @param size  The size of each element to be read, in bytes.
+     * @param count The number of elements to read.
+     * 
+     * @return The number of elements read.
+     * 
+     * @see canRead()
+     */
+    virtual size_t read(void* ptr, size_t size, size_t count) = 0;
+
+    /**
+     * Reads a line from the stream.
+     * 
+     * A new line is denoted by by either "\n", "\r" or "\r\n".
+     * The line break character is included in the string.
+     * The terminating null character is added to the end of the string.
+     * 
+     * @param str The array of chars to copy the string to.
+     * @param num The maximum number of characters to be copied.
+     * 
+     * @return On success, str is returned. On error, NULL is returned.
+     * 
+     * @see canRead()
+     */
+    virtual char* readLine(char* str, int num) = 0;
+    
+    /**
+     * Writes an array of <code>count</code> elements, each of size <code>size</code>.
+     * 
+     * \code
+     * int numbers[] = {1, 2, 3};
+     * if (stream->write(numbers, sizeof(int), 3) != 3)
+     *     print("Error writing to file");
+     * \endcode
+     * 
+     * @param ptr   The pointer to the array of elements to be written.
+     * @param size  The size of each element to be written, in bytes.
+     * @param count The number of elements to write.
+     * 
+     * @return The number of elements written.
+     * 
+     * @see canWrite()
+     */
+    virtual size_t write(const void* ptr, size_t size, size_t count) = 0;
+
+    /**
+     * Returns true if the end of the stream has been reached.
+     * 
+     * @return True if end of stream reached, false otherwise.
+     */
+    virtual bool eof() = 0;
+
+    /**
+     * Returns the length of the stream in bytes.
+     * 
+     * Zero is returned if the length of the stream is unknown and/or it cannot be seeked. 
+     * 
+     * Example: The length of a network stream is unknown and cannot be seeked.
+     * 
+     * @return The length of the stream in bytes.
+     */
+    virtual size_t length() = 0;
+
+    /**
+     * Returns the position of the file pointer. Zero is the start of the stream.
+     * 
+     * @return The file indicator offset in bytes. 
+     */
+    virtual long int position() = 0;
+
+    /**
+     * Sets the position of the file pointer.
+     * 
+     * Use canSeek() to determine if this method is supported.
+     * 
+     * @param offset The number of bytes to offset from origin.
+     * @param origin The position used as a reference for offset.
+     *               The supported values are the same as fseek().
+     *                - <code>SEEK_SET</code> relative to the beginning of the file.
+     *                - <code>SEEK_CUR</code> relative to the current position of the file pointer.
+     *                - <code>SEEK_END</code> relative to the end of file.
+     * 
+     * @return True if successful, false otherwise.
+     * 
+     * @see canSeek()
+     */
+    virtual bool seek(long int offset, int origin) = 0;
+
+    /**
+     * Moves the file pointer to the start of the file.
+     * 
+     * Use canSeek() to determine if this method is supported.
+     * 
+     * @return True if successful, false otherwise.
+     * 
+     * @see canSeek()
+     */
+    virtual bool rewind() = 0;
+
+protected:
+    Stream() {};
+private:
+    Stream(const Stream&);            // Hidden copy constructor.
+    Stream& operator=(const Stream&); // Hidden copy assignment operator.
+};
+
+}
+
+#endif

+ 2 - 3
gameplay/src/TextBox.cpp

@@ -289,7 +289,7 @@ bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         // Always check that the text still fits within the clip region.
                         Rectangle textBounds;
                         font->measureText(_text.c_str(), _textBounds, fontSize, &textBounds, textAlignment, true, true);
-                        if (textBounds.x <= _textBounds.x || textBounds.y <= _textBounds.y ||
+                        if (textBounds.x < _textBounds.x || textBounds.y < _textBounds.y ||
                             textBounds.width >= _textBounds.width || textBounds.height >= _textBounds.height)
                         {
                             // If not, undo the character insertion.
@@ -329,10 +329,9 @@ void TextBox::update(const Control* container, const Vector2& offset)
 
 void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
-    if (_state == ACTIVE || _state == FOCUS)
+    if (_caretImage && (_state == ACTIVE || _state == FOCUS))
     {
         // Draw the cursor at its current location.
-        GP_ASSERT(_caretImage);
         const Rectangle& region = _caretImage->getRegion();
         if (!region.isEmpty())
         {

+ 21 - 89
gameplay/src/Texture.cpp

@@ -220,8 +220,8 @@ static unsigned int computePVRTCDataSize(int width, int height, int bpp)
 
 Texture* Texture::createCompressedPVRTC(const char* path)
 {
-    FILE* file = FileSystem::openFile(path, "rb");
-    if (file == NULL)
+    std::auto_ptr<Stream> stream(FileSystem::open(path));
+    if (stream.get() == NULL || !stream->canRead())
     {
         GP_ERROR("Failed to load file '%s'.", path);
         return NULL;
@@ -230,27 +230,17 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     // Read first 4 bytes to determine PVRTC format.
     size_t read;
     unsigned int version;
-    read = fread(&version, sizeof(unsigned int), 1, file);
+    read = stream->read(&version, sizeof(unsigned int), 1);
     if (read != 1)
     {
         GP_ERROR("Failed to read PVR version.");
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close PVR file '%s'.", path);
-            return NULL;
-        }
         return NULL;
     }
 
     // Rewind to start of header.
-    if (fseek(file, 0, SEEK_SET) != 0)
+    if (stream->seek(0, SEEK_SET) == false)
     {
         GP_ERROR("Failed to seek backwards to beginning of file after reading PVR version.");
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close PVR file '%s'.", path);
-            return NULL;
-        }
         return NULL;
     }
 
@@ -263,29 +253,19 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     if (version == 0x03525650)
     {
         // Modern PVR file format.
-        data = readCompressedPVRTC(path, file, &width, &height, &format, &mipMapCount);
+        data = readCompressedPVRTC(path, stream.get(), &width, &height, &format, &mipMapCount);
     }
     else
     {
         // Legacy PVR file format.
-        data = readCompressedPVRTCLegacy(path, file, &width, &height, &format, &mipMapCount);
+        data = readCompressedPVRTCLegacy(path, stream.get(), &width, &height, &format, &mipMapCount);
     }
     if (data == NULL)
     {
         GP_ERROR("Failed to read texture data from PVR file '%s'.", path);
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close PVR file '%s'.", path);
-            return NULL;
-        }
-        return NULL;
-    }
-
-    if (fclose(file) != 0)
-    {
-        GP_ERROR("Failed to close PVR file '%s'.", path);
         return NULL;
     }
+    stream->close();
 
     int bpp = (format == GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG || format == GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG) ? 2 : 4;
 
@@ -322,9 +302,9 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     return texture;
 }
 
-GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount)
+GLubyte* Texture::readCompressedPVRTC(const char* path, Stream* stream, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount)
 {
-    GP_ASSERT(file);
+    GP_ASSERT(stream);
     GP_ASSERT(path);
     GP_ASSERT(width);
     GP_ASSERT(height);
@@ -351,7 +331,7 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
 
     // Read header data.
     pvrtc_file_header header;
-    read = fread(&header, sizeof(pvrtc_file_header), 1, file);
+    read = stream->read(&header, sizeof(pvrtc_file_header), 1);
     if (read != 1)
     {
         GP_ERROR("Failed to read PVR header data for file '%s'.", path);
@@ -395,7 +375,7 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
     *mipMapCount = header.mipMapCount;
 
     // Skip meta-data.
-    if (fseek(file, header.metaDataSize, SEEK_CUR) != 0)
+    if (stream->seek(header.metaDataSize, SEEK_CUR) == false)
     {
         GP_ERROR("Failed to seek past header meta data in PVR file '%s'.", path);
         return NULL;
@@ -414,7 +394,7 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
 
     // Read data.
     GLubyte* data = new GLubyte[dataSize];
-    read = fread(data, 1, dataSize, file);
+    read = stream->read(data, 1, dataSize);
     if (read != dataSize)
     {
         SAFE_DELETE_ARRAY(data);
@@ -425,7 +405,7 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
     return data;
 }
 
-GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount)
+GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, Stream* stream, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount)
 {
     char PVRTCIdentifier[] = "PVR!";
 
@@ -449,14 +429,10 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
     // Read the file header.
     unsigned int size = sizeof(pvrtc_file_header_legacy);
     pvrtc_file_header_legacy header;
-    unsigned int read = (int)fread(&header, 1, size, file);
+    unsigned int read = (int)stream->read(&header, 1, size);
     if (read != size)
     {
         GP_ERROR("Failed to read file header for pvrtc file '%s'.", path);
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -467,10 +443,6 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
         PVRTCIdentifier[3] != (char)((header.pvrtcTag >> 24) & 0xff))
      {
         GP_ERROR("Failed to load pvrtc file '%s': invalid header.", path);
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -486,10 +458,6 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
     else
     {
         GP_ERROR("Failed to load pvrtc file '%s': invalid pvrtc compressed texture format flags.", path);
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -498,14 +466,10 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
     *mipMapCount = header.mipmapCount + 1; // +1 because mipmapCount does not include the base level
 
     GLubyte* data = new GLubyte[header.dataSize];
-    read = (int)fread(data, 1, header.dataSize, file);
+    read = (int)stream->read(data, 1, header.dataSize);
     if (read != header.dataSize)
     {
         GP_ERROR("Failed to load texture data for pvrtc file '%s'.", path);
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         SAFE_DELETE_ARRAY(data);
         return NULL;
     }
@@ -559,8 +523,8 @@ Texture* Texture::createCompressedDDS(const char* path)
     Texture* texture = NULL;
 
     // Read DDS file.
-    FILE* fp = FileSystem::openFile(path, "rb");
-    if (fp == NULL)
+    std::auto_ptr<Stream> stream(FileSystem::open(path));
+    if (stream.get() == NULL || !stream->canRead())
     {
         GP_ERROR("Failed to open file '%s'.", path);
         return NULL;
@@ -568,25 +532,17 @@ Texture* Texture::createCompressedDDS(const char* path)
 
     // Validate DDS magic number.
     char code[4];
-    if (fread(code, 1, 4, fp) != 4 || strncmp(code, "DDS ", 4) != 0)
+    if (stream->read(code, 1, 4) != 4 || strncmp(code, "DDS ", 4) != 0)
     {
         GP_ERROR("Failed to read DDS file '%s': invalid DDS magic number.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
     // Read DDS header.
     dds_header header;
-    if (fread(&header, sizeof(dds_header), 1, fp) != 1)
+    if (stream->read(&header, sizeof(dds_header), 1) != 1)
     {
         GP_ERROR("Failed to read header for DDS file '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -639,10 +595,6 @@ Texture* Texture::createCompressedDDS(const char* path)
             break;
         default:
             GP_ERROR("Unsupported compressed texture format (%d) for DDS file '%s'.", header.ddspf.dwFourCC, path);
-            if (fclose(fp) != 0)
-            {
-                GP_ERROR("Failed to close file '%s'.", path);
-            }
             SAFE_DELETE_ARRAY(mipLevels);
             return NULL;
         }
@@ -654,7 +606,7 @@ Texture* Texture::createCompressedDDS(const char* path)
             mipLevels[i].size =  std::max(1, (width+3) >> 2) * std::max(1, (height+3) >> 2) * bytesPerBlock;
             mipLevels[i].data = new GLubyte[mipLevels[i].size];
 
-            if (fread(mipLevels[i].data, 1, mipLevels[i].size, fp) != (unsigned int)mipLevels[i].size)
+            if (stream->read(mipLevels[i].data, 1, mipLevels[i].size) != (unsigned int)mipLevels[i].size)
             {
                 GP_ERROR("Failed to load dds compressed texture bytes for texture: %s", path);
                 
@@ -662,11 +614,6 @@ Texture* Texture::createCompressedDDS(const char* path)
                 for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
                     SAFE_DELETE_ARRAY(mipLevels[i].data);
                 SAFE_DELETE_ARRAY(mipLevels);
-
-                if (fclose(fp) != 0)
-                {
-                    GP_ERROR("Failed to close file '%s'.", path);
-                }
                 return texture;
             }
 
@@ -679,10 +626,6 @@ Texture* Texture::createCompressedDDS(const char* path)
         // RGB (uncompressed)
         // Note: Use GL_BGR as internal format to flip bytes.
         GP_ERROR("Failed to create texture from DDS file '%s': uncompressed RGB format is not supported.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
     }
@@ -691,10 +634,6 @@ Texture* Texture::createCompressedDDS(const char* path)
         // RGBA (uncompressed)
         // Note: Use GL_BGRA as internal format to flip bytes.
         GP_ERROR("Failed to create texture from DDS file '%s': uncompressed RGBA format is not supported.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
     }
@@ -702,19 +641,12 @@ Texture* Texture::createCompressedDDS(const char* path)
     {
         // Unsupported.
         GP_ERROR("Failed to create texture from DDS file '%s': unsupported flags (%d).", path, header.ddspf.dwFlags);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
     }
     
     // Close file.
-    if (fclose(fp) != 0)
-    {
-        GP_ERROR("Failed to close file '%s'.", path);
-    }
+    stream->close();
 
     // Generate GL texture.
     GLuint textureId;

+ 3 - 2
gameplay/src/Texture.h

@@ -2,6 +2,7 @@
 #define TEXTURE_H_
 
 #include "Ref.h"
+#include "Stream.h"
 
 namespace gameplay
 {
@@ -285,9 +286,9 @@ private:
 
     static Texture* createCompressedDDS(const char* path);
 
-    static GLubyte* readCompressedPVRTC(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount);
+    static GLubyte* readCompressedPVRTC(const char* path, Stream* stream, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount);
 
-    static GLubyte* readCompressedPVRTCLegacy(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount);
+    static GLubyte* readCompressedPVRTCLegacy(const char* path, Stream* stream, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount);
 
     std::string _path;
     TextureHandle _handle;

+ 3 - 1
gameplay/src/Transform.h

@@ -682,7 +682,7 @@ public:
     void translateUp(float amount);
 
     /**
-     * Translates the camera foward by the specified amount in the z-axis.
+     * Translates the camera forward by the specified amount in the z-axis.
      *
      * @param amount The amount to translate.
      */
@@ -757,6 +757,8 @@ public:
 
     /**
      * Removes a transform listener.
+     * 
+     * @param listener The listener to remove.
      */
     void removeListener(Transform::Listener* listener);
     

+ 1 - 1
gameplay/src/Vector2.cpp

@@ -171,7 +171,7 @@ Vector2& Vector2::normalize()
     return *this;
 }
 
-void Vector2::normalize(Vector2* dst)
+void Vector2::normalize(Vector2* dst) const
 {
     GP_ASSERT(dst);
 

+ 1 - 1
gameplay/src/Vector2.h

@@ -245,7 +245,7 @@ public:
      *
      * @param dst The destination vector.
      */
-    void normalize(Vector2* dst);
+    void normalize(Vector2* dst) const;
 
     /**
      * Scales all elements of this vector by the specified value.

+ 4 - 1
gameplay/src/Vector3.cpp

@@ -173,7 +173,10 @@ void Vector3::cross(const Vector3& v1, const Vector3& v2, Vector3* dst)
 {
     GP_ASSERT(dst);
 
-    MathUtil::crossVector3((const float*)&v1, (const float*)&v2, (float*)dst);
+    // NOTE: This code assumes Vector3 struct members are contiguous floats in memory.
+    // We might want to revisit this (and other areas of code that make this assumption)
+    // later to guarantee 100% safety/compatibility.
+    MathUtil::crossVector3(&v1.x, &v2.x, &dst->x);
 }
 
 float Vector3::distance(const Vector3& v) const

+ 1 - 1
gameplay/src/Vector4.cpp

@@ -239,7 +239,7 @@ Vector4& Vector4::normalize()
     return *this;
 }
 
-void Vector4::normalize(Vector4* dst)
+void Vector4::normalize(Vector4* dst) const
 {
     GP_ASSERT(dst);
 

+ 1 - 1
gameplay/src/Vector4.h

@@ -283,7 +283,7 @@ public:
      *
      * @param dst The destination vector.
      */
-    void normalize(Vector4* dst);
+    void normalize(Vector4* dst) const;
 
     /**
      * Scales all elements of this vector by the specified value.

+ 153 - 66
gameplay/src/lua/lua_Button.cpp

@@ -32,8 +32,6 @@ void luaRegister_Button()
         {"createAnimationFromBy", lua_Button_createAnimationFromBy},
         {"createAnimationFromTo", lua_Button_createAnimationFromTo},
         {"destroyAnimation", lua_Button_destroyAnimation},
-        {"disable", lua_Button_disable},
-        {"enable", lua_Button_enable},
         {"getAlignment", lua_Button_getAlignment},
         {"getAnimation", lua_Button_getAnimation},
         {"getAnimationPropertyComponentCount", lua_Button_getAnimationPropertyComponentCount},
@@ -74,7 +72,9 @@ void luaRegister_Button()
         {"getZIndex", lua_Button_getZIndex},
         {"isContainer", lua_Button_isContainer},
         {"isEnabled", lua_Button_isEnabled},
+        {"isVisible", lua_Button_isVisible},
         {"release", lua_Button_release},
+        {"removeListener", lua_Button_removeListener},
         {"removeScriptCallback", lua_Button_removeScriptCallback},
         {"setAlignment", lua_Button_setAlignment},
         {"setAnimationPropertyValue", lua_Button_setAnimationPropertyValue},
@@ -85,6 +85,7 @@ void luaRegister_Button()
         {"setConsumeInputEvents", lua_Button_setConsumeInputEvents},
         {"setCursorColor", lua_Button_setCursorColor},
         {"setCursorRegion", lua_Button_setCursorRegion},
+        {"setEnabled", lua_Button_setEnabled},
         {"setFocusIndex", lua_Button_setFocusIndex},
         {"setFont", lua_Button_setFont},
         {"setFontSize", lua_Button_setFontSize},
@@ -104,6 +105,7 @@ void luaRegister_Button()
         {"setTextAlignment", lua_Button_setTextAlignment},
         {"setTextColor", lua_Button_setTextColor},
         {"setTextRightToLeft", lua_Button_setTextRightToLeft},
+        {"setVisible", lua_Button_setVisible},
         {"setWidth", lua_Button_setWidth},
         {"setZIndex", lua_Button_setZIndex},
         {NULL, NULL}
@@ -677,70 +679,6 @@ int lua_Button_destroyAnimation(lua_State* state)
     return 0;
 }
 
-int lua_Button_disable(lua_State* state)
-{
-    // Get the number of parameters.
-    int paramCount = lua_gettop(state);
-
-    // Attempt to match the parameters to a valid binding.
-    switch (paramCount)
-    {
-        case 1:
-        {
-            if ((lua_type(state, 1) == LUA_TUSERDATA))
-            {
-                Button* instance = getInstance(state);
-                instance->disable();
-                
-                return 0;
-            }
-
-            lua_pushstring(state, "lua_Button_disable - Failed to match the given parameters to a valid function signature.");
-            lua_error(state);
-            break;
-        }
-        default:
-        {
-            lua_pushstring(state, "Invalid number of parameters (expected 1).");
-            lua_error(state);
-            break;
-        }
-    }
-    return 0;
-}
-
-int lua_Button_enable(lua_State* state)
-{
-    // Get the number of parameters.
-    int paramCount = lua_gettop(state);
-
-    // Attempt to match the parameters to a valid binding.
-    switch (paramCount)
-    {
-        case 1:
-        {
-            if ((lua_type(state, 1) == LUA_TUSERDATA))
-            {
-                Button* instance = getInstance(state);
-                instance->enable();
-                
-                return 0;
-            }
-
-            lua_pushstring(state, "lua_Button_enable - Failed to match the given parameters to a valid function signature.");
-            lua_error(state);
-            break;
-        }
-        default:
-        {
-            lua_pushstring(state, "Invalid number of parameters (expected 1).");
-            lua_error(state);
-            break;
-        }
-    }
-    return 0;
-}
-
 int lua_Button_getAlignment(lua_State* state)
 {
     // Get the number of parameters.
@@ -2618,6 +2556,41 @@ int lua_Button_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_Button_isVisible(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Button* instance = getInstance(state);
+                bool result = instance->isVisible();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Button_isVisible - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Button_release(lua_State* state)
 {
     // Get the number of parameters.
@@ -2650,6 +2623,48 @@ int lua_Button_release(lua_State* state)
     return 0;
 }
 
+int lua_Button_removeListener(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                (lua_type(state, 2) == LUA_TUSERDATA || lua_type(state, 2) == LUA_TTABLE || lua_type(state, 2) == LUA_TNIL))
+            {
+                // Get parameter 1 off the stack.
+                bool param1Valid;
+                ScriptUtil::LuaArray<Control::Listener> param1 = ScriptUtil::getObjectPointer<Control::Listener>(2, "ControlListener", false, &param1Valid);
+                if (!param1Valid)
+                {
+                    lua_pushstring(state, "Failed to convert parameter 1 to type 'Control::Listener'.");
+                    lua_error(state);
+                }
+
+                Button* instance = getInstance(state);
+                instance->removeListener(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Button_removeListener - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Button_removeScriptCallback(lua_State* state)
 {
     // Get the number of parameters.
@@ -3128,6 +3143,42 @@ int lua_Button_setCursorRegion(lua_State* state)
     return 0;
 }
 
+int lua_Button_setEnabled(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                lua_type(state, 2) == LUA_TBOOLEAN)
+            {
+                // Get parameter 1 off the stack.
+                bool param1 = ScriptUtil::luaCheckBool(state, 2);
+
+                Button* instance = getInstance(state);
+                instance->setEnabled(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Button_setEnabled - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Button_setFocusIndex(lua_State* state)
 {
     // Get the number of parameters.
@@ -4158,6 +4209,42 @@ int lua_Button_setTextRightToLeft(lua_State* state)
     return 0;
 }
 
+int lua_Button_setVisible(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                lua_type(state, 2) == LUA_TBOOLEAN)
+            {
+                // Get parameter 1 off the stack.
+                bool param1 = ScriptUtil::luaCheckBool(state, 2);
+
+                Button* instance = getInstance(state);
+                instance->setVisible(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Button_setVisible - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Button_setWidth(lua_State* state)
 {
     // Get the number of parameters.

+ 4 - 2
gameplay/src/lua/lua_Button.h

@@ -13,8 +13,6 @@ int lua_Button_createAnimation(lua_State* state);
 int lua_Button_createAnimationFromBy(lua_State* state);
 int lua_Button_createAnimationFromTo(lua_State* state);
 int lua_Button_destroyAnimation(lua_State* state);
-int lua_Button_disable(lua_State* state);
-int lua_Button_enable(lua_State* state);
 int lua_Button_getAlignment(lua_State* state);
 int lua_Button_getAnimation(lua_State* state);
 int lua_Button_getAnimationPropertyComponentCount(lua_State* state);
@@ -55,7 +53,9 @@ int lua_Button_getY(lua_State* state);
 int lua_Button_getZIndex(lua_State* state);
 int lua_Button_isContainer(lua_State* state);
 int lua_Button_isEnabled(lua_State* state);
+int lua_Button_isVisible(lua_State* state);
 int lua_Button_release(lua_State* state);
+int lua_Button_removeListener(lua_State* state);
 int lua_Button_removeScriptCallback(lua_State* state);
 int lua_Button_setAlignment(lua_State* state);
 int lua_Button_setAnimationPropertyValue(lua_State* state);
@@ -66,6 +66,7 @@ int lua_Button_setBounds(lua_State* state);
 int lua_Button_setConsumeInputEvents(lua_State* state);
 int lua_Button_setCursorColor(lua_State* state);
 int lua_Button_setCursorRegion(lua_State* state);
+int lua_Button_setEnabled(lua_State* state);
 int lua_Button_setFocusIndex(lua_State* state);
 int lua_Button_setFont(lua_State* state);
 int lua_Button_setFontSize(lua_State* state);
@@ -85,6 +86,7 @@ int lua_Button_setText(lua_State* state);
 int lua_Button_setTextAlignment(lua_State* state);
 int lua_Button_setTextColor(lua_State* state);
 int lua_Button_setTextRightToLeft(lua_State* state);
+int lua_Button_setVisible(lua_State* state);
 int lua_Button_setWidth(lua_State* state);
 int lua_Button_setZIndex(lua_State* state);
 int lua_Button_static_ANIMATE_OPACITY(lua_State* state);

+ 153 - 66
gameplay/src/lua/lua_CheckBox.cpp

@@ -33,8 +33,6 @@ void luaRegister_CheckBox()
         {"createAnimationFromBy", lua_CheckBox_createAnimationFromBy},
         {"createAnimationFromTo", lua_CheckBox_createAnimationFromTo},
         {"destroyAnimation", lua_CheckBox_destroyAnimation},
-        {"disable", lua_CheckBox_disable},
-        {"enable", lua_CheckBox_enable},
         {"getAlignment", lua_CheckBox_getAlignment},
         {"getAnimation", lua_CheckBox_getAnimation},
         {"getAnimationPropertyComponentCount", lua_CheckBox_getAnimationPropertyComponentCount},
@@ -78,7 +76,9 @@ void luaRegister_CheckBox()
         {"isChecked", lua_CheckBox_isChecked},
         {"isContainer", lua_CheckBox_isContainer},
         {"isEnabled", lua_CheckBox_isEnabled},
+        {"isVisible", lua_CheckBox_isVisible},
         {"release", lua_CheckBox_release},
+        {"removeListener", lua_CheckBox_removeListener},
         {"removeScriptCallback", lua_CheckBox_removeScriptCallback},
         {"setAlignment", lua_CheckBox_setAlignment},
         {"setAnimationPropertyValue", lua_CheckBox_setAnimationPropertyValue},
@@ -90,6 +90,7 @@ void luaRegister_CheckBox()
         {"setConsumeInputEvents", lua_CheckBox_setConsumeInputEvents},
         {"setCursorColor", lua_CheckBox_setCursorColor},
         {"setCursorRegion", lua_CheckBox_setCursorRegion},
+        {"setEnabled", lua_CheckBox_setEnabled},
         {"setFocusIndex", lua_CheckBox_setFocusIndex},
         {"setFont", lua_CheckBox_setFont},
         {"setFontSize", lua_CheckBox_setFontSize},
@@ -110,6 +111,7 @@ void luaRegister_CheckBox()
         {"setTextAlignment", lua_CheckBox_setTextAlignment},
         {"setTextColor", lua_CheckBox_setTextColor},
         {"setTextRightToLeft", lua_CheckBox_setTextRightToLeft},
+        {"setVisible", lua_CheckBox_setVisible},
         {"setWidth", lua_CheckBox_setWidth},
         {"setZIndex", lua_CheckBox_setZIndex},
         {NULL, NULL}
@@ -683,70 +685,6 @@ int lua_CheckBox_destroyAnimation(lua_State* state)
     return 0;
 }
 
-int lua_CheckBox_disable(lua_State* state)
-{
-    // Get the number of parameters.
-    int paramCount = lua_gettop(state);
-
-    // Attempt to match the parameters to a valid binding.
-    switch (paramCount)
-    {
-        case 1:
-        {
-            if ((lua_type(state, 1) == LUA_TUSERDATA))
-            {
-                CheckBox* instance = getInstance(state);
-                instance->disable();
-                
-                return 0;
-            }
-
-            lua_pushstring(state, "lua_CheckBox_disable - Failed to match the given parameters to a valid function signature.");
-            lua_error(state);
-            break;
-        }
-        default:
-        {
-            lua_pushstring(state, "Invalid number of parameters (expected 1).");
-            lua_error(state);
-            break;
-        }
-    }
-    return 0;
-}
-
-int lua_CheckBox_enable(lua_State* state)
-{
-    // Get the number of parameters.
-    int paramCount = lua_gettop(state);
-
-    // Attempt to match the parameters to a valid binding.
-    switch (paramCount)
-    {
-        case 1:
-        {
-            if ((lua_type(state, 1) == LUA_TUSERDATA))
-            {
-                CheckBox* instance = getInstance(state);
-                instance->enable();
-                
-                return 0;
-            }
-
-            lua_pushstring(state, "lua_CheckBox_enable - Failed to match the given parameters to a valid function signature.");
-            lua_error(state);
-            break;
-        }
-        default:
-        {
-            lua_pushstring(state, "Invalid number of parameters (expected 1).");
-            lua_error(state);
-            break;
-        }
-    }
-    return 0;
-}
-
 int lua_CheckBox_getAlignment(lua_State* state)
 {
     // Get the number of parameters.
@@ -2738,6 +2676,41 @@ int lua_CheckBox_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_CheckBox_isVisible(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                CheckBox* instance = getInstance(state);
+                bool result = instance->isVisible();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_CheckBox_isVisible - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_CheckBox_release(lua_State* state)
 {
     // Get the number of parameters.
@@ -2770,6 +2743,48 @@ int lua_CheckBox_release(lua_State* state)
     return 0;
 }
 
+int lua_CheckBox_removeListener(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                (lua_type(state, 2) == LUA_TUSERDATA || lua_type(state, 2) == LUA_TTABLE || lua_type(state, 2) == LUA_TNIL))
+            {
+                // Get parameter 1 off the stack.
+                bool param1Valid;
+                ScriptUtil::LuaArray<Control::Listener> param1 = ScriptUtil::getObjectPointer<Control::Listener>(2, "ControlListener", false, &param1Valid);
+                if (!param1Valid)
+                {
+                    lua_pushstring(state, "Failed to convert parameter 1 to type 'Control::Listener'.");
+                    lua_error(state);
+                }
+
+                CheckBox* instance = getInstance(state);
+                instance->removeListener(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_CheckBox_removeListener - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_CheckBox_removeScriptCallback(lua_State* state)
 {
     // Get the number of parameters.
@@ -3284,6 +3299,42 @@ int lua_CheckBox_setCursorRegion(lua_State* state)
     return 0;
 }
 
+int lua_CheckBox_setEnabled(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                lua_type(state, 2) == LUA_TBOOLEAN)
+            {
+                // Get parameter 1 off the stack.
+                bool param1 = ScriptUtil::luaCheckBool(state, 2);
+
+                CheckBox* instance = getInstance(state);
+                instance->setEnabled(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_CheckBox_setEnabled - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_CheckBox_setFocusIndex(lua_State* state)
 {
     // Get the number of parameters.
@@ -4354,6 +4405,42 @@ int lua_CheckBox_setTextRightToLeft(lua_State* state)
     return 0;
 }
 
+int lua_CheckBox_setVisible(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                lua_type(state, 2) == LUA_TBOOLEAN)
+            {
+                // Get parameter 1 off the stack.
+                bool param1 = ScriptUtil::luaCheckBool(state, 2);
+
+                CheckBox* instance = getInstance(state);
+                instance->setVisible(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_CheckBox_setVisible - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_CheckBox_setWidth(lua_State* state)
 {
     // Get the number of parameters.

+ 4 - 2
gameplay/src/lua/lua_CheckBox.h

@@ -13,8 +13,6 @@ int lua_CheckBox_createAnimation(lua_State* state);
 int lua_CheckBox_createAnimationFromBy(lua_State* state);
 int lua_CheckBox_createAnimationFromTo(lua_State* state);
 int lua_CheckBox_destroyAnimation(lua_State* state);
-int lua_CheckBox_disable(lua_State* state);
-int lua_CheckBox_enable(lua_State* state);
 int lua_CheckBox_getAlignment(lua_State* state);
 int lua_CheckBox_getAnimation(lua_State* state);
 int lua_CheckBox_getAnimationPropertyComponentCount(lua_State* state);
@@ -58,7 +56,9 @@ int lua_CheckBox_getZIndex(lua_State* state);
 int lua_CheckBox_isChecked(lua_State* state);
 int lua_CheckBox_isContainer(lua_State* state);
 int lua_CheckBox_isEnabled(lua_State* state);
+int lua_CheckBox_isVisible(lua_State* state);
 int lua_CheckBox_release(lua_State* state);
+int lua_CheckBox_removeListener(lua_State* state);
 int lua_CheckBox_removeScriptCallback(lua_State* state);
 int lua_CheckBox_setAlignment(lua_State* state);
 int lua_CheckBox_setAnimationPropertyValue(lua_State* state);
@@ -70,6 +70,7 @@ int lua_CheckBox_setChecked(lua_State* state);
 int lua_CheckBox_setConsumeInputEvents(lua_State* state);
 int lua_CheckBox_setCursorColor(lua_State* state);
 int lua_CheckBox_setCursorRegion(lua_State* state);
+int lua_CheckBox_setEnabled(lua_State* state);
 int lua_CheckBox_setFocusIndex(lua_State* state);
 int lua_CheckBox_setFont(lua_State* state);
 int lua_CheckBox_setFontSize(lua_State* state);
@@ -90,6 +91,7 @@ int lua_CheckBox_setText(lua_State* state);
 int lua_CheckBox_setTextAlignment(lua_State* state);
 int lua_CheckBox_setTextColor(lua_State* state);
 int lua_CheckBox_setTextRightToLeft(lua_State* state);
+int lua_CheckBox_setVisible(lua_State* state);
 int lua_CheckBox_setWidth(lua_State* state);
 int lua_CheckBox_setZIndex(lua_State* state);
 int lua_CheckBox_static_ANIMATE_OPACITY(lua_State* state);

+ 153 - 66
gameplay/src/lua/lua_Container.cpp

@@ -45,8 +45,6 @@ void luaRegister_Container()
         {"createAnimationFromBy", lua_Container_createAnimationFromBy},
         {"createAnimationFromTo", lua_Container_createAnimationFromTo},
         {"destroyAnimation", lua_Container_destroyAnimation},
-        {"disable", lua_Container_disable},
-        {"enable", lua_Container_enable},
         {"getAlignment", lua_Container_getAlignment},
         {"getAnimation", lua_Container_getAnimation},
         {"getAnimationPropertyComponentCount", lua_Container_getAnimationPropertyComponentCount},
@@ -93,8 +91,10 @@ void luaRegister_Container()
         {"isEnabled", lua_Container_isEnabled},
         {"isScrollBarsAutoHide", lua_Container_isScrollBarsAutoHide},
         {"isScrolling", lua_Container_isScrolling},
+        {"isVisible", lua_Container_isVisible},
         {"release", lua_Container_release},
         {"removeControl", lua_Container_removeControl},
+        {"removeListener", lua_Container_removeListener},
         {"removeScriptCallback", lua_Container_removeScriptCallback},
         {"setAlignment", lua_Container_setAlignment},
         {"setAnimationPropertyValue", lua_Container_setAnimationPropertyValue},
@@ -105,6 +105,7 @@ void luaRegister_Container()
         {"setConsumeInputEvents", lua_Container_setConsumeInputEvents},
         {"setCursorColor", lua_Container_setCursorColor},
         {"setCursorRegion", lua_Container_setCursorRegion},
+        {"setEnabled", lua_Container_setEnabled},
         {"setFocusIndex", lua_Container_setFocusIndex},
         {"setFont", lua_Container_setFont},
         {"setFontSize", lua_Container_setFontSize},
@@ -125,6 +126,7 @@ void luaRegister_Container()
         {"setTextAlignment", lua_Container_setTextAlignment},
         {"setTextColor", lua_Container_setTextColor},
         {"setTextRightToLeft", lua_Container_setTextRightToLeft},
+        {"setVisible", lua_Container_setVisible},
         {"setWidth", lua_Container_setWidth},
         {"setZIndex", lua_Container_setZIndex},
         {NULL, NULL}
@@ -744,70 +746,6 @@ int lua_Container_destroyAnimation(lua_State* state)
     return 0;
 }
 
-int lua_Container_disable(lua_State* state)
-{
-    // Get the number of parameters.
-    int paramCount = lua_gettop(state);
-
-    // Attempt to match the parameters to a valid binding.
-    switch (paramCount)
-    {
-        case 1:
-        {
-            if ((lua_type(state, 1) == LUA_TUSERDATA))
-            {
-                Container* instance = getInstance(state);
-                instance->disable();
-                
-                return 0;
-            }
-
-            lua_pushstring(state, "lua_Container_disable - Failed to match the given parameters to a valid function signature.");
-            lua_error(state);
-            break;
-        }
-        default:
-        {
-            lua_pushstring(state, "Invalid number of parameters (expected 1).");
-            lua_error(state);
-            break;
-        }
-    }
-    return 0;
-}
-
-int lua_Container_enable(lua_State* state)
-{
-    // Get the number of parameters.
-    int paramCount = lua_gettop(state);
-
-    // Attempt to match the parameters to a valid binding.
-    switch (paramCount)
-    {
-        case 1:
-        {
-            if ((lua_type(state, 1) == LUA_TUSERDATA))
-            {
-                Container* instance = getInstance(state);
-                instance->enable();
-                
-                return 0;
-            }
-
-            lua_pushstring(state, "lua_Container_enable - Failed to match the given parameters to a valid function signature.");
-            lua_error(state);
-            break;
-        }
-        default:
-        {
-            lua_pushstring(state, "Invalid number of parameters (expected 1).");
-            lua_error(state);
-            break;
-        }
-    }
-    return 0;
-}
-
 int lua_Container_getAlignment(lua_State* state)
 {
     // Get the number of parameters.
@@ -2958,6 +2896,41 @@ int lua_Container_isScrolling(lua_State* state)
     return 0;
 }
 
+int lua_Container_isVisible(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Container* instance = getInstance(state);
+                bool result = instance->isVisible();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Container_isVisible - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Container_release(lua_State* state)
 {
     // Get the number of parameters.
@@ -3062,6 +3035,48 @@ int lua_Container_removeControl(lua_State* state)
     return 0;
 }
 
+int lua_Container_removeListener(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                (lua_type(state, 2) == LUA_TUSERDATA || lua_type(state, 2) == LUA_TTABLE || lua_type(state, 2) == LUA_TNIL))
+            {
+                // Get parameter 1 off the stack.
+                bool param1Valid;
+                ScriptUtil::LuaArray<Control::Listener> param1 = ScriptUtil::getObjectPointer<Control::Listener>(2, "ControlListener", false, &param1Valid);
+                if (!param1Valid)
+                {
+                    lua_pushstring(state, "Failed to convert parameter 1 to type 'Control::Listener'.");
+                    lua_error(state);
+                }
+
+                Container* instance = getInstance(state);
+                instance->removeListener(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Container_removeListener - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Container_removeScriptCallback(lua_State* state)
 {
     // Get the number of parameters.
@@ -3540,6 +3555,42 @@ int lua_Container_setCursorRegion(lua_State* state)
     return 0;
 }
 
+int lua_Container_setEnabled(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                lua_type(state, 2) == LUA_TBOOLEAN)
+            {
+                // Get parameter 1 off the stack.
+                bool param1 = ScriptUtil::luaCheckBool(state, 2);
+
+                Container* instance = getInstance(state);
+                instance->setEnabled(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Container_setEnabled - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Container_setFocusIndex(lua_State* state)
 {
     // Get the number of parameters.
@@ -4606,6 +4657,42 @@ int lua_Container_setTextRightToLeft(lua_State* state)
     return 0;
 }
 
+int lua_Container_setVisible(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                lua_type(state, 2) == LUA_TBOOLEAN)
+            {
+                // Get parameter 1 off the stack.
+                bool param1 = ScriptUtil::luaCheckBool(state, 2);
+
+                Container* instance = getInstance(state);
+                instance->setVisible(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Container_setVisible - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Container_setWidth(lua_State* state)
 {
     // Get the number of parameters.

+ 4 - 2
gameplay/src/lua/lua_Container.h

@@ -14,8 +14,6 @@ int lua_Container_createAnimation(lua_State* state);
 int lua_Container_createAnimationFromBy(lua_State* state);
 int lua_Container_createAnimationFromTo(lua_State* state);
 int lua_Container_destroyAnimation(lua_State* state);
-int lua_Container_disable(lua_State* state);
-int lua_Container_enable(lua_State* state);
 int lua_Container_getAlignment(lua_State* state);
 int lua_Container_getAnimation(lua_State* state);
 int lua_Container_getAnimationPropertyComponentCount(lua_State* state);
@@ -62,8 +60,10 @@ int lua_Container_isContainer(lua_State* state);
 int lua_Container_isEnabled(lua_State* state);
 int lua_Container_isScrollBarsAutoHide(lua_State* state);
 int lua_Container_isScrolling(lua_State* state);
+int lua_Container_isVisible(lua_State* state);
 int lua_Container_release(lua_State* state);
 int lua_Container_removeControl(lua_State* state);
+int lua_Container_removeListener(lua_State* state);
 int lua_Container_removeScriptCallback(lua_State* state);
 int lua_Container_setAlignment(lua_State* state);
 int lua_Container_setAnimationPropertyValue(lua_State* state);
@@ -74,6 +74,7 @@ int lua_Container_setBounds(lua_State* state);
 int lua_Container_setConsumeInputEvents(lua_State* state);
 int lua_Container_setCursorColor(lua_State* state);
 int lua_Container_setCursorRegion(lua_State* state);
+int lua_Container_setEnabled(lua_State* state);
 int lua_Container_setFocusIndex(lua_State* state);
 int lua_Container_setFont(lua_State* state);
 int lua_Container_setFontSize(lua_State* state);
@@ -94,6 +95,7 @@ int lua_Container_setStyle(lua_State* state);
 int lua_Container_setTextAlignment(lua_State* state);
 int lua_Container_setTextColor(lua_State* state);
 int lua_Container_setTextRightToLeft(lua_State* state);
+int lua_Container_setVisible(lua_State* state);
 int lua_Container_setWidth(lua_State* state);
 int lua_Container_setZIndex(lua_State* state);
 int lua_Container_static_ANIMATE_OPACITY(lua_State* state);

+ 153 - 66
gameplay/src/lua/lua_Control.cpp

@@ -30,8 +30,6 @@ void luaRegister_Control()
         {"createAnimationFromBy", lua_Control_createAnimationFromBy},
         {"createAnimationFromTo", lua_Control_createAnimationFromTo},
         {"destroyAnimation", lua_Control_destroyAnimation},
-        {"disable", lua_Control_disable},
-        {"enable", lua_Control_enable},
         {"getAlignment", lua_Control_getAlignment},
         {"getAnimation", lua_Control_getAnimation},
         {"getAnimationPropertyComponentCount", lua_Control_getAnimationPropertyComponentCount},
@@ -72,7 +70,9 @@ void luaRegister_Control()
         {"getZIndex", lua_Control_getZIndex},
         {"isContainer", lua_Control_isContainer},
         {"isEnabled", lua_Control_isEnabled},
+        {"isVisible", lua_Control_isVisible},
         {"release", lua_Control_release},
+        {"removeListener", lua_Control_removeListener},
         {"removeScriptCallback", lua_Control_removeScriptCallback},
         {"setAlignment", lua_Control_setAlignment},
         {"setAnimationPropertyValue", lua_Control_setAnimationPropertyValue},
@@ -83,6 +83,7 @@ void luaRegister_Control()
         {"setConsumeInputEvents", lua_Control_setConsumeInputEvents},
         {"setCursorColor", lua_Control_setCursorColor},
         {"setCursorRegion", lua_Control_setCursorRegion},
+        {"setEnabled", lua_Control_setEnabled},
         {"setFocusIndex", lua_Control_setFocusIndex},
         {"setFont", lua_Control_setFont},
         {"setFontSize", lua_Control_setFontSize},
@@ -101,6 +102,7 @@ void luaRegister_Control()
         {"setTextAlignment", lua_Control_setTextAlignment},
         {"setTextColor", lua_Control_setTextColor},
         {"setTextRightToLeft", lua_Control_setTextRightToLeft},
+        {"setVisible", lua_Control_setVisible},
         {"setWidth", lua_Control_setWidth},
         {"setZIndex", lua_Control_setZIndex},
         {NULL, NULL}
@@ -673,70 +675,6 @@ int lua_Control_destroyAnimation(lua_State* state)
     return 0;
 }
 
-int lua_Control_disable(lua_State* state)
-{
-    // Get the number of parameters.
-    int paramCount = lua_gettop(state);
-
-    // Attempt to match the parameters to a valid binding.
-    switch (paramCount)
-    {
-        case 1:
-        {
-            if ((lua_type(state, 1) == LUA_TUSERDATA))
-            {
-                Control* instance = getInstance(state);
-                instance->disable();
-                
-                return 0;
-            }
-
-            lua_pushstring(state, "lua_Control_disable - Failed to match the given parameters to a valid function signature.");
-            lua_error(state);
-            break;
-        }
-        default:
-        {
-            lua_pushstring(state, "Invalid number of parameters (expected 1).");
-            lua_error(state);
-            break;
-        }
-    }
-    return 0;
-}
-
-int lua_Control_enable(lua_State* state)
-{
-    // Get the number of parameters.
-    int paramCount = lua_gettop(state);
-
-    // Attempt to match the parameters to a valid binding.
-    switch (paramCount)
-    {
-        case 1:
-        {
-            if ((lua_type(state, 1) == LUA_TUSERDATA))
-            {
-                Control* instance = getInstance(state);
-                instance->enable();
-                
-                return 0;
-            }
-
-            lua_pushstring(state, "lua_Control_enable - Failed to match the given parameters to a valid function signature.");
-            lua_error(state);
-            break;
-        }
-        default:
-        {
-            lua_pushstring(state, "Invalid number of parameters (expected 1).");
-            lua_error(state);
-            break;
-        }
-    }
-    return 0;
-}
-
 int lua_Control_getAlignment(lua_State* state)
 {
     // Get the number of parameters.
@@ -2614,6 +2552,41 @@ int lua_Control_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_Control_isVisible(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Control* instance = getInstance(state);
+                bool result = instance->isVisible();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Control_isVisible - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Control_release(lua_State* state)
 {
     // Get the number of parameters.
@@ -2646,6 +2619,48 @@ int lua_Control_release(lua_State* state)
     return 0;
 }
 
+int lua_Control_removeListener(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                (lua_type(state, 2) == LUA_TUSERDATA || lua_type(state, 2) == LUA_TTABLE || lua_type(state, 2) == LUA_TNIL))
+            {
+                // Get parameter 1 off the stack.
+                bool param1Valid;
+                ScriptUtil::LuaArray<Control::Listener> param1 = ScriptUtil::getObjectPointer<Control::Listener>(2, "ControlListener", false, &param1Valid);
+                if (!param1Valid)
+                {
+                    lua_pushstring(state, "Failed to convert parameter 1 to type 'Control::Listener'.");
+                    lua_error(state);
+                }
+
+                Control* instance = getInstance(state);
+                instance->removeListener(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Control_removeListener - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Control_removeScriptCallback(lua_State* state)
 {
     // Get the number of parameters.
@@ -3124,6 +3139,42 @@ int lua_Control_setCursorRegion(lua_State* state)
     return 0;
 }
 
+int lua_Control_setEnabled(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                lua_type(state, 2) == LUA_TBOOLEAN)
+            {
+                // Get parameter 1 off the stack.
+                bool param1 = ScriptUtil::luaCheckBool(state, 2);
+
+                Control* instance = getInstance(state);
+                instance->setEnabled(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Control_setEnabled - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Control_setFocusIndex(lua_State* state)
 {
     // Get the number of parameters.
@@ -4118,6 +4169,42 @@ int lua_Control_setTextRightToLeft(lua_State* state)
     return 0;
 }
 
+int lua_Control_setVisible(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                lua_type(state, 2) == LUA_TBOOLEAN)
+            {
+                // Get parameter 1 off the stack.
+                bool param1 = ScriptUtil::luaCheckBool(state, 2);
+
+                Control* instance = getInstance(state);
+                instance->setVisible(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Control_setVisible - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Control_setWidth(lua_State* state)
 {
     // Get the number of parameters.

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików