Explorar el Código

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

Conflicts:
	gameplay/gameplay.vcxproj.filters
Steve Grenier hace 13 años
padre
commit
5f80ee4a2b
Se han modificado 100 ficheros con 3059 adiciones y 1087 borrados
  1. 1 0
      .gitignore
  2. 0 1
      .kateconfig
  3. 18 1
      CHANGES.md
  4. 20 15
      README.md
  5. 0 8
      cmake/README.md
  6. 104 12
      gameplay-encoder/src/FBXSceneEncoder.cpp
  7. 1 1
      gameplay-encoder/src/Scene.h
  8. 2 2
      gameplay-luagen/gameplay-luagen.vcxproj
  9. 74 60
      gameplay-luagen/src/FunctionBinding.cpp
  10. 2 0
      gameplay-luagen/src/FunctionBinding.h
  11. 11 5
      gameplay-luagen/src/Generator.cpp
  12. 31 31
      gameplay-template/android/template.AndroidManifest.xml
  13. 3 4
      gameplay-template/android/template.build.xml
  14. 4 4
      gameplay-template/gameplay-template.vcxproj
  15. 1 1
      gameplay.doxyfile
  16. 11 7
      gameplay/.cproject
  17. 9 0
      gameplay/gameplay.vcxproj
  18. 27 0
      gameplay/gameplay.vcxproj.filters
  19. 7 1
      gameplay/gameplay.xcodeproj/project.pbxproj
  20. 1 4
      gameplay/res/shaders/lib/lighting-point.vert
  21. 9 5
      gameplay/res/shaders/textured-bumped.frag
  22. 11 7
      gameplay/res/shaders/textured-unlit.frag
  23. 4 0
      gameplay/res/shaders/textured.frag
  24. 1 1
      gameplay/src/Animation.cpp
  25. 81 55
      gameplay/src/AudioBuffer.cpp
  26. 3 2
      gameplay/src/AudioBuffer.h
  27. 1 0
      gameplay/src/Base.h
  28. 84 102
      gameplay/src/Bundle.cpp
  29. 1 1
      gameplay/src/Bundle.h
  30. 10 8
      gameplay/src/Container.cpp
  31. 8 8
      gameplay/src/Container.h
  32. 66 13
      gameplay/src/Control.cpp
  33. 37 12
      gameplay/src/Control.h
  34. 106 3
      gameplay/src/DebugNew.h
  35. 8 8
      gameplay/src/Effect.cpp
  36. 3 1
      gameplay/src/Effect.h
  37. 441 33
      gameplay/src/FileSystem.cpp
  38. 28 0
      gameplay/src/FileSystem.h
  39. 2 2
      gameplay/src/Font.cpp
  40. 9 5
      gameplay/src/Font.h
  41. 14 6
      gameplay/src/Form.cpp
  42. 5 0
      gameplay/src/Form.h
  43. 1 1
      gameplay/src/FrameBuffer.cpp
  44. 2 2
      gameplay/src/Frustum.h
  45. 12 12
      gameplay/src/Game.cpp
  46. 10 4
      gameplay/src/Game.h
  47. 6 0
      gameplay/src/Game.inl
  48. 3 3
      gameplay/src/Gamepad.h
  49. 13 28
      gameplay/src/Image.cpp
  50. 0 2
      gameplay/src/Joystick.h
  51. 10 2
      gameplay/src/Layout.cpp
  52. 3 1
      gameplay/src/MaterialParameter.h
  53. 2 3
      gameplay/src/MathUtil.cpp
  54. 2 2
      gameplay/src/Model.cpp
  55. 12 0
      gameplay/src/Node.cpp
  56. 5 5
      gameplay/src/Node.h
  57. 1 2
      gameplay/src/ParticleEmitter.cpp
  58. 0 6
      gameplay/src/Pass.h
  59. 2 2
      gameplay/src/PhysicsCollisionObject.cpp
  60. 1 1
      gameplay/src/PhysicsCollisionObject.h
  61. 15 9
      gameplay/src/PhysicsController.cpp
  62. 1 1
      gameplay/src/PhysicsController.h
  63. 4 4
      gameplay/src/PhysicsGenericConstraint.cpp
  64. 2 2
      gameplay/src/PhysicsHingeConstraint.cpp
  65. 4 4
      gameplay/src/PhysicsSocketConstraint.cpp
  66. 2 2
      gameplay/src/PhysicsSpringConstraint.cpp
  67. 1 1
      gameplay/src/PhysicsVehicle.cpp
  68. 9 0
      gameplay/src/Platform.h
  69. 194 61
      gameplay/src/PlatformAndroid.cpp
  70. 35 22
      gameplay/src/PlatformBlackBerry.cpp
  71. 270 45
      gameplay/src/PlatformLinux.cpp
  72. 188 38
      gameplay/src/PlatformMacOSX.mm
  73. 44 12
      gameplay/src/PlatformWindows.cpp
  74. 141 2
      gameplay/src/PlatformiOS.mm
  75. 48 35
      gameplay/src/Properties.cpp
  76. 5 4
      gameplay/src/Properties.h
  77. 5 5
      gameplay/src/RenderState.h
  78. 2 2
      gameplay/src/Scene.h
  79. 47 23
      gameplay/src/ScriptController.cpp
  80. 107 17
      gameplay/src/ScriptController.h
  81. 8 7
      gameplay/src/SpriteBatch.cpp
  82. 167 0
      gameplay/src/Stream.h
  83. 48 12
      gameplay/src/Terrain.cpp
  84. 2 1
      gameplay/src/Terrain.h
  85. 109 60
      gameplay/src/TerrainPatch.cpp
  86. 24 1
      gameplay/src/TerrainPatch.h
  87. 2 3
      gameplay/src/TextBox.cpp
  88. 21 89
      gameplay/src/Texture.cpp
  89. 3 2
      gameplay/src/Texture.h
  90. 3 1
      gameplay/src/Transform.h
  91. 1 1
      gameplay/src/lua/lua_AIController.cpp
  92. 3 3
      gameplay/src/lua/lua_AIMessage.cpp
  93. 1 1
      gameplay/src/lua/lua_AIState.cpp
  94. 3 3
      gameplay/src/lua/lua_AIStateMachine.cpp
  95. 6 6
      gameplay/src/lua/lua_Animation.cpp
  96. 3 3
      gameplay/src/lua/lua_AnimationClip.cpp
  97. 9 9
      gameplay/src/lua/lua_AnimationTarget.cpp
  98. 1 1
      gameplay/src/lua/lua_AudioSource.cpp
  99. 6 6
      gameplay/src/lua/lua_Bundle.cpp
  100. 171 84
      gameplay/src/lua/lua_Button.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).

+ 20 - 15
README.md

@@ -1,29 +1,34 @@
-## 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. 
+
+- [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
-
-
-
-

+ 104 - 12
gameplay-encoder/src/FBXSceneEncoder.cpp

@@ -54,27 +54,30 @@ static void loadNormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex,
  * 
  * @param fbxMesh The mesh to load from.
  * @param vertexIndex The index of the vertex within fbxMesh.
+ * @param controlPointIndex The control point index.
  * @param vertex The vertex to copy to.
  */
-static void loadTangent(FbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
+static void loadTangent(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
 
 /**
  * Loads the binormal from the mesh and adds it to the given vertex.
  * 
  * @param fbxMesh The mesh to load from.
  * @param vertexIndex The index of the vertex within fbxMesh.
+ * @param controlPointIndex The control point index.
  * @param vertex The vertex to copy to.
  */
-static void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
+static void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
 
 /**
  * Loads the vertex diffuse color from the mesh and adds it to the given vertex.
  * 
  * @param fbxMesh The mesh to load from.
  * @param vertexIndex The index of the vertex within fbxMesh.
+ * @param controlPointIndex The control point index.
  * @param vertex The vertex to copy to.
  */
-static void loadVertexColor(FbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
+static void loadVertexColor(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
 
 /**
  * Loads the blend weight and blend indices data into the vertex.
@@ -942,9 +945,9 @@ Mesh* FBXSceneEncoder::loadMesh(FbxMesh* fbxMesh)
 
             // Load other data
             loadNormal(fbxMesh, vertexIndex, controlPointIndex, &vertex);
-            loadTangent(fbxMesh, vertexIndex, &vertex);
-            loadBinormal(fbxMesh, vertexIndex, &vertex);
-            loadVertexColor(fbxMesh, vertexIndex, &vertex);
+            loadTangent(fbxMesh, vertexIndex, controlPointIndex, &vertex);
+            loadBinormal(fbxMesh, vertexIndex, controlPointIndex, &vertex);
+            loadVertexColor(fbxMesh, vertexIndex, controlPointIndex, &vertex);
 
             if (hasSkin)
             {
@@ -1234,13 +1237,41 @@ void loadNormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex
     }
 }
 
-void loadTangent(FbxMesh* fbxMesh, int vertexIndex, Vertex* vertex)
+void loadTangent(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
 {
     if (fbxMesh->GetElementTangentCount() > 0)
     {
         // Get only the first tangent
         FbxGeometryElementTangent* tangent = fbxMesh->GetElementTangent(0);
-        if (tangent->GetMappingMode() == FbxGeometryElement::eByPolygonVertex)
+        FbxGeometryElement::EMappingMode mappingMode = tangent->GetMappingMode();
+        if (mappingMode == FbxGeometryElement::eByControlPoint)
+        {
+            switch (tangent->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(controlPointIndex);
+                    vertex->hasTangent = true;
+                    vertex->tangent.x = (float)vec4[0];
+                    vertex->tangent.y = (float)vec4[1];
+                    vertex->tangent.z = (float)vec4[2];
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = tangent->GetIndexArray().GetAt(controlPointIndex);
+                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(id);
+                    vertex->hasTangent = true;
+                    vertex->tangent.x = (float)vec4[0];
+                    vertex->tangent.y = (float)vec4[1];
+                    vertex->tangent.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
         {
             switch (tangent->GetReferenceMode())
             {
@@ -1270,13 +1301,42 @@ void loadTangent(FbxMesh* fbxMesh, int vertexIndex, Vertex* vertex)
     }
 }
 
-void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, Vertex* vertex)
+void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
 {
     if (fbxMesh->GetElementBinormalCount() > 0)
     {
         // Get only the first binormal.
         FbxGeometryElementBinormal* binormal = fbxMesh->GetElementBinormal(0);
-        if (binormal->GetMappingMode() == FbxGeometryElement::eByPolygonVertex)
+        FbxGeometryElement::EMappingMode mappingMode = binormal->GetMappingMode();
+
+        if (mappingMode == FbxGeometryElement::eByControlPoint)
+        {
+            switch (binormal->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(controlPointIndex);
+                    vertex->hasBinormal = true;
+                    vertex->binormal.x = (float)vec4[0];
+                    vertex->binormal.y = (float)vec4[1];
+                    vertex->binormal.z = (float)vec4[2];
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = binormal->GetIndexArray().GetAt(controlPointIndex);
+                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(id);
+                    vertex->hasBinormal = true;
+                    vertex->binormal.x = (float)vec4[0];
+                    vertex->binormal.y = (float)vec4[1];
+                    vertex->binormal.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
         {
             switch (binormal->GetReferenceMode())
             {
@@ -1306,13 +1366,45 @@ void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, Vertex* vertex)
     }
 }
 
-void loadVertexColor(FbxMesh* fbxMesh, int vertexIndex, Vertex* vertex)
+void loadVertexColor(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
 {
     if (fbxMesh->GetElementVertexColorCount() > 0)
     {
         // Get only the first vertex color.
         FbxGeometryElementVertexColor* vertexColor = fbxMesh->GetElementVertexColor(0);
-        if (vertexColor->GetMappingMode() == FbxGeometryElement::eByPolygonVertex)
+        FbxGeometryElement::EMappingMode mappingMode = vertexColor->GetMappingMode();
+        if (mappingMode == FbxGeometryElement::eByControlPoint)
+        {
+            switch (vertexColor->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxColor color = vertexColor->GetDirectArray().GetAt(controlPointIndex);
+
+                    vertex->hasDiffuse = true;
+                    vertex->diffuse.x = (float)color.mRed;
+                    vertex->diffuse.y = (float)color.mGreen;
+                    vertex->diffuse.z = (float)color.mBlue;
+                    vertex->diffuse.w = (float)color.mAlpha;
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = vertexColor->GetIndexArray().GetAt(controlPointIndex);
+                    FbxColor color = vertexColor->GetDirectArray().GetAt(id);
+
+                    vertex->hasDiffuse = true;
+                    vertex->diffuse.x = (float)color.mRed;
+                    vertex->diffuse.y = (float)color.mGreen;
+                    vertex->diffuse.z = (float)color.mBlue;
+                    vertex->diffuse.w = (float)color.mAlpha;
+                }
+                break;
+            default:
+                break;
+            }
+        }
+        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
         {
             switch (vertexColor->GetReferenceMode())
             {

+ 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>

+ 74 - 60
gameplay-luagen/src/FunctionBinding.cpp

@@ -14,7 +14,7 @@ static inline void outputReturnValue(ostream& o, const FunctionBinding& b, int i
 static inline std::string getTypeName(const FunctionBinding::Param& param);
 
 FunctionBinding::Param::Param(FunctionBinding::Param::Type type, Kind kind, const string& info) : 
-    type(type), kind(kind), info(info), hasDefaultValue(false)
+    type(type), kind(kind), info(info), hasDefaultValue(false), levelsOfIndirection(0)
 {
 }
 
@@ -496,7 +496,10 @@ ostream& operator<<(ostream& o, const FunctionBinding::Param& param)
     o << getTypeName(param);
 
     if (param.kind == FunctionBinding::Param::KIND_POINTER)
-        o << "*";
+    {
+        for (int i = 0; i < param.levelsOfIndirection; ++i)
+            o << "*";
+    }
 
     return o;
 }
@@ -695,129 +698,140 @@ static inline void outputBindingInvocation(ostream& o, const FunctionBinding& b,
     outputReturnValue(o, b, indentLevel);
 }
 
+void writeObjectTemplateType(ostream& o, const FunctionBinding::Param& p)
+{
+    o << getTypeName(p);
+    for (int i = 0; i < p.levelsOfIndirection-1; ++i)
+        o << "*";
+}
+
+void writePointerParameter(ostream& o, const char* primitiveType, const FunctionBinding::Param& p, int paramNum, int luaParamIndex, int indentLevel)
+{
+    o << "ScriptUtil::LuaArray<";
+    writeObjectTemplateType(o, p);
+    //o << "> param" << paramNum << "Pointer = ScriptUtil::get" << primitiveType << "Pointer(" << luaParamIndex << ");\n";
+    o << "> param" << paramNum << " = ScriptUtil::get" << primitiveType << "Pointer(" << luaParamIndex << ");\n";
+    //indent(o, indentLevel);
+    //o << p << " param" << paramNum << " = (" << p << ")param" << paramNum << "Pointer;\n";
+}
+
 static inline void outputGetParam(ostream& o, const FunctionBinding::Param& p, int i, int indentLevel, bool offsetIndex, int numBindings)
 {
     indent(o, indentLevel);
     o << "// Get parameter " << i + 1 << " off the stack.\n";
 
-    switch (p.type)
-    {
-    case FunctionBinding::Param::TYPE_UNRECOGNIZED:
-        indent(o, indentLevel);
-        o << "GP_WARN(\"Attempting to get parameter " << i + 1 << " with unrecognized type " << p.info << " as an unsigned integer.\");\n";
-    case FunctionBinding::Param::TYPE_BOOL:
-    case FunctionBinding::Param::TYPE_CHAR:
-    case FunctionBinding::Param::TYPE_SHORT:
-    case FunctionBinding::Param::TYPE_INT:
-    case FunctionBinding::Param::TYPE_LONG:
-    case FunctionBinding::Param::TYPE_UCHAR:
-    case FunctionBinding::Param::TYPE_USHORT:
-    case FunctionBinding::Param::TYPE_UINT:
-    case FunctionBinding::Param::TYPE_ULONG:
-    case FunctionBinding::Param::TYPE_FLOAT:
-    case FunctionBinding::Param::TYPE_DOUBLE:
-    case FunctionBinding::Param::TYPE_STRING:
-    case FunctionBinding::Param::TYPE_ENUM:
-        indent(o, indentLevel);
-        if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::LuaArray<" << getTypeName(p) << ">";
-        else
-            o << p;
-        o << " param" << i + 1 << " = ";
-        break;
-    default:
-        // Ignore these cases.
-        break;
-    }
-
     int paramIndex = (offsetIndex) ? i + 2 : i + 1;
+
     switch (p.type)
     {
     case FunctionBinding::Param::TYPE_BOOL:
+        indent(o, indentLevel);
         if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::getBoolPointer(" << paramIndex << ");\n";
+            writePointerParameter(o, "Bool", p, i+1, paramIndex, indentLevel);
         else
-            o << "ScriptUtil::luaCheckBool(state, " << paramIndex << ");\n";
+            o << p << " param" << i+1 << " = ScriptUtil::luaCheckBool(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_CHAR:
-        o << "(char)luaL_checkint(state, " << paramIndex << ");\n";
+        indent(o, indentLevel);
+        o << p << " param" << i+1 << " = (char)luaL_checkint(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_SHORT:
+        indent(o, indentLevel);
         if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::getShortPointer(" << paramIndex << ");\n";
+            writePointerParameter(o, "Short", p, i+1, paramIndex, indentLevel);
         else
-            o << "(short)luaL_checkint(state, " << paramIndex << ");\n";
+            o << p << " param" << i+1 << " = (short)luaL_checkint(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_INT:
+        indent(o, indentLevel);
         if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::getIntPointer(" << paramIndex << ");\n";
+            writePointerParameter(o, "Int", p, i+1, paramIndex, indentLevel);
         else
-            o << "(int)luaL_checkint(state, " << paramIndex << ");\n";
+            o << p << " param" << i+1 << " = (int)luaL_checkint(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_LONG:
+        indent(o, indentLevel);
         if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::getLongPointer(" << paramIndex << ");\n";
+            writePointerParameter(o, "Long", p, i+1, paramIndex, indentLevel);
         else
-            o << "(long)luaL_checklong(state, " << paramIndex << ");\n";
+            o << p << " param" << i+1 << " = (long)luaL_checklong(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_UCHAR:
+        indent(o, indentLevel);
         if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::getUnsignedCharPointer(" << paramIndex << ");\n";
+            writePointerParameter(o, "UnsignedChar", p, i+1, paramIndex, indentLevel);
         else
-            o << "(unsigned char)luaL_checkunsigned(state, " << paramIndex << ");\n";
+            o << p << " param" << i+1 << " = (unsigned char)luaL_checkunsigned(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_USHORT:
+        indent(o, indentLevel);
         if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::getUnsignedShortPointer(" << paramIndex << ");\n";
+            writePointerParameter(o, "UnsignedShort", p, i+1, paramIndex, indentLevel);
         else
-            o << "(unsigned short)luaL_checkunsigned(state, " << paramIndex << ");\n";
+            o << p << " param" << i+1 << " = (unsigned short)luaL_checkunsigned(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_UINT:
+        indent(o, indentLevel);
         if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::getUnsignedIntPointer(" << paramIndex << ");\n";
+            writePointerParameter(o, "UnsignedInt", p, i+1, paramIndex, indentLevel);
         else
-            o << "(unsigned int)luaL_checkunsigned(state, " << paramIndex << ");\n";
+            o << p << " param" << i+1 << " = (unsigned int)luaL_checkunsigned(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_ULONG:
+        indent(o, indentLevel);
         if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::getUnsignedLongPointer(" << paramIndex << ");\n";
+            writePointerParameter(o, "UnsignedLong", p, i+1, paramIndex, indentLevel);
         else
-            o << "(unsigned long)luaL_checkunsigned(state, " << paramIndex << ");\n";
+            o << p << " param" << i+1 << " = (unsigned long)luaL_checkunsigned(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_FLOAT:
+        indent(o, indentLevel);
         if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::getFloatPointer(" << paramIndex << ");\n";
+            writePointerParameter(o, "Float", p, i+1, paramIndex, indentLevel);
         else
-            o << "(float)luaL_checknumber(state, " << paramIndex << ");\n";
+            o << p << " param" << i+1 << " = (float)luaL_checknumber(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_DOUBLE:
+        indent(o, indentLevel);
         if (p.kind == FunctionBinding::Param::KIND_POINTER)
-            o << "ScriptUtil::getDoublePointer(" << paramIndex << ");\n";
+            writePointerParameter(o, "Double", p, i+1, paramIndex, indentLevel);
         else
-            o << "(double)luaL_checknumber(state, " << paramIndex << ");\n";
+            o << p << " param" << i+1 << " = (double)luaL_checknumber(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_STRING:
-        o << "ScriptUtil::getString(" << paramIndex << ", " << ((p.info == "string") ? "true" : "false") << ");\n";
+        indent(o, indentLevel);
+        o << p << " param" << i+1 << " = ScriptUtil::getString(" << paramIndex << ", " << ((p.info == "string") ? "true" : "false") << ");\n";
         break;
     case FunctionBinding::Param::TYPE_ENUM:
-        o << "(" << p << ")lua_enumFromString_" << Generator::getInstance()->getUniqueNameFromRef(p.info) << "(luaL_checkstring(state, " << paramIndex << "));\n";
+        indent(o, indentLevel);
+        o << p << " param" << i+1 << " = (" << p << ")lua_enumFromString_" << Generator::getInstance()->getUniqueNameFromRef(p.info) << "(luaL_checkstring(state, " << paramIndex << "));\n";
         break;
     case FunctionBinding::Param::TYPE_UNRECOGNIZED:
         // Attempt to retrieve the unrecognized type as an unsigned integer.
-        o << "(" << p.info << ")luaL_checkunsigned(state, " << paramIndex << ");\n";
+        indent(o, indentLevel);
+        o << "GP_WARN(\"Attempting to get parameter " << i + 1 << " with unrecognized type " << p.info << " as an unsigned integer.\");\n";
+        indent(o, indentLevel);
+        o << p << " param" << i+1 << " = (" << p.info << ")luaL_checkunsigned(state, " << paramIndex << ");\n";
         break;
     case FunctionBinding::Param::TYPE_OBJECT:
         {
             indent(o, indentLevel);
             o << "bool param" << i + 1 << "Valid;\n";
             indent(o, indentLevel);
-            o << "ScriptUtil::LuaArray<" << getTypeName(p) << ">";
-            o << " param" << i + 1 << " = ";
-            o << "ScriptUtil::getObjectPointer<";
-            o << Generator::getInstance()->getIdentifier(p.info) << ">(" << paramIndex;
+            o << "ScriptUtil::LuaArray<";
+            writeObjectTemplateType(o, p);
+            //o << "> param" << i+1 << "Pointer = ScriptUtil::getObjectPointer<";
+            o << "> param" << i+1 << " = ScriptUtil::getObjectPointer<";
+            writeObjectTemplateType(o, p);
+            o << ">(" << paramIndex;
             o << ", \"" << Generator::getInstance()->getUniqueNameFromRef(p.info) << "\", ";
             o << ((p.kind != FunctionBinding::Param::KIND_POINTER) ? "true" : "false") << ", &param" << i + 1 << "Valid);\n";
             indent(o, indentLevel);
+            //writeObjectTemplateType(o, p);
+            //o << "* param" << i+1 << " = (";
+            //writeObjectTemplateType(o, p);
+            //o << "*)param" << i+1 << "Pointer;\n";
+            //indent(o, indentLevel);
             o << "if (!param" << i + 1 << "Valid)\n";
             if (numBindings > 1)
             {

+ 2 - 0
gameplay-luagen/src/FunctionBinding.h

@@ -99,6 +99,8 @@ struct FunctionBinding
         string info;
         /** Whether the parameter has a default value. */
         bool hasDefaultValue;
+        /** For pointer types, the number of levels of indirection */
+        int levelsOfIndirection;
     };
 
     /**

+ 11 - 5
gameplay-luagen/src/Generator.cpp

@@ -9,7 +9,7 @@ static bool __printOperatorWarning = false;
 
 // Utility functions (local to this file).
 static string trim(const string& str);
-static string stripTypeQualifiers(const string& typeStr, FunctionBinding::Param::Kind& kind);
+static string stripTypeQualifiers(const string& typeStr, FunctionBinding::Param::Kind& kind, int& levelsOfIndirection);
 static inline bool isWantedFileNormal(const string& s);
 static inline bool isNamespaceFile(const string& s);
 static inline bool isGeneratedBindingFile(const string& s);
@@ -970,6 +970,7 @@ FunctionBinding::Param Generator::getParam(XMLElement* e, bool isVariable, strin
         // Get the type string without const or reference qualifiers (and trim whitespace).
         string refId = "";
         string typeStr = "";
+        int levelsOfIndirection = 0;
         FunctionBinding::Param::Kind kind;
         {
             // Attempt to process the type as reference (i.e. class, struct, enum, typedef, etc.) type.
@@ -989,7 +990,7 @@ FunctionBinding::Param Generator::getParam(XMLElement* e, bool isVariable, strin
                 node = node->NextSibling();
             }
 
-            typeStr = stripTypeQualifiers(typeStr, kind);
+            typeStr = stripTypeQualifiers(typeStr, kind, levelsOfIndirection);
         }
 
         if (typeStr == "void")
@@ -1062,6 +1063,7 @@ FunctionBinding::Param Generator::getParam(XMLElement* e, bool isVariable, strin
         {
             p = FunctionBinding::Param(FunctionBinding::Param::TYPE_UNRECOGNIZED, kind, (refId.size() > 0) ? refId : typeStr);
         }
+        p.levelsOfIndirection = levelsOfIndirection;
 
         // Check if the type is a pointer declared with square brackets (i.e. float x[4]).
         XMLElement* arrayElement = NULL;
@@ -1075,6 +1077,7 @@ FunctionBinding::Param Generator::getParam(XMLElement* e, bool isVariable, strin
             if (i != arrayString.npos && k != arrayString.npos)
             {
                 p.kind = FunctionBinding::Param::KIND_POINTER;
+                p.levelsOfIndirection = 1;
                 if (i != k - 1)
                     p.info = arrayString.substr(i + 1, k - (i + 1));
             }
@@ -1805,8 +1808,10 @@ static string trim(const string& str)
     return s;
 }
 
-static string stripTypeQualifiers(const string& typeStr, FunctionBinding::Param::Kind& kind)
+static string stripTypeQualifiers(const string& typeStr, FunctionBinding::Param::Kind& kind, int& levelsOfIndirection)
 {
+    levelsOfIndirection = 0;
+
     string type = typeStr;
     kind = FunctionBinding::Param::KIND_VALUE;
 
@@ -1819,11 +1824,12 @@ static string stripTypeQualifiers(const string& typeStr, FunctionBinding::Param:
     }
 
     // Check if the type is a pointer.
-    i = type.find("*");
-    if (i != type.npos)
+    while ((i = type.find("*")) != std::string::npos)
     {
         kind = FunctionBinding::Param::KIND_POINTER;
         type.erase(type.begin() + i);
+        ++levelsOfIndirection;
+        int j = 0;
     }
 
     // Ignore const qualifiers.

+ 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.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"/>

+ 9 - 0
gameplay/gameplay.vcxproj

@@ -225,6 +225,7 @@
     <ClCompile Include="src\lua\lua_RenderState.cpp" />
     <ClCompile Include="src\lua\lua_RenderStateAutoBinding.cpp" />
     <ClCompile Include="src\lua\lua_RenderStateBlend.cpp" />
+    <ClCompile Include="src\lua\lua_RenderStateDepthFunction.cpp" />
     <ClCompile Include="src\lua\lua_RenderStateStateBlock.cpp" />
     <ClCompile Include="src\lua\lua_RenderTarget.cpp" />
     <ClCompile Include="src\lua\lua_Scene.cpp" />
@@ -235,6 +236,9 @@
     <ClCompile Include="src\lua\lua_Slider.cpp" />
     <ClCompile Include="src\lua\lua_SpriteBatch.cpp" />
     <ClCompile Include="src\lua\lua_Technique.cpp" />
+    <ClCompile Include="src\lua\lua_Terrain.cpp" />
+    <ClCompile Include="src\lua\lua_TerrainFlags.cpp" />
+    <ClCompile Include="src\lua\lua_TerrainHeightField.cpp" />
     <ClCompile Include="src\lua\lua_TextBox.cpp" />
     <ClCompile Include="src\lua\lua_Texture.cpp" />
     <ClCompile Include="src\lua\lua_TextureFilter.cpp" />
@@ -496,6 +500,7 @@
     <ClInclude Include="src\lua\lua_RenderState.h" />
     <ClInclude Include="src\lua\lua_RenderStateAutoBinding.h" />
     <ClInclude Include="src\lua\lua_RenderStateBlend.h" />
+    <ClInclude Include="src\lua\lua_RenderStateDepthFunction.h" />
     <ClInclude Include="src\lua\lua_RenderStateStateBlock.h" />
     <ClInclude Include="src\lua\lua_RenderTarget.h" />
     <ClInclude Include="src\lua\lua_Scene.h" />
@@ -506,6 +511,9 @@
     <ClInclude Include="src\lua\lua_Slider.h" />
     <ClInclude Include="src\lua\lua_SpriteBatch.h" />
     <ClInclude Include="src\lua\lua_Technique.h" />
+    <ClInclude Include="src\lua\lua_Terrain.h" />
+    <ClInclude Include="src\lua\lua_TerrainFlags.h" />
+    <ClInclude Include="src\lua\lua_TerrainHeightField.h" />
     <ClInclude Include="src\lua\lua_TextBox.h" />
     <ClInclude Include="src\lua\lua_Texture.h" />
     <ClInclude Include="src\lua\lua_TextureFilter.h" />
@@ -575,6 +583,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\Terrain.h" />
     <ClInclude Include="src\TerrainPatch.h" />

+ 27 - 0
gameplay/gameplay.vcxproj.filters

@@ -825,6 +825,18 @@
     <ClCompile Include="src\TerrainPatch.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\lua\lua_TerrainFlags.cpp">
+      <Filter>lua</Filter>
+    </ClCompile>
+    <ClCompile Include="src\lua\lua_TerrainHeightField.cpp">
+      <Filter>lua</Filter>
+    </ClCompile>
+    <ClCompile Include="src\lua\lua_Terrain.cpp">
+      <Filter>lua</Filter>
+    </ClCompile>
+    <ClCompile Include="src\lua\lua_RenderStateDepthFunction.cpp">
+      <Filter>lua</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -1628,12 +1640,27 @@
     <ClInclude Include="src\lua\lua_LoggerLevel.h">
       <Filter>lua</Filter>
     </ClInclude>
+    <ClInclude Include="src\Stream.h">
+      <Filter>src</Filter>
+    </ClInclude>
     <ClInclude Include="src\Terrain.h">
       <Filter>src</Filter>
     </ClInclude>
     <ClInclude Include="src\TerrainPatch.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\lua\lua_TerrainFlags.h">
+      <Filter>lua</Filter>
+    </ClInclude>
+    <ClInclude Include="src\lua\lua_TerrainHeightField.h">
+      <Filter>lua</Filter>
+    </ClInclude>
+    <ClInclude Include="src\lua\lua_Terrain.h">
+      <Filter>lua</Filter>
+    </ClInclude>
+    <ClInclude Include="src\lua\lua_RenderStateDepthFunction.h">
+      <Filter>lua</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;
 		};

+ 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
 }

+ 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

+ 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.
      */ 

+ 66 - 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), _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,7 @@ float Control::getHeight() const
 void Control::setAlignment(Alignment alignment)
 {
     _alignment = alignment;
+    _dirty = true;
 }
 
 Control::Alignment Control::getAlignment() const
@@ -243,6 +244,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 +618,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 +718,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 +856,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 +1042,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 +1164,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 +1204,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;
     }

+ 37 - 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.
@@ -986,6 +1006,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)
@@ -462,7 +462,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)
         {

+ 3 - 1
gameplay/src/Effect.h

@@ -205,8 +205,10 @@ public:
      * @param uniform The uniform to set.
      * @param values The sampler array to set.
      * @param count The number of elements in the array.
+     *
+     * @script{ignore}
      */
-    void setValue(Uniform* uniform, const Texture::Sampler** values, unsigned int count = 1);
+    void setValue(Uniform* uniform, const Texture::Sampler** values, unsigned int count);
 
     /**
      * Binds this effect to make it the currently active effect for the rendering system.

+ 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.
      *

+ 2 - 2
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
@@ -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)

+ 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;

+ 14 - 6
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())
     {
@@ -710,13 +719,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 +739,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 - 1
gameplay/src/FrameBuffer.cpp

@@ -120,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);

+ 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
 {

+ 10 - 2
gameplay/src/Layout.cpp

@@ -13,7 +13,7 @@ void Layout::align(Control* control, const Container* container)
 
     if (control->_alignment != Control::ALIGN_TOP_LEFT ||
         control->_autoWidth || control->_autoHeight)
-    {
+    {
         Rectangle controlBounds = control->getBounds();
         const Theme::Margin& controlMargin = control->getMargin();
         const Rectangle& containerBounds = container->getBounds();
@@ -35,7 +35,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 +54,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 +68,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);
     }

+ 3 - 1
gameplay/src/MaterialParameter.h

@@ -127,8 +127,10 @@ public:
 
     /**
      * Sets the value of this parameter to the specified texture sampler array.
+     *
+     * @script{ignore}
      */
-    void setValue(const Texture::Sampler** samplers, unsigned int count = 1);
+    void setValue(const Texture::Sampler** samplers, unsigned int count);
 
     /**
      * Loads a texture sampler from the specified path and sets it as the value of this parameter.

+ 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));
     }
 }

+ 2 - 2
gameplay/src/Model.cpp

@@ -253,7 +253,7 @@ void Model::setNode(Node* node)
     }
 }
 
-bool drawWireframe(Mesh* mesh)
+static bool drawWireframe(Mesh* mesh)
 {
     switch (mesh->getPrimitiveType())
     {
@@ -283,7 +283,7 @@ bool drawWireframe(Mesh* mesh)
     }
 }
 
-bool drawWireframe(MeshPart* part)
+static bool drawWireframe(MeshPart* part)
 {
     unsigned int indexCount = part->getIndexCount();
     unsigned int indexSize = 0;

+ 12 - 0
gameplay/src/Node.cpp

@@ -339,6 +339,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;
 
@@ -522,11 +522,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.
@@ -534,12 +534,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;
@@ -259,8 +259,8 @@ void PhysicsCollisionObject::PhysicsMotionState::setCenterOfMassOffset(const Vec
 }
 
 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

@@ -346,7 +346,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

+ 35 - 22
gameplay/src/PlatformBlackBerry.cpp

@@ -34,6 +34,7 @@ static screen_context_t __screenContext;
 static screen_window_t __screenWindow;
 static screen_event_t __screenEvent;
 static int __screenWindowSize[2];
+static bool __screenFullscreen = false;
 static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
 static EGLContext __eglContext = EGL_NO_CONTEXT;
 static EGLSurface __eglSurface = EGL_NO_SURFACE;
@@ -775,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);
@@ -1069,14 +1070,18 @@ int Platform::enterMessagePump()
                     switch (state)
                     {
                     case NAVIGATOR_WINDOW_FULLSCREEN:
+                        if (!__screenFullscreen)
+                            __screenFullscreen = true;
                         _game->resume();
                         suspended = false;
                         break;
                     case NAVIGATOR_WINDOW_THUMBNAIL:
                     case NAVIGATOR_WINDOW_INVISIBLE:
-                        if (!suspended)
+                        if (__screenFullscreen && !suspended)
+                        {
                             _game->pause();
-                        suspended = true;
+                            suspended = true;
+                        }
                         break;
                     }
                     break;
@@ -1419,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

+ 270 - 45
gameplay/src/PlatformLinux.cpp

@@ -30,6 +30,7 @@ static Window   __window;
 static int __windowSize[2];
 static GLXContext __context;
 static Window __attachToWindow;
+static Atom __atomWmDeleteWindow;
 
 namespace gameplay
 {
@@ -355,6 +356,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 +499,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 +563,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 +591,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 +602,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 +645,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 +685,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 +717,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 +727,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 +751,7 @@ int Platform::enterMessagePump()
                 }
                 break;
 
-            case Expose: 
+            case Expose:
                 {
                     updateWindowSize();
                 }
@@ -574,9 +759,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 +785,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 +800,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 +822,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 +839,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;
@@ -688,7 +892,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 +908,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()
 {
@@ -929,6 +1139,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

+ 188 - 38
gameplay/src/PlatformMacOSX.mm

@@ -582,7 +582,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);
@@ -961,6 +961,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 +1099,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 +1314,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 +1461,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);
+            }
         }
     }
 
@@ -1816,25 +1948,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 +2013,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.
@@ -343,11 +343,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
@@ -358,7 +358,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
@@ -470,7 +470,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

@@ -375,76 +375,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

+ 48 - 12
gameplay/src/Terrain.cpp

@@ -20,8 +20,14 @@ namespace gameplay
 //
 #define DEFAULT_TERRAIN_HEIGHT_RATIO 0.3f
 
-// Forward declaration of helper functions
+/**
+ * @script{ignore}
+ */
 std::string getExtension(const char* filename);
+
+/**
+ * @script{ignore}
+ */
 float getDefaultHeight(unsigned int width, unsigned int height);
 
 Terrain::Terrain() :
@@ -225,6 +231,8 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
     terrain->_heightfield = heightfield;
     terrain->_localScale = scale;
 
+    BoundingBox& bounds = terrain->_boundingBox;
+
     if (normalMapPath)
         terrain->_normalMap = Texture::Sampler::create(normalMapPath, true);
 
@@ -245,7 +253,12 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
             x1 = x;
             x2 = std::min(x1 + patchSize, width-1);
 
-            terrain->_patches.push_back(TerrainPatch::create(terrain, row, column, heightfield->getArray(), width, height, x1, z1, x2, z2, -halfWidth, -halfHeight, maxStep, skirtScale));
+            // Create this patch
+            TerrainPatch* patch = TerrainPatch::create(terrain, row, column, heightfield->getArray(), width, height, x1, z1, x2, z2, -halfWidth, -halfHeight, maxStep, skirtScale);
+            terrain->_patches.push_back(patch);
+
+            // Append the new patch's local bounds to the terrain local bounds
+            bounds.merge(patch->getBoundingBox(false));
         }
     }
 
@@ -408,6 +421,31 @@ unsigned int Terrain::getVisiblePatchCount() const
     return visibleCount;
 }
 
+unsigned int Terrain::getTriangleCount() const
+{
+    unsigned int triangleCount = 0;
+    for (size_t i = 0, count = _patches.size(); i < count; ++i)
+    {
+        triangleCount += _patches[i]->getTriangleCount();
+    }
+    return triangleCount;
+}
+
+unsigned int Terrain::getVisibleTriangleCount() const
+{
+    unsigned int triangleCount = 0;
+    for (size_t i = 0, count = _patches.size(); i < count; ++i)
+    {
+        triangleCount += _patches[i]->getVisibleTriangleCount();
+    }
+    return triangleCount;
+}
+
+const BoundingBox& Terrain::getBoundingBox() const
+{
+    return _boundingBox;
+}
+
 void Terrain::draw(bool wireframe)
 {
     for (size_t i = 0, count = _patches.size(); i < count; ++i)
@@ -493,8 +531,16 @@ Terrain::HeightField* Terrain::HeightField::create(unsigned int columns, unsigne
     return new HeightField(columns, rows);
 }
 
+/**
+ * @script{ignore}
+ */
 float normalizedHeightPacked(float r, float g, float b)
 {
+    // This formula is intended for 24-bit packed heightmap images (that are generated
+    // with gameplay-encoder. However, it is also compatible with normal grayscale 
+    // heightmap images, with an error of approximately 0.4%. This can be seen by
+    // setting r=g=b=x and comparing the grayscale height expression to the packed
+    // height expression: the error is 2^-8 + 2^-16 which is just under 0.4%.
     return (256.0f*r + g + 0.00390625f*b) / 65536.0f;
 }
 
@@ -548,16 +594,6 @@ Terrain::HeightField* Terrain::HeightField::create(const char* imagePath, unsign
         {
             for (unsigned int x = 0, w = image->getWidth(); x < w; ++x)
             {
-                // Originally in GamePlay this was normalizedHeightGrayscale which yielded
-                // only 8-bit precision. This has been replaced by normalizedHeightPacked (with a
-                // corresponding change in gameplay-encoder).
-                //
-                // BACKWARD COMPATIBILITY
-                // In grayscale images where r=g=b this will maintain some degree of compatibility,
-                // to within 0.4%. This can be seen by setting r=g=b=x and comparing the grayscale
-                // height expression to the packed height expression: the error is 2^-8 + 2^-16
-                // which is just under 0.4%.
-                //
                 idx = (y*w + x) * pixelSize;
                 heights[i++] = minHeight + normalizedHeightPacked(data[idx], data[idx + 1], data[idx + 2]) * heightScale;
             }

+ 2 - 1
gameplay/src/Terrain.h

@@ -337,7 +337,7 @@ public:
     unsigned int getVisibleTriangleCount() const;
 
     /**
-     * Gets the local bounding box for this terrain.
+     * Returns the local bounding box for this terrain.
      *
      * @return The local bounding box for the terrain.
      */
@@ -397,6 +397,7 @@ private:
     unsigned int _flags;
     mutable Matrix _worldMatrix;
     mutable bool _worldMatrixDirty;
+    BoundingBox _boundingBox;
 
 };
 

+ 109 - 60
gameplay/src/TerrainPatch.cpp

@@ -8,8 +8,14 @@
 namespace gameplay
 {
 
-// Forward declarations
+/**
+ * @script{ignore}
+ */
 float calculateHeight(float* heights, unsigned int width, unsigned int height, unsigned int x, unsigned int z);
+
+/**
+ * @script{ignore}
+ */
 template <class T> T clamp(T value, T min, T max) { return value < min ? min : (value > max ? max : value); }
 
 TerrainPatch::TerrainPatch() :
@@ -52,6 +58,18 @@ TerrainPatch* TerrainPatch::create(Terrain* terrain,
         patch->addLOD(heights, width, height, x1, z1, x2, z2, xOffset, zOffset, step, verticalSkirtSize);
     }
 
+    // Set our bounding box using the base LOD mesh
+    BoundingBox& bounds = patch->_boundingBox;
+    bounds.set(patch->_levels[0]->model->getMesh()->getBoundingBox());
+
+    // Apply the terrain's local scale to our bounds
+    const Vector3& localScale = terrain->_localScale;
+    if (!localScale.isOne())
+    {
+        bounds.min.set(bounds.min.x * localScale.x, bounds.min.y * localScale.y, bounds.min.z * localScale.z);
+        bounds.max.set(bounds.max.x * localScale.x, bounds.max.y * localScale.y, bounds.max.z * localScale.z);
+    }
+
     return patch;
 }
 
@@ -324,21 +342,6 @@ void TerrainPatch::deleteLayer(Layer* layer)
     SAFE_DELETE(layer);
 }
 
-bool TerrainPatch::isVisible() const
-{
-    Scene* scene = _terrain->_node ? _terrain->_node->getScene() : NULL;
-    Camera* camera = scene ? scene->getActiveCamera() : NULL;
-    if (!camera)
-        return false;
-
-    // Get the world-space bounding box for the base patch
-    BoundingBox box = _levels[0]->model->getMesh()->getBoundingBox();
-    box.transform(_terrain->getWorldMatrix());
-
-    // If the box does not intersect the view frustum, cull it
-    return camera->getFrustum().intersects(box);
-}
-
 int TerrainPatch::addSampler(const char* path)
 {
     // TODO: Support shared samplers stored in Terrain class for layers that span all patches
@@ -508,59 +511,105 @@ void TerrainPatch::draw(bool wireframe)
     if (!camera)
         return;
 
-    // Get the local bounding box for the patch (at the base LOD) and
-    // transform it by the terrain's world matrix.
-    BoundingBox box = _levels[0]->model->getMesh()->getBoundingBox();
-    box.transform(_terrain->getWorldMatrix());
+    // Get our world-space bounding box
+    BoundingBox bounds = getBoundingBox(true);
 
     // If the box does not intersect the view frustum, cull it
-    if (_terrain->isFlagSet(Terrain::ENABLE_FRUSTUM_CULLING) && !camera->getFrustum().intersects(box))
+    if (_terrain->isFlagSet(Terrain::ENABLE_FRUSTUM_CULLING) && !camera->getFrustum().intersects(bounds))
         return;
 
     if (!updateMaterial())
         return;
 
-    size_t lod = 0;
-    if (_terrain->isFlagSet(Terrain::ENABLE_LEVEL_OF_DETAIL) && _levels.size() > 1)
-    {
-        // Compute LOD to use based on very simple distance metric
-        Game* game = Game::getInstance();
-        Rectangle vp(0, 0, game->getWidth(), game->getHeight());
-        Vector3 corners[8];
-        Vector2 min(FLT_MAX, FLT_MAX);
-        Vector2 max(-FLT_MAX, -FLT_MAX);
-        box.getCorners(corners);
-        for (unsigned int i = 0; i < 8; ++i)
-        {
-            const Vector3& corner = corners[i];
-            float x, y;
-            camera->project(vp, corners[i], &x, &y);
-            if (x < min.x)
-                min.x = x;
-            if (y < min.y)
-                min.y = y;
-            if (x > max.x)
-                max.x = x;
-            if (y > max.y)
-                max.y = y;
-        }
-        float area = (max.x - min.x) * (max.y - min.y);
-        float screenArea = game->getWidth() * game->getHeight() / 10.0f;
-        float error = screenArea / area;
-
-        // Level LOD based on distance from camera
-        size_t maxLod = _levels.size()-1;
-        lod = (size_t)error;
-        lod = std::max(lod, (size_t)0);
-        lod = std::min(lod, maxLod);
-    }
+    // Compute the LOD level from the camera's perspective
+    size_t lod = computeLOD(camera, bounds);
+
+    // Draw the model for the current LOD
+    _levels[lod]->model->draw(wireframe);
+}
+
+bool TerrainPatch::isVisible() const
+{
+    Scene* scene = _terrain->_node ? _terrain->_node->getScene() : NULL;
+    Camera* camera = scene ? scene->getActiveCamera() : NULL;
+    if (!camera)
+        return false;
+
+    // Does the current camera view frustum intersect our world bounds?
+    return camera->getFrustum().intersects(getBoundingBox(true));
+}
+
+unsigned int TerrainPatch::getTriangleCount() const
+{
+    // Patches are made up of a single mesh part using triangle strips
+    return _levels[0]->model->getMesh()->getPart(0)->getIndexCount() - 2;
+}
+
+unsigned int TerrainPatch::getVisibleTriangleCount() const
+{
+    Scene* scene = _terrain->_node ? _terrain->_node->getScene() : NULL;
+    Camera* camera = scene ? scene->getActiveCamera() : NULL;
+    if (!camera)
+        return 0;
+
+    // Does the current camera intersect this patch at all?
+    BoundingBox bounds = getBoundingBox(true);
+    if (!camera->getFrustum().intersects(bounds))
+        return 0;
+
+    // Return the triangle count of the LOD level depending on the camera
+    size_t lod = computeLOD(camera, bounds);
+    return _levels[0]->model->getMesh()->getPart(0)->getIndexCount() - 2;
+}
 
-    // Get the LOD to be drawn
-    Level* level = _levels[lod];
-    Model* model = level->model;
+BoundingBox TerrainPatch::getBoundingBox(bool worldSpace) const
+{
+    if (!worldSpace)
+        return _boundingBox;
+
+    // Apply a world-space transformation to our bounding box
+    BoundingBox bounds(_boundingBox);
+    bounds.transform(_terrain->getWorldMatrix());
+    return bounds;
+}
 
-    // Draw patch geometry
-    model->draw(wireframe);
+size_t TerrainPatch::computeLOD(Camera* camera, const BoundingBox& worldBounds) const
+{
+    if (!_terrain->isFlagSet(Terrain::ENABLE_LEVEL_OF_DETAIL) || _levels.size() == 0)
+        return 0; // base level
+
+    // Compute LOD to use based on very simple distance metric.
+    // TODO: Optimize this.
+    Game* game = Game::getInstance();
+    Rectangle vp(0, 0, game->getWidth(), game->getHeight());
+    Vector3 corners[8];
+    Vector2 min(FLT_MAX, FLT_MAX);
+    Vector2 max(-FLT_MAX, -FLT_MAX);
+    worldBounds.getCorners(corners);
+    for (unsigned int i = 0; i < 8; ++i)
+    {
+        const Vector3& corner = corners[i];
+        float x, y;
+        camera->project(vp, corners[i], &x, &y);
+        if (x < min.x)
+            min.x = x;
+        if (y < min.y)
+            min.y = y;
+        if (x > max.x)
+            max.x = x;
+        if (y > max.y)
+            max.y = y;
+    }
+    float area = (max.x - min.x) * (max.y - min.y);
+    float screenArea = game->getWidth() * game->getHeight() / 10.0f;
+    float error = screenArea / area;
+
+    // Level LOD based on distance from camera
+    size_t maxLod = _levels.size()-1;
+    size_t lod = (size_t)error;
+    lod = std::max(lod, (size_t)0);
+    lod = std::min(lod, maxLod);
+    return lod;
 }
 
 float calculateHeight(float* heights, unsigned int width, unsigned int height, unsigned int x, unsigned int z)

+ 24 - 1
gameplay/src/TerrainPatch.h

@@ -2,6 +2,7 @@
 #define TERRAINPATCH_H_
 
 #include "Model.h"
+#include "Camera.h"
 
 namespace gameplay
 {
@@ -12,6 +13,8 @@ class Terrain;
  * Represents a single patch for a Terrain.
  *
  * This is an internal class used exclusively by Terrain.
+ *
+ * @script{ignore}
  */
 class TerrainPatch
 {
@@ -108,6 +111,16 @@ private:
      */
     bool isVisible() const;
 
+    /**
+     * Returns the triangle count of the base LOD level of this terrain patch.
+     */
+    unsigned int getTriangleCount() const;
+
+    /**
+     * Returns the currently visible triangle count, taking the current LOD into account.
+     */
+    unsigned int getVisibleTriangleCount() const;
+
     /**
      * Draws the terrain patch.
      */
@@ -118,9 +131,18 @@ private:
      */
     bool updateMaterial();
 
+    /**
+     * Computes the current LOD for this patch, from the viewpoint of the specified camera.
+     */
+    size_t computeLOD(Camera* camera, const BoundingBox& worldBounds) const;
+
+    /**
+     * Returns the local bounding box for this patch, at the base LOD level.
+     */
+    BoundingBox getBoundingBox(bool worldSpace) const;
+
     Terrain* _terrain;
     std::vector<Level*> _levels;
-    unsigned int _lod;
     unsigned int _row;
     unsigned int _column;
     std::set<Layer*, LayerCompare> _layers;
@@ -130,6 +152,7 @@ private:
     std::vector<int> _blendIndex;
     std::vector<int> _blendChannel;
     bool _materialDirty;
+    BoundingBox _boundingBox;
 
 };
 

+ 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
 {
@@ -292,9 +293,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/lua/lua_AIController.cpp

@@ -43,7 +43,7 @@ int lua_AIController_findAgent(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 AIController* instance = getInstance(state);
                 void* returnPtr = (void*)instance->findAgent(param1);

+ 3 - 3
gameplay/src/lua/lua_AIMessage.cpp

@@ -680,7 +680,7 @@ int lua_AIMessage_setString(lua_State* state)
                 unsigned int param1 = (unsigned int)luaL_checkunsigned(state, 2);
 
                 // Get parameter 2 off the stack.
-                ScriptUtil::LuaArray<const char> param2 = ScriptUtil::getString(3, false);
+                const char* param2 = ScriptUtil::getString(3, false);
 
                 AIMessage* instance = getInstance(state);
                 instance->setString(param1, param2);
@@ -721,10 +721,10 @@ int lua_AIMessage_static_create(lua_State* state)
                 unsigned int param1 = (unsigned int)luaL_checkunsigned(state, 1);
 
                 // Get parameter 2 off the stack.
-                ScriptUtil::LuaArray<const char> param2 = ScriptUtil::getString(2, false);
+                const char* param2 = ScriptUtil::getString(2, false);
 
                 // Get parameter 3 off the stack.
-                ScriptUtil::LuaArray<const char> param3 = ScriptUtil::getString(3, false);
+                const char* param3 = ScriptUtil::getString(3, false);
 
                 // Get parameter 4 off the stack.
                 unsigned int param4 = (unsigned int)luaL_checkunsigned(state, 4);

+ 1 - 1
gameplay/src/lua/lua_AIState.cpp

@@ -349,7 +349,7 @@ int lua_AIState_static_create(lua_State* state)
             if ((lua_type(state, 1) == LUA_TSTRING || lua_type(state, 1) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(1, false);
+                const char* param1 = ScriptUtil::getString(1, false);
 
                 void* returnPtr = (void*)AIState::create(param1);
                 if (returnPtr)

+ 3 - 3
gameplay/src/lua/lua_AIStateMachine.cpp

@@ -51,7 +51,7 @@ int lua_AIStateMachine_addState(lua_State* state)
                     (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     AIStateMachine* instance = getInstance(state);
                     void* returnPtr = (void*)instance->addState(param1);
@@ -206,7 +206,7 @@ int lua_AIStateMachine_getState(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 AIStateMachine* instance = getInstance(state);
                 void* returnPtr = (void*)instance->getState(param1);
@@ -298,7 +298,7 @@ int lua_AIStateMachine_setState(lua_State* state)
                     (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     AIStateMachine* instance = getInstance(state);
                     void* returnPtr = (void*)instance->setState(param1);

+ 6 - 6
gameplay/src/lua/lua_Animation.cpp

@@ -132,7 +132,7 @@ int lua_Animation_createClip(lua_State* state)
                 lua_type(state, 4) == LUA_TNUMBER)
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 unsigned long param2 = (unsigned long)luaL_checkunsigned(state, 3);
@@ -186,7 +186,7 @@ int lua_Animation_createClips(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Animation* instance = getInstance(state);
                 instance->createClips(param1);
@@ -253,7 +253,7 @@ int lua_Animation_getClip(lua_State* state)
                     (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     Animation* instance = getInstance(state);
                     void* returnPtr = (void*)instance->getClip(param1);
@@ -483,7 +483,7 @@ int lua_Animation_pause(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Animation* instance = getInstance(state);
                 instance->pause(param1);
@@ -533,7 +533,7 @@ int lua_Animation_play(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Animation* instance = getInstance(state);
                 instance->play(param1);
@@ -615,7 +615,7 @@ int lua_Animation_stop(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Animation* instance = getInstance(state);
                 instance->stop(param1);

+ 3 - 3
gameplay/src/lua/lua_AnimationClip.cpp

@@ -134,7 +134,7 @@ int lua_AnimationClip_addBeginListener(lua_State* state)
                     (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     AnimationClip* instance = getInstance(state);
                     instance->addBeginListener(param1);
@@ -191,7 +191,7 @@ int lua_AnimationClip_addEndListener(lua_State* state)
                     (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     AnimationClip* instance = getInstance(state);
                     instance->addEndListener(param1);
@@ -253,7 +253,7 @@ int lua_AnimationClip_addListener(lua_State* state)
                     lua_type(state, 3) == LUA_TNUMBER)
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     // Get parameter 2 off the stack.
                     unsigned long param2 = (unsigned long)luaL_checkunsigned(state, 3);

+ 9 - 9
gameplay/src/lua/lua_AnimationTarget.cpp

@@ -55,10 +55,10 @@ int lua_AnimationTarget_createAnimation(lua_State* state)
                     (lua_type(state, 3) == LUA_TSTRING || lua_type(state, 3) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     // Get parameter 2 off the stack.
-                    ScriptUtil::LuaArray<const char> param2 = ScriptUtil::getString(3, false);
+                    const char* param2 = ScriptUtil::getString(3, false);
 
                     AnimationTarget* instance = getInstance(state);
                     void* returnPtr = (void*)instance->createAnimation(param1, param2);
@@ -86,7 +86,7 @@ int lua_AnimationTarget_createAnimation(lua_State* state)
                     (lua_type(state, 3) == LUA_TUSERDATA || lua_type(state, 3) == LUA_TTABLE || lua_type(state, 3) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     // Get parameter 2 off the stack.
                     bool param2Valid;
@@ -130,7 +130,7 @@ int lua_AnimationTarget_createAnimation(lua_State* state)
                     (lua_type(state, 7) == LUA_TSTRING || lua_type(state, 7) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     // Get parameter 2 off the stack.
                     int param2 = (int)luaL_checkint(state, 3);
@@ -185,7 +185,7 @@ int lua_AnimationTarget_createAnimation(lua_State* state)
                     (lua_type(state, 9) == LUA_TSTRING || lua_type(state, 9) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     // Get parameter 2 off the stack.
                     int param2 = (int)luaL_checkint(state, 3);
@@ -260,7 +260,7 @@ int lua_AnimationTarget_createAnimationFromBy(lua_State* state)
                 lua_type(state, 7) == LUA_TNUMBER)
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 int param2 = (int)luaL_checkint(state, 3);
@@ -328,7 +328,7 @@ int lua_AnimationTarget_createAnimationFromTo(lua_State* state)
                 lua_type(state, 7) == LUA_TNUMBER)
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 int param2 = (int)luaL_checkint(state, 3);
@@ -405,7 +405,7 @@ int lua_AnimationTarget_destroyAnimation(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 AnimationTarget* instance = getInstance(state);
                 instance->destroyAnimation(param1);
@@ -467,7 +467,7 @@ int lua_AnimationTarget_getAnimation(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 AnimationTarget* instance = getInstance(state);
                 void* returnPtr = (void*)instance->getAnimation(param1);

+ 1 - 1
gameplay/src/lua/lua_AudioSource.cpp

@@ -747,7 +747,7 @@ int lua_AudioSource_static_create(lua_State* state)
                 if ((lua_type(state, 1) == LUA_TSTRING || lua_type(state, 1) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(1, false);
+                    const char* param1 = ScriptUtil::getString(1, false);
 
                     void* returnPtr = (void*)AudioSource::create(param1);
                     if (returnPtr)

+ 6 - 6
gameplay/src/lua/lua_Bundle.cpp

@@ -130,7 +130,7 @@ int lua_Bundle_contains(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Bundle* instance = getInstance(state);
                 bool result = instance->contains(param1);
@@ -278,7 +278,7 @@ int lua_Bundle_loadFont(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Bundle* instance = getInstance(state);
                 void* returnPtr = (void*)instance->loadFont(param1);
@@ -326,7 +326,7 @@ int lua_Bundle_loadMesh(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Bundle* instance = getInstance(state);
                 void* returnPtr = (void*)instance->loadMesh(param1);
@@ -374,7 +374,7 @@ int lua_Bundle_loadNode(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Bundle* instance = getInstance(state);
                 void* returnPtr = (void*)instance->loadNode(param1);
@@ -448,7 +448,7 @@ int lua_Bundle_loadScene(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Bundle* instance = getInstance(state);
                 void* returnPtr = (void*)instance->loadScene(param1);
@@ -527,7 +527,7 @@ int lua_Bundle_static_create(lua_State* state)
             if ((lua_type(state, 1) == LUA_TSTRING || lua_type(state, 1) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(1, false);
+                const char* param1 = ScriptUtil::getString(1, false);
 
                 void* returnPtr = (void*)Bundle::create(param1);
                 if (returnPtr)

+ 171 - 84
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}
@@ -305,10 +307,10 @@ int lua_Button_createAnimation(lua_State* state)
                     (lua_type(state, 3) == LUA_TSTRING || lua_type(state, 3) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     // Get parameter 2 off the stack.
-                    ScriptUtil::LuaArray<const char> param2 = ScriptUtil::getString(3, false);
+                    const char* param2 = ScriptUtil::getString(3, false);
 
                     Button* instance = getInstance(state);
                     void* returnPtr = (void*)instance->createAnimation(param1, param2);
@@ -336,7 +338,7 @@ int lua_Button_createAnimation(lua_State* state)
                     (lua_type(state, 3) == LUA_TUSERDATA || lua_type(state, 3) == LUA_TTABLE || lua_type(state, 3) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     // Get parameter 2 off the stack.
                     bool param2Valid;
@@ -380,7 +382,7 @@ int lua_Button_createAnimation(lua_State* state)
                     (lua_type(state, 7) == LUA_TSTRING || lua_type(state, 7) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     // Get parameter 2 off the stack.
                     int param2 = (int)luaL_checkint(state, 3);
@@ -435,7 +437,7 @@ int lua_Button_createAnimation(lua_State* state)
                     (lua_type(state, 9) == LUA_TSTRING || lua_type(state, 9) == LUA_TNIL))
                 {
                     // Get parameter 1 off the stack.
-                    ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                    const char* param1 = ScriptUtil::getString(2, false);
 
                     // Get parameter 2 off the stack.
                     int param2 = (int)luaL_checkint(state, 3);
@@ -510,7 +512,7 @@ int lua_Button_createAnimationFromBy(lua_State* state)
                 lua_type(state, 7) == LUA_TNUMBER)
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 int param2 = (int)luaL_checkint(state, 3);
@@ -578,7 +580,7 @@ int lua_Button_createAnimationFromTo(lua_State* state)
                 lua_type(state, 7) == LUA_TNUMBER)
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 int param2 = (int)luaL_checkint(state, 3);
@@ -655,7 +657,7 @@ int lua_Button_destroyAnimation(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Button* instance = getInstance(state);
                 instance->destroyAnimation(param1);
@@ -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.
@@ -816,7 +754,7 @@ int lua_Button_getAnimation(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Button* instance = getInstance(state);
                 void* returnPtr = (void*)instance->getAnimation(param1);
@@ -1640,7 +1578,7 @@ int lua_Button_getImageColor(lua_State* state)
                 (lua_type(state, 3) == LUA_TSTRING || lua_type(state, 3) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 Control::State param2 = (Control::State)lua_enumFromString_ControlState(luaL_checkstring(state, 3));
@@ -1692,7 +1630,7 @@ int lua_Button_getImageRegion(lua_State* state)
                 (lua_type(state, 3) == LUA_TSTRING || lua_type(state, 3) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 Control::State param2 = (Control::State)lua_enumFromString_ControlState(luaL_checkstring(state, 3));
@@ -1744,7 +1682,7 @@ int lua_Button_getImageUVs(lua_State* state)
                 (lua_type(state, 3) == LUA_TSTRING || lua_type(state, 3) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 Control::State param2 = (Control::State)lua_enumFromString_ControlState(luaL_checkstring(state, 3));
@@ -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.
@@ -3343,7 +3394,7 @@ int lua_Button_setImageColor(lua_State* state)
                 (lua_type(state, 3) == LUA_TUSERDATA || lua_type(state, 3) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 bool param2Valid;
@@ -3372,7 +3423,7 @@ int lua_Button_setImageColor(lua_State* state)
                 lua_type(state, 4) == LUA_TNUMBER)
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 bool param2Valid;
@@ -3421,7 +3472,7 @@ int lua_Button_setImageRegion(lua_State* state)
                 (lua_type(state, 3) == LUA_TUSERDATA || lua_type(state, 3) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 bool param2Valid;
@@ -3450,7 +3501,7 @@ int lua_Button_setImageRegion(lua_State* state)
                 lua_type(state, 4) == LUA_TNUMBER)
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 // Get parameter 2 off the stack.
                 bool param2Valid;
@@ -3950,7 +4001,7 @@ int lua_Button_setText(lua_State* state)
                 (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(2, false);
+                const char* param1 = ScriptUtil::getString(2, false);
 
                 Button* instance = getInstance(state);
                 instance->setText(param1);
@@ -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.
@@ -4363,7 +4450,7 @@ int lua_Button_static_create(lua_State* state)
                 (lua_type(state, 2) == LUA_TUSERDATA || lua_type(state, 2) == LUA_TTABLE || lua_type(state, 2) == LUA_TNIL))
             {
                 // Get parameter 1 off the stack.
-                ScriptUtil::LuaArray<const char> param1 = ScriptUtil::getString(1, false);
+                const char* param1 = ScriptUtil::getString(1, false);
 
                 // Get parameter 2 off the stack.
                 bool param2Valid;

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio