Parcourir la source

Character sample updates.

Steve Grenier il y a 14 ans
Parent
commit
838b03705c
87 fichiers modifiés avec 6023 ajouts et 1370 suppressions
  1. 60 1
      .gitignore
  2. 16 12
      README.md
  3. 1 0
      gameplay-encoder/gameplay-encoder.vcxproj
  4. 3 0
      gameplay-encoder/gameplay-encoder.vcxproj.filters
  5. 84 0
      gameplay-encoder/gameplay-encoder.xcodeproj/xcshareddata/xcschemes/gameplay-encoder.xcscheme
  6. 81 37
      gameplay-encoder/src/Quaternion.cpp
  7. 95 39
      gameplay-encoder/src/Quaternion.h
  8. 19 0
      gameplay-encoder/src/Quaternion.inl
  9. 31 0
      gameplay-newproject.bat
  10. 32 0
      gameplay-template/TEMPLATE_PROJECT-ios.plist
  11. 0 0
      gameplay-template/TEMPLATE_PROJECT-macos.plist
  12. 1 0
      gameplay-template/android/jni/Application.mk
  13. 62 0
      gameplay-template/android/jni/template.Android.mk
  14. 4 0
      gameplay-template/android/res/values/template.strings.xml
  15. 32 0
      gameplay-template/android/template.AndroidManifest.xml
  16. 92 0
      gameplay-template/android/template.build.xml
  17. 210 35
      gameplay-template/gameplay-template.xcodeproj/project.pbxproj
  18. 2 2
      gameplay-template/res/box.material
  19. 5 5
      gameplay-template/src/TemplateGame.cpp
  20. 1 1
      gameplay-template/src/TemplateGame.h
  21. 2 2
      gameplay.doxyfile
  22. 8 0
      gameplay.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  23. 14 0
      gameplay/android/AndroidManifest.xml
  24. 85 0
      gameplay/android/build.xml
  25. 25 0
      gameplay/android/jni/Android.mk
  26. 2 0
      gameplay/android/jni/Application.mk
  27. 5 0
      gameplay/gameplay.vcxproj
  28. 15 0
      gameplay/gameplay.vcxproj.filters
  29. 578 45
      gameplay/gameplay.xcodeproj/project.pbxproj
  30. 57 0
      gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-MacOSX.xcscheme
  31. 57 0
      gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-iOS.xcscheme
  32. 4 1
      gameplay/res/shaders/colored-specular.fsh
  33. 4 1
      gameplay/res/shaders/colored.fsh
  34. 4 1
      gameplay/res/shaders/diffuse-specular.fsh
  35. 4 1
      gameplay/res/shaders/diffuse.fsh
  36. 108 204
      gameplay/src/AnimationClip.cpp
  37. 61 28
      gameplay/src/AnimationClip.h
  38. 12 1
      gameplay/src/AnimationController.cpp
  39. 2 1
      gameplay/src/AnimationController.h
  40. 1 2
      gameplay/src/AnimationTarget.cpp
  41. 4 3
      gameplay/src/AnimationTarget.h
  42. 7 9
      gameplay/src/AnimationValue.cpp
  43. 2 3
      gameplay/src/AnimationValue.h
  44. 65 1
      gameplay/src/AudioBuffer.cpp
  45. 14 1
      gameplay/src/AudioBuffer.h
  46. 141 1
      gameplay/src/AudioController.cpp
  47. 9 0
      gameplay/src/AudioController.h
  48. 238 3
      gameplay/src/AudioSource.cpp
  49. 21 0
      gameplay/src/AudioSource.h
  50. 78 15
      gameplay/src/Base.h
  51. 3 3
      gameplay/src/BoundingBox.cpp
  52. 14 12
      gameplay/src/Curve.cpp
  53. 11 6
      gameplay/src/Curve.h
  54. 1 1
      gameplay/src/Curve.inl
  55. 146 3
      gameplay/src/FileSystem.cpp
  56. 10 0
      gameplay/src/FileSystem.h
  57. 1 1
      gameplay/src/Font.cpp
  58. 10 0
      gameplay/src/Game.cpp
  59. 34 2
      gameplay/src/Game.h
  60. 5 0
      gameplay/src/Game.inl
  61. 158 156
      gameplay/src/Keyboard.h
  62. 98 18
      gameplay/src/MaterialParameter.cpp
  63. 5 1
      gameplay/src/MaterialParameter.h
  64. 39 0
      gameplay/src/Mouse.h
  65. 22 2
      gameplay/src/Package.cpp
  66. 12 0
      gameplay/src/Package.h
  67. 2 1
      gameplay/src/PhysicsRigidBody.h
  68. 7 0
      gameplay/src/Platform.h
  69. 843 0
      gameplay/src/PlatformAndroid.cpp
  70. 603 599
      gameplay/src/PlatformMacOS.mm
  71. 170 14
      gameplay/src/PlatformQNX.cpp
  72. 62 22
      gameplay/src/PlatformWin32.cpp
  73. 817 0
      gameplay/src/PlatformiOS.mm
  74. 138 7
      gameplay/src/Properties.cpp
  75. 12 3
      gameplay/src/Properties.h
  76. 42 28
      gameplay/src/Quaternion.cpp
  77. 30 2
      gameplay/src/Quaternion.h
  78. 8 8
      gameplay/src/SpriteBatch.cpp
  79. 1 1
      gameplay/src/Texture.cpp
  80. 134 16
      gameplay/src/Transform.cpp
  81. 29 3
      gameplay/src/Transform.h
  82. 1 1
      gameplay/src/VertexAttributeBinding.cpp
  83. 33 0
      gameplay/src/gameplay-main-android.cpp
  84. 20 0
      gameplay/src/gameplay-main-ios.mm
  85. 3 1
      gameplay/src/gameplay-main-macos.mm
  86. 39 0
      gameplay/src/gameplay.dox
  87. 2 4
      gameplay/src/gameplay.h

+ 60 - 1
.gitignore

@@ -1,6 +1,10 @@
 *.suo
 *.sdf
-*.opensdf
+*.opensdf
+.DS_Store*
+ehthumbs.db
+Icon?
+Thumbs.db
 /.metadata
 /169.254.0.1
 /ipch
@@ -77,3 +81,58 @@
 /gameplay-samples/sample03-character/Device-Release
 /gameplay-samples/sample03-character/res/shaders
 /gameplay-samples/sample03-character/sample03-character.xcodeproj/xcuserdata
+
+/gameplay-android/obj
+/gameplay-android/NUL
+/gameplay-samples/sample00-mesh/android/src
+/gameplay-samples/sample00-mesh/android/assets
+/gameplay-samples/sample00-mesh/android/bin
+/gameplay-samples/sample00-mesh/android/gen
+/gameplay-samples/sample00-mesh/android/libs
+/gameplay-samples/sample00-mesh/android/obj
+/gameplay-android/local.properties
+/gameplay-android/proguard.cfg
+/gameplay-android/project.properties
+/gameplay-samples/sample00-mesh/android/NUL
+/gameplay-samples/sample00-mesh/android/local.properties
+/gameplay-samples/sample00-mesh/android/proguard.cfg
+/gameplay-samples/sample00-mesh/android/project.properties
+/gameplay-samples/sample01-longboard/android/NUL
+/gameplay-samples/sample01-longboard/android/src
+/gameplay-samples/sample01-longboard/android/assets
+/gameplay-samples/sample01-longboard/android/bin
+/gameplay-samples/sample01-longboard/android/gen
+/gameplay-samples/sample01-longboard/android/libs
+/gameplay-samples/sample01-longboard/android/obj
+/gameplay-samples/sample01-longboard/android/project.properties
+/gameplay-samples/sample01-longboard/android/local.properties
+/gameplay-samples/sample02-spaceship/android/project.properties
+/gameplay-samples/sample02-spaceship/android/assets
+/gameplay-samples/sample02-spaceship/android/bin
+/gameplay-samples/sample02-spaceship/android/gen
+/gameplay-samples/sample02-spaceship/android/libs
+/gameplay-samples/sample02-spaceship/android/obj
+/gameplay-samples/sample02-spaceship/android/src
+/gameplay-samples/sample02-spaceship/android/proguard.cfg
+/gameplay-samples/sample02-spaceship/android/local.properties
+/gameplay-samples/sample03-character/android/project.properties
+/gameplay-samples/sample03-character/android/proguard.cfg
+/gameplay-samples/sample03-character/android/local.properties
+/gameplay-samples/sample01-longboard/android/proguard.cfg
+/gameplay-samples/sample02-spaceship/android/NUL
+/gameplay-samples/sample01-longboard/android/proguard.cfg
+/gameplay-samples/sample03-character/android/src
+/gameplay-samples/sample03-character/android/assets
+/gameplay-samples/sample03-character/android/bin
+/gameplay-samples/sample03-character/android/gen
+/gameplay-samples/sample03-character/android/libs
+/gameplay-samples/sample03-character/android/obj
+/gameplay-samples/sample03-character/android/NUL
+/gameplay-samples/sample01-longboard/NUL
+
+/gameplay/android/NUL
+/gameplay/android/proguard.cfg
+/gameplay/android/local.properties
+/gameplay/android/project.properties
+/gameplay/android/obj
+

+ 16 - 12
README.md

@@ -1,18 +1,23 @@
-## GamePlay v1.1.0
+## GamePlay v1.2.0
 GamePlay is a open-source, cross-platform 3D native gaming framework making it easy to learn and write mobile and desktop games. 
 
-## Supported Platforms
-- BlackBerry PlayBook 1.0/2.0 (using BlackBerry Native SDK 1.0/2.0)
-- Apple MacOS X (using Apple XCode 4.0)
-- Microsoft Windows 7 (using Microsoft Visual Studio 2010 Pro/Express)
-	* Requires OpenAL 1.1 (http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx)
+## Supported Mobile Platforms
+- BlackBerry PlayBook 1/2 (using BlackBerry Native SDK 2)
+- Google Android 4 (using Google Android NDK 7)
+- Apple iOS 4/5 (using Apple XCode 4)
+
+## Supported Desktop Platforms
+- Apple MacOS X (using Apple XCode 4)
+- Microsoft Windows XP/7 (using Microsoft Visual Studio 2010)
+    * Requires [Creative OpenAL 1.1] (http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx)
 
 ## Roadmap for 'next' branch
-- UI Forms with Themed Overlays.
-- Improvements to Lighting.
-- Developer Guide.
-- More Samples and Tutorials.
-- Apple iOS 5 Support.
+- UI Overlays
+- Improvements to Lighting
+- More Samples and Tutorials
+
+## Licence
+The project is open sourced under the Apache 2.0 license.
 
 ## Bug Reporting and Feature Requests
 If you find a bug in a Sample, or have an enhancement request, simply file an 
@@ -27,4 +32,3 @@ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIG
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
 OTHER DEALINGS IN THE SOFTWARE.
-

+ 1 - 0
gameplay-encoder/gameplay-encoder.vcxproj

@@ -98,6 +98,7 @@
     <ClInclude Include="src\VertexElement.h" />
   </ItemGroup>
   <ItemGroup>
+    <None Include="src\Quaternion.inl" />
     <None Include="src\Vector2.inl" />
     <None Include="src\Vector3.inl" />
     <None Include="src\Vector4.inl" />

+ 3 - 0
gameplay-encoder/gameplay-encoder.vcxproj.filters

@@ -263,6 +263,9 @@
     <None Include="src\Vector4.inl">
       <Filter>src</Filter>
     </None>
+    <None Include="src\Quaternion.inl">
+      <Filter>src</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="src">

+ 84 - 0
gameplay-encoder/gameplay-encoder.xcodeproj/xcshareddata/xcschemes/gameplay-encoder.xcscheme

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "42475CE5147208A000610A6A"
+               BuildableName = "gameplay-encoder"
+               BlueprintName = "gameplay-encoder"
+               ReferencedContainer = "container:gameplay-encoder.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <Testables>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "42475CE5147208A000610A6A"
+            BuildableName = "gameplay-encoder"
+            BlueprintName = "gameplay-encoder"
+            ReferencedContainer = "container:gameplay-encoder.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Debug"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "42475CE5147208A000610A6A"
+            BuildableName = "gameplay-encoder"
+            BlueprintName = "gameplay-encoder"
+            ReferencedContainer = "container:gameplay-encoder.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "42475CE5147208A000610A6A"
+            BuildableName = "gameplay-encoder"
+            BlueprintName = "gameplay-encoder"
+            ReferencedContainer = "container:gameplay-encoder.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 81 - 37
gameplay-encoder/src/Quaternion.cpp

@@ -19,6 +19,10 @@ Quaternion::Quaternion(float* array)
     set(array);
 }
 
+Quaternion::Quaternion(const Vector3& axis, float angle)
+{
+    set(axis, angle);
+}
 
 Quaternion::Quaternion(const Quaternion& copy)
 {
@@ -31,14 +35,14 @@ Quaternion::~Quaternion()
 
 const Quaternion& Quaternion::identity()
 {
-    static Quaternion* value = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
-    return *value;
+    static Quaternion value(0.0f, 0.0f, 0.0f, 1.0f);
+    return value;
 }
 
 const Quaternion& Quaternion::zero()
 {
-    static Quaternion* value = new Quaternion(0.0f, 0.0f, 0.0f, 0.0f);
-    return *value;
+    static Quaternion value(0.0f, 0.0f, 0.0f, 0.0f);
+    return value;
 }
 
 bool Quaternion::isIdentity() const
@@ -51,13 +55,6 @@ bool Quaternion::isZero() const
     return x == 0.0f && y == 0.0f && z == 0.0f && z == 0.0f;
 }
 
-void Quaternion::createFromRotationMatrix(const Matrix& m, Quaternion* dst)
-{
-    assert(dst);
-
-    m.decompose(NULL, dst, NULL);
-}
-
 void Quaternion::createFromAxisAngle(const Vector3& axis, float angle, Quaternion* dst)
 {
     assert(dst);
@@ -154,12 +151,12 @@ void Quaternion::normalize(Quaternion* dst) const
 
     float n = x * x + y * y + z * z + w * w;
 
-    // already normalized
+    // Already normalized.
     if (n == 1.0f)
         return;
 
     n = sqrt(n);
-    // too close to zero
+    // Too close to zero.
     if (n < 0.000001f)
         return;
 
@@ -188,6 +185,11 @@ void Quaternion::set(float* array)
     w = array[3];
 }
 
+void Quaternion::set(const Vector3& axis, float angle)
+{
+    Quaternion::createFromAxisAngle(axis, angle, this);
+}
+
 void Quaternion::set(const Quaternion& q)
 {
     this->x = q.x;
@@ -223,6 +225,17 @@ void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quate
     assert(dst);
     assert(!(t < 0.0f || t > 1.0f));
 
+    if (t == 0.0f)
+    {
+        memcpy(dst, &q1, sizeof(float) * 4);
+        return;
+    }
+    else if (t == 1.0f)
+    {
+        memcpy(dst, &q2, sizeof(float) * 4);
+        return;
+    }
+
     float t1 = 1.0f - t;
 
     dst->x = t1 * q1.x + t * q2.x;
@@ -232,21 +245,65 @@ void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quate
 }
 
 void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
+{
+    slerp(q1.x, q1.y, q1.z, q1.w, q2.x, q2.y, q2.z, q2.w, t, &dst->x, &dst->y, &dst->z, &dst->w);
+}
+
+void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
+{
+    assert(dst);
+    assert(!(t < 0.0f || t > 1.0f));
+
+    Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
+    Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
+
+    slerpForSquad(q1, q2, t, &dstQ);
+    slerpForSquad(s1, s2, t, &dstS);
+    slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
+}
+
+void Quaternion::slerp(float q1x, float q1y, float q1z, float q1w, float q2x, float q2y, float q2z, float q2w, float t, float* dstx, float* dsty, float* dstz, float* dstw)
 {
     // Fast slerp implementation by kwhatmough:
     // It contains no division operations, no trig, no inverse trig
     // and no sqrt. Not only does this code tolerate small constraint
     // errors in the input quaternions, it actually corrects for them.
-    assert(dst);
+    assert(dstx && dsty && dstz && dstw);
     assert(!(t < 0.0f || t > 1.0f));
 
+    if (t == 0.0f)
+    {
+        *dstx = q1x;
+        *dsty = q1y;
+        *dstz = q1z;
+        *dstw = q1w;
+        return;
+    }
+    else if (t == 1.0f)
+    {
+        *dstx = q2x;
+        *dsty = q2y;
+        *dstz = q2z;
+        *dstw = q2w;
+        return;
+    }
+
+    if (q1x == q2x && q1y == q2y && q1z == q2z && q1w == q2w)
+    {
+        *dstx = q1x;
+        *dsty = q1y;
+        *dstz = q1z;
+        *dstw = q1w;
+        return;
+    }
+
     float halfY, alpha, beta;
     float u, f1, f2a, f2b;
     float ratio1, ratio2;
     float halfSecHalfTheta, versHalfTheta;
     float sqNotU, sqU;
 
-    float cosTheta = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
+    float cosTheta = q1w * q2w + q1x * q2x + q1y * q2y + q1z * q2z;
 
     // As usual in all slerp implementations, we fold theta.
     alpha = cosTheta >= 0 ? 1.0f : -1.0f;
@@ -287,34 +344,21 @@ void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quat
     beta = f1 + f2b;
 
     // Apply final coefficients to a and b as usual.
-    float w = alpha * q1.w + beta * q2.w;
-    float x = alpha * q1.x + beta * q2.x;
-    float y = alpha * q1.y + beta * q2.y;
-    float z = alpha * q1.z + beta * q2.z;
+    float w = alpha * q1w + beta * q2w;
+    float x = alpha * q1x + beta * q2x;
+    float y = alpha * q1y + beta * q2y;
+    float z = alpha * q1z + beta * q2z;
 
     // This final adjustment to the quaternion's length corrects for
-    // any small constraint error in the inputs q1 and q2. But as you
+    // any small constraint error in the inputs q1 and q2 But as you
     // can see, it comes at the cost of 9 additional multiplication
     // operations. If this error-correcting feature is not required,
     // the following code may be removed.
     f1 = 1.5f - 0.5f * (w * w + x * x + y * y + z * z);
-    dst->w = w * f1;
-    dst->x = x * f1;
-    dst->y = y * f1;
-    dst->z = z * f1;
-}
-
-void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
-{
-    assert(dst);
-    assert(!(t < 0.0f || t > 1.0f));
-
-    Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
-    Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
-
-    slerpForSquad(q1, q2, t, &dstQ);
-    slerpForSquad(s1, s2, t, &dstS);
-    slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
+    *dstw = w * f1;
+    *dstx = x * f1;
+    *dsty = y * f1;
+    *dstz = z * f1;
 }
 
 void Quaternion::slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)

+ 95 - 39
gameplay-encoder/src/Quaternion.h

@@ -20,41 +20,46 @@ class Matrix;
  * lerp (linear interpolation): the interpolation curve gives a straight line in quaternion space. It is simple and fast to compute. The only problem is that it does not provide constant angular velocity. Note that a constant velocity is not necessarily a requirement for a curve;
  * slerp (spherical linear interpolation): the interpolation curve forms a great arc on the quaternion unit sphere. Slerp provides constant angular velocity;
  * squad (spherical spline interpolation): interpolating between a series of rotations using slerp leads to the following problems:
- * the curve is not smooth at the control points;
- * the angular velocity is not constant;
- * the angular velocity is not continuous at the control points.
+ * - the curve is not smooth at the control points;
+ * - the angular velocity is not constant;
+ * - the angular velocity is not continuous at the control points.
  *
  * Since squad is continuously differentiable, it remedies the first and third problems mentioned above.
  * The slerp method provided here is intended for interpolation of principal rotations. It treats +q and -q as the same principal rotation and is at liberty to use the negative of either input. The resulting path is always the shorter arc.
  *
  * The lerp method provided here interpolates strictly in quaternion space. Note that the resulting path may pass through the origin if interpolating between a quaternion and its exact negative.
  *
- * As an example, consider the following quaternions
+ * As an example, consider the following quaternions:
  *
  * q1 = (0.6, 0.8, 0.0, 0.0),
  * q2 = (0.0, 0.6, 0.8, 0.0),
  * q3 = (0.6, 0.0, 0.8, 0.0), and
  * q4 = (-0.8, 0.0, -0.6, 0.0).
  * For the point p = (1.0, 1.0, 1.0), the following figures show the trajectories of p using lerp, slerp, and squad.
- *
- * @image "http://www.blackberry.com/developers/docs/7.0.0api/net/rim/device/api/math/doc-files/LERP.PNG"
- * @image "http://www.blackberry.com/developers/docs/7.0.0api/net/rim/device/api/math/doc-files/SLERP.PNG"
- * @image "http://www.blackberry.com/developers/docs/7.0.0api/net/rim/device/api/math/doc-files/SQUAD.PNG"
  */
 class Quaternion
 {
+    friend class Curve;
+
 public:
 
-    /** The x-value of the quaternion's vector component. */
+    /**
+     * The x-value of the quaternion's vector component.
+     */
     float x;
-    /** The y-value of the quaternion's vector component. */
+    /**
+     * The y-value of the quaternion's vector component.
+     */
     float y;
-    /** The z-value of the quaternion's vector component. */
+    /**
+     * The z-value of the quaternion's vector component.
+     */
     float z;
-    /** The scalar component of the quaternion. */
+    /**
+     * The scalar component of the quaternion.
+     */
     float w;
 
-
     /**
      * Constructs a quaternion initialized to (0, 0, 0, 1).
      */
@@ -73,14 +78,22 @@ public:
     /**
      * Constructs a new quaternion from the values in the specified array.
      *
-     * @param array
+     * @param array The values for the new quaternion.
      */
     Quaternion(float* array);
 
+    /**
+     * Constructs a quaternion equal to the rotation from the specified axis and angle.
+     *
+     * @param axis A vector describing the axis of rotation.
+     * @param angle The angle of rotation (in radians).
+     */
+    Quaternion(const Vector3& axis, float angle);
+
     /**
      * Constructs a new quaternion that is a copy of the specified one.
      *
-     * @param copy The quaternion to copy
+     * @param copy The quaternion to copy.
      */
     Quaternion(const Quaternion& copy);
 
@@ -92,7 +105,7 @@ public:
     /**
      * Returns the identity quaternion.
      *
-     * @return The quaternion.
+     * @return The identity quaternion.
      */
     static const Quaternion& identity();
 
@@ -104,34 +117,25 @@ public:
     static const Quaternion& zero();
 
     /**
-     * Determines if this quaterion is equal to the identity quaternion.
+     * Determines if this quaternion is equal to the identity quaternion.
      *
-     * @return true if the identity, false otherwise.
+     * @return true if it is the identity quaternion, false otherwise.
      */
     bool isIdentity() const;
 
     /**
-     * Determines if this quaterion is all zeros.
+     * Determines if this quaternion is all zeros.
      *
-     * @return true if zeros, false otherwise.
+     * @return true if this quaternion is all zeros, false otherwise.
      */
     bool isZero() const;
 
-    /**
-     * Create a quaternion equal to the rotational part of the specified matrix
-     * and stores the result in dst.
-     *
-     * @param m The matrix.
-     * @param dst A quaternion to store the conjugate in.
-     */
-    static void createFromRotationMatrix(const Matrix& m, Quaternion* dst);
-
     /**
      * Creates this quaternion equal to the rotation from the specified axis and angle
-     * and store the result in dst.
+     * and stores the result in dst.
      *
      * @param axis A vector describing the axis of rotation.
-     * @param angle The angle of rotation, in radians.
+     * @param angle The angle of rotation (in radians).
      * @param dst A quaternion to store the conjugate in.
      */
     static void createFromAxisAngle(const Vector3& axis, float angle, Quaternion* dst);
@@ -226,6 +230,14 @@ public:
      */
     void set(float* array);
 
+    /**
+     * Sets the quaternion equal to the rotation from the specified axis and angle.
+     * 
+     * @param axis The axis of rotation.
+     * @param angle The angle of rotation (in radians).
+     */
+    void set(const Vector3& axis, float angle);
+
     /**
      * Sets the elements of this quaternion to a copy of the specified quaternion.
      *
@@ -256,10 +268,10 @@ public:
      * @param q1 The first quaternion.
      * @param q2 The second quaternion.
      * @param t The interpolation coefficient.
-     * @param dst A quaternion to store the result in
+     * @param dst A quaternion to store the result in.
      */
     static void lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
-
+    
     /**
      * Interpolates between two quaternions using spherical linear interpolation.
      *
@@ -273,10 +285,10 @@ public:
      * @param q1 The first quaternion.
      * @param q2 The second quaternion.
      * @param t The interpolation coefficient.
-     * @param dst A quaternion to store the result in
+     * @param dst A quaternion to store the result in.
      */
     static void slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
-
+    
     /**
      * Interpolates over a series of quaternions using spherical spline interpolation.
      *
@@ -289,20 +301,64 @@ public:
      *
      * @param q1 The first quaternion.
      * @param q2 The second quaternion.
-     * @param s1 The first control point
-     * @param s2 The second control point
+     * @param s1 The first control point.
+     * @param s2 The second control point.
      * @param t The interpolation coefficient.
-     * @param dst A quaternion to store the result in
+     * @param dst A quaternion to store the result in.
      */
     static void squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst);
 
+    /**
+     * Calculates the quaternion product of this quaternion with the given quaternion.
+     * 
+     * Note: this does not modify this quaternion.
+     * 
+     * @param q The quaternion to multiply.
+     * @return The quaternion product.
+     */
+    inline Quaternion operator*(const Quaternion& q) const;
+
+    /**
+     * Multiplies this quaternion with the given quaternion.
+     * 
+     * @param q The quaternion to multiply.
+     * @return This quaternion, after the multiplication occurs.
+     */
+    inline Quaternion& operator*=(const Quaternion& q);
 
 private:
 
-    static void slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
+    /**
+     * Interpolates between two quaternions using spherical linear interpolation.
+     *
+     * Spherical linear interpolation provides smooth transitions between different
+     * orientations and is often useful for animating models or cameras in 3D.
+     *
+     * Note: For accurate interpolation, the input quaternions must be at (or close to) unit length.
+     * This method does not automatically normalize the input quaternions, so it is up to the
+     * caller to ensure they call normalize beforehand, if necessary.
+     *
+     * @param q1x The x component of the first quaternion.
+     * @param q1y The y component of the first quaternion.
+     * @param q1z The z component of the first quaternion.
+     * @param q1w The w component of the first quaternion.
+     * @param q2x The x component of the second quaternion.
+     * @param q2y The y component of the second quaternion.
+     * @param q2z The z component of the second quaternion.
+     * @param q2w The w component of the second quaternion.
+     * @param t The interpolation coefficient.
+     * @param dstx A pointer to store the x component of the slerp in.
+     * @param dsty A pointer to store the y component of the slerp in.
+     * @param dstz A pointer to store the z component of the slerp in.
+     * @param dstw A pointer to store the w component of the slerp in.
+     */
+    static void slerp(float q1x, float q1y, float q1z, float q1w, float q2x, float q2y, float q2z, float q2w, float t, float* dstx, float* dsty, float* dstz, float* dstw);
 
+    static void slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
 };
 
 }
 
+#include "Quaternion.inl"
+
 #endif

+ 19 - 0
gameplay-encoder/src/Quaternion.inl

@@ -0,0 +1,19 @@
+#include "Quaternion.h"
+
+namespace gameplay
+{
+
+inline Quaternion Quaternion::operator*(const Quaternion& q) const
+{
+    Quaternion result(*this);
+    result.multiply(q);
+    return result;
+}
+
+inline Quaternion& Quaternion::operator*=(const Quaternion& q)
+{
+    multiply(q);
+    return *this;
+}
+
+}

+ 31 - 0
gameplay-newproject.bat

@@ -197,6 +197,37 @@ call:replace %projPath%\bar-descriptor.xml TEMPLATE_UUID "%uuid%"
 call:replace %projPath%\bar-descriptor.xml TEMPLATE_AUTHOR "%author%"
 call:replace %projPath%\bar-descriptor.xml TEMPLATE_DESCRIPTION "%desc%"
 
+REM Copy Android NDK project files
+mkdir %projPath%\android
+
+copy gameplay-template\android\template.AndroidManifest.xml %projPath%\android\AndroidManifest.xml
+call:replace %projPath%\android\AndroidManifest.xml TEMPLATE_PROJECT "%projName%"
+call:replace %projPath%\android\AndroidManifest.xml TEMPLATE_UUID "%uuid%"
+
+copy gameplay-template\android\template.build.xml %projPath%\android\build.xml
+call:replace %projPath%\android\build.xml TEMPLATE_PROJECT "%projName%"
+
+copy gameplay-template\android\template.project %projPath%\android\.project
+call:replace %projPath%\android\.project TEMPLATE_PROJECT "%projName%"
+
+copy gameplay-template\android\template.classpath %projPath%\android\.classpath
+
+mkdir %projPath%\android\jni
+
+copy gameplay-template\android\jni\Application.mk %projPath%\android\jni\Application.mk
+
+copy gameplay-template\android\jni\template.Android.mk %projPath%\android\jni\Android.mk
+call:replace %projPath%\android\jni\Android.mk TemplateGame "%className%"
+call:replace %projPath%\android\jni\Android.mk TEMPLATE_PROJECT "%projName%"
+
+copy gameplay-template\android\jni\main.cpp %projPath%\android\jni\main.cpp
+
+mkdir %projPath%\android\res\values
+
+copy gameplay-template\android\res\values\template.strings.xml %projPath%\android\res\values\strings.xml
+call:replace %projPath%\android\res\values\strings.xml TEMPLATE_TITLE "%title%"
+
+
 REM Copy source files
 copy gameplay-template\src\TemplateGame.h %projPath%\src\%className%.h
 copy gameplay-template\src\TemplateGame.cpp %projPath%\src\%className%.cpp

+ 32 - 0
gameplay-template/TEMPLATE_PROJECT-ios.plist

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string>icon.png</string>
+	<key>CFBundleIdentifier</key>
+	<string>TEMPLATE_UUID</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSApplicationCategoryType</key>
+	<string>public.app-category.games</string>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>

+ 0 - 0
gameplay-template/gameplay-template-macos.plist → gameplay-template/TEMPLATE_PROJECT-macos.plist


+ 1 - 0
gameplay-template/android/jni/Application.mk

@@ -0,0 +1 @@
+APP_STL     := stlport_static

+ 62 - 0
gameplay-template/android/jni/template.Android.mk

@@ -0,0 +1,62 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SAMPLE_PATH := $(call my-dir)/../../src
+LIBPNG_PATH := $(call my-dir)/../../../../external-deps/libpng/lib/android/arm
+ZLIB_PATH := $(call my-dir)/../../../../external-deps/zlib/lib/android/arm
+BULLET_PATH := $(call my-dir)/../../../../external-deps/bullet/lib/android/arm
+
+# gameplay
+LOCAL_PATH := $(call my-dir)/../../../../gameplay/android/obj/local/armeabi
+include $(CLEAR_VARS)
+LOCAL_MODULE    := libgameplay
+LOCAL_SRC_FILES := libgameplay.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+# libpng
+LOCAL_PATH := $(LIBPNG_PATH)
+include $(CLEAR_VARS)
+LOCAL_MODULE    := libpng 
+LOCAL_SRC_FILES := libpng.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+# libzlib
+LOCAL_PATH := $(ZLIB_PATH)
+include $(CLEAR_VARS)
+LOCAL_MODULE    := libzlib
+LOCAL_SRC_FILES := libzlib.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+# libbullet
+LOCAL_PATH := $(BULLET_PATH)
+include $(CLEAR_VARS)
+LOCAL_MODULE    := libbullet
+LOCAL_SRC_FILES := libbullet.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+# TEMPLATE_PROJECT
+LOCAL_PATH := $(SAMPLE_PATH)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE    := TEMPLATE_PROJECT
+LOCAL_SRC_FILES := ../../../gameplay/src/gameplay-main-android.cpp TemplateGame.cpp
+
+LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv2 -lOpenSLES
+LOCAL_CFLAGS    := -D__ANDROID__ -I"../../../external-deps/bullet/include" -I"../../../external-deps/libpng/include" -I"../../../gameplay/src"
+
+LOCAL_STATIC_LIBRARIES := android_native_app_glue libgameplay libpng libzlib libbullet
+
+include $(BUILD_SHARED_LIBRARY)
+$(call import-module,android/native_app_glue)

+ 4 - 0
gameplay-template/android/res/values/template.strings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">TEMPLATE_TITLE</string>
+</resources>

+ 32 - 0
gameplay-template/android/template.AndroidManifest.xml

@@ -0,0 +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: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> 

+ 92 - 0
gameplay-template/android/template.build.xml

@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="TEMPLATE_PROJECT" default="help">
+
+    <!-- The local.properties file is created and updated by the 'android' tool.
+         It contains the path to the SDK. It should *NOT* be checked into
+         Version Control Systems. -->
+    <property file="local.properties" />
+
+    <!-- The ant.properties file can be created by you. It is only edited by the
+         'android' tool to add properties to it.
+         This is the place to change some Ant specific build properties.
+         Here are some properties you may want to change/update:
+
+         source.dir
+             The name of the source directory. Default is 'src'.
+         out.dir
+             The name of the output directory. Default is 'bin'.
+
+         For other overridable properties, look at the beginning of the rules
+         files in the SDK, at tools/ant/build.xml
+
+         Properties related to the SDK location or the project target should
+         be updated using the 'android' tool with the 'update' action.
+
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems.
+
+         -->
+    <property file="ant.properties" />
+
+    <!-- The project.properties file is created and updated by the 'android'
+         tool, as well as ADT.
+
+         This contains project specific properties such as project target, and library
+         dependencies. Lower level build properties are stored in ant.properties
+         (or in .classpath for Eclipse projects).
+
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems. -->
+    <loadproperties srcFile="project.properties" />
+
+    <!-- quick check on sdk.dir -->
+    <fail
+            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
+            unless="sdk.dir"
+    />
+
+
+<!-- extension targets. Uncomment the ones where you want to do custom work
+     in between standard targets -->
+
+    <target name="-pre-build">
+		<mkdir dir="src"/>
+    </target>
+	
+<!--
+    <target name="-pre-compile">
+    </target>
+
+    /* This is typically used for code obfuscation.
+       Compiled code location: ${out.classes.absolute.dir}
+       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="../../../gameplay/res/shaders/colored.vsh" tofile="assets/res/shaders/colored.vsh"/>
+        <copy file="../../../gameplay/res/shaders/colored.fsh" tofile="assets/res/shaders/colored.fsh"/>
+    </target>
+
+    <!-- Import the actual build file.
+
+         To customize existing targets, there are two options:
+         - Customize only one target:
+             - copy/paste the target into this file, *before* the
+               <import> task.
+             - customize it to your needs.
+         - Customize the whole content of build.xml
+             - copy/paste the content of the rules files (minus the top node)
+               into this file, replacing the <import> task.
+             - customize to your needs.
+
+         ***********************
+         ****** IMPORTANT ******
+         ***********************
+         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+         in order to avoid having your file be overridden by tools such as "android update project"
+    -->
+    <!-- version-tag: 1 -->
+    <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>

+ 210 - 35
gameplay-template/gameplay-template.xcodeproj/project.pbxproj

@@ -7,14 +7,14 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		42C932F11491A5160098216A /* TemplateGame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42C932EF1491A5160098216A /* TemplateGame.cpp */; };
+		42438B531491AD2000D218B8 /* libgameplay.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42438B521491AD2000D218B8 /* libgameplay.a */; };
+		42C932C11491A0DB0098216A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C932C01491A0DB0098216A /* Cocoa.framework */; };
 		42C932EE1491A4CB0098216A /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 42C932ED1491A4CB0098216A /* icon.png */; };
+		42C932F11491A5160098216A /* TemplateGame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42C932EF1491A5160098216A /* TemplateGame.cpp */; };
 		42C932F31491A53E0098216A /* res in Resources */ = {isa = PBXBuildFile; fileRef = 42C932F21491A53E0098216A /* res */; };
-		42C932C11491A0DB0098216A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C932C01491A0DB0098216A /* Cocoa.framework */; };
-		42C9331D1491A6750098216A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C9331C1491A6750098216A /* QuartzCore.framework */; };
 		42C933171491A5EB0098216A /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C933161491A5EB0098216A /* OpenGL.framework */; };
+		42C9331D1491A6750098216A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C9331C1491A6750098216A /* QuartzCore.framework */; };
 		42C9331F1491A67F0098216A /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C9331E1491A67F0098216A /* OpenAL.framework */; };
-		42438B531491AD2000D218B8 /* libgameplay.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42438B521491AD2000D218B8 /* libgameplay.a */; };
 		42C933211491A6C70098216A /* libbullet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C933201491A6C70098216A /* libbullet.a */; };
 		42C933261491A6E50098216A /* libogg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C933221491A6E50098216A /* libogg.a */; };
 		42C933271491A6E50098216A /* libvorbis.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C933231491A6E50098216A /* libvorbis.a */; };
@@ -22,20 +22,37 @@
 		42C933291491A6E50098216A /* libvorbisfile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C933251491A6E50098216A /* libvorbisfile.a */; };
 		42C9332C1491A7680098216A /* libpng.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C9332A1491A7390098216A /* libpng.a */; };
 		42C9332F1491A78D0098216A /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C9332D1491A7810098216A /* libz.dylib */; };
+		5B61611314CCC2200073B857 /* TEMPLATE_PROJECT-macos.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5B61611214CCC2200073B857 /* TEMPLATE_PROJECT-macos.plist */; };
+		5B61611614CCC24C0073B857 /* TemplateGame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42C932EF1491A5160098216A /* TemplateGame.cpp */; };
+		5B61611814CCC24C0073B857 /* libgameplay.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42438B521491AD2000D218B8 /* libgameplay.a */; };
+		5B61611914CCC24C0073B857 /* libbullet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C933201491A6C70098216A /* libbullet.a */; };
+		5B61611A14CCC24C0073B857 /* libogg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C933221491A6E50098216A /* libogg.a */; };
+		5B61611B14CCC24C0073B857 /* libvorbis.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C933231491A6E50098216A /* libvorbis.a */; };
+		5B61611C14CCC24C0073B857 /* libvorbisenc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C933241491A6E50098216A /* libvorbisenc.a */; };
+		5B61611D14CCC24C0073B857 /* libvorbisfile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C933251491A6E50098216A /* libvorbisfile.a */; };
+		5B61611E14CCC24C0073B857 /* libpng.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C9332A1491A7390098216A /* libpng.a */; };
+		5B61611F14CCC24C0073B857 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C9332D1491A7810098216A /* libz.dylib */; };
+		5B61612614CCC24C0073B857 /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 42C932ED1491A4CB0098216A /* icon.png */; };
+		5B61612714CCC24C0073B857 /* res in Resources */ = {isa = PBXBuildFile; fileRef = 42C932F21491A53E0098216A /* res */; };
+		5B61612814CCC24C0073B857 /* TEMPLATE_PROJECT-macos.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5B61611214CCC2200073B857 /* TEMPLATE_PROJECT-macos.plist */; };
+		5B61613114CCC33A0073B857 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B61612F14CCC33A0073B857 /* OpenAL.framework */; };
+		5B61613214CCC33A0073B857 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B61613014CCC33A0073B857 /* OpenGLES.framework */; };
+		5B61613414CCC3420073B857 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B61613314CCC3420073B857 /* UIKit.framework */; };
+		5B61613614CCC34A0073B857 /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B61613514CCC3490073B857 /* CoreMotion.framework */; };
+		5B61613814CCC3500073B857 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B61613714CCC3500073B857 /* QuartzCore.framework */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
-		42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT.app"; sourceTree = BUILT_PRODUCTS_DIR; };
-		42C9336B1491AA0E0098216A /* TEMPLATE_PROJECT-macos.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "TEMPLATE_PROJECT-macos.plist"; sourceTree = "<group>"; };
+		42438B521491AD2000D218B8 /* libgameplay.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgameplay.a; path = "~/Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug/libgameplay.a"; sourceTree = "<group>"; };
+		42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT-MacOSX.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		42C932C01491A0DB0098216A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
 		42C932ED1491A4CB0098216A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
-		42C932F21491A53E0098216A /* res */ = {isa = PBXFileReference; lastKnownFileType = folder; path = res; sourceTree = "<group>"; };
 		42C932EF1491A5160098216A /* TemplateGame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TemplateGame.cpp; path = src/TemplateGame.cpp; sourceTree = SOURCE_ROOT; };
 		42C932F01491A5160098216A /* TemplateGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemplateGame.h; path = src/TemplateGame.h; sourceTree = SOURCE_ROOT; };
-		42C932C01491A0DB0098216A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
-		42C9331C1491A6750098216A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+		42C932F21491A53E0098216A /* res */ = {isa = PBXFileReference; lastKnownFileType = folder; path = res; sourceTree = "<group>"; };
 		42C933161491A5EB0098216A /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
+		42C9331C1491A6750098216A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		42C9331E1491A67F0098216A /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; };
-		42438B521491AD2000D218B8 /* libgameplay.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgameplay.a; path = "~/Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug/libgameplay.a"; sourceTree = "<group>"; };
 		42C933201491A6C70098216A /* libbullet.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libbullet.a; path = "GAMEPLAY_PATH/external-deps/bullet/lib/macos/libbullet.a"; sourceTree = "<group>"; };
 		42C933221491A6E50098216A /* libogg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libogg.a; path = "GAMEPLAY_PATH/external-deps/oggvorbis/lib/macos/libogg.a"; sourceTree = "<group>"; };
 		42C933231491A6E50098216A /* libvorbis.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libvorbis.a; path = "GAMEPLAY_PATH/external-deps/oggvorbis/lib/macos/libvorbis.a"; sourceTree = "<group>"; };
@@ -43,6 +60,14 @@
 		42C933251491A6E50098216A /* libvorbisfile.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libvorbisfile.a; path = "GAMEPLAY_PATH/external-deps/oggvorbis/lib/macos/libvorbisfile.a"; sourceTree = "<group>"; };
 		42C9332A1491A7390098216A /* libpng.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng.a; path = "GAMEPLAY_PATH/external-deps/libpng/lib/macos/libpng.a"; sourceTree = "<group>"; };
 		42C9332D1491A7810098216A /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
+		5B61611214CCC2200073B857 /* TEMPLATE_PROJECT-macos.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "TEMPLATE_PROJECT-macos.plist"; sourceTree = "<group>"; };
+		5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		5B61612E14CCC24D0073B857 /* TEMPLATE_PROJECT-ios.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "TEMPLATE_PROJECT-ios.plist"; path = "/Users/bslack/src/git/GamePlay/gameplay-template/TEMPLATE_PROJECT-ios.plist"; sourceTree = "<absolute>"; };
+		5B61612F14CCC33A0073B857 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/OpenAL.framework; sourceTree = DEVELOPER_DIR; };
+		5B61613014CCC33A0073B857 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/OpenGLES.framework; sourceTree = DEVELOPER_DIR; };
+		5B61613314CCC3420073B857 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
+		5B61613514CCC3490073B857 /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/CoreMotion.framework; sourceTree = DEVELOPER_DIR; };
+		5B61613714CCC3500073B857 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -65,13 +90,34 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		5B61611714CCC24C0073B857 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5B61613814CCC3500073B857 /* QuartzCore.framework in Frameworks */,
+				5B61613614CCC34A0073B857 /* CoreMotion.framework in Frameworks */,
+				5B61613414CCC3420073B857 /* UIKit.framework in Frameworks */,
+				5B61613114CCC33A0073B857 /* OpenAL.framework in Frameworks */,
+				5B61613214CCC33A0073B857 /* OpenGLES.framework in Frameworks */,
+				5B61611814CCC24C0073B857 /* libgameplay.a in Frameworks */,
+				5B61611914CCC24C0073B857 /* libbullet.a in Frameworks */,
+				5B61611A14CCC24C0073B857 /* libogg.a in Frameworks */,
+				5B61611B14CCC24C0073B857 /* libvorbis.a in Frameworks */,
+				5B61611C14CCC24C0073B857 /* libvorbisenc.a in Frameworks */,
+				5B61611D14CCC24C0073B857 /* libvorbisfile.a in Frameworks */,
+				5B61611E14CCC24C0073B857 /* libpng.a in Frameworks */,
+				5B61611F14CCC24C0073B857 /* libz.dylib in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
 		42C932B11491A0DB0098216A = {
 			isa = PBXGroup;
 			children = (
-				42C9336B1491AA0E0098216A /* TEMPLATE_PROJECT-macos.plist */,
+				5B61611214CCC2200073B857 /* TEMPLATE_PROJECT-macos.plist */,
+				5B61612E14CCC24D0073B857 /* TEMPLATE_PROJECT-ios.plist */,
 				42C932ED1491A4CB0098216A /* icon.png */,
 				42C932F21491A53E0098216A /* res */,
 				42C932C61491A0DB0098216A /* src */,
@@ -84,7 +130,8 @@
 		42C932BD1491A0DB0098216A /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT.app */,
+				42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX.app */,
+				5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-iOS.app */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -92,10 +139,8 @@
 		42C932BF1491A0DB0098216A /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
-				42C932C01491A0DB0098216A /* Cocoa.framework */,
-				42C9331C1491A6750098216A /* QuartzCore.framework */,
-				42C933161491A5EB0098216A /* OpenGL.framework */,
-				42C9331E1491A67F0098216A /* OpenAL.framework */,
+				5B61613A14CCC3590073B857 /* Mac OS X */,
+				5B61613914CCC3560073B857 /* iOS */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -106,8 +151,7 @@
 				42C932EF1491A5160098216A /* TemplateGame.cpp */,
 				42C932F01491A5160098216A /* TemplateGame.h */,
 			);
-			name = src;
-			path = "src";
+			path = src;
 			sourceTree = "<group>";
 		};
 		42C932DD1491A1050098216A /* Libraries */ = {
@@ -125,12 +169,35 @@
 			name = Libraries;
 			sourceTree = "<group>";
 		};
+		5B61613914CCC3560073B857 /* iOS */ = {
+			isa = PBXGroup;
+			children = (
+				5B61613714CCC3500073B857 /* QuartzCore.framework */,
+				5B61613514CCC3490073B857 /* CoreMotion.framework */,
+				5B61613314CCC3420073B857 /* UIKit.framework */,
+				5B61612F14CCC33A0073B857 /* OpenAL.framework */,
+				5B61613014CCC33A0073B857 /* OpenGLES.framework */,
+			);
+			name = iOS;
+			sourceTree = "<group>";
+		};
+		5B61613A14CCC3590073B857 /* Mac OS X */ = {
+			isa = PBXGroup;
+			children = (
+				42C932C01491A0DB0098216A /* Cocoa.framework */,
+				42C9331C1491A6750098216A /* QuartzCore.framework */,
+				42C933161491A5EB0098216A /* OpenGL.framework */,
+				42C9331E1491A67F0098216A /* OpenAL.framework */,
+			);
+			name = "Mac OS X";
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
-		42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT */ = {
+		42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT" */;
+			buildConfigurationList = 42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-MacOSX" */;
 			buildPhases = (
 				42C932B81491A0DB0098216A /* Sources */,
 				42C932B91491A0DB0098216A /* Frameworks */,
@@ -141,9 +208,27 @@
 			);
 			dependencies = (
 			);
-			name = "TEMPLATE_PROJECT";
-			productName = "TEMPLATE_PROJECT";
-			productReference = 42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT.app */;
+			name = "TEMPLATE_PROJECT-MacOSX";
+			productName = TEMPLATE_PROJECT;
+			productReference = 42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX.app */;
+			productType = "com.apple.product-type.application";
+		};
+		5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-iOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5B61612914CCC24C0073B857 /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-iOS" */;
+			buildPhases = (
+				5B61611514CCC24C0073B857 /* Sources */,
+				5B61611714CCC24C0073B857 /* Frameworks */,
+				5B61612414CCC24C0073B857 /* ShellScript */,
+				5B61612514CCC24C0073B857 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "TEMPLATE_PROJECT-iOS";
+			productName = TEMPLATE_PROJECT;
+			productReference = 5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-iOS.app */;
 			productType = "com.apple.product-type.application";
 		};
 /* End PBXNativeTarget section */
@@ -154,7 +239,7 @@
 			attributes = {
 				LastUpgradeCheck = 0420;
 			};
-			buildConfigurationList = 42C932B61491A0DB0098216A /* Build configuration list for PBXProject "TEMPLATE_PROJECT" */;
+			buildConfigurationList = 42C932B61491A0DB0098216A /* Build configuration list for PBXProject "gameplay-template" */;
 			compatibilityVersion = "Xcode 3.2";
 			developmentRegion = English;
 			hasScannedForEncodings = 0;
@@ -166,7 +251,8 @@
 			projectDirPath = "";
 			projectRoot = "";
 			targets = (
-				42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT */,
+				42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX */,
+				5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-iOS */,
 			);
 		};
 /* End PBXProject section */
@@ -178,6 +264,17 @@
 			files = (
 				42C932EE1491A4CB0098216A /* icon.png in Resources */,
 				42C932F31491A53E0098216A /* res in Resources */,
+				5B61611314CCC2200073B857 /* TEMPLATE_PROJECT-macos.plist in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5B61612514CCC24C0073B857 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5B61612614CCC24C0073B857 /* icon.png in Resources */,
+				5B61612714CCC24C0073B857 /* res in Resources */,
+				5B61612814CCC24C0073B857 /* TEMPLATE_PROJECT-macos.plist in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -197,6 +294,19 @@
 			shellPath = /bin/sh;
 			shellScript = "touch -cm ${SRCROOT}/res";
 		};
+		5B61612414CCC24C0073B857 /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "touch -cm ${SRCROOT}/res";
+		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -208,6 +318,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		5B61611514CCC24C0073B857 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5B61611614CCC24C0073B857 /* TemplateGame.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin XCBuildConfiguration section */
@@ -265,23 +383,22 @@
 		42C932DB1491A0DB0098216A /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
 				GCC_PRECOMPILE_PREFIX_HEADER = NO;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
 				GCC_WARN_UNUSED_VARIABLE = NO;
 				HEADER_SEARCH_PATHS = (
-					"GAMEPLAY_PATH/gameplay/src",
+					GAMEPLAY_PATH/gameplay/src,
 					"GAMEPLAY_PATH/external-deps/libpng/include",
 					"GAMEPLAY_PATH/external-deps/bullet/include",
 					"GAMEPLAY_PATH/external-deps/oggvorbis/include",
 				);
-				INFOPLIST_FILE = "TEMPLATE_PROJECT-macos.plist";
+				INFOPLIST_FILE = "gameplay-template-macos.plist";
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
 					"\"GAMEPLAY_PATH/external-deps/libpng/lib/macos\"",
 					"\"GAMEPLAY_PATH/external-deps/bullet/lib/macos\"",
 					"\"GAMEPLAY_PATH/external-deps/oggvorbis/lib/macos\"",
-					"\"~/Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug\"",
 				);
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				USER_HEADER_SEARCH_PATHS = "";
@@ -292,23 +409,22 @@
 		42C932DC1491A0DB0098216A /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
 				GCC_PRECOMPILE_PREFIX_HEADER = NO;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
 				GCC_WARN_UNUSED_VARIABLE = NO;
 				HEADER_SEARCH_PATHS = (
-					"GAMEPLAY_PATH/gameplay/src",
+					GAMEPLAY_PATH/gameplay/src,
 					"GAMEPLAY_PATH/external-deps/libpng/include",
 					"GAMEPLAY_PATH/external-deps/bullet/include",
 					"GAMEPLAY_PATH/external-deps/oggvorbis/include",
 				);
-				INFOPLIST_FILE = "TEMPLATE_PROJECT-macos.plist";
+				INFOPLIST_FILE = "gameplay-template-macos.plist";
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
 					"\"GAMEPLAY_PATH/external-deps/libpng/lib/macos\"",
 					"\"GAMEPLAY_PATH/external-deps/bullet/lib/macos\"",
 					"\"GAMEPLAY_PATH/external-deps/oggvorbis/lib/macos\"",
-					"\"~/Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug\"",
 				);
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				USER_HEADER_SEARCH_PATHS = "";
@@ -316,10 +432,60 @@
 			};
 			name = Release;
 		};
+		5B61612A14CCC24C0073B857 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				GCC_PRECOMPILE_PREFIX_HEADER = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+				GCC_WARN_UNUSED_VARIABLE = NO;
+				HEADER_SEARCH_PATHS = (
+					GAMEPLAY_PATH/gameplay/src,
+					"GAMEPLAY_PATH/external-deps/libpng/include",
+					"GAMEPLAY_PATH/external-deps/bullet/include",
+					"GAMEPLAY_PATH/external-deps/oggvorbis/include",
+				);
+				INFOPLIST_FILE = "gameplay-template-ios.plist";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"\"GAMEPLAY_PATH/external-deps/libpng/lib/ios/$(CURRENT_ARCH)\"",
+					"\"GAMEPLAY_PATH/external-deps/bullet/lib/ios/$(CURRENT_ARCH)\"",
+					"\"GAMEPLAY_PATH/external-deps/oggvorbis/lib/ios/$(CURRENT_ARCH)\"",
+				);
+				SDKROOT = iphoneos;
+				USER_HEADER_SEARCH_PATHS = "";
+				WRAPPER_EXTENSION = app;
+			};
+			name = Debug;
+		};
+		5B61612B14CCC24C0073B857 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				GCC_PRECOMPILE_PREFIX_HEADER = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+				GCC_WARN_UNUSED_VARIABLE = NO;
+				HEADER_SEARCH_PATHS = (
+					GAMEPLAY_PATH/gameplay/src,
+					"GAMEPLAY_PATH/external-deps/libpng/include",
+					"GAMEPLAY_PATH/external-deps/bullet/include",
+					"GAMEPLAY_PATH/external-deps/oggvorbis/include",
+				);
+				INFOPLIST_FILE = "gameplay-template-ios.plist";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"\"GAMEPLAY_PATH/external-deps/libpng/lib/ios/$(CURRENT_ARCH)\"",
+					"\"GAMEPLAY_PATH/external-deps/bullet/lib/ios/$(CURRENT_ARCH)\"",
+					"\"GAMEPLAY_PATH/external-deps/oggvorbis/lib/ios/$(CURRENT_ARCH)\"",
+				);
+				SDKROOT = iphoneos;
+				USER_HEADER_SEARCH_PATHS = "";
+				WRAPPER_EXTENSION = app;
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
-		42C932B61491A0DB0098216A /* Build configuration list for PBXProject "TEMPLATE_PROJECT" */ = {
+		42C932B61491A0DB0098216A /* Build configuration list for PBXProject "gameplay-template" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				42C932D81491A0DB0098216A /* Debug */,
@@ -328,7 +494,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT" */ = {
+		42C932DA1491A0DB0098216A /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-MacOSX" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				42C932DB1491A0DB0098216A /* Debug */,
@@ -337,6 +503,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		5B61612914CCC24C0073B857 /* Build configuration list for PBXNativeTarget "TEMPLATE_PROJECT-iOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5B61612A14CCC24C0073B857 /* Debug */,
+				5B61612B14CCC24C0073B857 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 42C932B31491A0DB0098216A /* Project object */;

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

@@ -5,8 +5,8 @@ material box
         pass
         {
             // shaders
-            vertexShader = res/colored.vsh
-            fragmentShader = res/colored.fsh
+            vertexShader = res/shaders/colored.vsh
+            fragmentShader = res/shaders/colored.fsh
             
             // uniforms
             u_worldViewProjectionMatrix = WORLD_VIEW_PROJECTION_MATRIX

+ 5 - 5
gameplay-template/src/TemplateGame.cpp

@@ -58,15 +58,15 @@ bool TemplateGame::drawScene(Node* node, void* cookie)
     return true;
 }
 
-void TemplateGame::touch(int x, int y, int touchEvent)
+void TemplateGame::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
-    switch (touchEvent)
+    switch (evt)
     {
-    case Input::TOUCHEVENT_PRESS:
+    case Touch::TOUCH_PRESS:
         break;
-    case Input::TOUCHEVENT_RELEASE:
+    case Touch::TOUCH_RELEASE:
         break;
-    case Input::TOUCHEVENT_MOVE:
+    case Touch::TOUCH_MOVE:
         break;
     };
 }

+ 1 - 1
gameplay-template/src/TemplateGame.h

@@ -20,7 +20,7 @@ public:
     /**
      * Touch event handler.
      */
-    void touch(int x, int y, int touchEvent);
+    void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
 protected:
 

+ 2 - 2
gameplay.doxyfile

@@ -26,13 +26,13 @@ DOXYFILE_ENCODING      = UTF-8
 # identify the project. Note that if you do not use Doxywizard you need 
 # to put quotes around the project name if it contains spaces.
 
-PROJECT_NAME           = GamePlay
+PROJECT_NAME           = gameplay
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number. 
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 
-PROJECT_NUMBER         = 1.1.0
+PROJECT_NUMBER         = 1.2.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 

+ 8 - 0
gameplay.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
+	<true/>
+</dict>
+</plist>

+ 14 - 0
gameplay/android/AndroidManifest.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.gameplay"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk android:minSdkVersion="15" />
+
+    <application
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name" >
+    </application>
+
+</manifest>

+ 85 - 0
gameplay/android/build.xml

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="gameplay-android" default="help">
+
+    <!-- The local.properties file is created and updated by the 'android' tool.
+         It contains the path to the SDK. It should *NOT* be checked into
+         Version Control Systems. -->
+    <property file="local.properties" />
+
+    <!-- The ant.properties file can be created by you. It is only edited by the
+         'android' tool to add properties to it.
+         This is the place to change some Ant specific build properties.
+         Here are some properties you may want to change/update:
+
+         source.dir
+             The name of the source directory. Default is 'src'.
+         out.dir
+             The name of the output directory. Default is 'bin'.
+
+         For other overridable properties, look at the beginning of the rules
+         files in the SDK, at tools/ant/build.xml
+
+         Properties related to the SDK location or the project target should
+         be updated using the 'android' tool with the 'update' action.
+
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems.
+
+         -->
+    <property file="ant.properties" />
+
+    <!-- The project.properties file is created and updated by the 'android'
+         tool, as well as ADT.
+
+         This contains project specific properties such as project target, and library
+         dependencies. Lower level build properties are stored in ant.properties
+         (or in .classpath for Eclipse projects).
+
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems. -->
+    <loadproperties srcFile="project.properties" />
+
+    <!-- quick check on sdk.dir -->
+    <fail
+            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
+            unless="sdk.dir"
+    />
+
+
+<!-- extension targets. Uncomment the ones where you want to do custom work
+     in between standard targets -->
+<!--
+    <target name="-pre-build">
+    </target>
+    <target name="-pre-compile">
+    </target>
+
+    /* This is typically used for code obfuscation.
+       Compiled code location: ${out.classes.absolute.dir}
+       If this is not done in place, override ${out.dex.input.absolute.dir} */
+    <target name="-post-compile">
+    </target>
+-->
+
+    <!-- Import the actual build file.
+
+         To customize existing targets, there are two options:
+         - Customize only one target:
+             - copy/paste the target into this file, *before* the
+               <import> task.
+             - customize it to your needs.
+         - Customize the whole content of build.xml
+             - copy/paste the content of the rules files (minus the top node)
+               into this file, replacing the <import> task.
+             - customize to your needs.
+
+         ***********************
+         ****** IMPORTANT ******
+         ***********************
+         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+         in order to avoid having your file be overridden by tools such as "android update project"
+    -->
+    <!-- version-tag: 1 -->
+    <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>

+ 25 - 0
gameplay/android/jni/Android.mk

@@ -0,0 +1,25 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)/../../src
+
+include $(CLEAR_VARS)
+LOCAL_MODULE    := libgameplay
+LOCAL_SRC_FILES := Animation.cpp DepthStencilTarget.cpp MeshBatch.cpp PhysicsRigidBody.cpp SceneLoader.cpp AnimationClip.cpp Effect.cpp MeshPart.cpp PhysicsSocketConstraint.cpp SpriteBatch.cpp AnimationController.cpp FileSystem.cpp MeshSkin.cpp PhysicsSpringConstraint.cpp Technique.cpp AnimationTarget.cpp Font.cpp Model.cpp Plane.cpp Texture.cpp AnimationValue.cpp FrameBuffer.cpp Node.cpp PlatformAndroid.cpp PlatformQNX.cpp AudioBuffer.cpp Frustum.cpp Package.cpp PlatformWin32.cpp Transform.cpp AudioController.cpp Game.cpp ParticleEmitter.cpp Properties.cpp Vector2.cpp AudioListener.cpp Image.cpp Pass.cpp Quaternion.cpp Vector3.cpp AudioSource.cpp Joint.cpp PhysicsConstraint.cpp Ray.cpp Vector4.cpp BoundingBox.cpp Light.cpp PhysicsController.cpp Rectangle.cpp VertexAttributeBinding.cpp BoundingSphere.cpp Material.cpp PhysicsFixedConstraint.cpp Ref.cpp VertexFormat.cpp Camera.cpp MaterialParameter.cpp PhysicsGenericConstraint.cpp RenderState.cpp Viewport.cpp Curve.cpp Matrix.cpp PhysicsHingeConstraint.cpp RenderTarget.cpp DebugNew.cpp Mesh.cpp PhysicsMotionState.cpp Scene.cpp
+LOCAL_CFLAGS := -D__ANDROID__ -I"../../external-deps/bullet/include" -I"../../external-deps/libpng/include"
+LOCAL_STATIC_LIBRARIES := android_native_app_glue
+
+include $(BUILD_STATIC_LIBRARY)
+
+$(call import-module,android/native_app_glue)

+ 2 - 0
gameplay/android/jni/Application.mk

@@ -0,0 +1,2 @@
+APP_STL     := stlport_static
+APP_MODULES := libgameplay

+ 5 - 0
gameplay/gameplay.vcxproj

@@ -36,6 +36,7 @@
     <ClCompile Include="src\FrameBuffer.cpp" />
     <ClCompile Include="src\Frustum.cpp" />
     <ClCompile Include="src\Game.cpp" />
+    <ClCompile Include="src\gameplay-main-android.cpp" />
     <ClCompile Include="src\gameplay-main-qnx.cpp" />
     <ClCompile Include="src\gameplay-main-win32.cpp" />
     <ClCompile Include="src\Image.cpp" />
@@ -63,6 +64,7 @@
     <ClCompile Include="src\PhysicsSocketConstraint.cpp" />
     <ClCompile Include="src\PhysicsSpringConstraint.cpp" />
     <ClCompile Include="src\Plane.cpp" />
+    <ClCompile Include="src\PlatformAndroid.cpp" />
     <ClCompile Include="src\PlatformQNX.cpp" />
     <ClCompile Include="src\PlatformWin32.cpp" />
     <ClCompile Include="src\Properties.cpp" />
@@ -115,6 +117,7 @@
     <ClInclude Include="src\Light.h" />
     <ClInclude Include="src\Material.h" />
     <ClInclude Include="src\MeshBatch.h" />
+    <ClInclude Include="src\Mouse.h" />
     <ClInclude Include="src\Pass.h" />
     <ClInclude Include="src\MaterialParameter.h" />
     <ClInclude Include="src\Matrix.h" />
@@ -183,11 +186,13 @@
     <None Include="src\BoundingSphere.inl" />
     <None Include="src\Curve.inl" />
     <None Include="src\Game.inl" />
+    <None Include="src\gameplay-main-ios.mm" />
     <None Include="src\gameplay-main-macos.mm" />
     <None Include="src\Image.inl" />
     <None Include="src\Matrix.inl" />
     <None Include="src\MeshBatch.inl" />
     <None Include="src\Plane.inl" />
+    <None Include="src\PlatformiOS.mm" />
     <None Include="src\PlatformMacOS.mm" />
     <None Include="src\Quaternion.inl" />
     <None Include="src\Ray.inl" />

+ 15 - 0
gameplay/gameplay.vcxproj.filters

@@ -222,6 +222,12 @@
     <ClCompile Include="src\MeshBatch.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\gameplay-main-android.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\PlatformAndroid.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -434,6 +440,9 @@
     <ClInclude Include="src\MeshBatch.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\Mouse.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
@@ -517,6 +526,12 @@
     <None Include="src\MeshBatch.inl">
       <Filter>src</Filter>
     </None>
+    <None Include="src\gameplay-main-ios.mm">
+      <Filter>src</Filter>
+    </None>
+    <None Include="src\PlatformiOS.mm">
+      <Filter>src</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\PhysicsFixedConstraint.inl">

+ 578 - 45
gameplay/gameplay.xcodeproj/project.pbxproj

@@ -19,7 +19,6 @@
 		4283909A1489D6E800E2B2F5 /* SceneLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 428390981489D6E800E2B2F5 /* SceneLoader.h */; };
 		4299EFA9146AC94300FF4A73 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4299EFA8146AC94300FF4A73 /* OpenGL.framework */; };
 		4299EFAB146AC94B00FF4A73 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4299EFAA146AC94B00FF4A73 /* OpenAL.framework */; };
-		42CCD554146EC1DD00353661 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CCD553146EC1DD00353661 /* libz.dylib */; };
 		42CCD556146EC1EB00353661 /* libpng.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CCD555146EC1EB00353661 /* libpng.a */; };
 		42CD0DAB147D8EA80000361E /* libbullet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CD0DA6147D8EA80000361E /* libbullet.a */; };
 		42CD0DAC147D8EA80000361E /* libogg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CD0DA7147D8EA80000361E /* libogg.a */; };
@@ -70,8 +69,6 @@
 		42CD0E6F147D8FF60000361E /* Game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DDC147D8FF50000361E /* Game.cpp */; };
 		42CD0E70147D8FF60000361E /* Game.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DDD147D8FF50000361E /* Game.h */; };
 		42CD0E71147D8FF60000361E /* gameplay-main-macos.mm in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DDE147D8FF50000361E /* gameplay-main-macos.mm */; };
-		42CD0E72147D8FF60000361E /* gameplay-main-qnx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DDF147D8FF50000361E /* gameplay-main-qnx.cpp */; };
-		42CD0E73147D8FF60000361E /* gameplay-main-win32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DE0147D8FF50000361E /* gameplay-main-win32.cpp */; };
 		42CD0E74147D8FF60000361E /* gameplay.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DE1147D8FF50000361E /* gameplay.h */; };
 		42CD0E77147D8FF60000361E /* Joint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DE4147D8FF50000361E /* Joint.cpp */; };
 		42CD0E78147D8FF60000361E /* Joint.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DE5147D8FF50000361E /* Joint.h */; };
@@ -121,8 +118,6 @@
 		42CD0EA4147D8FF60000361E /* Plane.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E17147D8FF50000361E /* Plane.h */; };
 		42CD0EA5147D8FF60000361E /* Platform.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E19147D8FF50000361E /* Platform.h */; };
 		42CD0EA6147D8FF60000361E /* PlatformMacOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E1A147D8FF50000361E /* PlatformMacOS.mm */; };
-		42CD0EA7147D8FF60000361E /* PlatformQNX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E1B147D8FF50000361E /* PlatformQNX.cpp */; };
-		42CD0EA8147D8FF60000361E /* PlatformWin32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E1C147D8FF50000361E /* PlatformWin32.cpp */; };
 		42CD0EA9147D8FF60000361E /* Properties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E1D147D8FF50000361E /* Properties.cpp */; };
 		42CD0EAA147D8FF60000361E /* Properties.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E1E147D8FF50000361E /* Properties.h */; };
 		42CD0EAB147D8FF60000361E /* Quaternion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E1F147D8FF50000361E /* Quaternion.cpp */; };
@@ -159,6 +154,159 @@
 		42CD0ECA147D8FF60000361E /* VertexFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E43147D8FF50000361E /* VertexFormat.h */; };
 		42CD0ECB147D8FF60000361E /* Viewport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E44147D8FF50000361E /* Viewport.cpp */; };
 		42CD0ECC147D8FF60000361E /* Viewport.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E45147D8FF50000361E /* Viewport.h */; };
+		5B04C52D14BFCFE100EB0071 /* Animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DB1147D8FF50000361E /* Animation.cpp */; };
+		5B04C52E14BFCFE100EB0071 /* AnimationClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DB3147D8FF50000361E /* AnimationClip.cpp */; };
+		5B04C52F14BFCFE100EB0071 /* AnimationController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DB5147D8FF50000361E /* AnimationController.cpp */; };
+		5B04C53014BFCFE100EB0071 /* AnimationTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DB7147D8FF50000361E /* AnimationTarget.cpp */; };
+		5B04C53114BFCFE100EB0071 /* AnimationValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DB9147D8FF50000361E /* AnimationValue.cpp */; };
+		5B04C53214BFCFE100EB0071 /* AudioBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DBB147D8FF50000361E /* AudioBuffer.cpp */; };
+		5B04C53314BFCFE100EB0071 /* AudioController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DBD147D8FF50000361E /* AudioController.cpp */; };
+		5B04C53414BFCFE100EB0071 /* AudioListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DBF147D8FF50000361E /* AudioListener.cpp */; };
+		5B04C53514BFCFE100EB0071 /* AudioSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DC1147D8FF50000361E /* AudioSource.cpp */; };
+		5B04C53614BFCFE100EB0071 /* BoundingBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DC4147D8FF50000361E /* BoundingBox.cpp */; };
+		5B04C53714BFCFE100EB0071 /* BoundingSphere.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DC7147D8FF50000361E /* BoundingSphere.cpp */; };
+		5B04C53814BFCFE100EB0071 /* Camera.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DCA147D8FF50000361E /* Camera.cpp */; };
+		5B04C53914BFCFE100EB0071 /* Curve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DCC147D8FF50000361E /* Curve.cpp */; };
+		5B04C53A14BFCFE100EB0071 /* DebugNew.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DCE147D8FF50000361E /* DebugNew.cpp */; };
+		5B04C53B14BFCFE100EB0071 /* DepthStencilTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DD0147D8FF50000361E /* DepthStencilTarget.cpp */; };
+		5B04C53C14BFCFE100EB0071 /* Effect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DD2147D8FF50000361E /* Effect.cpp */; };
+		5B04C53D14BFCFE100EB0071 /* FileSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DD4147D8FF50000361E /* FileSystem.cpp */; };
+		5B04C53E14BFCFE100EB0071 /* Font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DD6147D8FF50000361E /* Font.cpp */; };
+		5B04C53F14BFCFE100EB0071 /* FrameBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DD8147D8FF50000361E /* FrameBuffer.cpp */; };
+		5B04C54014BFCFE100EB0071 /* Frustum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DDA147D8FF50000361E /* Frustum.cpp */; };
+		5B04C54114BFCFE100EB0071 /* Game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DDC147D8FF50000361E /* Game.cpp */; };
+		5B04C54514BFCFE100EB0071 /* Joint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DE4147D8FF50000361E /* Joint.cpp */; };
+		5B04C54614BFCFE100EB0071 /* Light.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DE6147D8FF50000361E /* Light.cpp */; };
+		5B04C54714BFCFE100EB0071 /* Material.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DE8147D8FF50000361E /* Material.cpp */; };
+		5B04C54814BFCFE100EB0071 /* MaterialParameter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DEA147D8FF50000361E /* MaterialParameter.cpp */; };
+		5B04C54914BFCFE100EB0071 /* Matrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DEC147D8FF50000361E /* Matrix.cpp */; };
+		5B04C54A14BFCFE100EB0071 /* Mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DEF147D8FF50000361E /* Mesh.cpp */; };
+		5B04C54B14BFCFE100EB0071 /* MeshPart.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DF1147D8FF50000361E /* MeshPart.cpp */; };
+		5B04C54C14BFCFE100EB0071 /* MeshSkin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DF3147D8FF50000361E /* MeshSkin.cpp */; };
+		5B04C54D14BFCFE100EB0071 /* Model.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DF5147D8FF50000361E /* Model.cpp */; };
+		5B04C54E14BFCFE100EB0071 /* Node.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DF7147D8FF50000361E /* Node.cpp */; };
+		5B04C54F14BFCFE100EB0071 /* Package.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DF9147D8FF50000361E /* Package.cpp */; };
+		5B04C55014BFCFE100EB0071 /* ParticleEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DFB147D8FF50000361E /* ParticleEmitter.cpp */; };
+		5B04C55114BFCFE100EB0071 /* Pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DFD147D8FF50000361E /* Pass.cpp */; };
+		5B04C55214BFCFE100EB0071 /* PhysicsConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0DFF147D8FF50000361E /* PhysicsConstraint.cpp */; };
+		5B04C55314BFCFE100EB0071 /* PhysicsController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E02147D8FF50000361E /* PhysicsController.cpp */; };
+		5B04C55414BFCFE100EB0071 /* PhysicsFixedConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E04147D8FF50000361E /* PhysicsFixedConstraint.cpp */; };
+		5B04C55514BFCFE100EB0071 /* PhysicsGenericConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E07147D8FF50000361E /* PhysicsGenericConstraint.cpp */; };
+		5B04C55614BFCFE100EB0071 /* PhysicsHingeConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E0A147D8FF50000361E /* PhysicsHingeConstraint.cpp */; };
+		5B04C55714BFCFE100EB0071 /* PhysicsMotionState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E0C147D8FF50000361E /* PhysicsMotionState.cpp */; };
+		5B04C55814BFCFE100EB0071 /* PhysicsRigidBody.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E0E147D8FF50000361E /* PhysicsRigidBody.cpp */; };
+		5B04C55914BFCFE100EB0071 /* PhysicsSocketConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E11147D8FF50000361E /* PhysicsSocketConstraint.cpp */; };
+		5B04C55A14BFCFE100EB0071 /* PhysicsSpringConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E13147D8FF50000361E /* PhysicsSpringConstraint.cpp */; };
+		5B04C55B14BFCFE100EB0071 /* Plane.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E16147D8FF50000361E /* Plane.cpp */; };
+		5B04C55F14BFCFE100EB0071 /* Properties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E1D147D8FF50000361E /* Properties.cpp */; };
+		5B04C56014BFCFE100EB0071 /* Quaternion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E1F147D8FF50000361E /* Quaternion.cpp */; };
+		5B04C56114BFCFE100EB0071 /* Ray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E22147D8FF50000361E /* Ray.cpp */; };
+		5B04C56214BFCFE100EB0071 /* Rectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E25147D8FF50000361E /* Rectangle.cpp */; };
+		5B04C56314BFCFE100EB0071 /* Ref.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E27147D8FF50000361E /* Ref.cpp */; };
+		5B04C56414BFCFE100EB0071 /* RenderState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E29147D8FF50000361E /* RenderState.cpp */; };
+		5B04C56514BFCFE100EB0071 /* RenderTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E2B147D8FF50000361E /* RenderTarget.cpp */; };
+		5B04C56614BFCFE100EB0071 /* Scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E2D147D8FF50000361E /* Scene.cpp */; };
+		5B04C56714BFCFE100EB0071 /* SpriteBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E2F147D8FF50000361E /* SpriteBatch.cpp */; };
+		5B04C56814BFCFE100EB0071 /* Technique.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E31147D8FF50000361E /* Technique.cpp */; };
+		5B04C56914BFCFE100EB0071 /* Texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E33147D8FF50000361E /* Texture.cpp */; };
+		5B04C56A14BFCFE100EB0071 /* Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E35147D8FF50000361E /* Transform.cpp */; };
+		5B04C56B14BFCFE100EB0071 /* Vector2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E37147D8FF50000361E /* Vector2.cpp */; };
+		5B04C56C14BFCFE100EB0071 /* Vector3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E3A147D8FF50000361E /* Vector3.cpp */; };
+		5B04C56D14BFCFE100EB0071 /* Vector4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E3D147D8FF50000361E /* Vector4.cpp */; };
+		5B04C56E14BFCFE100EB0071 /* VertexAttributeBinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E40147D8FF50000361E /* VertexAttributeBinding.cpp */; };
+		5B04C56F14BFCFE100EB0071 /* VertexFormat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E42147D8FF50000361E /* VertexFormat.cpp */; };
+		5B04C57014BFCFE100EB0071 /* Viewport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42CD0E44147D8FF50000361E /* Viewport.cpp */; };
+		5B04C57114BFCFE100EB0071 /* SceneLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 428390971489D6E800E2B2F5 /* SceneLoader.cpp */; };
+		5B04C57214BFCFE100EB0071 /* Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4208DEE614A4079F00D3C511 /* Image.cpp */; };
+		5B04C57314BFCFE100EB0071 /* MeshBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4201818D14A41B18008C3F56 /* MeshBatch.cpp */; };
+		5B04C57514BFCFE100EB0071 /* libbullet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CD0DA6147D8EA80000361E /* libbullet.a */; };
+		5B04C57614BFCFE100EB0071 /* libogg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CD0DA7147D8EA80000361E /* libogg.a */; };
+		5B04C57714BFCFE100EB0071 /* libvorbis.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CD0DA8147D8EA80000361E /* libvorbis.a */; };
+		5B04C57814BFCFE100EB0071 /* libvorbisenc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CD0DA9147D8EA80000361E /* libvorbisenc.a */; };
+		5B04C57914BFCFE100EB0071 /* libvorbisfile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CD0DAA147D8EA80000361E /* libvorbisfile.a */; };
+		5B04C57A14BFCFE100EB0071 /* libpng.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CCD555146EC1EB00353661 /* libpng.a */; };
+		5B04C58114BFCFE100EB0071 /* Animation.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DB2147D8FF50000361E /* Animation.h */; };
+		5B04C58214BFCFE100EB0071 /* AnimationClip.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DB4147D8FF50000361E /* AnimationClip.h */; };
+		5B04C58314BFCFE100EB0071 /* AnimationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DB6147D8FF50000361E /* AnimationController.h */; };
+		5B04C58414BFCFE100EB0071 /* AnimationTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DB8147D8FF50000361E /* AnimationTarget.h */; };
+		5B04C58514BFCFE100EB0071 /* AnimationValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DBA147D8FF50000361E /* AnimationValue.h */; };
+		5B04C58614BFCFE100EB0071 /* AudioBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DBC147D8FF50000361E /* AudioBuffer.h */; };
+		5B04C58714BFCFE100EB0071 /* AudioController.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DBE147D8FF50000361E /* AudioController.h */; };
+		5B04C58814BFCFE100EB0071 /* AudioListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DC0147D8FF50000361E /* AudioListener.h */; };
+		5B04C58914BFCFE100EB0071 /* AudioSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DC2147D8FF50000361E /* AudioSource.h */; };
+		5B04C58A14BFCFE100EB0071 /* Base.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DC3147D8FF50000361E /* Base.h */; };
+		5B04C58B14BFCFE100EB0071 /* BoundingBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DC5147D8FF50000361E /* BoundingBox.h */; };
+		5B04C58C14BFCFE100EB0071 /* BoundingSphere.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DC8147D8FF50000361E /* BoundingSphere.h */; };
+		5B04C58D14BFCFE100EB0071 /* Camera.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DCB147D8FF50000361E /* Camera.h */; };
+		5B04C58E14BFCFE100EB0071 /* Curve.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DCD147D8FF50000361E /* Curve.h */; };
+		5B04C58F14BFCFE100EB0071 /* DebugNew.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DCF147D8FF50000361E /* DebugNew.h */; };
+		5B04C59014BFCFE100EB0071 /* DepthStencilTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DD1147D8FF50000361E /* DepthStencilTarget.h */; };
+		5B04C59114BFCFE100EB0071 /* Effect.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DD3147D8FF50000361E /* Effect.h */; };
+		5B04C59214BFCFE100EB0071 /* FileSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DD5147D8FF50000361E /* FileSystem.h */; };
+		5B04C59314BFCFE100EB0071 /* Font.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DD7147D8FF50000361E /* Font.h */; };
+		5B04C59414BFCFE100EB0071 /* FrameBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DD9147D8FF50000361E /* FrameBuffer.h */; };
+		5B04C59514BFCFE100EB0071 /* Frustum.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DDB147D8FF50000361E /* Frustum.h */; };
+		5B04C59614BFCFE100EB0071 /* Game.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DDD147D8FF50000361E /* Game.h */; };
+		5B04C59714BFCFE100EB0071 /* gameplay.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DE1147D8FF50000361E /* gameplay.h */; };
+		5B04C59814BFCFE100EB0071 /* Joint.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DE5147D8FF50000361E /* Joint.h */; };
+		5B04C59914BFCFE100EB0071 /* Light.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DE7147D8FF50000361E /* Light.h */; };
+		5B04C59A14BFCFE100EB0071 /* Material.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DE9147D8FF50000361E /* Material.h */; };
+		5B04C59B14BFCFE100EB0071 /* MaterialParameter.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DEB147D8FF50000361E /* MaterialParameter.h */; };
+		5B04C59C14BFCFE100EB0071 /* Matrix.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DED147D8FF50000361E /* Matrix.h */; };
+		5B04C59D14BFCFE100EB0071 /* Mesh.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DF0147D8FF50000361E /* Mesh.h */; };
+		5B04C59E14BFCFE100EB0071 /* MeshPart.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DF2147D8FF50000361E /* MeshPart.h */; };
+		5B04C59F14BFCFE100EB0071 /* MeshSkin.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DF4147D8FF50000361E /* MeshSkin.h */; };
+		5B04C5A014BFCFE100EB0071 /* Model.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DF6147D8FF50000361E /* Model.h */; };
+		5B04C5A114BFCFE100EB0071 /* Node.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DF8147D8FF50000361E /* Node.h */; };
+		5B04C5A214BFCFE100EB0071 /* Package.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DFA147D8FF50000361E /* Package.h */; };
+		5B04C5A314BFCFE100EB0071 /* ParticleEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DFC147D8FF50000361E /* ParticleEmitter.h */; };
+		5B04C5A414BFCFE100EB0071 /* Pass.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0DFE147D8FF50000361E /* Pass.h */; };
+		5B04C5A514BFCFE100EB0071 /* PhysicsConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E00147D8FF50000361E /* PhysicsConstraint.h */; };
+		5B04C5A614BFCFE100EB0071 /* PhysicsController.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E03147D8FF50000361E /* PhysicsController.h */; };
+		5B04C5A714BFCFE100EB0071 /* PhysicsFixedConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E05147D8FF50000361E /* PhysicsFixedConstraint.h */; };
+		5B04C5A814BFCFE100EB0071 /* PhysicsGenericConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E08147D8FF50000361E /* PhysicsGenericConstraint.h */; };
+		5B04C5A914BFCFE100EB0071 /* PhysicsHingeConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E0B147D8FF50000361E /* PhysicsHingeConstraint.h */; };
+		5B04C5AA14BFCFE100EB0071 /* PhysicsMotionState.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E0D147D8FF50000361E /* PhysicsMotionState.h */; };
+		5B04C5AB14BFCFE100EB0071 /* PhysicsRigidBody.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E0F147D8FF50000361E /* PhysicsRigidBody.h */; };
+		5B04C5AC14BFCFE100EB0071 /* PhysicsSocketConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E12147D8FF50000361E /* PhysicsSocketConstraint.h */; };
+		5B04C5AD14BFCFE100EB0071 /* PhysicsSpringConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E14147D8FF50000361E /* PhysicsSpringConstraint.h */; };
+		5B04C5AE14BFCFE100EB0071 /* Plane.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E17147D8FF50000361E /* Plane.h */; };
+		5B04C5AF14BFCFE100EB0071 /* Platform.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E19147D8FF50000361E /* Platform.h */; };
+		5B04C5B014BFCFE100EB0071 /* Properties.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E1E147D8FF50000361E /* Properties.h */; };
+		5B04C5B114BFCFE100EB0071 /* Quaternion.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E20147D8FF50000361E /* Quaternion.h */; };
+		5B04C5B214BFCFE100EB0071 /* Ray.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E23147D8FF50000361E /* Ray.h */; };
+		5B04C5B314BFCFE100EB0071 /* Rectangle.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E26147D8FF50000361E /* Rectangle.h */; };
+		5B04C5B414BFCFE100EB0071 /* Ref.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E28147D8FF50000361E /* Ref.h */; };
+		5B04C5B514BFCFE100EB0071 /* RenderState.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E2A147D8FF50000361E /* RenderState.h */; };
+		5B04C5B614BFCFE100EB0071 /* RenderTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E2C147D8FF50000361E /* RenderTarget.h */; };
+		5B04C5B714BFCFE100EB0071 /* Scene.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E2E147D8FF50000361E /* Scene.h */; };
+		5B04C5B814BFCFE100EB0071 /* SpriteBatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E30147D8FF50000361E /* SpriteBatch.h */; };
+		5B04C5B914BFCFE100EB0071 /* Technique.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E32147D8FF50000361E /* Technique.h */; };
+		5B04C5BA14BFCFE100EB0071 /* Texture.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E34147D8FF50000361E /* Texture.h */; };
+		5B04C5BB14BFCFE100EB0071 /* Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E36147D8FF50000361E /* Transform.h */; };
+		5B04C5BC14BFCFE100EB0071 /* Vector2.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E38147D8FF50000361E /* Vector2.h */; };
+		5B04C5BD14BFCFE100EB0071 /* Vector3.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E3B147D8FF50000361E /* Vector3.h */; };
+		5B04C5BE14BFCFE100EB0071 /* Vector4.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E3E147D8FF50000361E /* Vector4.h */; };
+		5B04C5BF14BFCFE100EB0071 /* VertexAttributeBinding.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E41147D8FF50000361E /* VertexAttributeBinding.h */; };
+		5B04C5C014BFCFE100EB0071 /* VertexFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E43147D8FF50000361E /* VertexFormat.h */; };
+		5B04C5C114BFCFE100EB0071 /* Viewport.h in Headers */ = {isa = PBXBuildFile; fileRef = 42CD0E45147D8FF50000361E /* Viewport.h */; };
+		5B04C5C214BFCFE100EB0071 /* SceneLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 428390981489D6E800E2B2F5 /* SceneLoader.h */; };
+		5B04C5C314BFCFE100EB0071 /* Image.h in Headers */ = {isa = PBXBuildFile; fileRef = 4208DEE714A4079F00D3C511 /* Image.h */; };
+		5B04C5C414BFCFE100EB0071 /* Keyboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4208DEEB14A407B900D3C511 /* Keyboard.h */; };
+		5B04C5C514BFCFE100EB0071 /* Touch.h in Headers */ = {isa = PBXBuildFile; fileRef = 4208DEED14A407D500D3C511 /* Touch.h */; };
+		5B04C5C614BFCFE100EB0071 /* MeshBatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 4201818E14A41B18008C3F56 /* MeshBatch.h */; };
+		5B04C5CD14BFD48500EB0071 /* gameplay-main-ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B04C5CB14BFD48500EB0071 /* gameplay-main-ios.mm */; };
+		5B04C5CE14BFD48500EB0071 /* PlatformiOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B04C5CC14BFD48500EB0071 /* PlatformiOS.mm */; };
+		5B04C5F614BFE50100EB0071 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B04C5F514BFE50100EB0071 /* UIKit.framework */; };
+		5B04C5F814BFE50B00EB0071 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B04C5F714BFE50B00EB0071 /* QuartzCore.framework */; };
+		5B04C5FA14BFE51100EB0071 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B04C5F914BFE51000EB0071 /* OpenGLES.framework */; };
+		5B04C5FC14BFE51600EB0071 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B04C5FB14BFE51600EB0071 /* OpenAL.framework */; };
+		5B5ADCE314C22DF900AC6109 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B5ADCE214C22DF900AC6109 /* libz.dylib */; };
+		5B5ADCE514C22E1F00AC6109 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B5ADCE414C22E1F00AC6109 /* libz.dylib */; };
+		5B5ADD2F14C2439700AC6109 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B5ADD2E14C2439700AC6109 /* Foundation.framework */; };
+		5BB0823D14C6FEC40019975F /* Mouse.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BB0823C14C6FEC40019975F /* Mouse.h */; };
+		5BB0823E14C6FEC40019975F /* Mouse.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BB0823C14C6FEC40019975F /* Mouse.h */; };
+		5BD776FD14C77E1F001CADA0 /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BD776FC14C77E1F001CADA0 /* CoreMotion.framework */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
@@ -179,7 +327,6 @@
 		4299EFA8146AC94300FF4A73 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = SDKs/MacOSX10.7.sdk/System/Library/Frameworks/OpenGL.framework; sourceTree = DEVELOPER_DIR; };
 		4299EFAA146AC94B00FF4A73 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = SDKs/MacOSX10.7.sdk/System/Library/Frameworks/OpenAL.framework; sourceTree = DEVELOPER_DIR; };
 		42C932AF14919FD10098216A /* Game.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Game.inl; path = src/Game.inl; sourceTree = SOURCE_ROOT; };
-		42CCD553146EC1DD00353661 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = SDKs/MacOSX10.7.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
 		42CCD555146EC1EB00353661 /* libpng.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng.a; path = "../external-deps/libpng/lib/macos/libpng.a"; sourceTree = "<group>"; };
 		42CD0DA6147D8EA80000361E /* libbullet.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libbullet.a; path = "../external-deps/bullet/lib/macos/libbullet.a"; sourceTree = "<group>"; };
 		42CD0DA7147D8EA80000361E /* libogg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libogg.a; path = "../external-deps/oggvorbis/lib/macos/libogg.a"; sourceTree = "<group>"; };
@@ -333,6 +480,27 @@
 		42CD0E43147D8FF50000361E /* VertexFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VertexFormat.h; path = src/VertexFormat.h; sourceTree = SOURCE_ROOT; };
 		42CD0E44147D8FF50000361E /* Viewport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Viewport.cpp; path = src/Viewport.cpp; sourceTree = SOURCE_ROOT; };
 		42CD0E45147D8FF50000361E /* Viewport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Viewport.h; path = src/Viewport.h; sourceTree = SOURCE_ROOT; };
+		5B04C5CA14BFCFE100EB0071 /* libgameplay.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgameplay.a; sourceTree = BUILT_PRODUCTS_DIR; };
+		5B04C5CB14BFD48500EB0071 /* gameplay-main-ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "gameplay-main-ios.mm"; path = "src/gameplay-main-ios.mm"; sourceTree = SOURCE_ROOT; };
+		5B04C5CC14BFD48500EB0071 /* PlatformiOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = PlatformiOS.mm; path = src/PlatformiOS.mm; sourceTree = SOURCE_ROOT; };
+		5B04C5F514BFE50100EB0071 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
+		5B04C5F714BFE50B00EB0071 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
+		5B04C5F914BFE51000EB0071 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/OpenGLES.framework; sourceTree = DEVELOPER_DIR; };
+		5B04C5FB14BFE51600EB0071 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/OpenAL.framework; sourceTree = DEVELOPER_DIR; };
+		5B43D17914C3497B008A5D9D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
+		5B5ADCE214C22DF900AC6109 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = SDKs/MacOSX10.7.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
+		5B5ADCE414C22E1F00AC6109 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
+		5B5ADD2E14C2439700AC6109 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
+		5B5DB92D14C25B7B007755DB /* libbullet.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libbullet.a; path = "../external-deps/bullet/lib/ios/i386/libbullet.a"; sourceTree = "<group>"; };
+		5B5DB92F14C25B94007755DB /* libpng.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng.a; path = "../external-deps/libpng/lib/ios/armv7/libpng.a"; sourceTree = "<group>"; };
+		5B5DB93114C25BA5007755DB /* libogg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libogg.a; path = "../external-deps/oggvorbis/lib/ios/armv7/libogg.a"; sourceTree = "<group>"; };
+		5B5DB93214C25BA5007755DB /* libvorbis.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libvorbis.a; path = "../external-deps/oggvorbis/lib/ios/armv7/libvorbis.a"; sourceTree = "<group>"; };
+		5B5DB93314C25BA5007755DB /* libvorbisenc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libvorbisenc.a; path = "../external-deps/oggvorbis/lib/ios/armv7/libvorbisenc.a"; sourceTree = "<group>"; };
+		5B5DB93414C25BA5007755DB /* libvorbisfile.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libvorbisfile.a; path = "../external-deps/oggvorbis/lib/ios/armv7/libvorbisfile.a"; sourceTree = "<group>"; };
+		5BB0823814C6FEB10019975F /* gameplay-main-android.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gameplay-main-android.cpp"; path = "src/gameplay-main-android.cpp"; sourceTree = SOURCE_ROOT; };
+		5BB0823914C6FEB10019975F /* PlatformAndroid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PlatformAndroid.cpp; path = src/PlatformAndroid.cpp; sourceTree = SOURCE_ROOT; };
+		5BB0823C14C6FEC40019975F /* Mouse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Mouse.h; path = src/Mouse.h; sourceTree = SOURCE_ROOT; };
+		5BD776FC14C77E1F001CADA0 /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/CoreMotion.framework; sourceTree = DEVELOPER_DIR; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -340,13 +508,13 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				5B5ADCE314C22DF900AC6109 /* libz.dylib in Frameworks */,
 				42CD0DAB147D8EA80000361E /* libbullet.a in Frameworks */,
 				42CD0DAC147D8EA80000361E /* libogg.a in Frameworks */,
 				42CD0DAD147D8EA80000361E /* libvorbis.a in Frameworks */,
 				42CD0DAE147D8EA80000361E /* libvorbisenc.a in Frameworks */,
 				42CD0DAF147D8EA80000361E /* libvorbisfile.a in Frameworks */,
 				42CCD556146EC1EB00353661 /* libpng.a in Frameworks */,
-				42CCD554146EC1DD00353661 /* libz.dylib in Frameworks */,
 				4234D99E14686C52003031B3 /* Cocoa.framework in Frameworks */,
 				4220A6E8146B122B00CAEB3A /* QuartzCore.framework in Frameworks */,
 				4299EFA9146AC94300FF4A73 /* OpenGL.framework in Frameworks */,
@@ -354,6 +522,26 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		5B04C57414BFCFE100EB0071 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5BD776FD14C77E1F001CADA0 /* CoreMotion.framework in Frameworks */,
+				5B5ADD2F14C2439700AC6109 /* Foundation.framework in Frameworks */,
+				5B5ADCE514C22E1F00AC6109 /* libz.dylib in Frameworks */,
+				5B04C5FC14BFE51600EB0071 /* OpenAL.framework in Frameworks */,
+				5B04C5FA14BFE51100EB0071 /* OpenGLES.framework in Frameworks */,
+				5B04C5F814BFE50B00EB0071 /* QuartzCore.framework in Frameworks */,
+				5B04C5F614BFE50100EB0071 /* UIKit.framework in Frameworks */,
+				5B04C57514BFCFE100EB0071 /* libbullet.a in Frameworks */,
+				5B04C57614BFCFE100EB0071 /* libogg.a in Frameworks */,
+				5B04C57714BFCFE100EB0071 /* libvorbis.a in Frameworks */,
+				5B04C57814BFCFE100EB0071 /* libvorbisenc.a in Frameworks */,
+				5B04C57914BFCFE100EB0071 /* libvorbisfile.a in Frameworks */,
+				5B04C57A14BFCFE100EB0071 /* libpng.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -371,6 +559,7 @@
 			isa = PBXGroup;
 			children = (
 				4234D99A14686C52003031B3 /* libgameplay.a */,
+				5B04C5CA14BFCFE100EB0071 /* libgameplay.a */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -378,6 +567,84 @@
 		4234D9A314686C52003031B3 /* src */ = {
 			isa = PBXGroup;
 			children = (
+				5B43D19714C35347008A5D9D /* gameplay */,
+				5B43D19614C35344008A5D9D /* platform */,
+			);
+			name = src;
+			path = gameplay;
+			sourceTree = "<group>";
+		};
+		427D4F42147DC8DE0076760E /* Libraries */ = {
+			isa = PBXGroup;
+			children = (
+				42CD0DA6147D8EA80000361E /* libbullet.a */,
+				42CD0DA7147D8EA80000361E /* libogg.a */,
+				42CD0DA8147D8EA80000361E /* libvorbis.a */,
+				42CD0DA9147D8EA80000361E /* libvorbisenc.a */,
+				42CD0DAA147D8EA80000361E /* libvorbisfile.a */,
+				42CCD555146EC1EB00353661 /* libpng.a */,
+				5B5ADCE114C22DC700AC6109 /* Mac OS X */,
+				5B5ADCE014C22DBE00AC6109 /* iOS */,
+			);
+			name = Libraries;
+			sourceTree = "<group>";
+		};
+		42CCD4AF146D811D00353661 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				5B04C5FE14BFE52F00EB0071 /* Mac OS X */,
+				5B04C5FD14BFE52300EB0071 /* iOS */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		5B04C5FD14BFE52300EB0071 /* iOS */ = {
+			isa = PBXGroup;
+			children = (
+				5BD776FC14C77E1F001CADA0 /* CoreMotion.framework */,
+				5B43D17914C3497B008A5D9D /* CoreGraphics.framework */,
+				5B5ADD2E14C2439700AC6109 /* Foundation.framework */,
+				5B04C5FB14BFE51600EB0071 /* OpenAL.framework */,
+				5B04C5F914BFE51000EB0071 /* OpenGLES.framework */,
+				5B04C5F714BFE50B00EB0071 /* QuartzCore.framework */,
+				5B04C5F514BFE50100EB0071 /* UIKit.framework */,
+			);
+			name = iOS;
+			sourceTree = "<group>";
+		};
+		5B04C5FE14BFE52F00EB0071 /* Mac OS X */ = {
+			isa = PBXGroup;
+			children = (
+				4234D99D14686C52003031B3 /* Cocoa.framework */,
+				4220A6E7146B122B00CAEB3A /* QuartzCore.framework */,
+				4299EFA8146AC94300FF4A73 /* OpenGL.framework */,
+				4299EFAA146AC94B00FF4A73 /* OpenAL.framework */,
+			);
+			name = "Mac OS X";
+			sourceTree = "<group>";
+		};
+		5B43D19614C35344008A5D9D /* platform */ = {
+			isa = PBXGroup;
+			children = (
+				5BB0823814C6FEB10019975F /* gameplay-main-android.cpp */,
+				42CD0DE0147D8FF50000361E /* gameplay-main-win32.cpp */,
+				42CD0DDE147D8FF50000361E /* gameplay-main-macos.mm */,
+				5B04C5CB14BFD48500EB0071 /* gameplay-main-ios.mm */,
+				42CD0DDF147D8FF50000361E /* gameplay-main-qnx.cpp */,
+				42CD0E19147D8FF50000361E /* Platform.h */,
+				5BB0823914C6FEB10019975F /* PlatformAndroid.cpp */,
+				42CD0E1C147D8FF50000361E /* PlatformWin32.cpp */,
+				42CD0E1A147D8FF50000361E /* PlatformMacOS.mm */,
+				5B04C5CC14BFD48500EB0071 /* PlatformiOS.mm */,
+				42CD0E1B147D8FF50000361E /* PlatformQNX.cpp */,
+			);
+			name = platform;
+			sourceTree = "<group>";
+		};
+		5B43D19714C35347008A5D9D /* gameplay */ = {
+			isa = PBXGroup;
+			children = (
+				5BB0823C14C6FEC40019975F /* Mouse.h */,
 				42CD0DB1147D8FF50000361E /* Animation.cpp */,
 				42CD0DB2147D8FF50000361E /* Animation.h */,
 				42CD0DB3147D8FF50000361E /* AnimationClip.cpp */,
@@ -425,9 +692,6 @@
 				42CD0DDC147D8FF50000361E /* Game.cpp */,
 				42CD0DDD147D8FF50000361E /* Game.h */,
 				42C932AF14919FD10098216A /* Game.inl */,
-				42CD0DDE147D8FF50000361E /* gameplay-main-macos.mm */,
-				42CD0DDF147D8FF50000361E /* gameplay-main-qnx.cpp */,
-				42CD0DE0147D8FF50000361E /* gameplay-main-win32.cpp */,
 				42CD0DE1147D8FF50000361E /* gameplay.h */,
 				4208DEE614A4079F00D3C511 /* Image.cpp */,
 				4208DEE714A4079F00D3C511 /* Image.h */,
@@ -489,10 +753,6 @@
 				42CD0E16147D8FF50000361E /* Plane.cpp */,
 				42CD0E17147D8FF50000361E /* Plane.h */,
 				42CD0E18147D8FF50000361E /* Plane.inl */,
-				42CD0E19147D8FF50000361E /* Platform.h */,
-				42CD0E1A147D8FF50000361E /* PlatformMacOS.mm */,
-				42CD0E1B147D8FF50000361E /* PlatformQNX.cpp */,
-				42CD0E1C147D8FF50000361E /* PlatformWin32.cpp */,
 				42CD0E1D147D8FF50000361E /* Properties.cpp */,
 				42CD0E1E147D8FF50000361E /* Properties.h */,
 				42CD0E1F147D8FF50000361E /* Quaternion.cpp */,
@@ -538,33 +798,29 @@
 				42CD0E44147D8FF50000361E /* Viewport.cpp */,
 				42CD0E45147D8FF50000361E /* Viewport.h */,
 			);
-			name = src;
-			path = gameplay;
+			name = gameplay;
 			sourceTree = "<group>";
 		};
-		427D4F42147DC8DE0076760E /* Libraries */ = {
+		5B5ADCE014C22DBE00AC6109 /* iOS */ = {
 			isa = PBXGroup;
 			children = (
-				42CD0DA6147D8EA80000361E /* libbullet.a */,
-				42CD0DA7147D8EA80000361E /* libogg.a */,
-				42CD0DA8147D8EA80000361E /* libvorbis.a */,
-				42CD0DA9147D8EA80000361E /* libvorbisenc.a */,
-				42CD0DAA147D8EA80000361E /* libvorbisfile.a */,
-				42CCD555146EC1EB00353661 /* libpng.a */,
-				42CCD553146EC1DD00353661 /* libz.dylib */,
+				5B5DB93114C25BA5007755DB /* libogg.a */,
+				5B5DB93214C25BA5007755DB /* libvorbis.a */,
+				5B5DB93314C25BA5007755DB /* libvorbisenc.a */,
+				5B5DB93414C25BA5007755DB /* libvorbisfile.a */,
+				5B5DB92F14C25B94007755DB /* libpng.a */,
+				5B5DB92D14C25B7B007755DB /* libbullet.a */,
+				5B5ADCE414C22E1F00AC6109 /* libz.dylib */,
 			);
-			name = Libraries;
+			name = iOS;
 			sourceTree = "<group>";
 		};
-		42CCD4AF146D811D00353661 /* Frameworks */ = {
+		5B5ADCE114C22DC700AC6109 /* Mac OS X */ = {
 			isa = PBXGroup;
 			children = (
-				4234D99D14686C52003031B3 /* Cocoa.framework */,
-				4220A6E7146B122B00CAEB3A /* QuartzCore.framework */,
-				4299EFA8146AC94300FF4A73 /* OpenGL.framework */,
-				4299EFAA146AC94B00FF4A73 /* OpenAL.framework */,
+				5B5ADCE214C22DF900AC6109 /* libz.dylib */,
 			);
-			name = Frameworks;
+			name = "Mac OS X";
 			sourceTree = "<group>";
 		};
 /* End PBXGroup section */
@@ -644,15 +900,94 @@
 				4208DEEC14A407B900D3C511 /* Keyboard.h in Headers */,
 				4208DEEE14A407D500D3C511 /* Touch.h in Headers */,
 				4201819114A41B18008C3F56 /* MeshBatch.h in Headers */,
+				5BB0823D14C6FEC40019975F /* Mouse.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5B04C58014BFCFE100EB0071 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5B04C58114BFCFE100EB0071 /* Animation.h in Headers */,
+				5B04C58214BFCFE100EB0071 /* AnimationClip.h in Headers */,
+				5B04C58314BFCFE100EB0071 /* AnimationController.h in Headers */,
+				5B04C58414BFCFE100EB0071 /* AnimationTarget.h in Headers */,
+				5B04C58514BFCFE100EB0071 /* AnimationValue.h in Headers */,
+				5B04C58614BFCFE100EB0071 /* AudioBuffer.h in Headers */,
+				5B04C58714BFCFE100EB0071 /* AudioController.h in Headers */,
+				5B04C58814BFCFE100EB0071 /* AudioListener.h in Headers */,
+				5B04C58914BFCFE100EB0071 /* AudioSource.h in Headers */,
+				5B04C58A14BFCFE100EB0071 /* Base.h in Headers */,
+				5B04C58B14BFCFE100EB0071 /* BoundingBox.h in Headers */,
+				5B04C58C14BFCFE100EB0071 /* BoundingSphere.h in Headers */,
+				5B04C58D14BFCFE100EB0071 /* Camera.h in Headers */,
+				5B04C58E14BFCFE100EB0071 /* Curve.h in Headers */,
+				5B04C58F14BFCFE100EB0071 /* DebugNew.h in Headers */,
+				5B04C59014BFCFE100EB0071 /* DepthStencilTarget.h in Headers */,
+				5B04C59114BFCFE100EB0071 /* Effect.h in Headers */,
+				5B04C59214BFCFE100EB0071 /* FileSystem.h in Headers */,
+				5B04C59314BFCFE100EB0071 /* Font.h in Headers */,
+				5B04C59414BFCFE100EB0071 /* FrameBuffer.h in Headers */,
+				5B04C59514BFCFE100EB0071 /* Frustum.h in Headers */,
+				5B04C59614BFCFE100EB0071 /* Game.h in Headers */,
+				5B04C59714BFCFE100EB0071 /* gameplay.h in Headers */,
+				5B04C59814BFCFE100EB0071 /* Joint.h in Headers */,
+				5B04C59914BFCFE100EB0071 /* Light.h in Headers */,
+				5B04C59A14BFCFE100EB0071 /* Material.h in Headers */,
+				5B04C59B14BFCFE100EB0071 /* MaterialParameter.h in Headers */,
+				5B04C59C14BFCFE100EB0071 /* Matrix.h in Headers */,
+				5B04C59D14BFCFE100EB0071 /* Mesh.h in Headers */,
+				5B04C59E14BFCFE100EB0071 /* MeshPart.h in Headers */,
+				5B04C59F14BFCFE100EB0071 /* MeshSkin.h in Headers */,
+				5B04C5A014BFCFE100EB0071 /* Model.h in Headers */,
+				5B04C5A114BFCFE100EB0071 /* Node.h in Headers */,
+				5B04C5A214BFCFE100EB0071 /* Package.h in Headers */,
+				5B04C5A314BFCFE100EB0071 /* ParticleEmitter.h in Headers */,
+				5B04C5A414BFCFE100EB0071 /* Pass.h in Headers */,
+				5B04C5A514BFCFE100EB0071 /* PhysicsConstraint.h in Headers */,
+				5B04C5A614BFCFE100EB0071 /* PhysicsController.h in Headers */,
+				5B04C5A714BFCFE100EB0071 /* PhysicsFixedConstraint.h in Headers */,
+				5B04C5A814BFCFE100EB0071 /* PhysicsGenericConstraint.h in Headers */,
+				5B04C5A914BFCFE100EB0071 /* PhysicsHingeConstraint.h in Headers */,
+				5B04C5AA14BFCFE100EB0071 /* PhysicsMotionState.h in Headers */,
+				5B04C5AB14BFCFE100EB0071 /* PhysicsRigidBody.h in Headers */,
+				5B04C5AC14BFCFE100EB0071 /* PhysicsSocketConstraint.h in Headers */,
+				5B04C5AD14BFCFE100EB0071 /* PhysicsSpringConstraint.h in Headers */,
+				5B04C5AE14BFCFE100EB0071 /* Plane.h in Headers */,
+				5B04C5AF14BFCFE100EB0071 /* Platform.h in Headers */,
+				5B04C5B014BFCFE100EB0071 /* Properties.h in Headers */,
+				5B04C5B114BFCFE100EB0071 /* Quaternion.h in Headers */,
+				5B04C5B214BFCFE100EB0071 /* Ray.h in Headers */,
+				5B04C5B314BFCFE100EB0071 /* Rectangle.h in Headers */,
+				5B04C5B414BFCFE100EB0071 /* Ref.h in Headers */,
+				5B04C5B514BFCFE100EB0071 /* RenderState.h in Headers */,
+				5B04C5B614BFCFE100EB0071 /* RenderTarget.h in Headers */,
+				5B04C5B714BFCFE100EB0071 /* Scene.h in Headers */,
+				5B04C5B814BFCFE100EB0071 /* SpriteBatch.h in Headers */,
+				5B04C5B914BFCFE100EB0071 /* Technique.h in Headers */,
+				5B04C5BA14BFCFE100EB0071 /* Texture.h in Headers */,
+				5B04C5BB14BFCFE100EB0071 /* Transform.h in Headers */,
+				5B04C5BC14BFCFE100EB0071 /* Vector2.h in Headers */,
+				5B04C5BD14BFCFE100EB0071 /* Vector3.h in Headers */,
+				5B04C5BE14BFCFE100EB0071 /* Vector4.h in Headers */,
+				5B04C5BF14BFCFE100EB0071 /* VertexAttributeBinding.h in Headers */,
+				5B04C5C014BFCFE100EB0071 /* VertexFormat.h in Headers */,
+				5B04C5C114BFCFE100EB0071 /* Viewport.h in Headers */,
+				5B04C5C214BFCFE100EB0071 /* SceneLoader.h in Headers */,
+				5B04C5C314BFCFE100EB0071 /* Image.h in Headers */,
+				5B04C5C414BFCFE100EB0071 /* Keyboard.h in Headers */,
+				5B04C5C514BFCFE100EB0071 /* Touch.h in Headers */,
+				5B04C5C614BFCFE100EB0071 /* MeshBatch.h in Headers */,
+				5BB0823E14C6FEC40019975F /* Mouse.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXHeadersBuildPhase section */
 
 /* Begin PBXNativeTarget section */
-		4234D99914686C52003031B3 /* gameplay */ = {
+		4234D99914686C52003031B3 /* gameplay-macos */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 4234D9AB14686C52003031B3 /* Build configuration list for PBXNativeTarget "gameplay" */;
+			buildConfigurationList = 4234D9AB14686C52003031B3 /* Build configuration list for PBXNativeTarget "gameplay-macos" */;
 			buildPhases = (
 				4234D99614686C52003031B3 /* Sources */,
 				4234D99714686C52003031B3 /* Frameworks */,
@@ -662,11 +997,28 @@
 			);
 			dependencies = (
 			);
-			name = gameplay;
+			name = "gameplay-macos";
 			productName = gameplay;
 			productReference = 4234D99A14686C52003031B3 /* libgameplay.a */;
 			productType = "com.apple.product-type.library.static";
 		};
+		5B04C52B14BFCFE100EB0071 /* gameplay-ios */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5B04C5C714BFCFE100EB0071 /* Build configuration list for PBXNativeTarget "gameplay-ios" */;
+			buildPhases = (
+				5B04C52C14BFCFE100EB0071 /* Sources */,
+				5B04C57414BFCFE100EB0071 /* Frameworks */,
+				5B04C58014BFCFE100EB0071 /* Headers */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "gameplay-ios";
+			productName = gameplay;
+			productReference = 5B04C5CA14BFCFE100EB0071 /* libgameplay.a */;
+			productType = "com.apple.product-type.library.static";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -687,7 +1039,8 @@
 			projectDirPath = "";
 			projectRoot = "";
 			targets = (
-				4234D99914686C52003031B3 /* gameplay */,
+				4234D99914686C52003031B3 /* gameplay-macos */,
+				5B04C52B14BFCFE100EB0071 /* gameplay-ios */,
 			);
 		};
 /* End PBXProject section */
@@ -719,8 +1072,6 @@
 				42CD0E6D147D8FF60000361E /* Frustum.cpp in Sources */,
 				42CD0E6F147D8FF60000361E /* Game.cpp in Sources */,
 				42CD0E71147D8FF60000361E /* gameplay-main-macos.mm in Sources */,
-				42CD0E72147D8FF60000361E /* gameplay-main-qnx.cpp in Sources */,
-				42CD0E73147D8FF60000361E /* gameplay-main-win32.cpp in Sources */,
 				42CD0E77147D8FF60000361E /* Joint.cpp in Sources */,
 				42CD0E79147D8FF60000361E /* Light.cpp in Sources */,
 				42CD0E7B147D8FF60000361E /* Material.cpp in Sources */,
@@ -745,8 +1096,6 @@
 				42CD0EA1147D8FF60000361E /* PhysicsSpringConstraint.cpp in Sources */,
 				42CD0EA3147D8FF60000361E /* Plane.cpp in Sources */,
 				42CD0EA6147D8FF60000361E /* PlatformMacOS.mm in Sources */,
-				42CD0EA7147D8FF60000361E /* PlatformQNX.cpp in Sources */,
-				42CD0EA8147D8FF60000361E /* PlatformWin32.cpp in Sources */,
 				42CD0EA9147D8FF60000361E /* Properties.cpp in Sources */,
 				42CD0EAB147D8FF60000361E /* Quaternion.cpp in Sources */,
 				42CD0EAD147D8FF60000361E /* Ray.cpp in Sources */,
@@ -771,18 +1120,96 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		5B04C52C14BFCFE100EB0071 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5B04C52D14BFCFE100EB0071 /* Animation.cpp in Sources */,
+				5B04C52E14BFCFE100EB0071 /* AnimationClip.cpp in Sources */,
+				5B04C52F14BFCFE100EB0071 /* AnimationController.cpp in Sources */,
+				5B04C53014BFCFE100EB0071 /* AnimationTarget.cpp in Sources */,
+				5B04C53114BFCFE100EB0071 /* AnimationValue.cpp in Sources */,
+				5B04C53214BFCFE100EB0071 /* AudioBuffer.cpp in Sources */,
+				5B04C53314BFCFE100EB0071 /* AudioController.cpp in Sources */,
+				5B04C53414BFCFE100EB0071 /* AudioListener.cpp in Sources */,
+				5B04C53514BFCFE100EB0071 /* AudioSource.cpp in Sources */,
+				5B04C53614BFCFE100EB0071 /* BoundingBox.cpp in Sources */,
+				5B04C53714BFCFE100EB0071 /* BoundingSphere.cpp in Sources */,
+				5B04C53814BFCFE100EB0071 /* Camera.cpp in Sources */,
+				5B04C53914BFCFE100EB0071 /* Curve.cpp in Sources */,
+				5B04C53A14BFCFE100EB0071 /* DebugNew.cpp in Sources */,
+				5B04C53B14BFCFE100EB0071 /* DepthStencilTarget.cpp in Sources */,
+				5B04C53C14BFCFE100EB0071 /* Effect.cpp in Sources */,
+				5B04C53D14BFCFE100EB0071 /* FileSystem.cpp in Sources */,
+				5B04C53E14BFCFE100EB0071 /* Font.cpp in Sources */,
+				5B04C53F14BFCFE100EB0071 /* FrameBuffer.cpp in Sources */,
+				5B04C54014BFCFE100EB0071 /* Frustum.cpp in Sources */,
+				5B04C54114BFCFE100EB0071 /* Game.cpp in Sources */,
+				5B04C54514BFCFE100EB0071 /* Joint.cpp in Sources */,
+				5B04C54614BFCFE100EB0071 /* Light.cpp in Sources */,
+				5B04C54714BFCFE100EB0071 /* Material.cpp in Sources */,
+				5B04C54814BFCFE100EB0071 /* MaterialParameter.cpp in Sources */,
+				5B04C54914BFCFE100EB0071 /* Matrix.cpp in Sources */,
+				5B04C54A14BFCFE100EB0071 /* Mesh.cpp in Sources */,
+				5B04C54B14BFCFE100EB0071 /* MeshPart.cpp in Sources */,
+				5B04C54C14BFCFE100EB0071 /* MeshSkin.cpp in Sources */,
+				5B04C54D14BFCFE100EB0071 /* Model.cpp in Sources */,
+				5B04C54E14BFCFE100EB0071 /* Node.cpp in Sources */,
+				5B04C54F14BFCFE100EB0071 /* Package.cpp in Sources */,
+				5B04C55014BFCFE100EB0071 /* ParticleEmitter.cpp in Sources */,
+				5B04C55114BFCFE100EB0071 /* Pass.cpp in Sources */,
+				5B04C55214BFCFE100EB0071 /* PhysicsConstraint.cpp in Sources */,
+				5B04C55314BFCFE100EB0071 /* PhysicsController.cpp in Sources */,
+				5B04C55414BFCFE100EB0071 /* PhysicsFixedConstraint.cpp in Sources */,
+				5B04C55514BFCFE100EB0071 /* PhysicsGenericConstraint.cpp in Sources */,
+				5B04C55614BFCFE100EB0071 /* PhysicsHingeConstraint.cpp in Sources */,
+				5B04C55714BFCFE100EB0071 /* PhysicsMotionState.cpp in Sources */,
+				5B04C55814BFCFE100EB0071 /* PhysicsRigidBody.cpp in Sources */,
+				5B04C55914BFCFE100EB0071 /* PhysicsSocketConstraint.cpp in Sources */,
+				5B04C55A14BFCFE100EB0071 /* PhysicsSpringConstraint.cpp in Sources */,
+				5B04C55B14BFCFE100EB0071 /* Plane.cpp in Sources */,
+				5B04C55F14BFCFE100EB0071 /* Properties.cpp in Sources */,
+				5B04C56014BFCFE100EB0071 /* Quaternion.cpp in Sources */,
+				5B04C56114BFCFE100EB0071 /* Ray.cpp in Sources */,
+				5B04C56214BFCFE100EB0071 /* Rectangle.cpp in Sources */,
+				5B04C56314BFCFE100EB0071 /* Ref.cpp in Sources */,
+				5B04C56414BFCFE100EB0071 /* RenderState.cpp in Sources */,
+				5B04C56514BFCFE100EB0071 /* RenderTarget.cpp in Sources */,
+				5B04C56614BFCFE100EB0071 /* Scene.cpp in Sources */,
+				5B04C56714BFCFE100EB0071 /* SpriteBatch.cpp in Sources */,
+				5B04C56814BFCFE100EB0071 /* Technique.cpp in Sources */,
+				5B04C56914BFCFE100EB0071 /* Texture.cpp in Sources */,
+				5B04C56A14BFCFE100EB0071 /* Transform.cpp in Sources */,
+				5B04C56B14BFCFE100EB0071 /* Vector2.cpp in Sources */,
+				5B04C56C14BFCFE100EB0071 /* Vector3.cpp in Sources */,
+				5B04C56D14BFCFE100EB0071 /* Vector4.cpp in Sources */,
+				5B04C56E14BFCFE100EB0071 /* VertexAttributeBinding.cpp in Sources */,
+				5B04C56F14BFCFE100EB0071 /* VertexFormat.cpp in Sources */,
+				5B04C57014BFCFE100EB0071 /* Viewport.cpp in Sources */,
+				5B04C57114BFCFE100EB0071 /* SceneLoader.cpp in Sources */,
+				5B04C57214BFCFE100EB0071 /* Image.cpp in Sources */,
+				5B04C57314BFCFE100EB0071 /* MeshBatch.cpp in Sources */,
+				5B04C5CD14BFD48500EB0071 /* gameplay-main-ios.mm in Sources */,
+				5B04C5CE14BFD48500EB0071 /* PlatformiOS.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin XCBuildConfiguration section */
 		4234D99114686BB6003031B3 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				SUPPORTED_PLATFORMS = "iphonesimulator macosx iphoneos";
+				VALID_ARCHS = "armv7 armv6 i386 x86_64";
 			};
 			name = Debug;
 		};
 		4234D99214686BB6003031B3 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				SUPPORTED_PLATFORMS = "iphonesimulator macosx iphoneos";
+				VALID_ARCHS = "armv7 armv6 i386 x86_64";
 			};
 			name = Release;
 		};
@@ -790,7 +1217,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
 				COPY_PHASE_STRIP = NO;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
 				GCC_C_LANGUAGE_STANDARD = gnu99;
@@ -823,12 +1250,14 @@
 					"\"$(SRCROOT)/../external-deps/oggvorbis/lib/macos\"",
 				);
 				MACOSX_DEPLOYMENT_TARGET = 10.7;
-				ONLY_ACTIVE_ARCH = YES;
+				ONLY_ACTIVE_ARCH = NO;
 				PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
-				PRODUCT_NAME = "$(TARGET_NAME)";
+				PRODUCT_NAME = gameplay;
 				SDKROOT = macosx;
 				SHARED_PRECOMPS_DIR = "";
+				SUPPORTED_PLATFORMS = macosx;
 				USER_HEADER_SEARCH_PATHS = "";
+				VALID_ARCHS = "i386 x86_64";
 			};
 			name = Debug;
 		};
@@ -836,7 +1265,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
 				COPY_PHASE_STRIP = YES;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@@ -864,10 +1293,105 @@
 				);
 				MACOSX_DEPLOYMENT_TARGET = 10.7;
 				PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
-				PRODUCT_NAME = "$(TARGET_NAME)";
+				PRODUCT_NAME = gameplay;
 				SDKROOT = macosx;
 				SHARED_PRECOMPS_DIR = "";
+				SUPPORTED_PLATFORMS = macosx;
+				USER_HEADER_SEARCH_PATHS = "";
+				VALID_ARCHS = "i386 x86_64";
+			};
+			name = Release;
+		};
+		5B04C5C814BFCFE100EB0071 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PRECOMPILE_PREFIX_HEADER = NO;
+				GCC_PREFIX_HEADER = "";
+				GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+				GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_CHECK_SWITCH_STATEMENTS = NO;
+				GCC_WARN_MISSING_PARENTHESES = NO;
+				GCC_WARN_UNUSED_VARIABLE = NO;
+				HEADER_SEARCH_PATHS = (
+					"../external-deps/libpng/include",
+					"../external-deps/bullet/include",
+					"../external-deps/oggvorbis/include",
+				);
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"\"$(SRCROOT)/../external-deps/libpng/lib/ios/$(CURRENT_ARCH)\"",
+					"\"$(SRCROOT)/../external-deps/bullet/lib/ios/$(CURRENT_ARCH)\"",
+					"\"$(SRCROOT)/../external-deps/oggvorbis/lib/ios/$(CURRENT_ARCH)\"",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 10.7;
+				ONLY_ACTIVE_ARCH = YES;
+				OTHER_LDFLAGS = "-ObjC";
+				PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
+				PRODUCT_NAME = gameplay;
+				PROVISIONING_PROFILE = "";
+				SDKROOT = iphoneos;
+				SHARED_PRECOMPS_DIR = "";
+				SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
+				USER_HEADER_SEARCH_PATHS = "";
+				VALID_ARCHS = "armv7 armv6";
+			};
+			name = Debug;
+		};
+		5B04C5C914BFCFE100EB0071 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				COPY_PHASE_STRIP = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+				GCC_PRECOMPILE_PREFIX_HEADER = NO;
+				GCC_PREFIX_HEADER = "";
+				GCC_PREPROCESSOR_DEFINITIONS = "";
+				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+				GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_CHECK_SWITCH_STATEMENTS = NO;
+				GCC_WARN_MISSING_PARENTHESES = NO;
+				GCC_WARN_UNUSED_VARIABLE = NO;
+				HEADER_SEARCH_PATHS = (
+					"../external-deps/libpng/include",
+					"../external-deps/bullet/include",
+					"../external-deps/oggvorbis/include",
+				);
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"\"$(SRCROOT)/../external-deps/libpng/lib/ios/$(CURRENT_ARCH)\"",
+					"\"$(SRCROOT)/../external-deps/bullet/lib/ios/$(CURRENT_ARCH)\"",
+					"\"$(SRCROOT)/../external-deps/oggvorbis/lib/ios/$(CURRENT_ARCH)\"",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 10.7;
+				OTHER_LDFLAGS = "-ObjC";
+				PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
+				PRODUCT_NAME = gameplay;
+				PROVISIONING_PROFILE = "";
+				SDKROOT = iphoneos;
+				SHARED_PRECOMPS_DIR = "";
+				SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 				USER_HEADER_SEARCH_PATHS = "";
+				VALID_ARCHS = "armv7 armv6";
 			};
 			name = Release;
 		};
@@ -883,7 +1407,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		4234D9AB14686C52003031B3 /* Build configuration list for PBXNativeTarget "gameplay" */ = {
+		4234D9AB14686C52003031B3 /* Build configuration list for PBXNativeTarget "gameplay-macos" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				4234D9A914686C52003031B3 /* Debug */,
@@ -892,6 +1416,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		5B04C5C714BFCFE100EB0071 /* Build configuration list for PBXNativeTarget "gameplay-ios" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5B04C5C814BFCFE100EB0071 /* Debug */,
+				5B04C5C914BFCFE100EB0071 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 4234D98C14686BB6003031B3 /* Project object */;

+ 57 - 0
gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-MacOSX.xcscheme

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "4234D99914686C52003031B3"
+               BuildableName = "libgameplay.a"
+               BlueprintName = "gameplay-macos"
+               ReferencedContainer = "container:gameplay.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Debug"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 57 - 0
gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-iOS.xcscheme

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5B04C52B14BFCFE100EB0071"
+               BuildableName = "libgameplay.a"
+               BlueprintName = "gameplay-ios"
+               ReferencedContainer = "container:gameplay.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Debug"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 4 - 1
gameplay/res/shaders/colored-specular.fsh

@@ -29,7 +29,10 @@ void lighting(vec3 normalVector, vec3 cameraDirection, vec3 lightDirection, floa
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-    float diffuseIntensity = attenuation * max(0.0, dot(normalVector, lightDirection));
+	float ddot = abs(dot(normalVector, lightDirection));
+	//if (ddot < 0)
+		//ddot = abs(ddot) * 0.25f; // simulate light bounce at half intensity
+    float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
 

+ 4 - 1
gameplay/res/shaders/colored.fsh

@@ -21,7 +21,10 @@ void lighting(vec3 normalVector, vec3 lightDirection, float attenuation)
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-    float diffuseIntensity = attenuation * max(0.0, dot(normalVector, lightDirection));
+	float ddot = dot(normalVector, lightDirection);
+	if (ddot < 0)
+		ddot = abs(ddot) * 0.5f; // simulate light bounce at half intensity
+    float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
 }

+ 4 - 1
gameplay/res/shaders/diffuse-specular.fsh

@@ -25,7 +25,10 @@ void lighting(vec3 normalVector, vec3 cameraDirection, vec3 lightDirection, floa
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-    float diffuseIntensity = attenuation * max(0.0, dot(normalVector, lightDirection));
+	float ddot = dot(normalVector, lightDirection);
+	if (ddot < 0)
+		ddot = abs(ddot) * 0.5f; // simulate light bounce at half intensity
+    float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
 

+ 4 - 1
gameplay/res/shaders/diffuse.fsh

@@ -22,7 +22,10 @@ void lighting(vec3 normalVector, vec3 lightDirection, float attenuation)
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-    float diffuseIntensity = attenuation * max(0.0, dot(normalVector, lightDirection));
+	float ddot = dot(normalVector, lightDirection);
+	if (ddot < 0)
+		ddot = abs(ddot) * 0.5f; // simulate light bounce at half intensity
+    float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
 }

+ 108 - 204
gameplay/src/AnimationClip.cpp

@@ -10,9 +10,9 @@ namespace gameplay
 
 AnimationClip::AnimationClip(const char* id, Animation* animation, unsigned long startTime, unsigned long endTime)
     : _id(id), _animation(animation), _startTime(startTime), _endTime(endTime), _duration(_endTime - _startTime), _repeatCount(1.0f), 
-      _activeDuration(_duration * _repeatCount), _speed(1.0f), _isPlaying(false), _timeStarted(0), _elapsedTime(0), _runningTime(0), 
-      _crossFadeToClip(NULL), _crossFadeStart(0), _crossFadeOutElapsed(0), _crossFadeOutDuration(0), _blendWeight(1.0f), 
-      _isFadingOutStarted(false), _isFadingOut(false), _isFadingIn(false), _beginListeners(NULL), _endListeners(NULL)
+      _activeDuration(_duration * _repeatCount), _speed(1.0f), _isPlaying(false), _timeStarted(0), _elapsedTime(0), 
+      _crossFadeToClip(NULL), _crossFadeOutElapsed(0), _crossFadeOutDuration(0), _blendWeight(1.0f), _isFadingOutStarted(false), 
+      _isFadingOut(false), _isFadingIn(false), _beginListeners(NULL), _endListeners(NULL), _listeners(NULL)
 {
     assert(0 <= startTime && startTime <= animation->_duration && 0 <= endTime && endTime <= animation->_duration);
     
@@ -36,6 +36,7 @@ AnimationClip::~AnimationClip()
     SAFE_RELEASE(_crossFadeToClip);
     SAFE_DELETE(_beginListeners);
     SAFE_DELETE(_endListeners);
+    SAFE_DELETE(_listeners);
 }
 
 const char* AnimationClip::getID() const
@@ -151,37 +152,71 @@ void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
 {
     assert(clip);
 
-    if (clip->_isPlaying && clip->_isFadingOut)
-    {
-        clip->_isFadingOut = false;
-        clip->_crossFadeToClip->_isFadingIn = false;
-        SAFE_RELEASE(clip->_crossFadeToClip);
-    }
-
-    // If I already have a clip I'm fading too.. release it.
+    // If I already have a clip I'm fading to and it's not the same as the given clip release it.
+    // Assign the new clip and increase it's ref count.
+    // TODO: Do I need to anything else to the crossFade clip here before releasing?
     if (_crossFadeToClip)
         SAFE_RELEASE(_crossFadeToClip);
-
-    // Assign the clip we're fading to, and increase its ref count.
+        
     _crossFadeToClip = clip;
     _crossFadeToClip->addRef();
         
-    // Set the fade in clip to fading in, and set the duration of the fade in.
+    // Set the crossfade clip to fading in, and initialize it's blend weight to zero.
     _crossFadeToClip->_isFadingIn = true;
+    _crossFadeToClip->_blendWeight = 0.0f;
     
-    // Set this clip to fade out, and reset the elapsed time for the fade out.
+    // Set this clip to fade out, set the fade duration and reset the fade elapsed time.
     _isFadingOut = true;
     _crossFadeOutElapsed = 0;
     _crossFadeOutDuration = duration;
-    _crossFadeStart = (Game::getGameTime() - _timeStarted);
     _isFadingOutStarted = true;
     
+    // If this clip is currently not playing, we should start playing it.
     if (!_isPlaying)
         play();
 
+    // Start playing the cross fade clip.
     _crossFadeToClip->play(); 
 }
 
+void AnimationClip::addListener(AnimationClip::Listener* listener, unsigned long eventTime)
+{
+    assert(listener);
+    assert(eventTime < _duration);
+
+    listener->_listenerTime = eventTime;
+
+    if (!_listeners)
+    {
+        _listeners = new ListenerList;
+        _listeners->_list.push_front(listener);
+        if (_isPlaying)
+            _listeners->_listItr = _listeners->_list.begin();
+    }
+    else
+    {
+        for (std::list<Listener*>::iterator itr = _listeners->_list.begin(); itr != _listeners->_list.begin(); itr++)
+        {
+            if (eventTime < (*itr)->_listenerTime)
+            {
+                itr = _listeners->_list.insert(itr, listener);
+
+                // If playing, update the iterator if we need to.
+                // otherwise, it will just be set the next time the clip gets played.
+                if (_isPlaying)
+                {
+                    unsigned long currentTime = _elapsedTime % _duration;
+                    if ((_speed >= 0.0f && currentTime < eventTime && eventTime < (*_listeners->_listItr)->_listenerTime) || 
+                        (_speed <= 0 && currentTime > eventTime && eventTime > (*_listeners->_listItr)->_listenerTime))
+                        _listeners->_listItr = itr;
+                }
+
+                return;
+            }
+        }
+    }
+}
+
 void AnimationClip::addBeginListener(AnimationClip::Listener* listener)
 {
     if (!_beginListeners)
@@ -198,85 +233,94 @@ void AnimationClip::addEndListener(AnimationClip::Listener* listener)
     _endListeners->push_back(listener);
 }
 
-bool AnimationClip::update(unsigned long elapsedTime)
+bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*>* activeTargets)
 {
-    float speed = _speed;
     if (!_isPlaying)
-    {
         onBegin();
-        _elapsedTime = Game::getGameTime() - _timeStarted;
-        _runningTime = _elapsedTime * speed;
-    }
-    else
-    {
-        // Update elapsed time.
-        _elapsedTime += elapsedTime;
-        _runningTime += elapsedTime * speed;
-    }
-
-    float percentComplete = 0.0f;
+    else 
+        _elapsedTime += elapsedTime * _speed;
 
+    unsigned long currentTime = 0L;
     // Check to see if clip is complete.
-    if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0 && _runningTime >= (long) _activeDuration) || (_speed < 0 && _runningTime <= 0)))
+    if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0.0f && _elapsedTime >= (long) _activeDuration) || (_speed <= 0.0f && _elapsedTime <= 0L)))
     {
         _isPlaying = false;
-        if (_speed >= 0)
+        if (_speed >= 0.0f)
         {
-            percentComplete = _activeDuration % _duration; // Get's the fractional part of the final repeat.
-            if (percentComplete == 0.0f)
-                percentComplete = _duration;
+            currentTime = _activeDuration % _duration; // Get's the fractional part of the final repeat.
+            if (currentTime == 0L)
+                currentTime = _duration;
         }
         else
         {
-            percentComplete = 0.0f; // If we are negative speed, the end value should be 0.
+            currentTime = 0L; // If we are negative speed, the end value should be 0.
         }
     }
     else
     {
         // Gets portion/fraction of the repeat.
-        percentComplete = (float) (_runningTime % _duration);
+        currentTime = _elapsedTime % _duration;
+    }
+
+    // Notify any listeners of Animation events.
+    if (_listeners)
+    {
+        while (_listeners->_listItr != _listeners->_list.end() && (_speed >= 0.0f && currentTime % _duration >= (*_listeners->_listItr)->_listenerTime) || 
+                                                     (_speed <= 0.0f && currentTime % _duration <= (*_listeners->_listItr)->_listenerTime))
+        {
+            
+            (*_listeners->_listItr)->animationEvent(this, Listener::DEFAULT);
+            (_listeners->_listItr)++;
+        }
     }
 
     // Add back in start time, and divide by the total animation's duration to get the actual percentage complete
-    percentComplete = (float)(_startTime + percentComplete) / (float) _animation->_duration;
+    float percentComplete = (float)(_startTime + currentTime) / (float) _animation->_duration;
     
     if (_isFadingOut)
     {
         if (_isFadingOutStarted) // Calculate elapsed time since the fade out begin.
         {
-            _crossFadeOutElapsed = (_elapsedTime - _crossFadeStart) * speed;
+            _crossFadeOutElapsed = (Game::getGameTime() - _crossFadeToClip->_timeStarted) * _speed; 
             _isFadingOutStarted = false;
         }
         else
         {
             // continue tracking elapsed time.
-            _crossFadeOutElapsed += elapsedTime * speed;
+            _crossFadeOutElapsed += elapsedTime * abs(_speed);
         }
 
         if (_crossFadeOutElapsed < _crossFadeOutDuration)
         {
+            // Calculate this clip's blend weight.
             float tempBlendWeight = (float) (_crossFadeOutDuration - _crossFadeOutElapsed) / (float) _crossFadeOutDuration;
-            _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight);
             
-            // adjust the clip your blending to's weight to be a percentage of your current blend weight
+            // If this clip is fading in, adjust the crossfade clip's weight to be a percentage of your current blend weight
             if (_isFadingIn)
             {
-                _crossFadeToClip->_blendWeight *= _blendWeight;
+                _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight) * _blendWeight;
                 _blendWeight -= _crossFadeToClip->_blendWeight;
             }
             else
             {
+                // Just set the blend weight.
+                _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight);
                 _blendWeight = tempBlendWeight;
             }
         }
         else
-        {   // Fade done.
+        {   // Fade is done.
+            // Set the crossFadeToClip's blend weight to 1
             _crossFadeToClip->_blendWeight = 1.0f;
-                
+            
+            // Adjust the crossfade clip's blend weight if this clip is also fading in.
             if (_isFadingIn)
                 _crossFadeToClip->_blendWeight *= _blendWeight;
 
+            // The crossfade clip is no longer fading in
             _crossFadeToClip->_isFadingIn = false;
+
+            // Release the crossfade clip, mark ourselves as done and set our blend weight to 0.
             SAFE_RELEASE(_crossFadeToClip);
             _blendWeight = 0.0f; 
             _isFadingOut = false;
@@ -295,146 +339,16 @@ bool AnimationClip::update(unsigned long elapsedTime)
         target = channel->_target;
         value = _values[i];
 
-        // Get the current value.
-        target->getAnimationPropertyValue(channel->_propertyId, value);
-
-        bool isHighest = false;
-        // My channel priority has changed if my priority is greater than the active animation count.
-        if (!target->_highestPriority)
-        {
-            target->_highestPriority = channel;
-            value->_isFirstActing = true;
-        }
-
-        if (_blendWeight != 0.0f)
-        {
-            // Evaluate point on Curve.
-            channel->_curve->evaluate(percentComplete, value->_interpolatedValue);
-
-            if (!channel->_curve->_quaternionOffset)
-            {
-                if (value->_isFirstActing)
-                {
-                    unsigned int componentCount = value->_componentCount;
-                    for (unsigned int j = 0; j < componentCount; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] = value->_interpolatedValue[j];
-                    }
-                }
-                else
-                {
-                    unsigned int componentCount = value->_componentCount;
-                    for (unsigned int j = 0; j < componentCount; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] += value->_interpolatedValue[j];
-                    }
-                }
-            }
-            else
-            {   //We have Quaternions!!!
-                unsigned int quaternionOffset = *(channel->_curve->_quaternionOffset);
-                
-                if (value->_isFirstActing)
-                {
-                    unsigned int j = 0;
-                    for (; j < quaternionOffset; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] = value->_interpolatedValue[j];
-                    }
-
-                    // We are at the index for a quaternion component. Handle the next for components as a whole quaternion.
-                    Quaternion* interpolatedQuaternion = (Quaternion*) (value->_interpolatedValue + j);
-                    Quaternion* currentQuaternion = (Quaternion*) (value->_currentValue + j);
-
-                    // If we have a blend weight, we apply it by slerping from the identity to our interpolated value at the given weight.
-                    if (_blendWeight != 1.0f)
-                        Quaternion::slerp(Quaternion::identity(), *interpolatedQuaternion, _blendWeight, interpolatedQuaternion);
-                    
-                    // Add in contribution.
-                    currentQuaternion->set(*interpolatedQuaternion);
-                    
-                    unsigned int componentCount = value->_componentCount;
-                    for (j += 4; j < componentCount; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] = value->_interpolatedValue[j];
-                    }
-                }
-                else
-                {
-                    unsigned int j = 0;
-                    for (; j < quaternionOffset; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] += value->_interpolatedValue[j];
-                    }
-                    // We are at the index for a quaternion component. Handle the next for components as a whole quaternion.
-
-                    Quaternion* interpolatedQuaternion = (Quaternion*) (value->_interpolatedValue + j);
-                    Quaternion* currentQuaternion = (Quaternion*) (value->_currentValue + j);
-
-                    // If we have a blend weight, we apply it by slerping from the identity to our interpolated value at the given weight.
-                    if (_blendWeight != 1.0f)
-                        Quaternion::slerp(Quaternion::identity(), *interpolatedQuaternion, _blendWeight, interpolatedQuaternion);
-                    
-                    // Add in contribution.
-                    currentQuaternion->multiply(*interpolatedQuaternion);
-                    
-                    unsigned int componentCount = value->_componentCount;
-                    for (j += 4; j < componentCount; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] += value->_interpolatedValue[j];
-                    }
-                }
-            }
-        }
-        else if (value->_isFirstActing)
-        {
-            if (!channel->_curve->_quaternionOffset)
-            {
-                memset(value->_currentValue, 0.0f, value->_componentCount);
-            }
-            else
-            {
-                unsigned int quaternionOffset = *(channel->_curve->_quaternionOffset);
-                unsigned int j = 0;
-                for (; j < quaternionOffset; j++)
-                {
-                    value->_currentValue[j] = 0.0f;
-                }
-
-                // We are at the index for a quaternion component. Handle the next for components as a whole quaternion.
-                Quaternion* currentQuaternion = (Quaternion*) (value->_currentValue + j);
+        // If the target's _animationPropertyBitFlag is clear, we can assume that this is the first
+        // animation channel to act on the target and we can add the target to the list of
+        // active targets stored by the AnimationController.
+        if (target->_animationPropertyBitFlag == 0x00)
+            activeTargets->push_front(target);
 
-                // Set it to identity.
-                currentQuaternion->setIdentity();
-                
-                unsigned int componentCount = value->_componentCount;
-                for (j += 4; j < componentCount; j++)
-                {
-                    value->_currentValue[j] = 0.0f;
-                }
-            }
-        }
-        
+        // Evaluate the point on Curve
+        channel->_curve->evaluate(percentComplete, value->_value);
         // Set the animation value on the target property.
-        target->setAnimationPropertyValue(channel->_propertyId, value);
+        target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
     }
 
     // When ended. Probably should move to it's own method so we can call it when the clip is ended early.
@@ -460,17 +374,24 @@ void AnimationClip::onBegin()
 {
     // Initialize animation to play.
     _isPlaying = true;
-    _elapsedTime = 0;
 
-    if (_speed > 0)
+    if (_speed >= 0)
     {
-        _runningTime = 0;
+        _elapsedTime = 0;
+
+        if (_listeners)
+            _listeners->_listItr = _listeners->_list.begin();
     }
     else
     {
-        _runningTime = _activeDuration;
+        _elapsedTime = _activeDuration;
+
+        if (_listeners)
+            _listeners->_listItr = _listeners->_list.end();
     }
 
+    _elapsedTime += (Game::getGameTime() - _timeStarted) * _speed;
+
     // Notify begin listeners.. if any.
     if (_beginListeners)
     {
@@ -485,23 +406,6 @@ void AnimationClip::onBegin()
 
 void AnimationClip::onEnd()
 {
-    AnimationValue* value;
-    Animation::Channel* channel = NULL;
-    AnimationTarget* target = NULL;
-    unsigned int channelCount = _animation->_channels.size();
-    for (unsigned int i = 0; i < channelCount; i++)
-    {
-        value = _values[i];
-
-        if (value->_isFirstActing)
-        {
-            channel = _animation->_channels[i];
-            target = channel->_target;
-            target->_highestPriority = NULL;
-            value->_isFirstActing = false;
-        }
-    }
-
     _blendWeight = 1.0f;
     _timeStarted = 0;
 }

+ 61 - 28
gameplay/src/AnimationClip.h

@@ -32,13 +32,25 @@ public:
      */
     class Listener
     {
+        friend AnimationClip;
+
     public:
 
+        Listener() 
+            : _listenerTime(0L)
+        {
+        }
+
         /**
          * The type of animation event.
          */
         enum EventType 
         {
+            /**
+             * Default event type.
+             */
+            DEFAULT,
+
             /**
              * Event fired when the clip begins.
              */
@@ -54,6 +66,9 @@ public:
          * Handles when animation event occurs.
          */
         virtual void animationEvent(AnimationClip* clip, EventType type) = 0;
+
+    private:
+        unsigned long _listenerTime;
     };
 
     /**
@@ -177,19 +192,30 @@ public:
     void crossFade(AnimationClip* clip, unsigned long duration);
 
     /**
-     * Adds a animation begin listener.
+     * Adds an animation begin listener.
      *
-     * @param listener The listener to be called when an animation clip begins.
+     * @param listener The listener to be called when an AnimationClip begins.
      */
     void addBeginListener(AnimationClip::Listener* listener);
 
     /**
-     * Adds a animation end listener.
+     * Adds an animation end listener.
      *
-     * @param listener The listener to be called when an animation clip ends.
+     * @param listener The listener to be called when an AnimationClip ends.
      */
     void addEndListener(AnimationClip::Listener* listener);
 
+    /**
+     * Adds an animation listener to be called back at the specified eventTime during the playback 
+     * of the AnimationClip.
+     *
+     * @param listener The listener to be called when the AnimationClip reaches the 
+     *      specified time in its playback.
+     * @param eventTime The time the listener will be called during the playback of the AnimationClip. 
+     *      Must be between 0 and the duration of the AnimationClip.
+     */
+    void addListener(AnimationClip::Listener* listener, unsigned long eventTime);
+
 private:
 
     /**
@@ -215,7 +241,7 @@ private:
     /**
      * Updates the animation with the elapsed time.
      */
-    bool update(unsigned long elapsedTime);
+    bool update(unsigned long elapsedTime, std::list<AnimationTarget*>* activeTargets);
 
     /**
      * Handles when the AnimationClip begins.
@@ -227,30 +253,37 @@ private:
      */
     void onEnd();
 
-    std::string _id;                          // AnimationClip ID.
-    Animation* _animation;                    // The Animation this clip is created from.
-    unsigned long _startTime;                 // Start time of the clip.
-    unsigned long _endTime;                   // End time of the clip.
-    unsigned long _duration;                  // The total duration.
-    float _repeatCount;                       // The clip's repeat count.
-    unsigned long _activeDuration;            // The active duration of the clip.
-    float _speed;                             // The speed that the clip is playing. Default is 1.0. Negative goes in reverse.
-    bool _isPlaying;                          // A flag to indicate whether the clip is playing.
-    unsigned long _timeStarted;               // The game time when this clip was actually started.
-    unsigned long _elapsedTime;               // Time elapsed while the clip is running.
-    long _runningTime;                        // Keeps track of the Animation's relative time in respect to the active duration.
-    AnimationClip* _crossFadeToClip;          // The clip to cross fade to
-    unsigned long _crossFadeStart;            // The time at which the cross fade started.
-    unsigned long _crossFadeOutElapsed;       // The amount of time that has elapsed for the crossfade.
-    unsigned long _crossFadeOutDuration;      // The duration of the cross fade.
-    float _blendWeight;                       // The clip's blendweight
-    bool _isFadingOutStarted;                 // Flag to indicate if the cross fade started
-    bool _isFadingOut;                        // Flag to indicate if the clip is fading out
-    bool _isFadingIn;                         // Flag to indicate if the clip is fading in.
-    std::vector<AnimationValue*> _values;     // AnimationValue holder.
-    std::vector<Listener*>* _beginListeners;  // Collection of begin listeners on the clip
-    std::vector<Listener*>* _endListeners;    // Collection of end listeners on the clip
+    /**
+     * A list that keeps a pointer to the next listener event to be triggered.
+     */
+    struct ListenerList
+    {
+        std::list<Listener*> _list;
+        std::list<Listener*>::iterator _listItr;
+    };
 
+    std::string _id;                                // AnimationClip ID.
+    Animation* _animation;                          // The Animation this clip is created from.
+    unsigned long _startTime;                       // Start time of the clip.
+    unsigned long _endTime;                         // End time of the clip.
+    unsigned long _duration;                        // The total duration.
+    float _repeatCount;                             // The clip's repeat count.
+    unsigned long _activeDuration;                  // The active duration of the clip.
+    float _speed;                                   // The speed that the clip is playing. Default is 1.0. Negative goes in reverse.
+    bool _isPlaying;                                // A flag to indicate whether the clip is playing.
+    unsigned long _timeStarted;                     // The game time when this clip was actually started.
+    long _elapsedTime;                              // Time elapsed while the clip is running.
+    AnimationClip* _crossFadeToClip;                // The clip to cross fade to.
+    unsigned long _crossFadeOutElapsed;             // The amount of time that has elapsed for the crossfade.
+    unsigned long _crossFadeOutDuration;            // The duration of the cross fade.
+    float _blendWeight;                             // The clip's blendweight.
+    bool _isFadingOutStarted;                       // Flag to indicate if the cross fade started.
+    bool _isFadingOut;                              // Flag to indicate if the clip is fading out.
+    bool _isFadingIn;                               // Flag to indicate if the clip is fading in.
+    std::vector<AnimationValue*> _values;           // AnimationValue holder.
+    std::vector<Listener*>* _beginListeners;        // Collection of begin listeners on the clip.
+    std::vector<Listener*>* _endListeners;          // Collection of end listeners on the clip.
+    ListenerList* _listeners;                       // Collection of listeners on the clip.
 };
 
 }

+ 12 - 1
gameplay/src/AnimationController.cpp

@@ -333,11 +333,12 @@ void AnimationController::update(long elapsedTime)
     if (_state != RUNNING)
         return;
 
+    // Loop through running clips and call update() on them.
     std::list<AnimationClip*>::iterator clipIter = _runningClips.begin();
     while (clipIter != _runningClips.end())
     {
         AnimationClip* clip = (*clipIter);
-        if (clip->update(elapsedTime))
+        if (clip->update(elapsedTime, &_activeTargets))
         {
             SAFE_RELEASE(clip);
             clipIter = _runningClips.erase(clipIter);
@@ -347,6 +348,16 @@ void AnimationController::update(long elapsedTime)
             clipIter++;
         }
     }
+
+    // Loop through active AnimationTarget's and reset their _animationPropertyBitFlag for the next frame.
+    std::list<AnimationTarget*>::iterator targetItr = _activeTargets.begin();
+    while (targetItr != _activeTargets.end())
+    {
+        AnimationTarget* target = (*targetItr);
+        target->_animationPropertyBitFlag = 0x00;
+        targetItr++;
+    }
+    _activeTargets.clear();
     
     if (_runningClips.empty())
         _state = IDLE;

+ 2 - 1
gameplay/src/AnimationController.h

@@ -205,7 +205,8 @@ private:
     void destroyAllAnimations();
     
     State _state;                               // The current state of the AnimationController.
-    std::list<AnimationClip*> _runningClips;    // A list of currently running AnimationClips.
+    std::list<AnimationClip*> _runningClips;    // A list of running AnimationClips.
+    std::list<AnimationTarget*> _activeTargets;   // A list of animating AnimationTargets.
     std::vector<Animation*> _animations;        // A list of animations registered with the AnimationController
 };
 

+ 1 - 2
gameplay/src/AnimationTarget.cpp

@@ -2,13 +2,12 @@
 #include "AnimationTarget.h"
 #include "Animation.h"
 #include "Game.h"
-#include "Transform.h"
 
 namespace gameplay
 {
 
 AnimationTarget::AnimationTarget()
-    : _targetType(SCALAR), _highestPriority(NULL), _animationChannels(NULL)
+    : _targetType(SCALAR), _animationPropertyBitFlag(0x00), _animationChannels(NULL)
 {
 }
 

+ 4 - 3
gameplay/src/AnimationTarget.h

@@ -45,7 +45,7 @@ public:
      * @param propertyId The ID of the property on the AnimationTarget to set the animation property value on.
      * @param value The container to set the animation property value in.
      */
-    virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value) = 0;
+    virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f) = 0;
 
 protected:
     
@@ -69,6 +69,8 @@ protected:
 
     TargetType _targetType;             // The type of target this is.
 
+    char _animationPropertyBitFlag;     // Bit flag used to indicate which properties on the AnimationTarget are currently animating.
+
 private:
 
     /**
@@ -86,9 +88,8 @@ private:
      */
     static int getPropertyId(TargetType type, const char* propertyIdStr);
 
-    Animation::Channel* _highestPriority;
     std::vector<Animation::Channel*>* _animationChannels;   // Collection of all animation channels that target the AnimationTarget
-
+    
 };
 }
 

+ 7 - 9
gameplay/src/AnimationValue.cpp

@@ -5,44 +5,42 @@ namespace gameplay
 {
 
 AnimationValue::AnimationValue(unsigned int componentCount)
-  : _isFirstActing(false), _componentCount(componentCount), _componentSize(componentCount * sizeof(float))
+  : _componentCount(componentCount), _componentSize(componentCount * sizeof(float))
 {
-    _currentValue = new float[_componentCount];
-    _interpolatedValue = new float[_componentCount];
+    _value = new float[_componentCount];
 }
 
 AnimationValue::~AnimationValue()
 {
-    SAFE_DELETE_ARRAY(_currentValue);
-    SAFE_DELETE_ARRAY(_interpolatedValue);
+    SAFE_DELETE_ARRAY(_value);
 }
 
 float AnimationValue::getFloat(unsigned int index) const
 {
     assert(index < _componentCount);
 
-    return _currentValue[index];
+    return _value[index];
 }
 
 void AnimationValue::setFloat(unsigned int index, float value)
 {
     assert(index < _componentCount);
 
-    _currentValue[index] = value;
+    _value[index] = value;
 }
 
 void AnimationValue::getFloat(float* value, unsigned int offset, unsigned int length) const
 {
     assert(value && offset < _componentCount && (offset + length) <= _componentCount);
 
-    memcpy(value + offset, _currentValue, length * sizeof(float));
+    memcpy(value + offset, _value, length * sizeof(float));
 }
 
 void AnimationValue::setFloat(float* value, unsigned int offset, unsigned int length)
 {
     assert(value && offset < _componentCount && (offset + length) <= _componentCount);
 
-    memcpy(_currentValue, value + offset, length * sizeof(float));
+    memcpy(_value, value + offset, length * sizeof(float));
 }
 
 }

+ 2 - 3
gameplay/src/AnimationValue.h

@@ -72,11 +72,10 @@ private:
      */
     ~AnimationValue();
 
-    bool _isFirstActing;            // Flag indicating if this value's channel is the first to act on the target.
     unsigned int _componentCount;   // The number of float values for the property.
     unsigned int _componentSize;    // The number of bytes of memory the property is.
-    float* _currentValue;           // The current value of the property.
-    float* _interpolatedValue;      // The last interpolated value of the property.
+    float* _value;                  // The current value of the property.
+
 };
 
 }

+ 65 - 1
gameplay/src/AudioBuffer.cpp

@@ -2,24 +2,36 @@
 #include "AudioBuffer.h"
 #include "FileSystem.h"
 
+#ifdef __ANDROID__
+extern AAssetManager* __assetManager;
+#endif
+
 namespace gameplay
 {
 
 // Audio buffer cache
 static std::vector<AudioBuffer*> __buffers;
 
+#ifndef __ANDROID__
 AudioBuffer::AudioBuffer(const char* path, ALuint buffer)
     : _filePath(path), _alBuffer(buffer)
 {
 }
+#else
+AudioBuffer::AudioBuffer(const char* path) : _filePath(path)
+{
+}
+#endif
 
 AudioBuffer::~AudioBuffer()
 {
+#ifndef __ANDROID__
     if (_alBuffer)
     {
         alDeleteBuffers(1, &_alBuffer);
         _alBuffer = 0;
     }
+#endif
 }
 
 AudioBuffer* AudioBuffer::create(const char* path)
@@ -39,6 +51,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
         }
     }
 
+#ifndef __ANDROID__
     ALuint alBuffer;
     ALCenum al_error;
 
@@ -106,8 +119,58 @@ cleanup:
     if (alBuffer)
         alDeleteBuffers(1, &alBuffer);
     return NULL;
+#else
+    // Get the file header in order to determine the type.
+    AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
+    char header[12];
+    if (AAsset_read(asset, header, 12) != 12)
+    {
+        LOG_ERROR_VARG("Invalid audio buffer file: %s", path);
+        return NULL;
+    }
+
+    // Get the file descriptor for the audio file.
+    off_t start, length;
+    int fd = AAsset_openFileDescriptor(asset, &start, &length);
+    if (fd < 0)
+    {
+        LOG_ERROR_VARG("Failed to open file descriptor for asset: %s", path);
+        return NULL;
+    }
+    AAsset_close(asset);
+    SLDataLocator_AndroidFD data = {SL_DATALOCATOR_ANDROIDFD, fd, start, length};
+
+    // Set the appropriate mime type information.
+    SLDataFormat_MIME mime;
+    mime.formatType = SL_DATAFORMAT_MIME;
+    std::string pathStr = path;
+    if (memcmp(header, "RIFF", 4) == 0)
+    {
+        mime.mimeType = (SLchar*)"audio/x-wav";
+        mime.containerType = SL_CONTAINERTYPE_WAV;
+    }
+    else if (memcmp(header, "OggS", 4) == 0)
+    {
+        mime.mimeType = (SLchar*)"application/ogg";
+        mime.containerType = SL_CONTAINERTYPE_OGG;
+    }
+    else
+    {
+        LOG_ERROR_VARG("Unsupported audio file: %s", path);
+    }
+
+    buffer = new AudioBuffer(path);
+    buffer->_data = data;
+    buffer->_mime = mime;
+
+    // Add the buffer to the cache.
+    __buffers.push_back(buffer);
+
+    return buffer;
+#endif
 }
-    
+
+#ifndef __ANDROID__
 bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
 {
     unsigned char stream[12];
@@ -265,5 +328,6 @@ bool AudioBuffer::loadOgg(FILE* file, ALuint buffer)
 
     return true;
 }
+#endif
 
 }

+ 14 - 1
gameplay/src/AudioBuffer.h

@@ -18,11 +18,17 @@ class AudioBuffer : public Ref
     friend class AudioSource;
 
 private:
-
+#ifndef __ANDROID__
     /**
      * Constructor.
      */
     AudioBuffer(const char* path, ALuint buffer);
+#else
+    /**
+     * Constructor.
+     */
+    AudioBuffer(const char* path);
+#endif
 
     /**
      * Destructor.
@@ -38,12 +44,19 @@ private:
      */
     static AudioBuffer* create(const char* path);
     
+#ifndef __ANDROID__
     static bool loadWav(FILE* file, ALuint buffer);
     
     static bool loadOgg(FILE* file, ALuint buffer);
+#endif
 
     std::string _filePath;
+#ifndef __ANDROID__
     ALuint _alBuffer;
+#else
+    SLDataLocator_AndroidFD _data;
+    SLDataFormat_MIME _mime;
+#endif
 };
 
 }

+ 141 - 1
gameplay/src/AudioController.cpp

@@ -10,17 +10,27 @@ namespace gameplay
 
 std::list<AudioSource*> AudioController::_playingSources;
 
+
+#ifndef __ANDROID__
 AudioController::AudioController() 
     : _alcDevice(NULL), _alcContext(NULL)
 {
 }
+#else
+AudioController::AudioController() 
+    : _engineObject(NULL), _engineEngine(NULL), _outputMixObject(NULL), _listenerObject(NULL),
+    _listenerDoppler(NULL), _listenerLocation(NULL)
+{
+}
+#endif
 
 AudioController::~AudioController()
 {
 }
 
 void AudioController::initialize()
-{    
+{
+#ifndef __ANDROID__
     _alcDevice = alcOpenDevice (NULL);
     if (!_alcDevice)
     {
@@ -43,10 +53,52 @@ void AudioController::initialize()
     {
         LOG_ERROR_VARG("AudioController::initialize() error. Unable to make OpenAL context current. Error: %d\n", alcErr);
     }
+#else
+    // Create the engine.
+    SLresult result = slCreateEngine(&_engineObject, 0, NULL, 0, NULL, NULL);
+    if (result != SL_RESULT_SUCCESS)
+    {
+        LOG_ERROR("AudioController::initialize() error. Unable to create OpenSL engine.");
+        return;
+    }
+
+    // Realize the engine.
+    result = (*_engineObject)->Realize(_engineObject, SL_BOOLEAN_FALSE);
+    if (result != SL_RESULT_SUCCESS)
+    {
+        LOG_ERROR("AudioController::initialize() error. Unable to realize OpenSL engine.");
+        return;
+    }
+
+    // Get the engine interface in order to create other objects later on.
+    result = (*_engineObject)->GetInterface(_engineObject, SL_IID_ENGINE, &_engineEngine);
+    if (result != SL_RESULT_SUCCESS)
+    {
+        LOG_ERROR("AudioController::initialize() error. Unable to retrieve OpenSL engine interface.");
+        return;
+    }
+
+    // Create the output mix.
+    result = (*_engineEngine)->CreateOutputMix(_engineEngine, &_outputMixObject, 0, NULL, NULL);
+    if (result != SL_RESULT_SUCCESS)
+    {
+        LOG_ERROR("AudioController::initialize() error. Unable to create OpenSL output mix.");
+        return;
+    }
+
+    // Realize the output mix.
+    result = (*_outputMixObject)->Realize(_outputMixObject, SL_BOOLEAN_FALSE);
+    if (result != SL_RESULT_SUCCESS)
+    {
+        LOG_ERROR("AudioController::initialize() error. Unable to realize OpenSL output mix.");
+        return;
+    }
+#endif
 }
 
 void AudioController::finalize()
 {
+#ifndef __ANDROID__
     alcMakeContextCurrent(NULL);
     if (_alcContext)
     {
@@ -58,6 +110,20 @@ void AudioController::finalize()
         alcCloseDevice(_alcDevice);
         _alcDevice = NULL;
     }
+#else
+    if (_outputMixObject != NULL)
+    {
+        (*_outputMixObject)->Destroy(_outputMixObject);
+        _outputMixObject = NULL;
+    }
+
+    if (_engineObject != NULL)
+    {
+        (*_engineObject)->Destroy(_engineObject);
+        _engineObject = NULL;
+        _engineEngine = NULL;
+    }
+#endif
 }
 
 void AudioController::pause()
@@ -79,6 +145,9 @@ void AudioController::pause()
 
 void AudioController::resume()
 {
+#ifndef __ANDROID__    
+    alcMakeContextCurrent(_alcContext);
+#endif
     std::list<AudioSource*>::iterator itr = _playingSources.begin();
 
     // For each source that is playing, resume it.
@@ -99,10 +168,81 @@ void AudioController::update(long elapsedTime)
     AudioListener* listener = AudioListener::getInstance();
     if (listener)
     {
+#ifndef __ANDROID__
         alListenerf(AL_GAIN, listener->getGain());
         alListenerfv(AL_ORIENTATION, (ALfloat*)&listener->getOrientationForward());
         alListenerfv(AL_VELOCITY, (ALfloat*)&listener->getVelocity());
         alListenerfv(AL_POSITION, (ALfloat*)&listener->getPosition());
+#else
+        if (!_listenerObject)
+        {
+            const SLInterfaceID interfaces[3] = {SL_IID_3DDOPPLER, SL_IID_3DLOCATION};
+            const SLboolean required[3] = {SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE};
+            SLresult result = (*_engineEngine)->CreateListener(_engineEngine, &_listenerObject, 2, interfaces, required);
+            if (result != SL_RESULT_SUCCESS)
+            {
+                WARN("AudioController: failed to create listener.");
+                return;
+            }
+
+            result = (*_listenerObject)->Realize(_listenerObject, SL_BOOLEAN_FALSE);
+            if (result != SL_RESULT_SUCCESS)
+            {
+                WARN("AudioController: failed to realize listener.");
+                return;
+            }
+
+            // Get the doppler interface in order to set the listener's velocity.
+            result = (*_listenerObject)->GetInterface(_listenerObject, SL_IID_3DDOPPLER, &_listenerDoppler);
+            if (result != SL_RESULT_SUCCESS)
+            {
+                WARN("AudioController: Unable to retrieve listener doppler interface.");
+                return;
+            }
+
+            // Get the location interface in order to set the listener's position and orientation.
+            result = (*_listenerObject)->GetInterface(_listenerObject, SL_IID_3DLOCATION, &_listenerLocation);
+            if (result != SL_RESULT_SUCCESS)
+            {
+                WARN("AudioController: Unable to retrieve listener location interface.");
+                return;
+            }
+        }
+        
+        SLVec3D f;
+        f.x = listener->getOrientationForward().x;
+        f.y = listener->getOrientationForward().y;
+        f.z = listener->getOrientationForward().z;
+        SLVec3D a;
+        a.x = listener->getOrientationUp().x;
+        a.y = listener->getOrientationUp().y;
+        a.z = listener->getOrientationUp().z;
+        SLresult result = (*_listenerLocation)->SetOrientationVectors(_listenerLocation, &f, &a);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioController: Unable to set listener orientation.");
+        }
+
+        SLVec3D p;
+        p.x = listener->getPosition().x;
+        p.y = listener->getPosition().y;
+        p.z = listener->getPosition().z;
+        result = (*_listenerLocation)->SetLocationCartesian(_listenerLocation, &p);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioController: Unable to set listener location.");
+        }
+
+        SLVec3D v;
+        v.x = listener->getVelocity().x;
+        v.y = listener->getVelocity().y;
+        v.z = listener->getVelocity().z;
+        result = (*_listenerDoppler)->SetVelocityCartesian(_listenerDoppler, &v);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioController: Unable to set listener velocity.");
+        }
+#endif
     }
 }
 

+ 9 - 0
gameplay/src/AudioController.h

@@ -54,8 +54,17 @@ private:
      */
     void update(long elapsedTime);
 
+#ifndef __ANDROID__
     ALCdevice* _alcDevice;
     ALCcontext* _alcContext;
+#else
+    SLObjectItf _engineObject;
+    SLEngineItf _engineEngine;
+    SLObjectItf _outputMixObject;
+    SLObjectItf _listenerObject;
+    SL3DDopplerItf _listenerDoppler;
+    SL3DLocationItf _listenerLocation;
+#endif
     static std::list<AudioSource*> _playingSources;     // List of currently running sources.
 };
 

+ 238 - 3
gameplay/src/AudioSource.cpp

@@ -1,11 +1,15 @@
 #include "Base.h"
 #include "Node.h"
 #include "AudioBuffer.h"
+#include "AudioController.h"
 #include "AudioSource.h"
+#include "Game.h"
 
 namespace gameplay
 {
 
+
+#ifndef __ANDROID__
 AudioSource::AudioSource(AudioBuffer* buffer, ALuint source) 
     : _alSource(source), _buffer(buffer), _looped(true), _gain(1.0f), _pitch(1.0f), _node(NULL)
 {
@@ -15,14 +19,86 @@ AudioSource::AudioSource(AudioBuffer* buffer, ALuint source)
     alSourcef(_alSource, AL_GAIN, _gain);
     alSourcefv(_alSource, AL_VELOCITY, (const ALfloat*)&_velocity);
 }
+#else
+AudioSource::AudioSource(AudioBuffer* buffer, const SLObjectItf& player)
+    : _playerObject(player), _playerDoppler(NULL), _playerLocation(NULL), _playerPlay(NULL), _playerPitch(NULL),
+    _playerSeek(NULL), _playerVolume(NULL), _buffer(buffer), _looped(true), _gain(1.0f), _pitch(1.0f), _node(NULL)
+{
+    // Get the different interfaces for the OpenSL audio player that we need.
+    SLresult result = (*_playerObject)->GetInterface(_playerObject, SL_IID_3DDOPPLER, &_playerDoppler);
+    if(result != SL_RESULT_SUCCESS)
+    {
+        WARN("AudioSource::AudioSource() - Failed to get 3D doppler interface for OpenSL audio player.");
+    }
+    
+    result = (*_playerObject)->GetInterface(_playerObject, SL_IID_3DLOCATION, &_playerLocation);
+    if(result != SL_RESULT_SUCCESS)
+    {
+        WARN("AudioSource::AudioSource() - Failed to get 3D location interface for OpenSL audio player.");
+    }
+
+    result = (*_playerObject)->GetInterface(_playerObject, SL_IID_PLAY, &_playerPlay);
+    if(result != SL_RESULT_SUCCESS)
+    {
+        WARN("AudioSource::AudioSource() - Failed to get play interface for OpenSL audio player.");
+    }
+
+    result = (*_playerObject)->GetInterface(_playerObject, SL_IID_PITCH, &_playerPitch);
+    if(result != SL_RESULT_SUCCESS)
+    {
+        WARN("AudioSource::AudioSource() - Failed to get rate pitch interface for OpenSL audio player.");
+    }
+
+    result = (*_playerObject)->GetInterface(_playerObject, SL_IID_SEEK, &_playerSeek);
+    if(result != SL_RESULT_SUCCESS)
+    {
+        WARN("AudioSource::AudioSource() - Failed to get seek interface for OpenSL audio player.");
+    }
+
+    result = (*_playerObject)->GetInterface(_playerObject, SL_IID_VOLUME, &_playerVolume);
+    if(result != SL_RESULT_SUCCESS)
+    {
+        WARN("AudioSource::AudioSource() - Failed to get volume interface for OpenSL audio player.");
+    }
+
+    // Get the max volume level (used to convert from our API's parameter to OpenSL's expected units).
+    if (_playerVolume)
+    {
+        result = (*_playerVolume)->GetMaxVolumeLevel(_playerVolume, &_maxVolume);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::AudioSource() - Failed to get the max volume level for OpenSL audio player (needed for parameter conversion).");
+        }
+    }
+
+    setLooped(_looped);
+    setPitch(_pitch);
+    setGain(_gain);
+    setVelocity(_velocity);
+}
+#endif
 
 AudioSource::~AudioSource()
 {
+#ifndef __ANDROID__
     if (_alSource)
     {
         alDeleteSources(1, &_alSource);
         _alSource = 0;
     }
+#else
+    if (_playerObject)
+    {
+        (*_playerObject)->Destroy(_playerObject);
+        _playerObject = NULL;
+        _playerDoppler = NULL;
+        _playerLocation = NULL;
+        _playerPlay = NULL;
+        _playerPitch = NULL;
+        _playerSeek = NULL;
+        _playerVolume = NULL;
+    }
+#endif
 
     SAFE_RELEASE(_buffer);
 }
@@ -52,17 +128,44 @@ AudioSource* AudioSource::create(const char* path)
     if (buffer == NULL)
         return NULL;
 
+#ifndef __ANDROID__
     // Load the audio source.
-    ALuint alSource;
+    ALuint alSource = 0;
+
     alGenSources(1, &alSource);
     if (alGetError() != AL_NO_ERROR)
     {
         SAFE_RELEASE(buffer);
-        LOG_ERROR("AudioSource::createAudioSource Error generating audio source.");
+        LOG_ERROR("AudioSource::createAudioSource - Error generating audio source.");
         return NULL;
     }
-
+    
     return new AudioSource(buffer, alSource);
+#else
+    AudioController* audioController = Game::getInstance()->getAudioController();
+    SLDataLocator_OutputMix locator = {SL_DATALOCATOR_OUTPUTMIX, audioController->_outputMixObject};
+
+    SLDataSource dataSource = {&buffer->_data, &buffer->_mime};
+    SLDataSink dataSink = {&locator, NULL};
+
+    SLObjectItf player;
+    const SLInterfaceID interfaces[] = {SL_IID_3DDOPPLER, SL_IID_3DLOCATION, SL_IID_PLAY, SL_IID_PITCH, SL_IID_SEEK, SL_IID_VOLUME};
+    const SLboolean required[] = {SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE};
+    SLresult result = (*audioController->_engineEngine)->CreateAudioPlayer(audioController->_engineEngine, &player, &dataSource, &dataSink, 6, interfaces, required);
+    if (result != SL_RESULT_SUCCESS)
+    {
+        WARN("AudioSource::create - Failed to create OpenSL audio player.");
+        return NULL;
+    }
+
+    result = (*player)->Realize(player, SL_BOOLEAN_FALSE);
+    if(result != SL_RESULT_SUCCESS)
+    {
+        WARN("AudioSource::create - Failed to realize OpenSL audio player.");
+    }
+
+    return new AudioSource(buffer, player);
+#endif
 }
 
 AudioSource* AudioSource::create(Properties* properties)
@@ -114,6 +217,7 @@ AudioSource* AudioSource::create(Properties* properties)
 
 AudioSource::State AudioSource::getState() const
 {
+#ifndef __ANDROID__
     ALint state;
     alGetSourcei(_alSource, AL_SOURCE_STATE, &state);
 
@@ -128,18 +232,63 @@ AudioSource::State AudioSource::getState() const
         default:         
             return INITIAL;
     }
+#else
+    if (_playerPlay != NULL)
+    {
+        SLuint32 state;
+        SLresult result = (*_playerPlay)->GetPlayState(_playerPlay, &state);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::getState() failed to get player state.");
+        }
+
+        switch (state)
+        {
+            case SL_PLAYSTATE_PLAYING:
+                return PLAYING;
+            case SL_PLAYSTATE_PAUSED:
+                return PAUSED;
+            case SL_PLAYSTATE_STOPPED:
+                return STOPPED;
+            default:
+                return INITIAL;
+        }
+    }
+#endif
 
     return INITIAL;
 }
 
 void AudioSource::play()
 {
+#ifndef __ANDROID__
     alSourcePlay(_alSource);
+#else
+    if (_playerPlay != NULL)
+    {
+        SLresult result = (*_playerPlay)->SetPlayState(_playerPlay, SL_PLAYSTATE_PLAYING);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::play() failed to set player state.");
+        }
+    }
+#endif
 }
 
 void AudioSource::pause()
 {
+#ifndef __ANDROID__
     alSourcePause(_alSource);
+#else
+    if (_playerPlay != NULL)
+    {
+        SLresult result = (*_playerPlay)->SetPlayState(_playerPlay, SL_PLAYSTATE_PAUSED);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::pause() failed to set player state.");
+        }
+    }
+#endif
 }
 
 void AudioSource::resume()
@@ -152,12 +301,34 @@ void AudioSource::resume()
 
 void AudioSource::stop()
 {
+#ifndef __ANDROID__
     alSourceStop(_alSource);
+#else
+    if (_playerPlay != NULL)
+    {
+        SLresult result = (*_playerPlay)->SetPlayState(_playerPlay, SL_PLAYSTATE_STOPPED);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::stop() failed to set player state.");
+        }
+    }
+#endif 
 }
 
 void AudioSource::rewind()
 {
+#ifndef __ANDROID__
     alSourceRewind(_alSource);
+#else
+    if (_playerPlay != NULL)
+    {
+        SLresult result = (*_playerPlay)->SetMarkerPosition(_playerPlay, 0);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::rewind() failed to set player marker position.");
+        }
+    }
+#endif
 }
 
 bool AudioSource::isLooped() const
@@ -167,6 +338,7 @@ bool AudioSource::isLooped() const
 
 void AudioSource::setLooped(bool looped)
 {
+#ifndef __ANDROID__
      // Clear error state.
     alGetError();
     alSourcei(_alSource, AL_LOOPING, (looped) ? AL_TRUE : AL_FALSE);
@@ -176,6 +348,16 @@ void AudioSource::setLooped(bool looped)
     {
         LOG_ERROR_VARG("AudioSource::setLooped Error: %d", error);
     }
+#else
+    if (_playerSeek)
+    {
+        SLresult result = (*_playerSeek)->SetLoop(_playerSeek, looped, 0, SL_TIME_UNKNOWN);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::setLooped() failed.");
+        }
+    }
+#endif
 
     _looped = looped;
 }
@@ -187,7 +369,19 @@ float AudioSource::getGain() const
 
 void AudioSource::setGain(float gain)
 {
+#ifndef __ANDROID__
     alSourcef(_alSource, AL_GAIN, gain);
+#else
+    if (_playerVolume)
+    {
+        SLmillibel volume = (gain < MATH_EPSILON) ? SL_MILLIBEL_MIN : (10.0f * log10(gain)) * 100;
+        SLresult result = (*_playerVolume)->SetVolumeLevel(_playerVolume, volume);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::setGain() failed to set player gain.");
+        }
+    }
+#endif
     _gain = gain;
 }
 
@@ -198,7 +392,18 @@ float AudioSource::getPitch() const
 
 void AudioSource::setPitch(float pitch)
 {
+#ifndef __ANDROID__
     alSourcef(_alSource, AL_PITCH, pitch);
+#else
+    if (_playerPitch)
+    {
+        SLresult result = (*_playerPitch)->SetPitch(_playerPitch, (SLpermille)(pitch * 1000));
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::setPitch() failed to set player pitch.");
+        }
+    }
+#endif
     _pitch = pitch;
 }
 
@@ -209,7 +414,22 @@ const Vector3& AudioSource::getVelocity() const
 
 void AudioSource::setVelocity(const Vector3& velocity)
 {
+#ifndef __ANDROID__
     alSourcefv(_alSource, AL_VELOCITY, (ALfloat*)&velocity);
+#else
+    if (_playerDoppler)
+    {
+        SLVec3D v;
+        v.x = velocity.x;
+        v.y = velocity.y;
+        v.z = velocity.z;
+        SLresult result = (*_playerDoppler)->SetVelocityCartesian(_playerDoppler, &v);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::setVelocity - failed to set velocity.");
+        }
+    }
+#endif
     _velocity = velocity;
 }
 
@@ -240,7 +460,22 @@ void AudioSource::setNode(Node* node)
 
 void AudioSource::transformChanged(Transform* transform, long cookie)
 {
+#ifndef __ANDROID__
     alSourcefv(_alSource, AL_POSITION, (const ALfloat*)&transform->getTranslation());
+#else
+    if (_playerLocation)
+    {
+        SLVec3D position;
+        position.x = transform->getTranslationX();
+        position.y = transform->getTranslationY();
+        position.z = transform->getTranslationZ();
+        SLresult result = (*_playerLocation)->SetLocationCartesian(_playerLocation, &position);
+        if (result != SL_RESULT_SUCCESS)
+        {
+            WARN("AudioSource::transformChanged - failed to update location.");
+        }
+    }
+#endif
 }
 
 }

+ 21 - 0
gameplay/src/AudioSource.h

@@ -146,11 +146,21 @@ public:
 
 private:
 
+#ifndef __ANDROID__
     /**
      * Constructor that takes an AudioBuffer.
      */
     AudioSource(AudioBuffer* buffer, ALuint source);
 
+#else
+
+    /**
+     * Constructor that takes an AudioBuffer.
+     */
+    AudioSource(AudioBuffer* buffer, const SLObjectItf& player);
+#endif
+
+
     /**
      * Destructor.
      */
@@ -166,7 +176,18 @@ private:
      */
     void transformChanged(Transform* transform, long cookie);
 
+#ifndef __ANDROID__
     ALuint _alSource;
+#else
+    SLObjectItf _playerObject;
+    SL3DDopplerItf _playerDoppler;
+    SL3DLocationItf _playerLocation;
+    SLPlayItf _playerPlay;
+    SLPitchItf _playerPitch;
+    SLSeekItf _playerSeek;
+    SLVolumeItf _playerVolume;
+    SLmillibel _maxVolume;
+#endif
     AudioBuffer* _buffer;
     bool _looped;
     float _gain;

+ 78 - 15
gameplay/src/Base.h

@@ -39,6 +39,10 @@ using std::min;
 using std::max;
 using std::modf;
 
+#ifdef __ANDROID__
+#include <android/asset_manager.h>
+#endif
+
 // Common
 #ifndef NULL
 #define NULL     0
@@ -50,6 +54,31 @@ namespace gameplay
 extern void printError(const char* format, ...);
 }
 
+#ifdef __ANDROID__
+#include <android/log.h>
+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
+
+// System Errors
+#define LOG_ERROR(x) \
+    { \
+        LOGI(x); \
+        assert(#x == 0); \
+    }
+#define LOG_ERROR_VARG(x, ...) \
+    { \
+        LOGI(x, __VA_ARGS__); \
+        assert(#x == 0); \
+    }
+
+// Warning macro
+#ifdef WARN
+#undef WARN
+#endif
+#define WARN(x) LOGI(x)
+#define WARN_VARG(x, ...) LOGI(x, __VA_ARGS__)
+
+#else
+
 // System Errors
 #define LOG_ERROR(x) \
     { \
@@ -69,6 +98,8 @@ extern void printError(const char* format, ...);
 #define WARN(x) printError(x)
 #define WARN_VARG(x, ...) printError(x, __VA_ARGS__)
 
+#endif
+
 // Bullet Physics
 #include <btBulletDynamicsCommon.h>
 
@@ -123,7 +154,12 @@ extern void printError(const char* format, ...);
     #define NOMINMAX
 #endif
 
-// Audio (OpenAL)
+// Audio (OpenAL, OpenSL, OggVorbis)
+#ifdef __ANDROID__
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+#else
+
 #ifdef __QNX__
 #include <AL/al.h>
 #include <AL/alc.h>
@@ -134,19 +170,18 @@ extern void printError(const char* format, ...);
 #include <OpenAL/al.h>
 #include <OpenAL/alc.h>
 #endif
-#include <vorbis/vorbisfile.h>
 
-// Screen/Window
-#define WINDOW_WIDTH        1024
-#define WINDOW_HEIGHT       600
-#define WINDOW_VSYNC        1
-#define WINDOW_FULLSCREEN   0
+#include <vorbis/vorbisfile.h>
+#endif
 
 // Image
 #include <png.h>
 
+#define WINDOW_VSYNC        1
+#define WINDOW_FULLSCREEN   0
+
 // Graphics (OpenGL)
-#ifdef __QNX__
+#if defined (__QNX__) || defined(__ANDROID__)
     #include <EGL/egl.h>
     #include <GLES2/gl2.h>
     #include <GLES2/gl2ext.h>
@@ -155,19 +190,43 @@ extern void printError(const char* format, ...);
     extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays;
     extern PFNGLISVERTEXARRAYOESPROC glIsVertexArray;
     #define glClearDepth glClearDepthf
-   #define OPENGL_ES
+    #define OPENGL_ES
+    #define WINDOW_WIDTH    1024
+    #define WINDOW_HEIGHT   600
 #elif WIN32
     #define WIN32_LEAN_AND_MEAN
     #include <GL/glew.h>
+    #define WINDOW_WIDTH    1024
+    #define WINDOW_HEIGHT   600
 #elif __APPLE__
-#include <OpenGL/gl.h>
-#include <OpenGL/glext.h>
-#define glBindVertexArray glBindVertexArrayAPPLE
-#define glDeleteVertexArrays glDeleteVertexArraysAPPLE
-#define glGenVertexArrays glGenVertexArraysAPPLE
-#define glIsVertexArray glIsVertexArrayAPPLE
+    #include "TargetConditionals.h"
+    #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
+        #include <OpenGLES/ES2/gl.h>
+        #include <OpenGLES/ES2/glext.h>
+        #define glBindVertexArray glBindVertexArrayOES
+        #define glDeleteVertexArrays glDeleteVertexArraysOES
+        #define glGenVertexArrays glGenVertexArraysOES
+        #define glIsVertexArray glIsVertexArrayOES
+        #define glClearDepth glClearDepthf
+        #define OPENGL_ES
+        #define WINDOW_WIDTH    480
+        #define WINDOW_HEIGHT   360
+    #elif TARGET_OS_MAC
+        #include <OpenGL/gl.h>
+        #include <OpenGL/glext.h>
+        #define glBindVertexArray glBindVertexArrayAPPLE
+        #define glDeleteVertexArrays glDeleteVertexArraysAPPLE
+        #define glGenVertexArrays glGenVertexArraysAPPLE
+        #define glIsVertexArray glIsVertexArrayAPPLE
+        #define WINDOW_WIDTH    960
+        #define WINDOW_HEIGHT   640
+    #else
+        #error "Unsupported Apple Device"
+    #endif
 #endif
 
+
+
 // Graphics (GLSL)
 #define VERTEX_ATTRIBUTE_POSITION_NAME              "a_position"
 #define VERTEX_ATTRIBUTE_NORMAL_NAME                "a_normal"
@@ -250,5 +309,9 @@ extern GLenum __gl_error_code;
     #pragma warning( disable : 4996 )
 #endif
 
+#ifdef __ANDROID__
+#include <android_native_app_glue.h>
+extern void amain(struct android_app* state);
+#endif
 
 #endif

+ 3 - 3
gameplay/src/BoundingBox.cpp

@@ -69,9 +69,9 @@ bool BoundingBox::intersects(const BoundingSphere& sphere) const
 
 bool BoundingBox::intersects(const BoundingBox& box) const
 {
-    return ((min.x >= box.min.x && min.x <= max.x) || (box.min.x >= min.x && box.min.x <= max.x)) &&
-            ((min.y >= box.min.y && min.y <= max.y) || (box.min.y >= min.y && box.min.y <= max.y)) &&
-            ((min.z >= box.min.z && min.z <= max.z) || (box.min.z >= min.z && box.min.z <= max.z));
+    return ((min.x >= box.min.x && min.x <= box.max.x) || (box.min.x >= min.x && box.min.x <= max.x)) &&
+            ((min.y >= box.min.y && min.y <= box.max.y) || (box.min.y >= min.y && box.min.y <= max.y)) &&
+            ((min.z >= box.min.z && min.z <= box.max.z) || (box.min.z >= min.z && box.min.z <= max.z));
 }
 
 bool BoundingBox::intersects(const Frustum& frustum) const

+ 14 - 12
gameplay/src/Curve.cpp

@@ -703,6 +703,11 @@ void Curve::evaluate(float time, float* dst) const
     interpolateLinear(t, from, to, dst);
 }
 
+float Curve::lerp(float t, float from, float to)
+{
+    return lerpInl(t, from, to);
+}
+
 void Curve::setQuaternionOffset(unsigned int offset)
 {
     assert(offset <= (_componentCount - 4));
@@ -1078,7 +1083,7 @@ void Curve::interpolateLinear(float s, Point* from, Point* to, float* dst) const
             if (fromValue[i] == toValue[i])
                 dst[i] = fromValue[i];
             else
-                dst[i] = lerp(s, fromValue[i], toValue[i]);
+                dst[i] = lerpInl(s, fromValue[i], toValue[i]);
         }
     }
     else
@@ -1091,7 +1096,7 @@ void Curve::interpolateLinear(float s, Point* from, Point* to, float* dst) const
             if (fromValue[i] == toValue[i])
                 dst[i] = fromValue[i];
             else
-                dst[i] = lerp(s, fromValue[i], toValue[i]);
+                dst[i] = lerpInl(s, fromValue[i], toValue[i]);
         }
 
         // Handle quaternion component.
@@ -1103,25 +1108,22 @@ void Curve::interpolateLinear(float s, Point* from, Point* to, float* dst) const
             if (fromValue[i] == toValue[i])
                 dst[i] = fromValue[i];
             else
-                dst[i] = lerp(s, fromValue[i], toValue[i]);
+                dst[i] = lerpInl(s, fromValue[i], toValue[i]);
         }
     }
 }
 
 void Curve::interpolateQuaternion(float s, float* from, float* to, float* dst) const
 {
-    Quaternion quatFrom(from);
-    Quaternion quatTo(to);
-
-    // Normalize the quaternions.
-    quatFrom.normalize();
-    quatTo.normalize();
-        
     // Evaluate.
     if (s >= 0)
-        Quaternion::slerp(quatFrom, quatTo, s, (Quaternion*)dst);
+    {
+        Quaternion::slerp(from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], s, dst, dst + 1, dst + 2, dst + 3);
+    }
     else
-        Quaternion::slerp(quatTo, quatFrom, -s, (Quaternion*)dst);
+        Quaternion::slerp(to[0], to[1], to[2], to[3], from[0], from[1], from[2], from[3], s, dst, dst + 1, dst + 2, dst + 3);
+
+    //((Quaternion*) dst)->normalize();
 }
 
 int Curve::determineIndex(float time) const

+ 11 - 6
gameplay/src/Curve.h

@@ -354,6 +354,11 @@ public:
      */
     void evaluate(float time, float* dst) const;
 
+    /**
+     * Linear interpolation function.
+     */
+    static float lerp(float t, float from, float to);
+
 private:
 
     /**
@@ -460,17 +465,17 @@ private:
     Point* _points;                     // The points on the curve.
 };
 
-inline float bezier(float eq0, float eq1, float eq2, float eq3, float from, float out, float to, float in);
+inline static float bezier(float eq0, float eq1, float eq2, float eq3, float from, float out, float to, float in);
 
-inline float bspline(float eq0, float eq1, float eq2, float eq3, float c0, float c1, float c2, float c3);
+inline static float bspline(float eq0, float eq1, float eq2, float eq3, float c0, float c1, float c2, float c3);
 
-inline float hermite(float h00, float h01, float h10, float h11, float from, float out, float to, float in);
+inline static float hermite(float h00, float h01, float h10, float h11, float from, float out, float to, float in);
 
-inline float hermiteFlat(float h00, float h01, float from, float to);
+inline static float hermiteFlat(float h00, float h01, float from, float to);
 
-inline float hermiteSmooth(float h00, float h01, float h10, float h11, float from, float out, float to, float in);
+inline static float hermiteSmooth(float h00, float h01, float h10, float h11, float from, float out, float to, float in);
 
-inline float lerp(float s, float from, float to);
+inline static float lerpInl(float s, float from, float to);
 
 }
 

+ 1 - 1
gameplay/src/Curve.inl

@@ -28,7 +28,7 @@ inline float hermiteSmooth(float h00, float h01, float h10, float h11, float fro
     return h00 * from + h01 * to + h10 * out + h11 * in;
 }
 
-inline float lerp(float s, float from, float to)
+inline float lerpInl(float s, float from, float to)
 {
     return from + (to - from) * s;
 }

+ 146 - 3
gameplay/src/FileSystem.cpp

@@ -1,9 +1,64 @@
 #include "Base.h"
 #include "FileSystem.h"
 
+#ifdef WIN32
+    #include <windows.h>
+    #include <tchar.h>
+    #include <stdio.h>
+#else
+    #include <dirent.h>
+    #include <sys/stat.h>
+#endif
+
+#ifdef __ANDROID__
+extern AAssetManager* __assetManager;
+#endif
+
 namespace gameplay
 {
 
+#ifdef __ANDROID__
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+void makepath(std::string path, int mode)
+{
+    std::vector<std::string> dirs;
+    while (path.length() > 0)
+    {
+        int index = path.find('/');
+        std::string dir = (index == -1 ) ? path : path.substr(0, index);
+        if (dir.length() > 0)
+            dirs.push_back(dir);
+        
+        if (index + 1 >= path.length() || index == -1)
+            break;
+            
+        path = path.substr(index + 1);
+    }
+    
+    struct stat s;
+    std::string dirPath;
+    for (unsigned int i = 0; i < dirs.size(); i++)
+    {
+        dirPath += "/";
+        dirPath += dirs[i];
+        if (stat(dirPath.c_str(), &s) != 0)
+        {
+            // Directory does not exist.
+            if (mkdir(dirPath.c_str(), 0777) != 0)
+            {
+                WARN_VARG("Failed to create directory: '%s'", dirPath.c_str());
+                return;
+            }
+        }
+    }
+    
+    return;
+}
+#endif
+
 static std::string __resourcePath("./");
 
 FileSystem::FileSystem()
@@ -24,14 +79,102 @@ const char* FileSystem::getResourcePath()
     return __resourcePath.c_str();
 }
 
+bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
+{
+    // TODO make this method work with absolute and relative paths.
+#ifdef WIN32
+    std::string path(FileSystem::getResourcePath());
+    if (dirPath && strlen(dirPath) > 0)
+    {
+        path.append(dirPath);
+    }
+    path.append("/*");
+    // Convert char to wchar
+    std::basic_string<TCHAR> wPath;
+    wPath.assign(path.begin(), path.end());
+
+    WIN32_FIND_DATA FindFileData;
+    HANDLE hFind = FindFirstFile(wPath.c_str(), &FindFileData);
+    if (hFind == INVALID_HANDLE_VALUE) 
+    {
+        return false;
+    }
+    do
+    {
+        // Add to the list if this is not a directory
+        if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+        {
+            // Convert wchar to char
+            std::basic_string<TCHAR> wfilename(FindFileData.cFileName);
+            std::string filename;
+            filename.assign(wfilename.begin(), wfilename.end());
+            files.push_back(filename);
+        }
+    } while(FindNextFile(hFind, &FindFileData) != 0);
+
+    FindClose(hFind);
+    return true;
+#else
+    std::string path(FileSystem::getResourcePath());
+    if (dirPath && strlen(dirPath) > 0)
+	{
+		path.append(dirPath);
+	}
+    path.append("/.");
+    struct dirent* dp;
+    DIR* dir = opendir(path.c_str());
+    if (!dir)
+    {
+        return false;
+    }
+	while ((dp = readdir(dir)) != NULL)
+	{
+		std::string filepath(path);
+		filepath.append("/");
+		filepath.append(dp->d_name);
+
+		struct stat buf;
+		if (!stat(filepath.c_str(), &buf))
+		{
+            // Add to the list if this is not a directory
+			if (!S_ISDIR(buf.st_mode))
+			{
+				files.push_back(dp->d_name);
+			}
+		}
+	}
+	closedir(dir);
+    return true;
+#endif
+}
+
 FILE* FileSystem::openFile(const char* path, const char* mode)
 {
     std::string fullPath(__resourcePath);
     fullPath += path;
-
+    
+#ifdef __ANDROID__
+    std::string directoryPath = fullPath.substr(0, fullPath.rfind('/'));
+    struct stat s;
+    if (stat(directoryPath.c_str(), &s) != 0)
+        makepath(directoryPath.c_str(), 0777);
+
+    if (stat(fullPath.c_str(), &s) != 0)
+    {
+        AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
+        const void* data = AAsset_getBuffer(asset);
+        int length = AAsset_getLength(asset);
+        FILE* file = fopen(fullPath.c_str(), "wb");
+        
+        int ret = fwrite(data, sizeof(unsigned char), length, file);
+        assert(ret == length);
+        fclose(file);
+    }
+#endif
+    
     FILE* fp = fopen(fullPath.c_str(), mode);
-
-// Win32 doesnt support a asset or bundle definitions.
+    
+// Win32 doesn't support an asset or bundle definitions.
 #ifdef WIN32
     if (fp == NULL)
     {

+ 10 - 0
gameplay/src/FileSystem.h

@@ -33,6 +33,16 @@ public:
      */
     static const char* getResourcePath();
 
+    /**
+     * Lists the files in the specified directory and adds the files to the vector. Excludes directories.
+     * 
+     * @param dirPath Directory path relative to the path set in <code>setResourcePath(const char*)</code>.
+     * @param files The vector to append the files to.
+     * 
+     * @return True if successful, false if error.
+     */
+    static bool listFiles(const char* dirPath, std::vector<std::string>& files);
+
     /**
      * Opens the specified file.
      *

+ 1 - 1
gameplay/src/Font.cpp

@@ -53,7 +53,7 @@ Font::Font(const Font& copy)
 Font::~Font()
 {
     // Remove this Font from the font cache.
-    std::vector<Font*>::iterator itr = find(__fontCache.begin(), __fontCache.end(), this);
+    std::vector<Font*>::iterator itr = std::find(__fontCache.begin(), __fontCache.end(), this);
     if (itr != __fontCache.end())
     {
         __fontCache.erase(itr);

+ 10 - 0
gameplay/src/Game.cpp

@@ -175,6 +175,7 @@ void Game::frame()
 
     // Update the scheduled and running animations.
     _animationController->update(elapsedTime);
+    
     // Update the physics.
     _physicsController->update(elapsedTime);
     // Application Update.
@@ -250,4 +251,13 @@ void Game::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactI
 {
 }
 
+bool Game::mouseEvent(Mouse::MouseEvent evt, int x, int y)
+{
+    return false;
+}
+
+void Game::mouseWheelEvent(int x, int y, int delta)
+{
+}
+
 }

+ 34 - 2
gameplay/src/Game.h

@@ -3,6 +3,7 @@
 
 #include "Keyboard.h"
 #include "Touch.h"
+#include "Mouse.h"
 #include "AudioController.h"
 #include "AnimationController.h"
 #include "PhysicsController.h"
@@ -184,12 +185,20 @@ public:
      * Menu callback on menu events.
      */
     virtual void menu();
-
+    
+    /**
+     * Shows or hides the virtual keyboard (if supported).
+     *
+     * @param display true when virtual keyboard needs to be displayed; false otherwise.
+     */
+     inline void displayKeyboard(bool display);
+     
     /**
      * Keyboard callback on keyPress events.
      *
      * @param evt The key event that occured.
-     * @param key The key code that was pressed, released or repeated.
+     * @param key If evt is KEY_PRESS or KEY_RELEASE then key is the key code from Keyboard::Key.
+     *            If evt is KEY_CHAR then key is the unicode value of the character.
      * 
      * @see Keyboard::KeyEvent
      * @see Keyboard::Key
@@ -208,6 +217,29 @@ public:
      */
     virtual void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    /**
+     * Mouse callback on mouse events. If the game does not consume the mouse move event or left mouse click event
+     * then it is interpreted as a touch event instead.
+     *
+     * @param evt The mouse event that occurred.
+     * @param x The x position of the mouse in pixels. Left edge is zero.
+     * @param y The y position of the mouse in pixels. Top edge is zero.
+     *
+     * @return True if the mouse event is consumed or false if it is not consumed.
+     *
+     * @see Mouse::MouseEvent
+     */
+    virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y);
+
+    /**
+     * Mouse callback on mouse wheel events.
+     *
+     * @param x The x position of the mouse in pixels. Left edge is zero.
+     * @param y The y position of the mouse in pixels. Top edge is zero.
+     * @param delta The number of mouse wheel ticks. Positive is up (forward), negative is down (backward).
+     */
+    virtual void mouseWheelEvent(int x, int y, int delta);
+
     /**
      * Sets muli-touch is to be enabled/disabled. Default is disabled.
      *

+ 5 - 0
gameplay/src/Game.inl

@@ -61,4 +61,9 @@ inline void Game::getAccelerometerValues(float* pitch, float* roll)
     Platform::getAccelerometerValues(pitch, roll);
 }
 
+inline void Game::displayKeyboard(bool display)
+{
+    Platform::displayKeyboard(display);
+}
+
 }

+ 158 - 156
gameplay/src/Keyboard.h

@@ -20,7 +20,8 @@ public:
     enum KeyEvent
     {
         KEY_PRESS,
-        KEY_RELEASE
+        KEY_RELEASE,
+        KEY_CHAR
     };
 
     /**
@@ -28,161 +29,162 @@ public:
      */
     enum Key
     {
-        KEY_NONE,
-        KEY_PAUSE,
-        KEY_SCROLL_LOCK,
-        KEY_PRINT,
-        KEY_SYSREQ,
-        KEY_BREAK,
-        KEY_ESCAPE,
-        KEY_BACKSPACE,
-        KEY_TAB,
-        KEY_BACK_TAB,
-        KEY_RETURN,
-        KEY_CAPS_LOCK,
-        KEY_LEFT_SHIFT,
-        KEY_RIGHT_SHIFT,
-        KEY_LEFT_CTRL,
-        KEY_RIGHT_CTRL,
-        KEY_LEFT_ALT,
-        KEY_RIGHT_ALT,
-        KEY_MENU,
-        KEY_LEFT_HYPER,
-        KEY_RIGHT_HYPER,
-        KEY_INSERT,
-        KEY_HOME,
-        KEY_PG_UP,
-        KEY_DELETE,
-        KEY_END,
-        KEY_PG_DOWN,
-        KEY_LEFT_ARROW,
-        KEY_RIGHT_ARROW,
-        KEY_UP_ARROW,
-        KEY_DOWN_ARROW,
-        KEY_NUM_LOCK,
-        KEY_KP_PLUS,
-        KEY_KP_MINUS,
-        KEY_KP_MULTIPLY,
-        KEY_KP_DIVIDE,
-        KEY_KP_ENTER,
-        KEY_KP_HOME,
-        KEY_KP_UP,
-        KEY_KP_PG_UP,
-        KEY_KP_LEFT,
-        KEY_KP_FIVE,
-        KEY_KP_RIGHT,
-        KEY_KP_END,
-        KEY_KP_DOWN,
-        KEY_KP_PG_DOWN,
-        KEY_KP_INSERT,
-        KEY_KP_DELETE,
-        KEY_F1,
-        KEY_F2,
-        KEY_F3,
-        KEY_F4,
-        KEY_F5,
-        KEY_F6,
-        KEY_F7,
-        KEY_F8,
-        KEY_F9,
-        KEY_F10,
-        KEY_F11,
-        KEY_F12,
-        KEY_SPACE,
-        KEY_EXCLAM,
-        KEY_QUOTE,
-        KEY_NUMBER,
-        KEY_DOLLAR,
-        KEY_PERCENT,
-        KEY_AMPERSAND,
-        KEY_APOSTROPHE,
-        KEY_LEFT_PARENTHESIS,
-        KEY_RIGHT_PARENTHESIS,
-        KEY_ASTERISK,
-        KEY_PLUS,
-        KEY_COMMA,
-        KEY_MINUS,
-        KEY_PERIOD,
-        KEY_SLASH,
-        KEY_ZERO,
-        KEY_ONE,
-        KEY_TWO,
-        KEY_THREE,
-        KEY_FOUR,
-        KEY_FIVE,
-        KEY_SIX,
-        KEY_SEVEN,
-        KEY_EIGHT,
-        KEY_NINE,
-        KEY_COLON,
-        KEY_SEMICOLON,
-        KEY_LESS_THAN,
-        KEY_EQUAL,
-        KEY_GREATER_THAN,
-        KEY_QUESTION,
-        KEY_AT,
-        KEY_CAPITAL_A,
-        KEY_CAPITAL_B,
-        KEY_CAPITAL_C,
-        KEY_CAPITAL_D,
-        KEY_CAPITAL_E,
-        KEY_CAPITAL_F,
-        KEY_CAPITAL_G,
-        KEY_CAPITAL_H,
-        KEY_CAPITAL_I,
-        KEY_CAPITAL_J,
-        KEY_CAPITAL_K,
-        KEY_CAPITAL_L,
-        KEY_CAPITAL_M,
-        KEY_CAPITAL_N,
-        KEY_CAPITAL_O,
-        KEY_CAPITAL_P,
-        KEY_CAPITAL_Q,
-        KEY_CAPITAL_R,
-        KEY_CAPITAL_S,
-        KEY_CAPITAL_T,
-        KEY_CAPITAL_U,
-        KEY_CAPITAL_V,
-        KEY_CAPITAL_W,
-        KEY_CAPITAL_X,
-        KEY_CAPITAL_Y,
-        KEY_CAPITAL_Z,
-        KEY_LEFT_BRACKET,
-        KEY_BACK_SLASH,
-        KEY_RIGHT_BRACKET,
-        KEY_CIRCUMFLEX,
-        KEY_UNDERSCORE,
-        KEY_GRAVE,
-        KEY_A,
-        KEY_B,
-        KEY_C,
-        KEY_D,
-        KEY_E,
-        KEY_F,
-        KEY_G,
-        KEY_H,
-        KEY_I,
-        KEY_J,
-        KEY_K,
-        KEY_L,
-        KEY_M,
-        KEY_N,
-        KEY_O,
-        KEY_P,
-        KEY_Q,
-        KEY_R,
-        KEY_S,
-        KEY_T,
-        KEY_U,
-        KEY_V,
-        KEY_W,
-        KEY_X,
-        KEY_Y,
-        KEY_Z,
-        KEY_LEFT_BRACE,
-        KEY_BAR,
-        KEY_RIGHT_BRACE,
-        KEY_TILDE
+        KEY_NONE              = 0,
+        KEY_PAUSE             = 0x0013,
+        KEY_SCROLL_LOCK       = 0x1014,
+        KEY_PRINT             = 0x1061,
+        KEY_SYSREQ            = 0x106A,
+        KEY_BREAK             = 0x106B,
+        KEY_ESCAPE            = 0x001B,
+        KEY_BACKSPACE         = 0x0008,
+        KEY_TAB               = 0x0009,
+        KEY_BACK_TAB          = 0x0089,
+        KEY_RETURN            = 0x000D,
+        KEY_CAPS_LOCK         = 0x00E5,
+        KEY_SHIFT             = 0x00E1,
+        KEY_CTRL              = 0x00E3,
+        KEY_ALT               = 0x00E9,
+        KEY_MENU              = 0x1067,
+        KEY_HYPER             = 0x10ED,
+        KEY_INSERT            = 0x1063,
+        KEY_HOME              = 0x1050,
+        KEY_PG_UP             = 0x1055,
+        KEY_DELETE            = 0x10FF,
+        KEY_END               = 0x1057,
+        KEY_PG_DOWN           = 0x1056,
+        KEY_LEFT_ARROW        = 0x1051,
+        KEY_RIGHT_ARROW       = 0x1053,
+        KEY_UP_ARROW          = 0x1052,
+        KEY_DOWN_ARROW        = 0x1054,
+        KEY_NUM_LOCK          = 0x107F,
+        KEY_KP_PLUS           = 0x10AB,
+        KEY_KP_MINUS          = 0x10AD,
+        KEY_KP_MULTIPLY       = 0x10AA,
+        KEY_KP_DIVIDE         = 0x10AF,
+        KEY_KP_ENTER          = 0x108D,
+        KEY_KP_HOME           = 0x10B7,
+        KEY_KP_UP             = 0x10B8,
+        KEY_KP_PG_UP          = 0x10B9,
+        KEY_KP_LEFT           = 0x10B4,
+        KEY_KP_FIVE           = 0x10B5,
+        KEY_KP_RIGHT          = 0x10B6,
+        KEY_KP_END            = 0x10B1,
+        KEY_KP_DOWN           = 0x10B2,
+        KEY_KP_PG_DOWN        = 0x10B3,
+        KEY_KP_INSERT         = 0x10B0,
+        KEY_KP_DELETE         = 0x10AE,
+        KEY_F1                = 0x00BE,
+        KEY_F2                = 0x00BF,
+        KEY_F3                = 0x00C0,
+        KEY_F4                = 0x00C1,
+        KEY_F5                = 0x00C2,
+        KEY_F6                = 0x00C3,
+        KEY_F7                = 0x00C4,
+        KEY_F8                = 0x00C5,
+        KEY_F9                = 0x00C6,
+        KEY_F10               = 0x00C7,
+        KEY_F11               = 0x00C8,
+        KEY_F12               = 0x00C9,
+        KEY_SPACE             = ' ',
+        KEY_EXCLAM            = '!',
+        KEY_QUOTE             = '"',
+        KEY_NUMBER            = '#',
+        KEY_DOLLAR            = '$',
+        KEY_PERCENT           = '%',
+        KEY_CIRCUMFLEX        = '^',
+        KEY_AMPERSAND         = '&',
+        KEY_APOSTROPHE        = '\'',
+        KEY_LEFT_PARENTHESIS  = '(',
+        KEY_RIGHT_PARENTHESIS = ')',
+        KEY_ASTERISK          = '*',
+        KEY_PLUS              = '+',
+        KEY_COMMA             = ',',
+        KEY_MINUS             = '-',
+        KEY_PERIOD            = '.',
+        KEY_SLASH             = '/',
+        KEY_ZERO              = '0',
+        KEY_ONE               = '1',
+        KEY_TWO               = '2',
+        KEY_THREE             = '3',
+        KEY_FOUR              = '4',
+        KEY_FIVE              = '5',
+        KEY_SIX               = '6',
+        KEY_SEVEN             = '7',
+        KEY_EIGHT             = '8',
+        KEY_NINE              = '9',
+        KEY_COLON             = ':',
+        KEY_SEMICOLON         = ';',
+        KEY_LESS_THAN         = '<',
+        KEY_EQUAL             = '=',
+        KEY_GREATER_THAN      = '>',
+        KEY_QUESTION          = '?',
+        KEY_AT                = '@',
+        KEY_CAPITAL_A         = 'A',
+        KEY_CAPITAL_B         = 'B',
+        KEY_CAPITAL_C         = 'C',
+        KEY_CAPITAL_D         = 'D',
+        KEY_CAPITAL_E         = 'E',
+        KEY_CAPITAL_F         = 'F',
+        KEY_CAPITAL_G         = 'G',
+        KEY_CAPITAL_H         = 'H',
+        KEY_CAPITAL_I         = 'I',
+        KEY_CAPITAL_J         = 'J',
+        KEY_CAPITAL_K         = 'K',
+        KEY_CAPITAL_L         = 'L',
+        KEY_CAPITAL_M         = 'M',
+        KEY_CAPITAL_N         = 'N',
+        KEY_CAPITAL_O         = 'O',
+        KEY_CAPITAL_P         = 'P',
+        KEY_CAPITAL_Q         = 'Q',
+        KEY_CAPITAL_R         = 'R',
+        KEY_CAPITAL_S         = 'S',
+        KEY_CAPITAL_T         = 'T',
+        KEY_CAPITAL_U         = 'U',
+        KEY_CAPITAL_V         = 'V',
+        KEY_CAPITAL_W         = 'W',
+        KEY_CAPITAL_X         = 'X',
+        KEY_CAPITAL_Y         = 'Y',
+        KEY_CAPITAL_Z         = 'Z',
+        KEY_LEFT_BRACKET      = '[',
+        KEY_BACK_SLASH        = '\\',
+        KEY_RIGHT_BRACKET     = ']',
+        KEY_UNDERSCORE        = '_',
+        KEY_GRAVE             = '`',
+        KEY_A                 = 'a',
+        KEY_B                 = 'b',
+        KEY_C                 = 'c',
+        KEY_D                 = 'd',
+        KEY_E                 = 'e',
+        KEY_F                 = 'f',
+        KEY_G                 = 'g',
+        KEY_H                 = 'h',
+        KEY_I                 = 'i',
+        KEY_J                 = 'j',
+        KEY_K                 = 'k',
+        KEY_L                 = 'l',
+        KEY_M                 = 'm',
+        KEY_N                 = 'n',
+        KEY_O                 = 'o',
+        KEY_P                 = 'p',
+        KEY_Q                 = 'q',
+        KEY_R                 = 'r',
+        KEY_S                 = 's',
+        KEY_T                 = 't',
+        KEY_U                 = 'u',
+        KEY_V                 = 'v',
+        KEY_W                 = 'w',
+        KEY_X                 = 'x',
+        KEY_Y                 = 'y',
+        KEY_Z                 = 'z',
+        KEY_LEFT_BRACE        = '{',
+        KEY_BAR               = '|',
+        KEY_RIGHT_BRACE       = '}',
+        KEY_TILDE             = '~',
+        KEY_EURO              = 0x20AC,
+        KEY_POUND             = 0x00A3,
+        KEY_YEN               = 0x00A5,
+        KEY_MIDDLE_DOT        = 0x0095,
+        KEY_SEARCH            = 0xFFAA
     };
 
 private:

+ 98 - 18
gameplay/src/MaterialParameter.cpp

@@ -304,7 +304,7 @@ unsigned int MaterialParameter::getAnimationPropertyComponentCount(int propertyI
                     return 0;
                 case FLOAT:
                 case INT:
-                    return 1;
+                    return _count;
                 case VECTOR2:
                     return 2 * _count;
                 case VECTOR3:
@@ -329,10 +329,30 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
             switch (_type)
             {
                 case FLOAT:
-                    value->setFloat(0, _value.floatValue);
+                    if (_count == 1)
+                    {
+                        value->setFloat(0, _value.floatValue);
+                    }
+                    else
+                    {
+                        for (unsigned int i = 0; i < _count; i++)
+                        {
+                            value->setFloat(i, _value.floatPtrValue[i]);
+                        }
+                    }
                     break;
                 case INT:
-                    value->setFloat(0, _value.intValue);
+                    if (_count == 1)
+                    {
+                        value->setFloat(0, _value.intValue);
+                    }
+                    else
+                    {
+                        for (unsigned int i = 0; i < _count; i++)
+                        {
+                            value->setFloat(i, _value.intPtrValue[i]);
+                        }
+                    }
                     break;
                 case VECTOR2:
                     for (unsigned int i = 0; i < _count; i++)
@@ -359,8 +379,10 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
     }
 }
 
-void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue* value)
+void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
+    assert(blendWeight >= 0.0f && blendWeight <= 1.0f);
+
     switch (propertyId)
     {
         case ANIMATE_UNIFORM:
@@ -368,29 +390,70 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
             switch (_type)
             {
                 case FLOAT:
-                    _value.floatValue = value->getFloat(0);
+                {
+                    if (_count == 1)
+                    {
+                        if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
+                        {
+                            _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
+                            _value.floatValue = value->getFloat(0);
+                        }
+                        else
+                        {
+                            _value.floatValue = Curve::lerp(blendWeight, _value.floatValue, value->getFloat(0));
+                        }
+                    }
+                    else
+                    {
+                        applyAnimationValue(value, blendWeight, 1);
+                    }                    
                     break;
+                }
                 case INT:
-                    _value.intValue = value->getFloat(0);
-                    break;
-                case VECTOR2:
-                    for (unsigned int i = 0; i < _count; i++)
+                {
+                    if (_count == 1)
                     {
-                        value->getFloat(_value.floatPtrValue, i * 2, 2);
+                        if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
+                        {
+                            _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
+                            _value.intValue = value->getFloat(0);
+                        }
+                        else
+                        {
+                            _value.intValue = Curve::lerp(blendWeight, _value.intValue, value->getFloat(0));
+                        }
                     }
-                    break;
-                case VECTOR3:
-                    for (unsigned int i = 0; i < _count; i++)
+                    else
                     {
-                        value->getFloat(_value.floatPtrValue, i * 3, 3);
+                        if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
+                        {
+                            _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
+                            for (unsigned int i = 0; i < _count; i++)
+                                _value.intPtrValue[i] = value->getFloat(i);
+                        }
+                        else
+                        {
+                            for (unsigned int i = 0; i < _count; i++)
+                                _value.intPtrValue[i] = Curve::lerp(blendWeight, _value.intPtrValue[i], value->getFloat(i));
+                        }
                     }
                     break;
+                }
+                case VECTOR2:
+                {
+                    applyAnimationValue(value, blendWeight, 2);
+                    break;
+                }
+                case VECTOR3:
+                {
+                    applyAnimationValue(value, blendWeight, 3);
+                    break;
+                }
                 case VECTOR4:
-                    for (unsigned int i = 0; i < _count; i++)
-                    {
-                        value->getFloat(_value.floatPtrValue, i * 4, 4);
-                    }
+                {
+                    applyAnimationValue(value, blendWeight, 4);
                     break;
+                }
 
                 // UNSUPPORTED: NONE, MATRIX, METHOD, SAMPLER 
             }
@@ -398,4 +461,21 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
     }
 }
 
+void MaterialParameter::applyAnimationValue(AnimationValue* value, float blendWeight, int components)
+{
+    unsigned int count = _count * components;
+    if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
+    {
+        _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
+
+        for (unsigned int i = 0; i < count; i++)
+            _value.floatPtrValue[i] = value->getFloat(i);
+    }
+    else
+    {
+        for (unsigned int i = 0; i < count; i++)
+            _value.floatPtrValue[i] = Curve::lerp(blendWeight, _value.floatPtrValue[i], value->getFloat(i));
+    }
+}
+
 }

+ 5 - 1
gameplay/src/MaterialParameter.h

@@ -164,7 +164,7 @@ public:
     /**
      * @see AnimationTarget#setAnimationProperty
      */
-    void setAnimationPropertyValue(int propertyId, AnimationValue* value);
+    void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
 private:
 
@@ -253,6 +253,10 @@ private:
         METHOD
     } _type;
 
+    static const char ANIMATION_UNIFORM_BIT = 0x01;
+
+    void applyAnimationValue(AnimationValue* value, float blendWeight, int components);
+
     unsigned int _count;
     bool _dynamic;
     std::string _name;

+ 39 - 0
gameplay/src/Mouse.h

@@ -0,0 +1,39 @@
+#ifndef MOUSE_H_
+#define MOUSE_H_
+
+namespace gameplay
+{
+
+/**
+ * Mouse event
+ */
+class Mouse
+{
+public:
+
+    /**
+     * The mouse event type.
+     */
+    enum MouseEvent
+    {
+        MOUSE_LEFT_BUTTON_PRESS,
+        MOUSE_LEFT_BUTTON_RELEASE,
+        MOUSE_MIDDLE_BUTTON_PRESS,
+        MOUSE_MIDDLE_BUTTON_RELEASE,
+        MOUSE_RIGHT_BUTTON_PRESS,
+        MOUSE_RIGHT_BUTTON_RELEASE,
+        MOUSE_MOVE
+    };
+
+
+private:
+
+    /**
+     * Constructor. Used internally.
+     */
+    Mouse();
+};
+
+}
+
+#endif

+ 22 - 2
gameplay/src/Package.cpp

@@ -93,6 +93,26 @@ bool Package::readArray(unsigned int* length, std::vector<T>* values)
     return true;
 }
 
+template <class T>
+bool Package::readArray(unsigned int* length, std::vector<T>* values, unsigned int readSize)
+{
+    assert(sizeof(T) >= readSize);
+
+    if (!read(length))
+    {
+        return false;
+    }
+    if (*length > 0 && values)
+    {
+        values->resize(*length);
+        if (fread(&(*values)[0], readSize, *length, _file) != *length)
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
 std::string readString(FILE* fp)
 {
     unsigned int length;
@@ -900,7 +920,7 @@ Animation* Package::readAnimationChannel(Scene* scene, Animation* animation, con
     unsigned int interpolationCount;
 
     // read key times
-    if (!readArray(&keyTimesCount, &keyTimes))
+    if (!readArray(&keyTimesCount, &keyTimes, sizeof(unsigned int)))
     {
         LOG_ERROR_VARG("Failed to read %s for %s: %s", "keyTimes", "animation", id);
         return NULL;
@@ -928,7 +948,7 @@ Animation* Package::readAnimationChannel(Scene* scene, Animation* animation, con
     }
     
     // read interpolations
-    if (!readArray(&interpolationCount, &interpolation))
+    if (!readArray(&interpolationCount, &interpolation, sizeof(unsigned int)))
     {
         LOG_ERROR_VARG("Failed to read %s for %s: %s", "interpolation", "animation", id);
         return NULL;

+ 12 - 0
gameplay/src/Package.h

@@ -264,6 +264,18 @@ private:
     template <class T>
     bool readArray(unsigned int* length, std::vector<T>* values);
 
+    /**
+     * Reads an array of values and the array length from the current file position.
+     * 
+     * @param length A pointer to where the length of the array will be copied to.
+     * @param values A pointer to the vector to copy the values to. The vector will be resized if it is smaller than length.
+     * @param readSize The size that reads will be preformed at, size must be the same as or smaller then the sizeof(T)
+     * 
+     * @return True if successful, false if an error occurred.
+     */
+    template <class T>
+    bool readArray(unsigned int* length, std::vector<T>* values, unsigned int readSize);
+    
     /**
      * Reads 16 floats from the current file position.
      *

+ 2 - 1
gameplay/src/PhysicsRigidBody.h

@@ -35,7 +35,8 @@ public:
     {
         SHAPE_BOX,
         SHAPE_SPHERE,
-        SHAPE_NONE
+        SHAPE_NONE,
+        SHAPE_MAX = 10
     };
 
     /** 

+ 7 - 0
gameplay/src/Platform.h

@@ -94,6 +94,13 @@ public:
      * Swaps the frame buffer on the device.
      */
     static void swapBuffers();
+    
+    /**
+     * Shows or hides the virtual keyboard (if supported).
+     *
+     * @param display true when virtual keyboard needs to be displayed and false otherwise.
+     */
+     static void displayKeyboard(bool display);
 
 private:
 

+ 843 - 0
gameplay/src/PlatformAndroid.cpp

@@ -0,0 +1,843 @@
+#ifdef __ANDROID__
+
+#include "Base.h"
+#include "Platform.h"
+#include "FileSystem.h"
+#include "Game.h"
+#include <unistd.h>
+
+#include <android/sensor.h>
+#include <android_native_app_glue.h>
+
+#include <android/log.h>
+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
+
+#define TOUCH_COUNT_MAX     4
+
+using namespace std;
+
+struct android_app* __state;
+AAssetManager* __assetManager;
+std::string __assetsPath;
+bool __initialized = false;
+bool __destroyed = false;
+
+static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
+static EGLContext __eglContext = EGL_NO_CONTEXT;
+static EGLSurface __eglSurface = EGL_NO_SURFACE;
+static EGLConfig __eglConfig = 0;
+int __width;
+int __height;
+
+struct timespec __timespec;
+static long __timeStart;
+static long __timeAbsolute;
+static bool __vsync = WINDOW_VSYNC;
+
+ASensorManager* __sensorManager;
+ASensorEventQueue* __sensorEventQueue;
+ASensorEvent __sensorEvent;
+const ASensor* __accelerometerSensor;
+
+static int __orientationAngle = 90; // Landscape by default.
+static bool __multiTouch = false;
+bool __displayKeyboard = false;
+
+static const char* __glExtensions;
+PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
+PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL;
+PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL;
+PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;
+
+namespace gameplay
+{
+
+long timespec2millis(struct timespec *a)
+{
+    return a->tv_sec*1000 + a->tv_nsec/1000000;
+}
+
+extern void printError(const char* format, ...)
+{
+    va_list argptr;
+    va_start(argptr, format);
+    LOGI(format, argptr);
+    va_end(argptr);
+}
+
+EGLenum checkErrorEGL(const char* msg)
+{
+    static const char* errmsg[] =
+    {
+        "EGL function succeeded",
+        "EGL is not initialized, or could not be initialized, for the specified display",
+        "EGL cannot access a requested resource",
+        "EGL failed to allocate resources for the requested operation",
+        "EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list",
+        "EGLConfig argument does not name a valid EGLConfig",
+        "EGLContext argument does not name a valid EGLContext",
+        "EGL current surface of the calling thread is no longer valid",
+        "EGLDisplay argument does not name a valid EGLDisplay",
+        "EGL arguments are inconsistent",
+        "EGLNativePixmapType argument does not refer to a valid native pixmap",
+        "EGLNativeWindowType argument does not refer to a valid native window",
+        "EGL one or more argument values are invalid",
+        "EGLSurface argument does not name a valid surface configured for rendering",
+        "EGL power management event has occurred",
+    };
+    EGLenum error = eglGetError();
+    LOGI("%s: %s\n", msg, errmsg[error - EGL_SUCCESS]);
+    return error;
+}
+
+// Initialized EGL resources.
+bool initEGL()
+{
+    // Hard-coded to 32-bit/OpenGL ES 2.0.
+    const EGLint eglConfigAttrs[] =
+    {
+        EGL_RED_SIZE,           8,
+        EGL_GREEN_SIZE,         8,
+        EGL_BLUE_SIZE,          8,
+        EGL_ALPHA_SIZE,         8,
+        EGL_DEPTH_SIZE,         24,
+        EGL_STENCIL_SIZE,       8,
+        EGL_SURFACE_TYPE,       EGL_WINDOW_BIT,
+        EGL_RENDERABLE_TYPE,    EGL_OPENGL_ES2_BIT,
+        EGL_NONE
+    };
+    
+    EGLint eglConfigCount;
+    const EGLint eglContextAttrs[] =
+    {
+        EGL_CONTEXT_CLIENT_VERSION,    2,
+        EGL_NONE
+    };
+
+    const EGLint eglSurfaceAttrs[] =
+    {
+        EGL_RENDER_BUFFER,    EGL_BACK_BUFFER,
+        EGL_NONE
+    };
+
+    // Get the EGL display and initialize.
+    __eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (__eglDisplay == EGL_NO_DISPLAY)
+    {
+        checkErrorEGL("eglGetDisplay");
+        goto error;
+    }
+    
+    if (eglInitialize(__eglDisplay, NULL, NULL) != EGL_TRUE)
+    {
+        checkErrorEGL("eglInitialize");
+        goto error;
+    }
+    
+    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) != EGL_TRUE || eglConfigCount == 0)
+    {
+        checkErrorEGL("eglChooseConfig");
+        goto error;
+    }
+    
+    __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
+    if (__eglContext == EGL_NO_CONTEXT)
+    {
+        checkErrorEGL("eglCreateContext");
+        goto error;
+    }
+    
+    // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
+    // guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
+    // As soon as we picked a EGLConfig, we can safely reconfigure the
+    // ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID.
+    EGLint format;
+    eglGetConfigAttrib(__eglDisplay, __eglConfig, EGL_NATIVE_VISUAL_ID, &format);
+    ANativeWindow_setBuffersGeometry(__state->window, 0, 0, format);
+    
+    __eglSurface = eglCreateWindowSurface(__eglDisplay, __eglConfig, __state->window, eglSurfaceAttrs);
+    if (__eglSurface == EGL_NO_SURFACE)
+    {
+        checkErrorEGL("eglCreateWindowSurface");
+        goto error;
+    }
+    
+    if (eglMakeCurrent(__eglDisplay, __eglSurface, __eglSurface, __eglContext) != EGL_TRUE)
+    {
+        checkErrorEGL("eglMakeCurrent");
+        goto error;
+    }
+    
+    eglQuerySurface(__eglDisplay, __eglSurface, EGL_WIDTH, &__width);
+    eglQuerySurface(__eglDisplay, __eglSurface, EGL_HEIGHT, &__height);
+    
+    // Set vsync.
+    eglSwapInterval(__eglDisplay, WINDOW_VSYNC ? 1 : 0);
+    
+    // Initialize OpenGL ES extensions.
+    __glExtensions = (const char*)glGetString(GL_EXTENSIONS);
+    
+    if (strstr(__glExtensions, "GL_OES_vertex_array_object") || strstr(__glExtensions, "GL_ARB_vertex_array_object"))
+    {
+        // Disable VAO extension for now.
+        glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
+        glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArrays");
+        glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
+        glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES");
+    }
+    
+    return true;
+    
+error:
+
+    return false;
+}
+
+// Display the android virtual keyboard.
+void displayKeyboard(android_app* state, bool pShow)
+{ 
+    
+    // The following functions is supposed to show / hide functins from a native activity.. but currently
+    // do not work. 
+    // ANativeActivity_showSoftInput(state->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT);
+    // ANativeActivity_hideSoftInput(state->activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY);
+    
+    // Show or hide the keyboard by calling the appropriate Java method through JNI instead.
+    // Attaches the current thread to the JVM.
+    jint lResult;
+    jint lFlags = 0;
+    JavaVM* lJavaVM = state->activity->vm;
+    JNIEnv* lJNIEnv = state->activity->env; 
+    JavaVMAttachArgs lJavaVMAttachArgs;
+    lJavaVMAttachArgs.version = JNI_VERSION_1_6;
+    lJavaVMAttachArgs.name = "NativeThread";
+    lJavaVMAttachArgs.group = NULL;
+    lResult=lJavaVM->AttachCurrentThread(&lJNIEnv, &lJavaVMAttachArgs); 
+    if (lResult == JNI_ERR)
+    { 
+        return; 
+    } 
+    // Retrieves NativeActivity. 
+    jobject lNativeActivity = state->activity->clazz;
+    jclass ClassNativeActivity = lJNIEnv->GetObjectClass(lNativeActivity);
+
+    // Retrieves Context.INPUT_METHOD_SERVICE.
+    jclass ClassContext = lJNIEnv->FindClass("android/content/Context");
+    jfieldID FieldINPUT_METHOD_SERVICE = lJNIEnv->GetStaticFieldID(ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;");
+    jobject INPUT_METHOD_SERVICE = lJNIEnv->GetStaticObjectField(ClassContext, FieldINPUT_METHOD_SERVICE);
+    
+    // Runs getSystemService(Context.INPUT_METHOD_SERVICE).
+    jclass ClassInputMethodManager = lJNIEnv->FindClass("android/view/inputmethod/InputMethodManager");
+    jmethodID MethodGetSystemService = lJNIEnv->GetMethodID(ClassNativeActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+    jobject lInputMethodManager = lJNIEnv->CallObjectMethod(lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE);
+    
+    // Runs getWindow().getDecorView().
+    jmethodID MethodGetWindow = lJNIEnv->GetMethodID(ClassNativeActivity, "getWindow", "()Landroid/view/Window;");
+    jobject lWindow = lJNIEnv->CallObjectMethod(lNativeActivity, MethodGetWindow);
+    jclass ClassWindow = lJNIEnv->FindClass("android/view/Window");
+    jmethodID MethodGetDecorView = lJNIEnv->GetMethodID(ClassWindow, "getDecorView", "()Landroid/view/View;");
+    jobject lDecorView = lJNIEnv->CallObjectMethod(lWindow, MethodGetDecorView);
+    if (pShow)
+    {
+        // Runs lInputMethodManager.showSoftInput(...).
+        jmethodID MethodShowSoftInput = lJNIEnv->GetMethodID( ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
+        jboolean lResult = lJNIEnv->CallBooleanMethod(lInputMethodManager, MethodShowSoftInput, lDecorView, lFlags); 
+    } 
+    else 
+    { 
+        // Runs lWindow.getViewToken() 
+        jclass ClassView = lJNIEnv->FindClass("android/view/View");
+        jmethodID MethodGetWindowToken = lJNIEnv->GetMethodID(ClassView, "getWindowToken", "()Landroid/os/IBinder;");
+        jobject lBinder = lJNIEnv->CallObjectMethod(lDecorView, MethodGetWindowToken); 
+        
+        // lInputMethodManager.hideSoftInput(...). 
+        jmethodID MethodHideSoftInput = lJNIEnv->GetMethodID(ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z"); 
+        jboolean lRes = lJNIEnv->CallBooleanMethod( lInputMethodManager, MethodHideSoftInput, lBinder, lFlags); 
+    }
+    
+    // Finished with the JVM.
+    lJavaVM->DetachCurrentThread(); 
+}
+
+// Gets the Keyboard::Key enumeration constant that corresponds to the given Android key code.
+Keyboard::Key getKey(int keycode, int metastate)
+{
+    bool shiftOn = (metastate == AMETA_SHIFT_ON);
+    
+    switch(keycode)
+    {
+        case AKEYCODE_HOME:
+            return Keyboard::KEY_HOME;
+        case AKEYCODE_0:
+            return Keyboard::KEY_ZERO;
+        case AKEYCODE_1:
+            return Keyboard::KEY_ONE;
+        case AKEYCODE_2:
+            return Keyboard::KEY_TWO;
+        case AKEYCODE_3:
+            return Keyboard::KEY_THREE;
+        case AKEYCODE_4:
+            return Keyboard::KEY_FOUR;
+        case AKEYCODE_5:
+            return Keyboard::KEY_FIVE;
+        case AKEYCODE_6:
+            return Keyboard::KEY_SIX;
+        case AKEYCODE_7:
+            return Keyboard::KEY_SEVEN;
+        case AKEYCODE_8:
+            return Keyboard::KEY_EIGHT;
+        case AKEYCODE_9:
+            return Keyboard::KEY_NINE;
+        case AKEYCODE_STAR:
+            return Keyboard::KEY_ASTERISK;
+        case AKEYCODE_POUND:
+            return Keyboard::KEY_NUMBER;
+        case AKEYCODE_DPAD_UP:
+            return Keyboard::KEY_UP_ARROW;
+        case AKEYCODE_DPAD_DOWN:
+            return Keyboard::KEY_DOWN_ARROW;
+        case AKEYCODE_DPAD_LEFT:
+            return Keyboard::KEY_LEFT_ARROW;
+        case AKEYCODE_DPAD_RIGHT:
+            return Keyboard::KEY_RIGHT_ARROW;
+        case AKEYCODE_A:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
+        case AKEYCODE_B:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
+       case AKEYCODE_C:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
+        case AKEYCODE_D:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
+        case AKEYCODE_E:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
+        case AKEYCODE_F:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
+        case AKEYCODE_G:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
+        case AKEYCODE_H:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
+        case AKEYCODE_I:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
+        case AKEYCODE_J:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
+        case AKEYCODE_K:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
+        case AKEYCODE_L:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
+        case AKEYCODE_M:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
+        case AKEYCODE_N:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
+        case AKEYCODE_O:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
+        case AKEYCODE_P:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
+        case AKEYCODE_Q:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
+        case AKEYCODE_R:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
+        case AKEYCODE_S:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
+        case AKEYCODE_T:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
+        case AKEYCODE_U:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
+        case AKEYCODE_V:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
+        case AKEYCODE_W:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
+        case AKEYCODE_X:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
+        case AKEYCODE_Y:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
+        case AKEYCODE_Z:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
+        case AKEYCODE_COMMA:
+            return Keyboard::KEY_COMMA;
+        case AKEYCODE_PERIOD:
+            return Keyboard::KEY_PERIOD;
+        case AKEYCODE_ALT_LEFT:
+        case AKEYCODE_ALT_RIGHT:
+            return Keyboard::KEY_ALT;
+        case AKEYCODE_SHIFT_LEFT:
+        case AKEYCODE_SHIFT_RIGHT:
+            return Keyboard::KEY_SHIFT;
+        case AKEYCODE_TAB:
+            return Keyboard::KEY_TAB;
+        case AKEYCODE_SPACE:
+            return Keyboard::KEY_SPACE;
+        case AKEYCODE_ENTER:
+            return Keyboard::KEY_RETURN;
+        case AKEYCODE_DEL:
+            return Keyboard::KEY_DELETE;
+        case AKEYCODE_GRAVE:
+            return Keyboard::KEY_GRAVE;
+        case AKEYCODE_MINUS:
+            return Keyboard::KEY_MINUS;
+        case AKEYCODE_EQUALS:
+            return Keyboard::KEY_EQUAL;
+        case AKEYCODE_LEFT_BRACKET:
+            return Keyboard::KEY_LEFT_BRACKET;
+        case AKEYCODE_RIGHT_BRACKET:
+            return Keyboard::KEY_RIGHT_BRACKET;
+        case AKEYCODE_BACKSLASH:
+            return Keyboard::KEY_BACK_SLASH;
+        case AKEYCODE_SEMICOLON:
+            return Keyboard::KEY_SEMICOLON;
+        case AKEYCODE_APOSTROPHE:
+            return Keyboard::KEY_APOSTROPHE;
+        case AKEYCODE_SLASH:
+            return Keyboard::KEY_SLASH;
+        case AKEYCODE_AT:
+            return Keyboard::KEY_AT;
+        case AKEYCODE_PLUS:
+            return Keyboard::KEY_PLUS;
+        case AKEYCODE_PAGE_UP:
+            return Keyboard::KEY_PG_UP;
+        case AKEYCODE_PAGE_DOWN:
+            return Keyboard::KEY_PG_DOWN;
+        case AKEYCODE_MENU:
+            return Keyboard::KEY_MENU;
+        case AKEYCODE_SEARCH:
+            return Keyboard::KEY_SEARCH;
+        default:
+            return Keyboard::KEY_NONE;
+    }
+}
+
+/**
+ * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
+ */
+static int getUnicode(int keycode, int metastate)
+{
+    if (keycode == AKEYCODE_DEL)
+        return 0x0008;
+    // TODO: Doesn't support unicode currently.
+    Keyboard::Key key = getKey(keycode, metastate);
+    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;
+    }
+}
+
+// Process the next input event.
+static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
+{
+    if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION)
+    {
+        int32_t data = AMotionEvent_getAction(event);
+        Touch::TouchEvent touchEvent;
+        size_t pointerCount = AMotionEvent_getPointerCount(event);
+        for (size_t i = 0; i < pointerCount; ++i)
+        {
+            switch (data & AMOTION_EVENT_ACTION_MASK)
+            {
+                case AMOTION_EVENT_ACTION_DOWN:
+                    touchEvent = Touch::TOUCH_PRESS;
+                    break;
+                case AMOTION_EVENT_ACTION_UP:
+                    touchEvent = Touch::TOUCH_RELEASE;
+                    break;
+                case AMOTION_EVENT_ACTION_MOVE:
+                    touchEvent = Touch::TOUCH_MOVE;
+                    break;
+            }
+            size_t pointerId = AMotionEvent_getPointerId(event, i);
+            Game::getInstance()->touchEvent(touchEvent, AMotionEvent_getX(event, i), AMotionEvent_getY(event, i), pointerId);
+        }
+        return 1;
+    } 
+    else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY)
+    {
+        int32_t action = AKeyEvent_getAction(event);
+        int32_t keycode = AKeyEvent_getKeyCode(event);
+        int32_t metastate = AKeyEvent_getMetaState(event); 
+        
+        switch(action)
+        {
+            case AKEY_EVENT_ACTION_DOWN:
+                Game::getInstance()->keyEvent(Keyboard::KEY_PRESS, getKey(keycode, metastate));
+                if (int character = getUnicode(keycode, metastate))
+                    Game::getInstance()->keyEvent(Keyboard::KEY_CHAR, character);
+                break;
+                    
+            case AKEY_EVENT_ACTION_UP:
+                Game::getInstance()->keyEvent(Keyboard::KEY_RELEASE, getKey(keycode, metastate));
+                break;
+        }
+    }
+    return 0;
+}
+
+// Process the next main command.
+static void engine_handle_cmd(struct android_app* app, int32_t cmd) 
+{
+    switch (cmd) 
+    {
+        case APP_CMD_INIT_WINDOW:
+            // The window is being shown, get it ready.
+            if (app->window != NULL)
+            {
+                __initialized = true;
+            }
+            break;
+        case APP_CMD_TERM_WINDOW:
+            {
+                __destroyed = true;
+                break;
+            }
+        case APP_CMD_GAINED_FOCUS:
+            // When our app gains focus, we start monitoring the accelerometer.
+            if (__accelerometerSensor != NULL) 
+            {
+                ASensorEventQueue_enableSensor(__sensorEventQueue, __accelerometerSensor);
+                // We'd like to get 60 events per second (in us).
+                ASensorEventQueue_setEventRate(__sensorEventQueue, __accelerometerSensor, (1000L/60)*1000);
+            }
+            Game::getInstance()->resume();
+            break;
+        case APP_CMD_LOST_FOCUS:
+            // When our app loses focus, we stop monitoring the accelerometer.
+            // This is to avoid consuming battery while not being used.
+            if (__accelerometerSensor != NULL) 
+            {
+                ASensorEventQueue_disableSensor(__sensorEventQueue, __accelerometerSensor);
+            }
+            Game::getInstance()->pause();
+            break;
+    }
+}
+
+Platform::Platform(Game* game)
+    : _game(game)
+{
+}
+
+Platform::Platform(const Platform& copy)
+{
+    // hidden
+}
+
+Platform::~Platform()
+{
+    if (__eglDisplay != EGL_NO_DISPLAY)
+    {
+        eglMakeCurrent(__eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    }
+
+    if (__eglSurface != EGL_NO_SURFACE)
+    {
+        eglDestroySurface(__eglDisplay, __eglSurface);
+        __eglSurface = EGL_NO_SURFACE;
+    }
+
+    if (__eglContext != EGL_NO_CONTEXT)
+    {
+        eglDestroyContext(__eglDisplay, __eglContext);
+        __eglContext = EGL_NO_CONTEXT;
+    }
+
+    if (__eglDisplay != EGL_NO_DISPLAY)
+    {
+        eglTerminate(__eglDisplay);
+        __eglDisplay = EGL_NO_DISPLAY;
+    }
+}
+
+Platform* Platform::create(Game* game)
+{
+    Platform* platform = new Platform(game);
+    
+    return platform;
+}
+
+int Platform::enterMessagePump()
+{
+    // Get the android application's activity.
+    ANativeActivity* activity = __state->activity;
+    JNIEnv* env = activity->env;
+
+    // Get the package name for this app from Java.
+    jclass clazz = env->GetObjectClass(activity->clazz);
+    jmethodID methodID = env->GetMethodID(clazz, "getPackageName", "()Ljava/lang/String;");
+    jobject result = env->CallObjectMethod(activity->clazz, methodID);
+    
+    const char* packageName;
+    jboolean isCopy;
+    packageName = env->GetStringUTFChars((jstring)result, &isCopy);
+    
+    // Set the default path to store the resources.
+    __assetsPath = "/mnt/sdcard/android/data/";
+    __assetsPath += packageName;
+    __assetsPath += "/";
+    FileSystem::setResourcePath(__assetsPath.c_str());    
+        
+    // Get the asset manager to get the resources from the .apk file.
+    __assetManager = __state->activity->assetManager; 
+    
+    // Set the event call back functions.
+    __state->onAppCmd = engine_handle_cmd;
+    __state->onInputEvent = engine_handle_input;
+    
+    // Prepare to monitor accelerometer.
+    __sensorManager = ASensorManager_getInstance();
+    __accelerometerSensor = ASensorManager_getDefaultSensor(__sensorManager, ASENSOR_TYPE_ACCELEROMETER);
+    __sensorEventQueue = ASensorManager_createEventQueue(__sensorManager, __state->looper, LOOPER_ID_USER, NULL, NULL);
+    
+    // Get the initial time.
+    clock_gettime(CLOCK_REALTIME, &__timespec);
+    __timeStart = timespec2millis(&__timespec);
+    __timeAbsolute = 0L;
+    
+    bool initializeGame = true;
+    
+    while (true)
+    {
+        // Read all pending events.
+        int ident;
+        int events;
+        struct android_poll_source* source;
+        
+        bool _shouldPoll = !(__initialized && Game::getInstance()->getState() == Game::UNINITIALIZED) && (Game::getInstance()->getState() != Game::PAUSED);
+        
+        while ((ident=ALooper_pollAll( _shouldPoll ? 0 : -1, NULL, &events, (void**)&source)) >= 0) 
+        {
+            // Process this event.
+            if (source != NULL) 
+                source->process(__state, source);
+            
+            // If a sensor has data, process it now.
+            if (ident == LOOPER_ID_USER && __accelerometerSensor != NULL)
+                ASensorEventQueue_getEvents(__sensorEventQueue, &__sensorEvent, 1);
+            
+            if (__state->destroyRequested != 0)
+                break;
+        }
+        
+        if (__initialized && initializeGame)
+        {
+            gameplay::initEGL();
+            WARN_VARG("Platform::enterMessagePump() - width: %d  height: %d assetsPath: %s", __width, __height, __assetsPath.c_str());
+            _game->run(__width, __height);
+            initializeGame = false;
+        }
+        
+        // Idle time (no events left to process) is spent rendering.
+        // We skip rendering when the app is paused.
+        if (__initialized && _game->getState() != Game::PAUSED)
+        {
+            _game->frame();
+
+            // Post the new frame to the display.
+            // Note that there are a couple cases where eglSwapBuffers could fail
+            // with an error code that requires a certain level of re-initialization:
+            //
+            // 1) EGL_BAD_NATIVE_WINDOW - Called when the surface we're currently using
+            //    is invalidated. This would require us to destroy our EGL surface,
+            //    close our OpenKODE window, and start again.
+            //
+            // 2) EGL_CONTEXT_LOST - Power management event that led to our EGL context
+            //    being lost. Requires us to re-create and re-initalize our EGL context
+            //    and all OpenGL ES state.
+            //
+            // For now, if we get these, we'll simply exit.
+            int rc = eglSwapBuffers(__eglDisplay, __eglSurface);
+            if (rc != EGL_TRUE)
+            {
+                perror("eglSwapBuffers");
+                _game->exit();
+                break;
+            }
+        }
+        
+        // Check if we are exiting.
+        if ((__state->destroyRequested != 0) || (__initialized && Game::getInstance()->getState() == Game::UNINITIALIZED))
+        {
+            break;
+        }
+            
+        // Display the keyboard.
+        gameplay::displayKeyboard(__state, __displayKeyboard);
+    }
+}
+
+long Platform::getAbsoluteTime()
+{
+    clock_gettime(CLOCK_REALTIME, &__timespec);
+    long now = timespec2millis(&__timespec);
+    __timeAbsolute = now - __timeStart;
+
+    return __timeAbsolute;
+}
+
+void Platform::setAbsoluteTime(long time)
+{
+    __timeAbsolute = time;
+}
+
+bool Platform::isVsync()
+{
+    return __vsync;
+}
+
+void Platform::setVsync(bool enable)
+{
+    eglSwapInterval(__eglDisplay, enable ? 1 : 0);
+    __vsync = enable;
+}
+
+int Platform::getOrientationAngle()
+{
+    return __orientationAngle;
+}
+
+void Platform::setMultiTouch(bool enabled)
+{
+    __multiTouch = enabled;
+}
+
+bool Platform::isMultiTouch()
+{
+    return __multiTouch;
+}
+
+void Platform::getAccelerometerValues(float* pitch, float* roll)
+{
+    double tx, ty, tz;
+    ASensorEvent event;
+    
+    // By default, android accelerometer values are oriented to the portrait mode.
+    // flipping the x and y to get the desired landscape mode values.
+    tx = -__sensorEvent.acceleration.y;
+    ty = __sensorEvent.acceleration.x;
+    tz = -__sensorEvent.acceleration.z;
+    
+    if (pitch != NULL)
+        *pitch = atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
+    if (roll != NULL)
+        *roll = atan(tx / sqrt(ty * ty + tz * tz)) * 180.0f * M_1_PI;
+}
+
+void Platform::swapBuffers()
+{
+    if (__eglDisplay && __eglSurface)
+        eglSwapBuffers(__eglDisplay, __eglSurface);
+}
+
+void Platform::displayKeyboard(bool display)
+{
+    if (display)
+        __displayKeyboard = true;
+    else
+        __displayKeyboard = false;
+}
+
+}
+
+#endif

+ 603 - 599
gameplay/src/PlatformMacOS.mm

@@ -1,599 +1,603 @@
-#ifdef __APPLE__
-
-#include "Base.h"
-#include "Platform.h"
-#include "FileSystem.h"
-#include "Game.h"
-
-#import <Cocoa/Cocoa.h>
-#import <QuartzCore/CVDisplayLink.h>
-#import <OpenGL/OpenGL.h>
-#import <mach/mach_time.h>
-
-using namespace std;
-using namespace gameplay;
-
-static const float ACCELEROMETER_X_FACTOR = 90.0f / WINDOW_WIDTH;
-static const float ACCELEROMETER_Y_FACTOR = 90.0f / WINDOW_HEIGHT;
-
-static long __timeStart;
-static long __timeAbsolute;
-static bool __vsync = WINDOW_VSYNC;
-static float __pitch;
-static float __roll;
-static int __lx;
-static int __ly;
-static bool __hasMouse = false;
-static bool __leftMouseDown = false;
-static bool __rightMouseDown = false;
-static bool __shiftDown = false;
-
-long getMachTimeInMilliseconds()
-{
-    static const int64_t kOneMillion = 1000 * 1000;
-    static mach_timebase_info_data_t s_timebase_info;
-    
-    if (s_timebase_info.denom == 0) 
-        (void) mach_timebase_info(&s_timebase_info);
-    
-    // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
-    return (long)((mach_absolute_time() * s_timebase_info.numer) / (kOneMillion * s_timebase_info.denom));
-}
-
-
-@class View;
-
-@interface View : NSOpenGLView <NSWindowDelegate> 
-{
-    CVDisplayLinkRef displayLink;
-    NSRecursiveLock* lock;
-    Game* _game;
-}
-
-@end
-
-
-static View* __view = NULL;
-
-@implementation View
-
--(void)windowWillClose:(NSNotification*)note 
-{
-    [lock lock];
-    _game->exit();
-    [lock unlock];
-    [[NSApplication sharedApplication] terminate:self];
-}
-
-
-- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
-{
-    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-    
-    [self update];
-    
-    [pool release];
-    
-    return kCVReturnSuccess;
-}
-
--(void) update
-{       
-    [lock lock];
-
-    [[self openGLContext] makeCurrentContext];
-    
-    CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
-    
-    if (_game && _game->getState() == Game::RUNNING)       
-        _game->frame();
-    
-    CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
-    CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);  
-    
-    [lock unlock];
-}
-
-static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, 
-                                      CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
-{
-    CVReturn result = [(View*)displayLinkContext getFrameForTime:outputTime];
-    return result;
-}
-
-- (id) initWithFrame: (NSRect) frame
-{    
-    lock = [[NSRecursiveLock alloc] init];
-    _game = Game::getInstance();
-    __timeStart = getMachTimeInMilliseconds();
-    NSOpenGLPixelFormatAttribute attrs[] = 
-    {
-        NSOpenGLPFAAccelerated,
-        NSOpenGLPFADoubleBuffer,
-        NSOpenGLPFAColorSize, 32,
-        NSOpenGLPFADepthSize, 24,
-        NSOpenGLPFAAlphaSize, 8,
-        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
-        0
-    };
-    
-    NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
-    if (!pf)
-        NSLog(@"OpenGL pixel format not supported.");
-    
-    self = [super initWithFrame:frame pixelFormat:[pf autorelease]];  
-    
-    return self;
-}
-
-- (void) prepareOpenGL
-{
-    [super prepareOpenGL];
-    
-    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
-    NSString* path = [bundlePath stringByAppendingString:@"/Contents/Resources/"];
-    FileSystem::setResourcePath([path cStringUsingEncoding:NSASCIIStringEncoding]);
-    _game->run(WINDOW_WIDTH, WINDOW_HEIGHT);
-    
-    [[self window] setLevel: NSFloatingWindowLevel];
-    [[self window] makeKeyAndOrderFront: self];
-    [[self window] setTitle: [NSString stringWithUTF8String: ""]];
-    
-    // Make all the OpenGL calls to setup rendering and build the necessary rendering objects
-    [[self openGLContext] makeCurrentContext];
-    // Synchronize buffer swaps with vertical refresh rate
-    GLint swapInt = __vsync ? 1 : 0;
-    [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
-    
-    // Create a display link capable of being used with all active displays
-    CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
-    
-    // Set the renderer output callback function
-    CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self);
-    
-    CGLContextObj cglContext = (CGLContextObj)[[self openGLContext] CGLContextObj];
-    CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj];
-    CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
-    
-    // Activate the display link
-    CVDisplayLinkStart(displayLink);
-}
-
-- (void) dealloc
-{   
-    [lock lock];
-    
-    // Release the display link
-    CVDisplayLinkStop(displayLink);
-    CVDisplayLinkRelease(displayLink);
-    
-    _game->exit();
-    
-    [lock unlock];
-
-    [super dealloc];
-}
-
-- (void) mouseDown: (NSEvent*) event
-{
-    NSPoint point = [event locationInWindow];
-    __leftMouseDown = true;
-    _game->touchEvent(Touch::TOUCH_PRESS, point.x, WINDOW_HEIGHT - point.y, 0);
-}
-
-- (void) mouseUp: (NSEvent*) event
-{
-    NSPoint point = [event locationInWindow];
-    __leftMouseDown = false;
-    _game->touchEvent(Touch::TOUCH_RELEASE, point.x, WINDOW_HEIGHT - point.y, 0);
-}
-
-- (void) mouseDragged: (NSEvent*) event
-{
-    NSPoint point = [event locationInWindow];
-    if (__leftMouseDown)
-    {
-        _game->touchEvent(Touch::TOUCH_MOVE, point.x, WINDOW_HEIGHT - point.y, 0);
-    }
-}
-
-- (void) rightMouseDown: (NSEvent*) event
-{
-    __rightMouseDown = true;
-     NSPoint point = [event locationInWindow];
-    __lx = point.x;
-    __ly = WINDOW_HEIGHT - point.y;
-}
-
-- (void) rightMouseUp: (NSEvent*) event
-{
-   __rightMouseDown = false;
-}
-
-- (void) rightMouseDragged: (NSEvent*) event
-{
-    NSPoint point = [event locationInWindow];
-    if (__rightMouseDown)
-    {
-        // Update the pitch and roll by adding the scaled deltas.
-        __roll += -(float)(point.x - __lx) * ACCELEROMETER_X_FACTOR;
-        __pitch -= (float)(point.y - (WINDOW_HEIGHT - __ly)) * ACCELEROMETER_Y_FACTOR;
-    
-        // Clamp the values to the valid range.
-        __roll = max(min(__roll, 90.0f), -90.0f);
-        __pitch = max(min(__pitch, 90.0f), -90.0f);
-    
-        // Update the last X/Y values.
-        __lx = point.x;
-        __ly = (WINDOW_HEIGHT - point.y);
-    }
-}
-
-- (void) mouseEntered: (NSEvent*)event
-{
-    __hasMouse = true;
-}
-
-- (void) mouseExited: (NSEvent*)event
-{
-    __leftMouseDown = false;
-    __rightMouseDown = false;
-    __hasMouse = false;
-}
-
-- (BOOL)acceptsFirstResponder
-{
-    return YES;
-}
-
-int getKey(unsigned short keyCode, unsigned int modifierFlags)
-{
-    __shiftDown = (modifierFlags & NSShiftKeyMask);
-    switch(keyCode)
-    {
-        case 0x69:
-            return Keyboard::KEY_PRINT;
-        case 0x35:
-            return Keyboard::KEY_ESCAPE;
-        case 0x33:
-            return Keyboard::KEY_BACKSPACE;
-        case 0x30:
-            return Keyboard::KEY_TAB;
-        case 0x24:
-            return Keyboard::KEY_RETURN;
-        case 0x72:
-            return Keyboard::KEY_INSERT;
-        case 0x73:
-            return Keyboard::KEY_HOME;
-        case 0x74:
-            return Keyboard::KEY_PG_UP;
-        case 0x79:
-            return Keyboard::KEY_PG_DOWN;
-        case 0x75:
-            return Keyboard::KEY_DELETE;
-        case 0x77:
-            return Keyboard::KEY_END;
-        case 0x7B:
-            return Keyboard::KEY_LEFT_ARROW;
-        case 0x7C:
-            return Keyboard::KEY_RIGHT_ARROW;
-        case 0x7E:
-            return Keyboard::KEY_UP_ARROW;
-        case 0x7D:
-            return Keyboard::KEY_DOWN_ARROW;
-        case 0x47:
-            return Keyboard::KEY_NUM_LOCK;
-        case 0x45:
-            return Keyboard::KEY_KP_PLUS;
-        case 0x4E:
-            return Keyboard::KEY_KP_MINUS;
-        case 0x43:
-            return Keyboard::KEY_KP_MULTIPLY;
-        case 0x4B:
-            return Keyboard::KEY_KP_DIVIDE;
-        case 0x59:
-            return Keyboard::KEY_KP_HOME;
-        case 0x5B:
-            return Keyboard::KEY_KP_UP;
-        case 0x5C:
-            return Keyboard::KEY_KP_PG_UP;
-        case 0x56:
-            return Keyboard::KEY_KP_LEFT;
-        case 0x57:
-            return Keyboard::KEY_KP_FIVE;
-        case 0x58:
-            return Keyboard::KEY_KP_RIGHT;
-        case 0x53:
-            return Keyboard::KEY_KP_END;
-        case 0x54:
-            return Keyboard::KEY_KP_DOWN;
-        case 0x55:
-            return Keyboard::KEY_KP_PG_DOWN;
-        case 0x52:
-            return Keyboard::KEY_KP_INSERT;
-        case 0x41:
-            return Keyboard::KEY_KP_DELETE;
-        case 0x7A:
-            return Keyboard::KEY_F1;
-        case 0x78:
-            return Keyboard::KEY_F2;
-        case 0x63:
-            return Keyboard::KEY_F3;
-        case 0x76:
-            return Keyboard::KEY_F4;
-        case 0x60:
-            return Keyboard::KEY_F5;
-        case 0x61:
-            return Keyboard::KEY_F6;
-        case 0x62:
-            return Keyboard::KEY_F7;
-        case 0x64:
-            return Keyboard::KEY_F8;
-        case 0x65:
-            return Keyboard::KEY_F9;
-        case 0x6D:
-            return Keyboard::KEY_F10;
-        
-        // MACOS reserved:
-        //return Keyboard::KEY_F11;
-        //return Keyboard::KEY_F12;
-        // return Keyboard::KEY_PAUSE;
-        // return Keyboard::KEY_SCROLL_LOCK;
-            
-        case 0x31:
-            return Keyboard::KEY_SPACE;
-        case 0x1D:
-            return __shiftDown ? Keyboard::KEY_RIGHT_PARENTHESIS : Keyboard::KEY_ZERO;
-        case 0x12:
-            return __shiftDown ? Keyboard::KEY_EXCLAM : Keyboard::KEY_ONE;
-        case 0x13:
-            return __shiftDown ? Keyboard::KEY_AT : Keyboard::KEY_TWO;
-        case 0x14:
-            return __shiftDown ? Keyboard::KEY_NUMBER : Keyboard::KEY_THREE;
-        case 0x15:
-            return __shiftDown ? Keyboard::KEY_DOLLAR : Keyboard::KEY_FOUR;
-        case 0x17:
-            return __shiftDown ? Keyboard::KEY_PERCENT : Keyboard::KEY_FIVE;
-        case 0x16:
-            return __shiftDown ? Keyboard::KEY_CIRCUMFLEX : Keyboard::KEY_SIX;
-        case 0x1A:
-            return __shiftDown ? Keyboard::KEY_AMPERSAND : Keyboard::KEY_SEVEN;
-        case 0x1C:
-            return __shiftDown ? Keyboard::KEY_ASTERISK : Keyboard::KEY_EIGHT;
-        case 0x19:
-            return __shiftDown ? Keyboard::KEY_LEFT_PARENTHESIS : Keyboard::KEY_NINE;
-        case 0x18:
-            return __shiftDown ? Keyboard::KEY_EQUAL : Keyboard::KEY_PLUS;
-        case 0x2B:
-            return __shiftDown ? Keyboard::KEY_LESS_THAN : Keyboard::KEY_COMMA;
-        case 0x1B:
-            return __shiftDown ? Keyboard::KEY_UNDERSCORE : Keyboard::KEY_MINUS;
-        case 0x2F:
-            return __shiftDown ? Keyboard::KEY_GREATER_THAN : Keyboard::KEY_PERIOD;
-        case 0x29:
-            return __shiftDown ? Keyboard::KEY_COLON : Keyboard::KEY_SEMICOLON;
-        case 0x2C:
-            return __shiftDown ? Keyboard::KEY_QUESTION : Keyboard::KEY_SLASH;
-        case 0x32:
-            return __shiftDown ? Keyboard::KEY_GRAVE : Keyboard::KEY_TILDE;
-        case 0x21:
-            return __shiftDown ? Keyboard::KEY_LEFT_BRACE : Keyboard::KEY_LEFT_BRACKET;
-        case 0x2A:
-            return __shiftDown ? Keyboard::KEY_BAR : Keyboard::KEY_BACK_SLASH;
-        case 0x1E:
-            return __shiftDown ? Keyboard::KEY_RIGHT_BRACE : Keyboard::KEY_RIGHT_BRACKET;
-        case 0x27:
-            return __shiftDown ? Keyboard::KEY_QUOTE : Keyboard::KEY_APOSTROPHE;
-            
-        case 0x00:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
-        case 0x0B:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
-        case 0x08:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
-        case 0x02:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
-        case 0x0E:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
-        case 0x03:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
-        case 0x05:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
-        case 0x04:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
-        case 0x22:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
-        case 0x26:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
-        case 0x28:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
-        case 0x25:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
-        case 0x2E:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
-        case 0x2D:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
-        case 0x1F:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
-        case 0x23:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
-        case 0x0C:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
-        case 0x0F:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
-        case 0x01:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
-        case 0x11:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
-        case 0x20:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
-        case 0x09:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
-        case 0x0D:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
-        case 0x07:
-             return __shiftDown ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
-        case 0x10:
-            return __shiftDown ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
-        case 0x06:
-            return __shiftDown ? Keyboard::KEY_CAPITAL_Z : Keyboard::KEY_Z;
-
-        default:
-            return Keyboard::KEY_NONE;
-    }
-}
-
-- (void)flagsChanged: (NSEvent*)event
-{
-    unsigned int keyCode = [event keyCode];
-    unsigned int flags = [event modifierFlags];
-    switch (keyCode) 
-    {
-        case 0x39:
-            _game->keyEvent((flags & NSAlphaShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CAPS_LOCK);
-            break;
-        case 0x38:
-            _game->keyEvent((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_LEFT_SHIFT);
-            break;
-        case 0x3C:
-            _game->keyEvent((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_RIGHT_SHIFT);
-            break;
-        case 0x3A:
-            _game->keyEvent((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_LEFT_ALT);
-            break;
-        case 0x3D:
-            _game->keyEvent((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_RIGHT_ALT);
-            break;
-        case 0x3B:
-            _game->keyEvent((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_LEFT_CTRL);
-            break;
-        case 0x3E:
-            _game->keyEvent((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_RIGHT_CTRL);
-            break;
-        case 0x37:
-            _game->keyEvent((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_LEFT_HYPER);
-            break;
-        case 0x36:
-            _game->keyEvent((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_RIGHT_HYPER);
-            break;
-    }
-}
-
-- (void) keyDown: (NSEvent*) event
-{    
-    _game->keyEvent(Keyboard::KEY_PRESS, getKey([event keyCode], [event modifierFlags]));
-}
-
-- (void) keyUp: (NSEvent*) event
-{    
-    _game->keyEvent(Keyboard::KEY_RELEASE, getKey([event keyCode], [event modifierFlags]));
-}
-
-@end
-
-
-namespace gameplay
-{
-
-extern void printError(const char* format, ...)
-{
-    va_list argptr;
-    va_start(argptr, format);
-    vfprintf(stderr, format, argptr);
-    fprintf(stderr, "\n");
-    va_end(argptr);
-}
-    
-    
-Platform::Platform(Game* game)
-: _game(game)
-{
-}
-
-Platform::Platform(const Platform& copy)
-{
-    // hidden
-}
-
-Platform::~Platform()
-{
-}
-
-Platform* Platform::create(Game* game)
-{
-    Platform* platform = new Platform(game);
-    
-    return platform;
-}
-
-int Platform::enterMessagePump()
-{
-    NSAutoreleasePool* pool = [NSAutoreleasePool new];
-    NSApplication* NSApp = [NSApplication sharedApplication];
-    NSRect screenBounds = [[NSScreen mainScreen] frame];
-    NSRect viewBounds = NSMakeRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
-    
-    __view = [[View alloc] initWithFrame:viewBounds];
-    
-    NSRect centered = NSMakeRect(NSMidX(screenBounds) - NSMidX(viewBounds),
-                                 NSMidY(screenBounds) - NSMidY(viewBounds),
-                                 viewBounds.size.width, 
-                                 viewBounds.size.height);
-    
-    NSWindow* window = [[NSWindow alloc]
-                        initWithContentRect:centered
-                        styleMask:NSTitledWindowMask | NSClosableWindowMask
-                        backing:NSBackingStoreBuffered
-                        defer:NO];
-    
-    [window setContentView:__view];
-    [window setDelegate:__view];
-    [__view release];
-    
-    [NSApp run];
-    
-    [pool release];
-    return EXIT_SUCCESS;
-}
-    
-long Platform::getAbsoluteTime()
-{
-    __timeAbsolute = getMachTimeInMilliseconds();
-    return __timeAbsolute;
-}
-
-void Platform::setAbsoluteTime(long time)
-{
-    __timeAbsolute = time;
-}
-
-bool Platform::isVsync()
-{
-    return __vsync;
-}
-
-void Platform::setVsync(bool enable)
-{
-    __vsync = enable;
-}
-
-int Platform::getOrientationAngle()
-{
-    return 0;
-}
-
-void Platform::getAccelerometerValues(float* pitch, float* roll)
-{
-    *pitch = __pitch;
-    *roll = __roll;
-}
-
-void Platform::swapBuffers()
-{
-    if (__view)
-        CGLFlushDrawable((CGLContextObj)[[__view openGLContext] CGLContextObj]);
-}
-
-}
-
-#endif
+#ifdef __APPLE__
+
+#include "Base.h"
+#include "Platform.h"
+#include "FileSystem.h"
+#include "Game.h"
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CVDisplayLink.h>
+#import <OpenGL/OpenGL.h>
+#import <mach/mach_time.h>
+
+using namespace std;
+using namespace gameplay;
+
+static const float ACCELEROMETER_X_FACTOR = 90.0f / WINDOW_WIDTH;
+static const float ACCELEROMETER_Y_FACTOR = 90.0f / WINDOW_HEIGHT;
+
+static long __timeStart;
+static long __timeAbsolute;
+static bool __vsync = WINDOW_VSYNC;
+static float __pitch;
+static float __roll;
+static int __lx;
+static int __ly;
+static bool __hasMouse = false;
+static bool __leftMouseDown = false;
+static bool __rightMouseDown = false;
+static bool __shiftDown = false;
+
+long getMachTimeInMilliseconds()
+{
+    static const int64_t kOneMillion = 1000 * 1000;
+    static mach_timebase_info_data_t s_timebase_info;
+    
+    if (s_timebase_info.denom == 0) 
+        (void) mach_timebase_info(&s_timebase_info);
+    
+    // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
+    return (long)((mach_absolute_time() * s_timebase_info.numer) / (kOneMillion * s_timebase_info.denom));
+}
+
+
+@class View;
+
+@interface View : NSOpenGLView <NSWindowDelegate> 
+{
+    CVDisplayLinkRef displayLink;
+    NSRecursiveLock* lock;
+    Game* _game;
+}
+
+@end
+
+
+static View* __view = NULL;
+
+@implementation View
+
+-(void)windowWillClose:(NSNotification*)note 
+{
+    [lock lock];
+    _game->exit();
+    [lock unlock];
+    [[NSApplication sharedApplication] terminate:self];
+}
+
+- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
+{
+    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+    
+    [self update];
+    
+    [pool release];
+    
+    return kCVReturnSuccess;
+}
+
+-(void) update
+{       
+    [lock lock];
+
+    [[self openGLContext] makeCurrentContext];
+    
+    CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
+    
+    if (_game && _game->getState() == Game::RUNNING)       
+        _game->frame();
+    
+    CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
+    CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);  
+    
+    [lock unlock];
+}
+
+static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, 
+                                      CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
+{
+    CVReturn result = [(View*)displayLinkContext getFrameForTime:outputTime];
+    return result;
+}
+
+- (id) initWithFrame: (NSRect) frame
+{    
+    lock = [[NSRecursiveLock alloc] init];
+    _game = Game::getInstance();
+    __timeStart = getMachTimeInMilliseconds();
+    NSOpenGLPixelFormatAttribute attrs[] = 
+    {
+        NSOpenGLPFAAccelerated,
+        NSOpenGLPFADoubleBuffer,
+        NSOpenGLPFAColorSize, 32,
+        NSOpenGLPFADepthSize, 24,
+        NSOpenGLPFAAlphaSize, 8,
+        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
+        0
+    };
+    
+    NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+    if (!pf)
+        NSLog(@"OpenGL pixel format not supported.");
+    
+    self = [super initWithFrame:frame pixelFormat:[pf autorelease]];  
+    
+    return self;
+}
+
+- (void) prepareOpenGL
+{
+    [super prepareOpenGL];
+    
+    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
+    NSString* path = [bundlePath stringByAppendingString:@"/Contents/Resources/"];
+    FileSystem::setResourcePath([path cStringUsingEncoding:NSASCIIStringEncoding]);
+    _game->run(WINDOW_WIDTH, WINDOW_HEIGHT);
+    
+    [[self window] setLevel: NSFloatingWindowLevel];
+    [[self window] makeKeyAndOrderFront: self];
+    [[self window] setTitle: [NSString stringWithUTF8String: ""]];
+    
+    // Make all the OpenGL calls to setup rendering and build the necessary rendering objects
+    [[self openGLContext] makeCurrentContext];
+    // Synchronize buffer swaps with vertical refresh rate
+    GLint swapInt = __vsync ? 1 : 0;
+    [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
+    
+    // Create a display link capable of being used with all active displays
+    CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
+    
+    // Set the renderer output callback function
+    CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self);
+    
+    CGLContextObj cglContext = (CGLContextObj)[[self openGLContext] CGLContextObj];
+    CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj];
+    CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
+    
+    // Activate the display link
+    CVDisplayLinkStart(displayLink);
+}
+
+- (void) dealloc
+{   
+    [lock lock];
+    
+    // Release the display link
+    CVDisplayLinkStop(displayLink);
+    CVDisplayLinkRelease(displayLink);
+    
+    _game->exit();
+    
+    [lock unlock];
+
+    [super dealloc];
+}
+
+- (void) mouseDown: (NSEvent*) event
+{
+    NSPoint point = [event locationInWindow];
+    __leftMouseDown = true;
+    _game->touchEvent(Touch::TOUCH_PRESS, point.x, WINDOW_HEIGHT - point.y, 0);
+}
+
+- (void) mouseUp: (NSEvent*) event
+{
+    NSPoint point = [event locationInWindow];
+    __leftMouseDown = false;
+    _game->touchEvent(Touch::TOUCH_RELEASE, point.x, WINDOW_HEIGHT - point.y, 0);
+}
+
+- (void) mouseDragged: (NSEvent*) event
+{
+    NSPoint point = [event locationInWindow];
+    if (__leftMouseDown)
+    {
+        _game->touchEvent(Touch::TOUCH_MOVE, point.x, WINDOW_HEIGHT - point.y, 0);
+    }
+}
+
+- (void) rightMouseDown: (NSEvent*) event
+{
+    __rightMouseDown = true;
+     NSPoint point = [event locationInWindow];
+    __lx = point.x;
+    __ly = WINDOW_HEIGHT - point.y;
+}
+
+- (void) rightMouseUp: (NSEvent*) event
+{
+   __rightMouseDown = false;
+}
+
+- (void) rightMouseDragged: (NSEvent*) event
+{
+    NSPoint point = [event locationInWindow];
+    if (__rightMouseDown)
+    {
+        // Update the pitch and roll by adding the scaled deltas.
+        __roll += -(float)(point.x - __lx) * ACCELEROMETER_X_FACTOR;
+        __pitch -= (float)(point.y - (WINDOW_HEIGHT - __ly)) * ACCELEROMETER_Y_FACTOR;
+    
+        // Clamp the values to the valid range.
+        __roll = max(min(__roll, 90.0f), -90.0f);
+        __pitch = max(min(__pitch, 90.0f), -90.0f);
+    
+        // Update the last X/Y values.
+        __lx = point.x;
+        __ly = (WINDOW_HEIGHT - point.y);
+    }
+}
+
+- (void) mouseEntered: (NSEvent*)event
+{
+    __hasMouse = true;
+}
+
+- (void) mouseExited: (NSEvent*)event
+{
+    __leftMouseDown = false;
+    __rightMouseDown = false;
+    __hasMouse = false;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+    return YES;
+}
+
+int getKey(unsigned short keyCode, unsigned int modifierFlags)
+{
+    __shiftDown = (modifierFlags & NSShiftKeyMask);
+    switch(keyCode)
+    {
+        case 0x69:
+            return Keyboard::KEY_PRINT;
+        case 0x35:
+            return Keyboard::KEY_ESCAPE;
+        case 0x33:
+            return Keyboard::KEY_BACKSPACE;
+        case 0x30:
+            return Keyboard::KEY_TAB;
+        case 0x24:
+            return Keyboard::KEY_RETURN;
+        case 0x72:
+            return Keyboard::KEY_INSERT;
+        case 0x73:
+            return Keyboard::KEY_HOME;
+        case 0x74:
+            return Keyboard::KEY_PG_UP;
+        case 0x79:
+            return Keyboard::KEY_PG_DOWN;
+        case 0x75:
+            return Keyboard::KEY_DELETE;
+        case 0x77:
+            return Keyboard::KEY_END;
+        case 0x7B:
+            return Keyboard::KEY_LEFT_ARROW;
+        case 0x7C:
+            return Keyboard::KEY_RIGHT_ARROW;
+        case 0x7E:
+            return Keyboard::KEY_UP_ARROW;
+        case 0x7D:
+            return Keyboard::KEY_DOWN_ARROW;
+        case 0x47:
+            return Keyboard::KEY_NUM_LOCK;
+        case 0x45:
+            return Keyboard::KEY_KP_PLUS;
+        case 0x4E:
+            return Keyboard::KEY_KP_MINUS;
+        case 0x43:
+            return Keyboard::KEY_KP_MULTIPLY;
+        case 0x4B:
+            return Keyboard::KEY_KP_DIVIDE;
+        case 0x59:
+            return Keyboard::KEY_KP_HOME;
+        case 0x5B:
+            return Keyboard::KEY_KP_UP;
+        case 0x5C:
+            return Keyboard::KEY_KP_PG_UP;
+        case 0x56:
+            return Keyboard::KEY_KP_LEFT;
+        case 0x57:
+            return Keyboard::KEY_KP_FIVE;
+        case 0x58:
+            return Keyboard::KEY_KP_RIGHT;
+        case 0x53:
+            return Keyboard::KEY_KP_END;
+        case 0x54:
+            return Keyboard::KEY_KP_DOWN;
+        case 0x55:
+            return Keyboard::KEY_KP_PG_DOWN;
+        case 0x52:
+            return Keyboard::KEY_KP_INSERT;
+        case 0x41:
+            return Keyboard::KEY_KP_DELETE;
+        case 0x7A:
+            return Keyboard::KEY_F1;
+        case 0x78:
+            return Keyboard::KEY_F2;
+        case 0x63:
+            return Keyboard::KEY_F3;
+        case 0x76:
+            return Keyboard::KEY_F4;
+        case 0x60:
+            return Keyboard::KEY_F5;
+        case 0x61:
+            return Keyboard::KEY_F6;
+        case 0x62:
+            return Keyboard::KEY_F7;
+        case 0x64:
+            return Keyboard::KEY_F8;
+        case 0x65:
+            return Keyboard::KEY_F9;
+        case 0x6D:
+            return Keyboard::KEY_F10;
+        
+        // MACOS reserved:
+        //return Keyboard::KEY_F11;
+        //return Keyboard::KEY_F12;
+        // return Keyboard::KEY_PAUSE;
+        // return Keyboard::KEY_SCROLL_LOCK;
+            
+        case 0x31:
+            return Keyboard::KEY_SPACE;
+        case 0x1D:
+            return __shiftDown ? Keyboard::KEY_RIGHT_PARENTHESIS : Keyboard::KEY_ZERO;
+        case 0x12:
+            return __shiftDown ? Keyboard::KEY_EXCLAM : Keyboard::KEY_ONE;
+        case 0x13:
+            return __shiftDown ? Keyboard::KEY_AT : Keyboard::KEY_TWO;
+        case 0x14:
+            return __shiftDown ? Keyboard::KEY_NUMBER : Keyboard::KEY_THREE;
+        case 0x15:
+            return __shiftDown ? Keyboard::KEY_DOLLAR : Keyboard::KEY_FOUR;
+        case 0x17:
+            return __shiftDown ? Keyboard::KEY_PERCENT : Keyboard::KEY_FIVE;
+        case 0x16:
+            return __shiftDown ? Keyboard::KEY_CIRCUMFLEX : Keyboard::KEY_SIX;
+        case 0x1A:
+            return __shiftDown ? Keyboard::KEY_AMPERSAND : Keyboard::KEY_SEVEN;
+        case 0x1C:
+            return __shiftDown ? Keyboard::KEY_ASTERISK : Keyboard::KEY_EIGHT;
+        case 0x19:
+            return __shiftDown ? Keyboard::KEY_LEFT_PARENTHESIS : Keyboard::KEY_NINE;
+        case 0x18:
+            return __shiftDown ? Keyboard::KEY_EQUAL : Keyboard::KEY_PLUS;
+        case 0x2B:
+            return __shiftDown ? Keyboard::KEY_LESS_THAN : Keyboard::KEY_COMMA;
+        case 0x1B:
+            return __shiftDown ? Keyboard::KEY_UNDERSCORE : Keyboard::KEY_MINUS;
+        case 0x2F:
+            return __shiftDown ? Keyboard::KEY_GREATER_THAN : Keyboard::KEY_PERIOD;
+        case 0x29:
+            return __shiftDown ? Keyboard::KEY_COLON : Keyboard::KEY_SEMICOLON;
+        case 0x2C:
+            return __shiftDown ? Keyboard::KEY_QUESTION : Keyboard::KEY_SLASH;
+        case 0x32:
+            return __shiftDown ? Keyboard::KEY_GRAVE : Keyboard::KEY_TILDE;
+        case 0x21:
+            return __shiftDown ? Keyboard::KEY_LEFT_BRACE : Keyboard::KEY_LEFT_BRACKET;
+        case 0x2A:
+            return __shiftDown ? Keyboard::KEY_BAR : Keyboard::KEY_BACK_SLASH;
+        case 0x1E:
+            return __shiftDown ? Keyboard::KEY_RIGHT_BRACE : Keyboard::KEY_RIGHT_BRACKET;
+        case 0x27:
+            return __shiftDown ? Keyboard::KEY_QUOTE : Keyboard::KEY_APOSTROPHE;
+            
+        case 0x00:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
+        case 0x0B:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
+        case 0x08:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
+        case 0x02:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
+        case 0x0E:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
+        case 0x03:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
+        case 0x05:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
+        case 0x04:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
+        case 0x22:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
+        case 0x26:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
+        case 0x28:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
+        case 0x25:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
+        case 0x2E:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
+        case 0x2D:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
+        case 0x1F:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
+        case 0x23:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
+        case 0x0C:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
+        case 0x0F:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
+        case 0x01:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
+        case 0x11:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
+        case 0x20:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
+        case 0x09:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
+        case 0x0D:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
+        case 0x07:
+             return __shiftDown ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
+        case 0x10:
+            return __shiftDown ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
+        case 0x06:
+            return __shiftDown ? Keyboard::KEY_CAPITAL_Z : Keyboard::KEY_Z;
+
+        default:
+            return Keyboard::KEY_NONE;
+    }
+}
+
+- (void)flagsChanged: (NSEvent*)event
+{
+    unsigned int keyCode = [event keyCode];
+    unsigned int flags = [event modifierFlags];
+    switch (keyCode) 
+    {
+        case 0x39:
+            _game->keyEvent((flags & NSAlphaShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CAPS_LOCK);
+            break;
+        case 0x38:
+            _game->keyEvent((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
+            break;
+        case 0x3C:
+            _game->keyEvent((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
+            break;
+        case 0x3A:
+            _game->keyEvent((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
+            break;
+        case 0x3D:
+            _game->keyEvent((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
+            break;
+        case 0x3B:
+            _game->keyEvent((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
+            break;
+        case 0x3E:
+            _game->keyEvent((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
+            break;
+        case 0x37:
+            _game->keyEvent((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
+            break;
+        case 0x36:
+            _game->keyEvent((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
+            break;
+    }
+}
+
+- (void) keyDown: (NSEvent*) event
+{    
+    _game->keyEvent(Keyboard::KEY_PRESS, getKey([event keyCode], [event modifierFlags]));
+}
+
+- (void) keyUp: (NSEvent*) event
+{    
+    _game->keyEvent(Keyboard::KEY_RELEASE, getKey([event keyCode], [event modifierFlags]));
+}
+
+@end
+
+
+namespace gameplay
+{
+
+extern void printError(const char* format, ...)
+{
+    va_list argptr;
+    va_start(argptr, format);
+    vfprintf(stderr, format, argptr);
+    fprintf(stderr, "\n");
+    va_end(argptr);
+}
+    
+    
+Platform::Platform(Game* game)
+: _game(game)
+{
+}
+
+Platform::Platform(const Platform& copy)
+{
+    // hidden
+}
+
+Platform::~Platform()
+{
+}
+
+Platform* Platform::create(Game* game)
+{
+    Platform* platform = new Platform(game);
+    
+    return platform;
+}
+
+int Platform::enterMessagePump()
+{
+    NSAutoreleasePool* pool = [NSAutoreleasePool new];
+    NSApplication* app = [NSApplication sharedApplication];
+    NSRect screenBounds = [[NSScreen mainScreen] frame];
+    NSRect viewBounds = NSMakeRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
+    
+    __view = [[View alloc] initWithFrame:viewBounds];
+    
+    NSRect centered = NSMakeRect(NSMidX(screenBounds) - NSMidX(viewBounds),
+                                 NSMidY(screenBounds) - NSMidY(viewBounds),
+                                 viewBounds.size.width, 
+                                 viewBounds.size.height);
+    
+    NSWindow* window = [[NSWindow alloc]
+                        initWithContentRect:centered
+                        styleMask:NSTitledWindowMask | NSClosableWindowMask
+                        backing:NSBackingStoreBuffered
+                        defer:NO];
+    
+    [window setContentView:__view];
+    [window setDelegate:__view];
+    [__view release];
+    
+    [app run];
+    
+    [pool release];
+    return EXIT_SUCCESS;
+}
+    
+long Platform::getAbsoluteTime()
+{
+    __timeAbsolute = getMachTimeInMilliseconds();
+    return __timeAbsolute;
+}
+
+void Platform::setAbsoluteTime(long time)
+{
+    __timeAbsolute = time;
+}
+
+bool Platform::isVsync()
+{
+    return __vsync;
+}
+
+void Platform::setVsync(bool enable)
+{
+    __vsync = enable;
+}
+
+int Platform::getOrientationAngle()
+{
+    return 0;
+}
+
+void Platform::getAccelerometerValues(float* pitch, float* roll)
+{
+    *pitch = __pitch;
+    *roll = __roll;
+}
+
+void Platform::swapBuffers()
+{
+    if (__view)
+        CGLFlushDrawable((CGLContextObj)[[__view openGLContext] CGLContextObj]);
+}
+
+void Platform::displayKeyboard(bool display)
+{
+    // Do nothing.
+}
+
+}
+
+#endif

+ 170 - 14
gameplay/src/PlatformQNX.cpp

@@ -13,6 +13,7 @@
 #include <bps/navigator.h>
 #include <bps/accelerometer.h>
 #include <bps/orientation.h>
+#include <bps/virtualkeyboard.h>
 
 #define TOUCH_COUNT_MAX     4
 
@@ -74,21 +75,17 @@ static Keyboard::Key getKey(int qnxKeycode)
     case KEYCODE_CAPS_LOCK:
         return Keyboard::KEY_CAPS_LOCK;
     case KEYCODE_LEFT_SHIFT:
-        return Keyboard::KEY_LEFT_SHIFT;
     case KEYCODE_RIGHT_SHIFT:
-        return Keyboard::KEY_RIGHT_SHIFT;
+        return Keyboard::KEY_SHIFT;
     case KEYCODE_LEFT_CTRL:
-        return Keyboard::KEY_LEFT_CTRL;
     case KEYCODE_RIGHT_CTRL:
-        return Keyboard::KEY_RIGHT_CTRL;
+        return Keyboard::KEY_CTRL;
     case KEYCODE_LEFT_ALT:
-        return Keyboard::KEY_LEFT_ALT;
     case KEYCODE_RIGHT_ALT:
-        return Keyboard::KEY_RIGHT_ALT;
+        return Keyboard::KEY_ALT;
     case KEYCODE_LEFT_HYPER:
-        return Keyboard::KEY_LEFT_HYPER;
     case KEYCODE_RIGHT_HYPER:
-        return Keyboard::KEY_RIGHT_HYPER;
+        return Keyboard::KEY_HYPER;
     case KEYCODE_INSERT:
         return Keyboard::KEY_INSERT;
     case KEYCODE_HOME:
@@ -251,6 +248,14 @@ static Keyboard::Key getKey(int qnxKeycode)
         return Keyboard::KEY_QUOTE;
     case KEYCODE_APOSTROPHE:
         return Keyboard::KEY_APOSTROPHE;
+    case 0x20AC:
+        return Keyboard::KEY_EURO;
+    case KEYCODE_POUND_SIGN:
+        return Keyboard::KEY_POUND;
+    case KEYCODE_YEN_SIGN:
+        return Keyboard::KEY_YEN;
+    case KEYCODE_MIDDLE_DOT:
+        return Keyboard::KEY_MIDDLE_DOT;
     case KEYCODE_CAPITAL_A:
         return Keyboard::KEY_CAPITAL_A;
     case KEYCODE_A:
@@ -360,6 +365,37 @@ static Keyboard::Key getKey(int qnxKeycode)
     }
 }
 
+/**
+ * Returns the unicode value from the given QNX key code value.
+ * Some non-printable characters also have corresponding unicode values, such as backspace.
+ *
+ * @param qnxKeyCode The keyboard key code.
+ *
+ * @return The unicode value or 0 if the keycode did not represent a unicode key.
+ */
+static int getUnicode(int qnxKeyCode)
+{
+    if (qnxKeyCode >= KEYCODE_PC_KEYS && qnxKeyCode <= UNICODE_PRIVATE_USE_AREA_LAST)
+    {
+        switch (qnxKeyCode)
+        {
+        case KEYCODE_BACKSPACE:
+            return 0x0008;
+        case KEYCODE_TAB:
+            return 0x0009;
+        case KEYCODE_KP_ENTER:
+        case KEYCODE_RETURN:
+            return 0x000A;
+        case KEYCODE_ESCAPE:
+            return 0x001B;
+        // Win32 doesn't consider delete to be a key char.
+        default:
+            return 0;
+        }
+    }
+    return qnxKeyCode;
+}
+
 extern void printError(const char* format, ...)
 {
     va_list argptr;
@@ -665,13 +701,29 @@ long timespec2millis(struct timespec *a)
     return a->tv_sec*1000 + a->tv_nsec/1000000;
 }
 
+/**
+ * Fires a mouse event or a touch event on the game.
+ * If the mouse event is not consumed, a touch event is fired instead.
+ *
+ * @param mouseEvent The mouse event to fire.
+ * @param touchEvent The touch event to fire.
+ * @param x The x position of the touch in pixels.
+ * @param y The y position of the touch in pixels.
+ */
+void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEvent, int x, int y)
+{
+    if (!Game::getInstance()->mouseEvent(mouseEvent, x, y))
+    {
+        Game::getInstance()->touchEvent(touchEvent, x, y, 0);
+    }
+}
+
 int Platform::enterMessagePump()
 {
     int rc;
     int eventType;
     int flags;
     int value;
-    int visible = 1;
     int position[2];
     int domain;
     mtouch_event_t touchEvent;
@@ -710,7 +762,7 @@ int Platform::enterMessagePump()
                         if (!__multiTouch)
                         {
                             screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_POSITION, position);
-                           Game::getInstance()->touchEvent(Touch::TOUCH_PRESS, position[0], position[1], 0);
+                            Game::getInstance()->touchEvent(Touch::TOUCH_PRESS, position[0], position[1], 0);
                         }
                         else
                         {
@@ -725,7 +777,7 @@ int Platform::enterMessagePump()
                         if (!__multiTouch)
                         {
                             screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_POSITION, position);
-                           Game::getInstance()->touchEvent(Touch::TOUCH_RELEASE, position[0], position[1], 0);
+                            Game::getInstance()->touchEvent(Touch::TOUCH_RELEASE, position[0], position[1], 0);
                         }
                         else
                         {
@@ -751,12 +803,108 @@ int Platform::enterMessagePump()
                         break;
                     }
 
+                    case SCREEN_EVENT_POINTER:
+                    {
+                        static int mouse_pressed = 0;
+                        int buttons;
+                        int wheel;
+                        // A move event will be fired unless a button state changed.
+                        bool move = true;
+                        bool left_move = false;
+                        //This is a mouse move event, it is applicable to a device with a usb mouse or simulator
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_BUTTONS, &buttons);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel);
+
+                        // Handle left mouse. Interpret as touch if the left mouse event is not consumed.
+                        if (buttons & SCREEN_LEFT_MOUSE_BUTTON)
+                        {
+                            if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
+                            {
+                                left_move = true;
+                            }
+                            else
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_LEFT_MOUSE_BUTTON;
+                                mouseOrTouchEvent(Mouse::MOUSE_LEFT_BUTTON_PRESS, Touch::TOUCH_PRESS, position[0], position[1]);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_LEFT_MOUSE_BUTTON;
+                            mouseOrTouchEvent(Mouse::MOUSE_LEFT_BUTTON_RELEASE, Touch::TOUCH_RELEASE, position[0], position[1]);
+                        }
+
+                        // Handle right mouse
+                        if (buttons & SCREEN_RIGHT_MOUSE_BUTTON)
+                        {
+                            if ((mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) == 0)
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_RIGHT_MOUSE_BUTTON;
+                                Game::getInstance()->mouseEvent(Mouse::MOUSE_RIGHT_BUTTON_PRESS, position[0], position[1]);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_RIGHT_MOUSE_BUTTON;
+                            Game::getInstance()->mouseEvent(Mouse::MOUSE_RIGHT_BUTTON_RELEASE, position[0], position[1]);
+                        }
+
+                        // Handle middle mouse
+                        if (buttons & SCREEN_MIDDLE_MOUSE_BUTTON)
+                        {
+                            if ((mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) == 0)
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_MIDDLE_MOUSE_BUTTON;
+                                Game::getInstance()->mouseEvent(Mouse::MOUSE_MIDDLE_BUTTON_PRESS, position[0], position[1]);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_MIDDLE_MOUSE_BUTTON;
+                            Game::getInstance()->mouseEvent(Mouse::MOUSE_MIDDLE_BUTTON_RELEASE, position[0], position[1]);
+                        }
+
+                        // Fire a move event if none of the buttons changed.
+                        if (left_move)
+                        {
+                            mouseOrTouchEvent(Mouse::MOUSE_MOVE, Touch::TOUCH_MOVE, position[0], position[1]);
+                        }
+                        else if (move)
+                        {
+                            Game::getInstance()->mouseEvent(Mouse::MOUSE_MOVE, position[0], position[1]);
+                        }
+
+                        // Handle mouse wheel events
+                        if (wheel)
+                        {
+                            Game::getInstance()->mouseWheelEvent(position[0], position[1], -wheel);
+                        }
+                        break;
+                    }
+
                     case SCREEN_EVENT_KEYBOARD:
                     {
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_FLAGS, &flags);
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_SYM, &value);
                         gameplay::Keyboard::KeyEvent evt = (flags & KEY_DOWN) ? gameplay::Keyboard::KEY_PRESS :  gameplay::Keyboard::KEY_RELEASE;
-                        Game::getInstance()->keyEvent(evt, getKey(value));
+                        // Suppress key repeats
+                        if ((flags & KEY_REPEAT) == 0)
+                        {
+                            Game::getInstance()->keyEvent(evt, getKey(value));
+                            if (evt == gameplay::Keyboard::KEY_PRESS && flags & KEY_SYM_VALID)
+                            {
+                                int unicode = getUnicode(value);
+                                if (unicode)
+                                    Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_CHAR, unicode);
+                            }
+                        }
                         break;
                     }
                 }
@@ -780,8 +928,8 @@ int Platform::enterMessagePump()
             break;
 
         // Idle time (no events left to process) is spent rendering.
-        // We skip rendering when the window is not visible or the app is paused.
-        if (visible && _game->getState() != Game::PAUSED)
+        // We skip rendering when the app is paused.
+        if (_game->getState() != Game::PAUSED)
         {
             _game->frame();
 
@@ -880,6 +1028,14 @@ void Platform::swapBuffers()
         eglSwapBuffers(__eglDisplay, __eglSurface);
 }
 
+void Platform::displayKeyboard(bool display)
+{
+    if (display)
+        virtualkeyboard_show();
+    else
+        virtualkeyboard_hide();
+}
+
 }
 
 #endif

+ 62 - 22
gameplay/src/PlatformWin32.cpp

@@ -45,22 +45,19 @@ static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
         return gameplay::Keyboard::KEY_RETURN;
     case VK_CAPITAL:
         return gameplay::Keyboard::KEY_CAPS_LOCK;
-    case VK_LSHIFT:
-        return gameplay::Keyboard::KEY_LEFT_SHIFT;
-    case VK_RSHIFT:
-        return gameplay::Keyboard::KEY_RIGHT_SHIFT;
-    case VK_LCONTROL:
-        return gameplay::Keyboard::KEY_LEFT_CTRL;
-    case VK_RCONTROL:
-        return gameplay::Keyboard::KEY_RIGHT_CTRL;
-    case VK_LMENU:
-        return gameplay::Keyboard::KEY_LEFT_ALT;
-    case VK_RMENU:
-        return gameplay::Keyboard::KEY_RIGHT_ALT;
+    case VK_SHIFT:
+        return gameplay::Keyboard::KEY_SHIFT;
+    case VK_CONTROL:
+        return gameplay::Keyboard::KEY_CTRL;
+    case VK_MENU:
+        return gameplay::Keyboard::KEY_ALT;
+    case VK_APPS:
+        return gameplay::Keyboard::KEY_MENU;
     case VK_LWIN:
-        return gameplay::Keyboard::KEY_LEFT_HYPER;
     case VK_RWIN:
-        return gameplay::Keyboard::KEY_RIGHT_HYPER;
+        return gameplay::Keyboard::KEY_HYPER;
+    case VK_BROWSER_SEARCH:
+        return gameplay::Keyboard::KEY_SEARCH;
     case VK_INSERT:
         return gameplay::Keyboard::KEY_INSERT;
     case VK_HOME:
@@ -172,7 +169,7 @@ static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
     case VK_OEM_2:
         return shiftDown ? gameplay::Keyboard::KEY_QUESTION : gameplay::Keyboard::KEY_SLASH;
     case VK_OEM_3:
-        return shiftDown ? gameplay::Keyboard::KEY_GRAVE : gameplay::Keyboard::KEY_TILDE;
+        return shiftDown ? gameplay::Keyboard::KEY_TILDE : gameplay::Keyboard::KEY_GRAVE;
     case VK_OEM_4:
         return shiftDown ? gameplay::Keyboard::KEY_LEFT_BRACE : gameplay::Keyboard::KEY_LEFT_BRACKET;
     case VK_OEM_5:
@@ -267,26 +264,43 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         return 0;
 
     case WM_LBUTTONDOWN:
-        gameplay::Game::getInstance()->touchEvent(gameplay::Touch::TOUCH_PRESS, LOWORD(lParam), HIWORD(lParam), 0);
+        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_LEFT_BUTTON_PRESS, LOWORD(lParam), HIWORD(lParam)))
+        {
+            gameplay::Game::getInstance()->touchEvent(gameplay::Touch::TOUCH_PRESS, LOWORD(lParam), HIWORD(lParam), 0);
+        }
         lMouseDown = true;
         return 0;
 
     case WM_LBUTTONUP:
         lMouseDown = false;
-        gameplay::Game::getInstance()->touchEvent(gameplay::Touch::TOUCH_RELEASE, LOWORD(lParam), HIWORD(lParam), 0);
+        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_LEFT_BUTTON_RELEASE, LOWORD(lParam), HIWORD(lParam)))
+        {
+            gameplay::Game::getInstance()->touchEvent(gameplay::Touch::TOUCH_RELEASE, LOWORD(lParam), HIWORD(lParam), 0);
+        }
         return 0;
 
     case WM_RBUTTONDOWN:
+        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RIGHT_BUTTON_PRESS, LOWORD(lParam), HIWORD(lParam));
         rMouseDown = true;
         lx = LOWORD(lParam);
         ly = HIWORD(lParam);
         break;
 
     case WM_RBUTTONUP:
+        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RIGHT_BUTTON_RELEASE, LOWORD(lParam), HIWORD(lParam));
         rMouseDown = false;
         break;
 
+    case WM_MBUTTONDOWN:
+        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MIDDLE_BUTTON_PRESS, LOWORD(lParam), HIWORD(lParam));
+        break;
+
+    case WM_MBUTTONUP:
+        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MIDDLE_BUTTON_RELEASE, LOWORD(lParam), HIWORD(lParam));
+        break;
+
     case WM_MOUSEMOVE:
+    {
         if (!hasMouse)
         {
             hasMouse = true;
@@ -299,8 +313,10 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
             TrackMouseEvent(&tme);
         }
 
-        if (lMouseDown)
+        bool consumed = gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MOVE, LOWORD(lParam), HIWORD(lParam));
+        if (lMouseDown && !consumed)
         {
+            // Mouse move events should be interpreted as touch move only if left mouse is held and the game did not consume the mouse event.
             gameplay::Game::getInstance()->touchEvent(gameplay::Touch::TOUCH_MOVE, LOWORD(lParam), HIWORD(lParam), 0);
             return 0;
         }
@@ -319,6 +335,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
             ly = HIWORD(lParam);
         }
         break;
+    }
 
     case WM_MOUSELEAVE:
         hasMouse = false;
@@ -326,20 +343,38 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         rMouseDown = false;
         break;
 
+    case WM_MOUSEWHEEL:
+        gameplay::Game::getInstance()->mouseWheelEvent(LOWORD(lParam), HIWORD(lParam), GET_WHEEL_DELTA_WPARAM(wParam) / 120);
+        break;
+
     case WM_KEYDOWN:
-        if (wParam == VK_LSHIFT || wParam == VK_RSHIFT)
+        if (wParam == VK_SHIFT)
             shiftDown = true;
 
-        gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown));
+        // Suppress key repeats
+        if ((lParam & 0x40000000) == 0)
+            gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown));
         break;
-
+        
     case WM_KEYUP:
-        if (wParam == VK_LSHIFT || wParam == VK_RSHIFT)
+        if (wParam == VK_SHIFT)
             shiftDown = false;
 
         gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_RELEASE, getKey(wParam, shiftDown));
         break;
 
+    case WM_CHAR:
+        // Suppress key repeats
+        if ((lParam & 0x40000000) == 0)
+            gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_CHAR, wParam);
+        break;
+
+    case WM_UNICHAR:
+        // Suppress key repeats
+        if ((lParam & 0x40000000) == 0)
+            gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_CHAR, wParam);
+        break;
+
     case WM_SETFOCUS:
         break;
 
@@ -586,6 +621,11 @@ void Platform::swapBuffers()
         SwapBuffers(__hdc);
 }
 
+void Platform::displayKeyboard(bool display)
+{
+    // Do nothing.
+}
+
 }
 
 #endif

+ 817 - 0
gameplay/src/PlatformiOS.mm

@@ -0,0 +1,817 @@
+#ifdef __APPLE__
+
+#include "Base.h"
+#include "Platform.h"
+#include "FileSystem.h"
+#include "Game.h"
+
+#import <UIKit/UIKit.h>
+#import <QuartzCore/QuartzCore.h>
+#import <CoreMotion/CoreMotion.h>
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/EAGLDrawable.h>
+#import <OpenGLES/ES2/gl.h>
+#import <OpenGLES/ES2/glext.h>
+#import <mach/mach_time.h>
+
+
+using namespace std;
+using namespace gameplay;
+
+static const float ACCELEROMETER_X_FACTOR = 90.0f / WINDOW_WIDTH;
+static const float ACCELEROMETER_Y_FACTOR = 90.0f / WINDOW_HEIGHT;
+
+@class AppDelegate;
+@class View;
+
+static AppDelegate *__appDelegate = NULL;
+static View* __view = NULL;
+
+static long __timeStart;
+static long __timeAbsolute;
+static bool __vsync = WINDOW_VSYNC;
+static float __pitch;
+static float __roll;
+
+
+long getMachTimeInMilliseconds(); 
+int getKey(unichar keyCode);
+
+@interface View : UIView <UIKeyInput>
+{
+    EAGLContext* context;	
+    CADisplayLink* displayLink;
+	GLuint defaultFramebuffer;
+    GLuint colorRenderbuffer;
+    GLuint depthRenderbuffer;
+    GLint framebufferWidth;
+    GLint framebufferHeight;    
+    NSInteger swapInterval;
+    BOOL updating;
+    Game* _game;
+}
+
+@property (readonly, nonatomic, getter=isUpdating) BOOL updating;
+@property (readonly, nonatomic, getter=getContext) EAGLContext* context;
+
+- (void)startUpdating;
+- (void)stopUpdating;
+- (void)update:(id)sender;
+- (void)setSwapInterval:(NSInteger)interval;
+- (int)swapInterval;
+
+- (BOOL)showKeyboard;
+- (BOOL)dismissKeyboard;
+@end
+
+@interface View (Private)
+- (void)createFramebuffer;
+- (void)deleteFramebuffer;
+@end
+
+
+@implementation View
+
+@synthesize updating;
+@synthesize context;
+
++ (Class) layerClass
+{
+    return [CAEAGLLayer class];
+}
+
+- (id) initWithFrame:(CGRect)frame
+{
+    if ((self = [super initWithFrame:frame]))
+	{
+        // Do a sanity check
+        // A system version of 3.1 or greater is required to use CADisplayLink. 
+		NSString *reqSysVer = @"3.1";
+		NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
+		if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
+        {
+            // Log the system version
+            NSLog(@"System Version: %@", currSysVer);
+        }
+        else
+        {
+            printError("Invalid OS Version: %s\n", (currSysVer == NULL?"NULL":[currSysVer cStringUsingEncoding:NSASCIIStringEncoding]));
+            [self release];
+            return nil;
+        }
+        
+        
+        // Configure the CAEAGLLayer and setup out the rendering context
+        CAEAGLLayer* layer = (CAEAGLLayer *)self.layer;
+        layer.opaque = TRUE;
+        layer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
+                                   [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, 
+                                    kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
+		context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+        if (!context || ![EAGLContext setCurrentContext:context])
+		{
+			[self release];
+			return nil;
+		}
+        
+        // Initialize Internal Defaults
+        displayLink = nil;
+        defaultFramebuffer = 0;
+        colorRenderbuffer = 0;
+        depthRenderbuffer = 0;
+        framebufferWidth = 0;
+        framebufferHeight = 0;
+		swapInterval = 1;        
+        updating = FALSE;
+        
+        [self createFramebuffer];
+        
+        // Set the resource path and initalize the game
+        NSString* bundlePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/"];
+        FileSystem::setResourcePath([bundlePath fileSystemRepresentation]); 
+        
+        _game = Game::getInstance();
+        __timeStart = getMachTimeInMilliseconds();
+        _game->run(WINDOW_WIDTH, WINDOW_HEIGHT);    // TODO: Handle based on current orientation            
+    }
+    return self;
+}
+- (void) dealloc
+{
+    _game->exit();
+    [self deleteFramebuffer];
+    
+	if ([EAGLContext currentContext] == context)
+        [EAGLContext setCurrentContext:nil];
+	[context release];
+    [super dealloc];
+}
+
+- (BOOL)canBecomeFirstResponder 
+{
+    // Override so we can control the keyboard
+    return YES;
+}
+
+- (void) layoutSubviews
+{
+    // Called on 'resize'
+    [self deleteFramebuffer];
+}
+
+- (void)createFramebuffer
+{
+    // iOS Requires all content go to a rendering buffer then it is swapped into the windows rendering surface
+    assert(defaultFramebuffer == 0);
+    //NSLog(@"EAGLView: creating Framebuffer");
+    
+    // Create the default frame buffer, and render buffer
+    glGenFramebuffers(1, &defaultFramebuffer);
+    glGenRenderbuffers(1, &colorRenderbuffer);
+    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);    
+    glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
+    
+    // request storage, width, and height of the view that we will render in
+    [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
+    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth);
+    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight);
+    
+    glGenRenderbuffers(1, &depthRenderbuffer);
+    glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, framebufferWidth, framebufferHeight);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
+    
+    // Sanity check, ensure that the framebuffer is valid
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+        NSLog(@"ERROR: Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+}
+
+- (void)deleteFramebuffer
+{
+    // Deleting the framebuffer and all the buffers it contains
+    if (context) 
+    {
+        [EAGLContext setCurrentContext:context];        
+        if (defaultFramebuffer) 
+        {
+            glDeleteFramebuffers(1, &defaultFramebuffer);
+            defaultFramebuffer = 0;
+        }        
+        if (colorRenderbuffer) 
+        {
+            glDeleteRenderbuffers(1, &colorRenderbuffer);
+            colorRenderbuffer = 0;
+        }
+        if (depthRenderbuffer) 
+        {
+            glDeleteRenderbuffers(1, &depthRenderbuffer);
+            depthRenderbuffer = 0;
+        }
+    }
+}
+
+- (void)setSwapInterval:(NSInteger)interval
+{
+	if (interval >= 1)
+	{
+		swapInterval = interval;		
+		if (updating)
+		{
+			[self stopUpdating];
+			[self startUpdating];
+		}
+	}
+}
+
+- (int)swapInterval 
+{
+    return swapInterval;
+}
+
+- (void)startUpdating
+{
+	if (!updating)
+	{
+        displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
+        [displayLink setFrameInterval:swapInterval];
+        [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+        _game->resume();
+		updating = TRUE;
+	}
+}
+
+- (void)stopUpdating
+{
+	if (updating)
+	{
+        _game->pause();
+		[displayLink invalidate];
+        displayLink = nil;
+		updating = FALSE;
+	}
+}
+
+- (void)update:(id)sender
+{   
+    if (context != nil)
+    {
+        [EAGLContext setCurrentContext:context];
+        if (!defaultFramebuffer)
+            [self createFramebuffer];
+        
+        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
+        glViewport(0, 0, framebufferWidth, framebufferHeight);
+
+        if (_game && _game->getState() == Game::RUNNING)       
+            _game->frame();
+        
+        glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
+        [context presentRenderbuffer:GL_RENDERBUFFER];
+    }
+}
+
+- (BOOL)showKeyboard {
+    return [self becomeFirstResponder];
+}
+- (BOOL)dismissKeyboard {
+    return [self resignFirstResponder];
+}
+
+/*
+ * Virtual Keyboard Support
+ */
+- (void)insertText:(NSString *)text 
+{
+    if([text length] == 0) return;
+    assert([text length] == 1);
+    unichar c = [text characterAtIndex:0];
+    int gpk = getKey(c);
+    Game::getInstance()->keyEvent(Keyboard::KEY_PRESS, gpk);    
+    Game::getInstance()->keyEvent(Keyboard::KEY_RELEASE, gpk);    
+}
+- (void)deleteBackward 
+{
+    Game::getInstance()->keyEvent(Keyboard::KEY_PRESS, Keyboard::KEY_BACKSPACE);    
+    Game::getInstance()->keyEvent(Keyboard::KEY_RELEASE, Keyboard::KEY_BACKSPACE);    
+}
+- (BOOL)hasText 
+{
+    return YES;
+}
+
+/*
+ * Touch Support
+ */
+- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event 
+{
+    unsigned int uniqueTouch = 0;
+    for(UITouch *t in touches) 
+    {
+        CGPoint touchLoc = [t locationInView:self];
+        if(self.multipleTouchEnabled == YES) 
+            uniqueTouch = [t hash];
+        Game::getInstance()->touchEvent(Touch::TOUCH_PRESS, touchLoc.x,  touchLoc.y, uniqueTouch);
+    }
+}
+
+- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent *)event 
+{
+    unsigned int uniqueTouch = 0;
+    for(UITouch* t in touches) 
+    {
+        CGPoint touchLoc = [t locationInView:self];
+        if(self.multipleTouchEnabled == YES) 
+            uniqueTouch = [t hash];
+        Game::getInstance()->touchEvent(Touch::TOUCH_RELEASE, touchLoc.x, touchLoc.y, uniqueTouch);
+    }
+}
+
+- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event 
+{
+    // No equivalent for this in GamePlay -- treat as touch end
+    [self touchesEnded:touches withEvent:event];
+}
+
+- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event 
+{
+    unsigned int uniqueTouch = 0;
+    for(UITouch* t in touches) 
+    {
+        CGPoint touchLoc = [t locationInView:self];
+        if(self.multipleTouchEnabled == YES) 
+            uniqueTouch = [t hash];
+        Game::getInstance()->touchEvent(Touch::TOUCH_MOVE, touchLoc.x,  touchLoc.y, uniqueTouch);
+    }
+}
+
+@end
+
+
+@interface ViewController : UIViewController
+- (void)startUpdating;
+- (void)stopUpdating;
+@end
+
+
+@implementation ViewController 
+
+- (id)init 
+{
+    if((self = [super init])) 
+    {
+    }
+    return self;
+}
+
+- (void)dealloc 
+{
+    __view = nil;
+    [super dealloc];
+}
+
+- (void)didReceiveMemoryWarning
+{
+    [super didReceiveMemoryWarning];    
+    // Release any cached data, images, etc that aren't in use.
+}
+
+#pragma mark - View lifecycle
+- (void)loadView
+{
+    self.view = [[[View alloc] init] autorelease];
+    if(__view == nil) 
+    {
+        __view = (View*)self.view;
+    }
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+    // Return YES for supported orientation, currently support landscape only?
+    return UIInterfaceOrientationIsLandscape(interfaceOrientation);
+}
+
+- (void)startUpdating 
+{
+    [(View*)self.view startUpdating];
+}
+
+- (void)stopUpdating 
+{
+    [(View*)self.view stopUpdating];
+}
+
+@end
+
+
+@interface AppDelegate : UIApplication <UIApplicationDelegate>
+{
+    UIWindow* window;
+    ViewController* viewController;
+    CMMotionManager *motionManager;
+}
+@end
+
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
+{
+    __appDelegate = self;
+    [UIApplication sharedApplication].statusBarHidden = YES;
+
+    
+    motionManager = [[CMMotionManager alloc] init];
+    if([motionManager isAccelerometerAvailable] == YES) 
+    {
+        motionManager.accelerometerUpdateInterval = 1 / 40.0;    // 40Hz
+        [motionManager startAccelerometerUpdates];
+    }
+        
+    window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
+    viewController = [[ViewController alloc] init];
+    [window setRootViewController:viewController];
+    [window makeKeyAndVisible];
+    return YES;
+}
+
+- (void)getAccelerometerPitch:(float *)pitch roll:(float *)roll 
+{
+    float p = 0.0f;
+    float r = 0.0f;
+    CMAccelerometerData *accelerometerData = motionManager.accelerometerData;
+    if(accelerometerData != nil) 
+    {
+        float tx, ty, tz;
+        tx = accelerometerData.acceleration.y;
+        ty = -accelerometerData.acceleration.x;
+        tz = -accelerometerData.acceleration.z;      
+        p = atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
+        r = atan(tx / sqrt(ty * ty + tz * tz)) * 180.0f * M_1_PI;     
+    }
+    
+    if(pitch != NULL) *pitch = p;
+    if(roll != NULL) *roll = r;
+}
+
+- (void)applicationWillResignActive:(UIApplication*)application
+{    
+	[viewController stopUpdating];
+}
+
+- (void)applicationDidEnterBackground:(UIApplication*)application 
+{
+	[viewController stopUpdating];
+}
+
+- (void)applicationWillEnterForeground:(UIApplication*)application 
+{	
+	[viewController startUpdating];
+}
+
+- (void)applicationDidBecomeActive:(UIApplication*)application 
+{
+	[viewController startUpdating];
+}
+
+- (void)applicationWillTerminate:(UIApplication*)application 
+{	
+	[viewController stopUpdating];
+}
+
+- (void)dealloc 
+{
+    [window setRootViewController:nil];
+    [viewController release];
+    [window release];
+    [motionManager release];
+	[super dealloc];
+}
+
+@end
+
+long getMachTimeInMilliseconds()
+{
+    static const int64_t kOneMillion = 1000 * 1000;
+    static mach_timebase_info_data_t s_timebase_info;
+    
+    if (s_timebase_info.denom == 0) 
+        (void) mach_timebase_info(&s_timebase_info);
+    
+    // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
+    return (long)((mach_absolute_time() * s_timebase_info.numer) / (kOneMillion * s_timebase_info.denom));
+}
+
+int getKey(unichar keyCode) 
+{
+    switch(keyCode) {
+        case 0x30:
+            return Keyboard::KEY_ZERO;
+        case 0x31:
+            return Keyboard::KEY_ONE;
+        case 0x32:
+            return Keyboard::KEY_TWO;
+        case 0x33:
+            return Keyboard::KEY_THREE;
+        case 0x34:
+            return Keyboard::KEY_FOUR;
+        case 0x35:
+            return Keyboard::KEY_FIVE;
+        case 0x36:
+            return Keyboard::KEY_SIX;
+        case 0x37:
+            return Keyboard::KEY_SEVEN;
+        case 0x38:
+            return Keyboard::KEY_EIGHT;
+        case 0x39:
+            return Keyboard::KEY_NINE;
+            
+        case 0x41:
+            return Keyboard::KEY_CAPITAL_A;
+        case 0x42:
+            return Keyboard::KEY_CAPITAL_B;
+        case 0x43:
+            return Keyboard::KEY_CAPITAL_C;
+        case 0x44:
+            return Keyboard::KEY_CAPITAL_D;
+        case 0x45:
+            return Keyboard::KEY_CAPITAL_E;
+        case 0x46:
+            return Keyboard::KEY_CAPITAL_F;
+        case 0x47:
+            return Keyboard::KEY_CAPITAL_G;
+        case 0x48:
+            return Keyboard::KEY_CAPITAL_H;
+        case 0x49:
+            return Keyboard::KEY_CAPITAL_I;
+        case 0x4A:
+            return Keyboard::KEY_CAPITAL_J;
+        case 0x4B:
+            return Keyboard::KEY_CAPITAL_K;
+        case 0x4C:
+            return Keyboard::KEY_CAPITAL_L;
+        case 0x4D:
+            return Keyboard::KEY_CAPITAL_M;
+        case 0x4E:
+            return Keyboard::KEY_CAPITAL_N;
+        case 0x4F:
+            return Keyboard::KEY_CAPITAL_O;
+        case 0x50:
+            return Keyboard::KEY_CAPITAL_P;
+        case 0x51:
+            return Keyboard::KEY_CAPITAL_Q;
+        case 0x52:
+            return Keyboard::KEY_CAPITAL_R;
+        case 0x53:
+            return Keyboard::KEY_CAPITAL_S;
+        case 0x54:
+            return Keyboard::KEY_CAPITAL_T;
+        case 0x55:
+            return Keyboard::KEY_CAPITAL_U;
+        case 0x56:
+            return Keyboard::KEY_CAPITAL_V;
+        case 0x57:
+            return Keyboard::KEY_CAPITAL_W;
+        case 0x58:
+            return Keyboard::KEY_CAPITAL_X;
+        case 0x59:
+            return Keyboard::KEY_CAPITAL_Y;
+        case 0x5A:
+            return Keyboard::KEY_CAPITAL_Z;
+            
+            
+        case 0x61:
+            return Keyboard::KEY_A;
+        case 0x62:
+            return Keyboard::KEY_B;
+        case 0x63:
+            return Keyboard::KEY_C;
+        case 0x64:
+            return Keyboard::KEY_D;
+        case 0x65:
+            return Keyboard::KEY_E;
+        case 0x66:
+            return Keyboard::KEY_F;
+        case 0x67:
+            return Keyboard::KEY_G;
+        case 0x68:
+            return Keyboard::KEY_H;
+        case 0x69:
+            return Keyboard::KEY_I;
+        case 0x6A:
+            return Keyboard::KEY_J;
+        case 0x6B:
+            return Keyboard::KEY_K;
+        case 0x6C:
+            return Keyboard::KEY_L;
+        case 0x6D:
+            return Keyboard::KEY_M;
+        case 0x6E:
+            return Keyboard::KEY_N;
+        case 0x6F:
+            return Keyboard::KEY_O;
+        case 0x70:
+            return Keyboard::KEY_P;
+        case 0x71:
+            return Keyboard::KEY_Q;
+        case 0x72:
+            return Keyboard::KEY_R;
+        case 0x73:
+            return Keyboard::KEY_S;
+        case 0x74:
+            return Keyboard::KEY_T;
+        case 0x75:
+            return Keyboard::KEY_U;
+        case 0x76:
+            return Keyboard::KEY_V;
+        case 0x77:
+            return Keyboard::KEY_W;
+        case 0x78:
+            return Keyboard::KEY_X;
+        case 0x79:
+            return Keyboard::KEY_Y;
+        case 0x7A:
+            return Keyboard::KEY_Z;
+        default:
+            break;
+            
+       // Symbol Row 3
+        case 0x2E:
+            return Keyboard::KEY_PERIOD;
+        case 0x2C:
+            return Keyboard::KEY_COMMA;
+        case 0x3F:
+            return Keyboard::KEY_QUESTION;
+        case 0x21:
+            return Keyboard::KEY_EXCLAM;
+        case 0x27:
+            return Keyboard::KEY_APOSTROPHE;
+            
+        // Symbols Row 2
+        case 0x2D:
+            return Keyboard::KEY_MINUS;
+        case 0x2F:
+            return Keyboard::KEY_SLASH;
+        case 0x3A:
+            return Keyboard::KEY_COLON;
+        case 0x3B:
+            return Keyboard::KEY_SEMICOLON;
+        case 0x28:
+            return Keyboard::KEY_LEFT_PARENTHESIS;
+        case 0x29:
+            return Keyboard::KEY_RIGHT_PARENTHESIS;
+        case 0x24:
+            return Keyboard::KEY_DOLLAR;
+        case 0x26:
+            return Keyboard::KEY_AMPERSAND;
+        case 0x40:
+            return Keyboard::KEY_AT;
+        case 0x22:
+            return Keyboard::KEY_QUOTE;
+            
+        // Numeric Symbols Row 1
+        case 0x5B:
+            return Keyboard::KEY_LEFT_BRACKET;
+        case 0x5D:
+            return Keyboard::KEY_RIGHT_BRACKET;
+        case 0x7B:
+            return Keyboard::KEY_LEFT_BRACE;
+        case 0x7D:
+            return Keyboard::KEY_RIGHT_BRACE;
+        case 0x23:
+            return Keyboard::KEY_NUMBER;
+        case 0x25:
+            return Keyboard::KEY_PERCENT;
+        case 0x5E:
+            return Keyboard::KEY_CIRCUMFLEX;
+        case 0x2A:
+            return Keyboard::KEY_ASTERISK;
+        case 0x2B:
+            return Keyboard::KEY_PLUS;
+        case 0x3D:
+            return Keyboard::KEY_EQUAL;
+            
+        // Numeric Symbols Row 2
+        case 0x5F:
+            return Keyboard::KEY_UNDERSCORE;
+        case 0x5C:
+            return Keyboard::KEY_BACK_SLASH;
+        case 0x7C:
+            return Keyboard::KEY_BAR;
+        case 0x7E:
+            return Keyboard::KEY_TILDE;
+        case 0x3C:
+            return Keyboard::KEY_LESS_THAN;
+        case 0x3E:
+            return Keyboard::KEY_GREATER_THAN;
+        case 0x80:
+            return Keyboard::KEY_EURO;
+        case 0xA3:
+            return Keyboard::KEY_POUND;
+        case 0xA5:
+            return Keyboard::KEY_YEN;
+        case 0xB7:
+            return Keyboard::KEY_MIDDLE_DOT;
+    }
+    return Keyboard::KEY_NONE;
+}
+
+
+namespace gameplay
+{
+    extern void printError(const char* format, ...)
+    {
+        va_list argptr;
+        va_start(argptr, format);
+        vfprintf(stderr, format, argptr);
+        fprintf(stderr, "\n");
+        va_end(argptr);
+    }
+    
+    Platform::Platform(Game* game)
+        : _game(game)
+    {
+    }
+    
+    Platform::Platform(const Platform& copy)
+    {
+        // hidden
+    }
+    
+    Platform::~Platform()
+    {
+    }
+    
+    Platform* Platform::create(Game* game)
+    {
+        Platform* platform = new Platform(game);
+        return platform;
+    }
+    
+    int Platform::enterMessagePump()
+    {
+        NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+        [AppDelegate load];
+        UIApplicationMain(0, nil, NSStringFromClass([AppDelegate class]), NSStringFromClass([AppDelegate class]));
+        [pool release];
+        return EXIT_SUCCESS;
+    }
+    
+    long Platform::getAbsoluteTime()
+    {
+        __timeAbsolute = getMachTimeInMilliseconds();
+        return __timeAbsolute;
+    }
+    
+    void Platform::setAbsoluteTime(long time)
+    {
+        __timeAbsolute = time;
+    }
+    
+    bool Platform::isVsync()
+    {
+        return __vsync;
+    }
+    
+    void Platform::setVsync(bool enable)
+    {
+        __vsync = enable;
+    }
+    
+    int Platform::getOrientationAngle()
+    {
+        return 0;
+    }
+    
+    void Platform::getAccelerometerValues(float* pitch, float* roll)
+    {
+        [__appDelegate getAccelerometerPitch:pitch roll:roll];
+    }
+    
+    void Platform::setMultiTouch(bool enabled) 
+    {
+        __view.multipleTouchEnabled = enabled;
+    }
+    
+    bool Platform::isMultiTouch() 
+    {
+        return __view.multipleTouchEnabled;
+    }
+    
+    void Platform::swapBuffers()
+    {
+        if (__view)
+            [[__view getContext] presentRenderbuffer:GL_RENDERBUFFER];
+    }
+    
+    void displayKeyboard(bool display) {
+        if(__view) {
+            if(display) [__view showKeyboard];
+            else [__view dismissKeyboard];
+        }
+    }
+    
+}
+
+#endif

+ 138 - 7
gameplay/src/Properties.cpp

@@ -13,12 +13,16 @@ Properties::Properties(FILE* file)
     _namespacesItr = _namespaces.end();
 }
 
-Properties::Properties(FILE* file, const char* name, const char* id) : _namespace(name)
+Properties::Properties(FILE* file, const char* name, const char* id, const char* parentID) : _namespace(name)
 {
     if (id)
     {
         _id = id;
     }
+    if (parentID)
+    {
+        _parentID = parentID;
+    }
     readProperties(file);
     _propertiesItr = _properties.end();
     _namespacesItr = _namespaces.end();
@@ -36,6 +40,8 @@ Properties* Properties::create(const char* filePath)
 
     Properties* properties = new Properties(file);
 
+    properties->resolveInheritance();
+
     fclose(file);
 
     return properties;
@@ -47,7 +53,9 @@ void Properties::readProperties(FILE* file)
     int c;
     char* name;
     char* value;
+    char* parentID;
     char* rc;
+    char* rcc;
 
     while (true)
     {
@@ -104,11 +112,16 @@ void Properties::readProperties(FILE* file)
             }
             else
             {
+                parentID = NULL;
+
                 // This line might begin or end a namespace,
                 // or it might be a key/value pair without '='.
 
                 // Check for '{' on same line.
                 rc = strchr(line, '{');
+
+                // Check for inheritance: ':'
+                rcc = strchr(line, ':');
             
                 // Get the name of the namespace.
                 name = strtok(line, " \t\n{");
@@ -124,12 +137,18 @@ void Properties::readProperties(FILE* file)
                 }
 
                 // Get its ID if it has one.
-                value = strtok(NULL, "{");
+                value = strtok(NULL, ":{");
                 value = trimWhiteSpace(value);
+                if (rcc != NULL)
+                {
+                    parentID = strtok(NULL, "{");
+                    parentID = trimWhiteSpace(parentID);
+                }
+
                 if (value != NULL && value[0] == '{')
                 {
                     // New namespace without an ID.
-                    Properties* space = new Properties(file, name, NULL);
+                    Properties* space = new Properties(file, name, NULL, parentID);
                     _namespaces.push_back(space);
                 }
                 else
@@ -138,7 +157,7 @@ void Properties::readProperties(FILE* file)
                     if (rc != NULL)
                     {
                         // Create new namespace.
-                        Properties* space = new Properties(file, name, value);
+                        Properties* space = new Properties(file, name, value, parentID);
                         _namespaces.push_back(space);
                     }
                     else
@@ -149,7 +168,7 @@ void Properties::readProperties(FILE* file)
                         if (c == '{')
                         {
                             // Create new namespace.
-                            Properties* space = new Properties(file, name, value);
+                            Properties* space = new Properties(file, name, value, parentID);
                             _namespaces.push_back(space);
                         }
                         else
@@ -228,7 +247,119 @@ char* Properties::trimWhiteSpace(char *str)
     return str;
 }
 
-const char* Properties::getNextProperty(const char** value)
+void Properties::resolveInheritance(const char* id)
+{
+    // Namespaces can be defined like so:
+    // "name id : parentID { }"
+    // This method merges data from the parent namespace into the child.
+
+    // Get a top-level namespace.
+    Properties* derived;
+    if (id)
+    {
+        derived = getNamespace(id);
+    }
+    else
+    {
+        derived = getNextNamespace();
+    }
+    while (derived)
+    {
+        // If the namespace has a parent ID, find the parent.
+        if (!derived->_parentID.empty())
+        {
+            Properties* parent = getNamespace(derived->_parentID.c_str());
+            if (parent)
+            {
+                resolveInheritance(parent->getId());
+
+                // Copy the child.
+                Properties* overrides = new Properties(*derived);
+                overrides->_propertiesItr = overrides->_properties.end();
+                overrides->_namespacesItr = overrides->_namespaces.end();
+
+                // Copy data from the parent into the child.
+                derived->_properties = parent->_properties;
+                derived->_propertiesItr = derived->_properties.end();
+                derived->_namespaces = std::vector<Properties*>();
+                std::vector<Properties*>::const_iterator it;
+                for (it = parent->_namespaces.begin(); it < parent->_namespaces.end(); it++)
+                {
+                    derived->_namespaces.push_back(new Properties(**it));
+                }
+                derived->_namespacesItr = derived->_namespaces.end();
+
+                // Take the original copy of the child and override the data copied from the parent.
+                derived->mergeWith(overrides);
+
+                // Delete the child copy.
+                delete overrides;
+            }
+        }
+
+        // Resolve inheritance within this namespace.
+        derived->resolveInheritance();
+
+        // Get the next top-level namespace and check again.
+        if (!id)
+        {
+            derived = getNextNamespace();
+        }
+        else
+        {
+            derived = NULL;
+        }
+    }
+}
+
+void Properties::mergeWith(Properties* overrides)
+{
+    // Overwrite or add each property found in child.
+    char* value = new char[255];
+    const char* name = overrides->getNextProperty(&value);
+    while (name)
+    {
+        this->_properties[name] = value;
+        name = overrides->getNextProperty(&value);
+    }
+    this->_propertiesItr = this->_properties.end();
+
+    // Merge all common nested namespaces, add new ones.
+    Properties* overridesNamespace = overrides->getNextNamespace();
+    while (overridesNamespace)
+    {
+        bool merged = false;
+
+        rewind();
+        Properties* derivedNamespace = getNextNamespace();
+        while (derivedNamespace)
+        {
+            if (strcmp(derivedNamespace->getNamespace(), overridesNamespace->getNamespace()) == 0 &&
+                strcmp(derivedNamespace->getId(), overridesNamespace->getId()) == 0)
+            {
+                derivedNamespace->mergeWith(overridesNamespace);
+                merged = true;
+            }
+
+            derivedNamespace = getNextNamespace();
+        }
+
+        if (!merged)
+        {
+            // Add this new namespace.
+            Properties* newNamespace = new Properties(*overridesNamespace);
+            newNamespace->_propertiesItr = newNamespace->_properties.end();
+            newNamespace->_namespacesItr = newNamespace->_namespaces.end();
+
+            this->_namespaces.push_back(newNamespace);
+            this->_namespacesItr = this->_namespaces.end();
+        }
+
+        overridesNamespace = overrides->getNextNamespace();
+    }
+}
+
+const char* Properties::getNextProperty(char** value)
 {
     if (_propertiesItr == _properties.end())
     {
@@ -248,7 +379,7 @@ const char* Properties::getNextProperty(const char** value)
         {
             if (value)
             {
-                *value = _propertiesItr->second.c_str();
+                strcpy(*value, _propertiesItr->second.c_str());
             }
             return name.c_str();
         }

+ 12 - 3
gameplay/src/Properties.h

@@ -97,7 +97,7 @@ void printProperties(Properties* properties)
 {
     // Print the name and ID of the current namespace.
     const char* spacename = properties->getNamespace();
-    const char* id = properties->getID();
+    const char* id = properties->getId();
     WARN_VARG("Namespace: %s  ID: %s\n{", spacename, id);
  
     // Print all properties in this namespace.
@@ -163,7 +163,7 @@ public:
      * 
      * @return The name of the next property, or NULL if there are no properties remaining.
      */
-    const char* getNextProperty(const char** value = NULL);
+    const char* getNextProperty(char** value = NULL);
 
     /**
      * Get the next namespace.
@@ -378,7 +378,9 @@ private:
     /**
      * Constructor. Read from the beginning of namespace specified
      */
-    Properties(FILE* file, const char* name, const char* id = NULL);
+    Properties(FILE* file, const char* name, const char* id = NULL, const char* parentID = NULL);
+
+    //Properties(const Properties& copy);
 
     void readProperties(FILE* file);
 
@@ -386,8 +388,15 @@ private:
 
     char* trimWhiteSpace(char* str);
 
+    // Called after create(); copies info from parents into derived namespaces.
+    void resolveInheritance(const char* id = NULL);
+
+    // Called by resolveInheritance().
+    void mergeWith(Properties* overrides);
+
     std::string _namespace;
     std::string _id;
+    std::string _parentID;
     std::map<std::string, std::string> _properties;
     std::map<std::string, std::string>::const_iterator _propertiesItr;
     std::vector<Properties*> _namespaces;

+ 42 - 28
gameplay/src/Quaternion.cpp

@@ -262,28 +262,55 @@ void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quate
 }
 
 void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
+{
+    slerp(q1.x, q1.y, q1.z, q1.w, q2.x, q2.y, q2.z, q2.w, t, &dst->x, &dst->y, &dst->z, &dst->w);
+}
+
+void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
+{
+    assert(dst);
+    assert(!(t < 0.0f || t > 1.0f));
+
+    Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
+    Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
+
+    slerpForSquad(q1, q2, t, &dstQ);
+    slerpForSquad(s1, s2, t, &dstS);
+    slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
+}
+
+void Quaternion::slerp(float q1x, float q1y, float q1z, float q1w, float q2x, float q2y, float q2z, float q2w, float t, float* dstx, float* dsty, float* dstz, float* dstw)
 {
     // Fast slerp implementation by kwhatmough:
     // It contains no division operations, no trig, no inverse trig
     // and no sqrt. Not only does this code tolerate small constraint
     // errors in the input quaternions, it actually corrects for them.
-    assert(dst);
+    assert(dstx && dsty && dstz && dstw);
     assert(!(t < 0.0f || t > 1.0f));
 
     if (t == 0.0f)
     {
-        memcpy(dst, &q1, sizeof(float) * 4);
+        *dstx = q1x;
+        *dsty = q1y;
+        *dstz = q1z;
+        *dstw = q1w;
         return;
     }
     else if (t == 1.0f)
     {
-        memcpy(dst, &q2, sizeof(float) * 4);
+        *dstx = q2x;
+        *dsty = q2y;
+        *dstz = q2z;
+        *dstw = q2w;
         return;
     }
 
-    if (q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w)
+    if (q1x == q2x && q1y == q2y && q1z == q2z && q1w == q2w)
     {
-        memcpy(dst, &q1, sizeof(float) * 4);
+        *dstx = q1x;
+        *dsty = q1y;
+        *dstz = q1z;
+        *dstw = q1w;
         return;
     }
 
@@ -293,7 +320,7 @@ void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quat
     float halfSecHalfTheta, versHalfTheta;
     float sqNotU, sqU;
 
-    float cosTheta = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
+    float cosTheta = q1w * q2w + q1x * q2x + q1y * q2y + q1z * q2z;
 
     // As usual in all slerp implementations, we fold theta.
     alpha = cosTheta >= 0 ? 1.0f : -1.0f;
@@ -334,34 +361,21 @@ void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quat
     beta = f1 + f2b;
 
     // Apply final coefficients to a and b as usual.
-    float w = alpha * q1.w + beta * q2.w;
-    float x = alpha * q1.x + beta * q2.x;
-    float y = alpha * q1.y + beta * q2.y;
-    float z = alpha * q1.z + beta * q2.z;
+    float w = alpha * q1w + beta * q2w;
+    float x = alpha * q1x + beta * q2x;
+    float y = alpha * q1y + beta * q2y;
+    float z = alpha * q1z + beta * q2z;
 
     // This final adjustment to the quaternion's length corrects for
-    // any small constraint error in the inputs q1 and q2. But as you
+    // any small constraint error in the inputs q1 and q2 But as you
     // can see, it comes at the cost of 9 additional multiplication
     // operations. If this error-correcting feature is not required,
     // the following code may be removed.
     f1 = 1.5f - 0.5f * (w * w + x * x + y * y + z * z);
-    dst->w = w * f1;
-    dst->x = x * f1;
-    dst->y = y * f1;
-    dst->z = z * f1;
-}
-
-void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
-{
-    assert(dst);
-    assert(!(t < 0.0f || t > 1.0f));
-
-    Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
-    Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
-
-    slerpForSquad(q1, q2, t, &dstQ);
-    slerpForSquad(s1, s2, t, &dstS);
-    slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
+    *dstw = w * f1;
+    *dstx = x * f1;
+    *dsty = y * f1;
+    *dstz = z * f1;
 }
 
 void Quaternion::slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)

+ 30 - 2
gameplay/src/Quaternion.h

@@ -40,6 +40,8 @@ class Matrix;
  */
 class Quaternion
 {
+    friend class Curve;
+
 public:
 
     /**
@@ -293,7 +295,7 @@ public:
      * @param dst A quaternion to store the result in.
      */
     static void lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
-
+    
     /**
      * Interpolates between two quaternions using spherical linear interpolation.
      *
@@ -310,7 +312,7 @@ public:
      * @param dst A quaternion to store the result in.
      */
     static void slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
-
+    
     /**
      * Interpolates over a series of quaternions using spherical spline interpolation.
      *
@@ -350,6 +352,32 @@ public:
 
 private:
 
+    /**
+     * Interpolates between two quaternions using spherical linear interpolation.
+     *
+     * Spherical linear interpolation provides smooth transitions between different
+     * orientations and is often useful for animating models or cameras in 3D.
+     *
+     * Note: For accurate interpolation, the input quaternions must be at (or close to) unit length.
+     * This method does not automatically normalize the input quaternions, so it is up to the
+     * caller to ensure they call normalize beforehand, if necessary.
+     *
+     * @param q1x The x component of the first quaternion.
+     * @param q1y The y component of the first quaternion.
+     * @param q1z The z component of the first quaternion.
+     * @param q1w The w component of the first quaternion.
+     * @param q2x The x component of the second quaternion.
+     * @param q2y The y component of the second quaternion.
+     * @param q2z The z component of the second quaternion.
+     * @param q2w The w component of the second quaternion.
+     * @param t The interpolation coefficient.
+     * @param dstx A pointer to store the x component of the slerp in.
+     * @param dsty A pointer to store the y component of the slerp in.
+     * @param dstz A pointer to store the z component of the slerp in.
+     * @param dstw A pointer to store the w component of the slerp in.
+     */
+    static void slerp(float q1x, float q1y, float q1z, float q1w, float q2x, float q2y, float q2z, float q2w, float t, float* dstx, float* dsty, float* dstz, float* dstw);
+
     static void slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
 };
 

+ 8 - 8
gameplay/src/SpriteBatch.cpp

@@ -14,14 +14,6 @@
     vtx.u = vu; vtx.v = vv; \
     vtx.r = vr; vtx.g = vg; vtx.b = vb; vtx.a = va
 
-// Sprite vertex structured used for batching
-struct SpriteVertex
-{
-    float x, y, z;
-    float u, v;
-    float r, g, b, a;
-};
-
 // Default sprite vertex shader
 #define SPRITE_VSH \
     "uniform mat4 u_projectionMatrix;\n" \
@@ -53,6 +45,14 @@ struct SpriteVertex
 namespace gameplay
 {
 
+// Sprite vertex structured used for batching
+struct SpriteVertex
+{
+    float x, y, z;
+    float u, v;
+    float r, g, b, a;
+};
+
 // Shared sprite effects
 static Effect* __spriteEffect = NULL;
 

+ 1 - 1
gameplay/src/Texture.cpp

@@ -26,7 +26,7 @@ Texture::~Texture()
     // Remove ourself from the texture cache.
     if (_cached)
     {
-        std::vector<Texture*>::iterator itr = find(__textureCache.begin(), __textureCache.end(), this);
+        std::vector<Texture*>::iterator itr = std::find(__textureCache.begin(), __textureCache.end(), this);
         if (itr != __textureCache.end())
         {
             __textureCache.erase(itr);

+ 134 - 16
gameplay/src/Transform.cpp

@@ -226,6 +226,13 @@ void Transform::getRightVector(Vector3* dst) const
     getMatrix().getRightVector(dst);
 }
 
+void Transform::rotate(float qx, float qy, float qz, float qw)
+{
+    Quaternion q(qx, qy, qz, qw);
+    _rotation.multiply(q);
+    dirty();
+}
+
 void Transform::rotate(const Quaternion& rotation)
 {
     _rotation.multiply(rotation);
@@ -640,49 +647,90 @@ void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
     }
 }
 
-void Transform::setAnimationPropertyValue(int propertyId, AnimationValue* value)
+void Transform::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
+    assert(blendWeight >= 0.0f && blendWeight <= 1.0f);
+
     switch (propertyId)
     {
         case ANIMATE_SCALE_UNIT:
-            setScale(value->getFloat(0));
+        {
+            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
+            applyAnimationValueScaleY(value->getFloat(0), blendWeight);
+            applyAnimationValueScaleZ(value->getFloat(0), blendWeight);
             break;
+        }   
         case ANIMATE_SCALE:
-            setScale(value->getFloat(0), value->getFloat(1), value->getFloat(2));
+        {
+            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
+            applyAnimationValueScaleY(value->getFloat(1), blendWeight);
+            applyAnimationValueScaleZ(value->getFloat(2), blendWeight);
             break;
+        }
         case ANIMATE_SCALE_X:
-            setScaleX(value->getFloat(0));
+        {
+            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
             break;
+        }
         case ANIMATE_SCALE_Y:
-            setScaleY(value->getFloat(0));
+        {
+            applyAnimationValueScaleY(value->getFloat(0), blendWeight);
             break;
+        }
         case ANIMATE_SCALE_Z:
-            setScaleZ(value->getFloat(0));
+        {
+            applyAnimationValueScaleZ(value->getFloat(0), blendWeight);
             break;
+        }
         case ANIMATE_ROTATE:
-            setRotation(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
+        {
+            Quaternion q(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
+            applyAnimationValueRotation(&q, blendWeight);
             break;
+        }
         case ANIMATE_TRANSLATE:
-            setTranslation(value->getFloat(0), value->getFloat(1), value->getFloat(2));
+        {
+            applyAnimationValueTranslationX(value->getFloat(0), blendWeight);
+            applyAnimationValueTranslationY(value->getFloat(1), blendWeight);
+            applyAnimationValueTranslationZ(value->getFloat(2), blendWeight);
             break;
+        }
         case ANIMATE_TRANSLATE_X:
-            setTranslationX(value->getFloat(0));
+        {
+            applyAnimationValueTranslationX(value->getFloat(0), blendWeight);
             break;
+        }
         case ANIMATE_TRANSLATE_Y:
-            setTranslationY(value->getFloat(0));
+        {
+            applyAnimationValueTranslationY(value->getFloat(0), blendWeight);
             break;
+        }
         case ANIMATE_TRANSLATE_Z:
-            setTranslationZ(value->getFloat(0));
+        {
+            applyAnimationValueTranslationZ(value->getFloat(0), blendWeight);
             break;
+        }
         case ANIMATE_ROTATE_TRANSLATE:
-            setRotation(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
-            setTranslation(value->getFloat(4), value->getFloat(5), value->getFloat(6));
+        {
+            Quaternion q(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
+            applyAnimationValueRotation(&q, blendWeight);
+            applyAnimationValueTranslationX(value->getFloat(4), blendWeight);
+            applyAnimationValueTranslationY(value->getFloat(5), blendWeight);
+            applyAnimationValueTranslationZ(value->getFloat(6), blendWeight);
             break;
+        }
         case ANIMATE_SCALE_ROTATE_TRANSLATE:
-            setScale(value->getFloat(0), value->getFloat(1), value->getFloat(2));
-            setRotation(value->getFloat(3), value->getFloat(4), value->getFloat(5), value->getFloat(6));
-            setTranslation(value->getFloat(7), value->getFloat(8), value->getFloat(9));
+        {
+            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
+            applyAnimationValueScaleY(value->getFloat(1), blendWeight);
+            applyAnimationValueScaleZ(value->getFloat(2), blendWeight);
+            Quaternion q(value->getFloat(3), value->getFloat(4), value->getFloat(5), value->getFloat(6));
+            applyAnimationValueRotation(&q, blendWeight);
+            applyAnimationValueTranslationX(value->getFloat(7), blendWeight);
+            applyAnimationValueTranslationY(value->getFloat(8), blendWeight);
+            applyAnimationValueTranslationZ(value->getFloat(9), blendWeight);
             break;
+        }
         default:
             break;
     }
@@ -732,4 +780,74 @@ void Transform::transformChanged()
     }
 }
 
+void Transform::applyAnimationValueScaleX(float sx, float blendWeight)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_SCALE_X_BIT) != ANIMATION_SCALE_X_BIT)
+        _animationPropertyBitFlag |= ANIMATION_SCALE_X_BIT;
+    else
+        sx = Curve::lerp(blendWeight, _scale.x, sx);
+
+    setScaleX(sx);
+}
+
+void Transform::applyAnimationValueScaleY(float sy, float blendWeight)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_SCALE_Y_BIT) != ANIMATION_SCALE_Y_BIT)
+        _animationPropertyBitFlag |= ANIMATION_SCALE_Y_BIT;
+    else
+        sy = Curve::lerp(blendWeight, _scale.y, sy);
+
+    setScaleY(sy);
+}
+
+void Transform::applyAnimationValueScaleZ(float sz, float blendWeight)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_SCALE_Z_BIT) != ANIMATION_SCALE_Z_BIT)
+        _animationPropertyBitFlag |= ANIMATION_SCALE_Z_BIT;
+    else
+        sz = Curve::lerp(blendWeight, _scale.z, sz);
+
+    setScaleZ(sz);
+}
+
+void Transform::applyAnimationValueRotation(Quaternion* q, float blendWeight)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_ROTATION_BIT) != ANIMATION_ROTATION_BIT)
+        _animationPropertyBitFlag |= ANIMATION_ROTATION_BIT;
+    else
+        Quaternion::slerp(_rotation, *q, blendWeight, q);
+     
+    setRotation(*q);
+}
+
+void Transform::applyAnimationValueTranslationX(float tx, float blendWeight)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_X_BIT) != ANIMATION_TRANSLATION_X_BIT)
+        _animationPropertyBitFlag |= ANIMATION_TRANSLATION_X_BIT;
+    else
+        tx = Curve::lerp(blendWeight, _translation.x, tx);
+
+    setTranslationX(tx);
+}
+
+void Transform::applyAnimationValueTranslationY(float ty, float blendWeight)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_Y_BIT) != ANIMATION_TRANSLATION_Y_BIT)
+        _animationPropertyBitFlag |= ANIMATION_TRANSLATION_Y_BIT;
+    else
+        ty = Curve::lerp(blendWeight, _translation.y, ty);
+
+    setTranslationY(ty);
+}
+
+void Transform::applyAnimationValueTranslationZ(float tz, float blendWeight)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_Z_BIT) != ANIMATION_TRANSLATION_Z_BIT)
+        _animationPropertyBitFlag |= ANIMATION_TRANSLATION_Z_BIT;
+    else
+        tz = Curve::lerp(blendWeight, _translation.z, tz);
+
+    setTranslationZ(tz);
+}
+
 }

+ 29 - 3
gameplay/src/Transform.h

@@ -31,12 +31,12 @@ class Transform : public AnimationTarget
 public:
 
     /**
-     * Scale animation property. Data=sx,sy,sz
+     * Scale animation property. Data=scale
      */
     static const int ANIMATE_SCALE_UNIT = 0;
 
     /**
-     * Scale animation property. Data=scale
+     * Scale animation property. Data=sx,sy,sz
      */
     static const int ANIMATE_SCALE = 1;
 
@@ -324,6 +324,16 @@ public:
      */
     void getRightVector(Vector3* dst) const;
 
+    /**
+     * Rotates this transform's rotation component by the given rotation.
+     *
+     * @param qx The quaternion x value.
+     * @param qy The quaternion y value.
+     * @param qz The quaternion z value.
+     * @param qw The quaternion w value.
+     */
+    void rotate(float qx, float qy, float qz, float qw);
+
     /**
      * Rotates this transform's rotation component by the given rotation.
      *
@@ -715,7 +725,7 @@ public:
     /**
      * @see AnimationTarget#setAnimationProperty
      */
-    void setAnimationPropertyValue(int propertyId, AnimationValue* value);
+    void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
 protected:
 
@@ -735,6 +745,22 @@ protected:
     mutable bool _matrixDirty;
     std::list<TransformListener>* _listeners;
 
+private:
+    static const char ANIMATION_SCALE_X_BIT = 0x01; 
+    static const char ANIMATION_SCALE_Y_BIT = 0x02; 
+    static const char ANIMATION_SCALE_Z_BIT = 0x04; 
+    static const char ANIMATION_ROTATION_BIT = 0x08;  
+    static const char ANIMATION_TRANSLATION_X_BIT = 0x10; 
+    static const char ANIMATION_TRANSLATION_Y_BIT = 0x20; 
+    static const char ANIMATION_TRANSLATION_Z_BIT = 0x40; 
+
+    void applyAnimationValueScaleX(float sx, float blendWeight);
+    void applyAnimationValueScaleY(float sy, float blendWeight);
+    void applyAnimationValueScaleZ(float sz, float blendWeight);
+    void applyAnimationValueRotation(Quaternion* q, float blendWeight);
+    void applyAnimationValueTranslationX(float tx, float blendWeight);
+    void applyAnimationValueTranslationY(float ty, float blendWeight);
+    void applyAnimationValueTranslationZ(float tz, float blendWeight);
 };
 
 }

+ 1 - 1
gameplay/src/VertexAttributeBinding.cpp

@@ -17,7 +17,7 @@ VertexAttributeBinding::VertexAttributeBinding() :
 VertexAttributeBinding::~VertexAttributeBinding()
 {
     // Delete from the vertex attribute binding cache.
-    std::vector<VertexAttributeBinding*>::iterator itr = find(__vertexAttributeBindingCache.begin(), __vertexAttributeBindingCache.end(), this);
+    std::vector<VertexAttributeBinding*>::iterator itr = std::find(__vertexAttributeBindingCache.begin(), __vertexAttributeBindingCache.end(), this);
     if (itr != __vertexAttributeBindingCache.end())
     {
         __vertexAttributeBindingCache.erase(itr);

+ 33 - 0
gameplay/src/gameplay-main-android.cpp

@@ -0,0 +1,33 @@
+#ifdef __ANDROID__
+
+#include <android_native_app_glue.h>
+#include "gameplay.h"
+
+using namespace gameplay;
+
+extern struct android_app* __state;
+
+/**
+ * Main entry point.
+ */
+void android_main(struct android_app* state)
+{
+    // Android specific : Dummy function that needs to be called to 
+    // ensure that the native activity works properly behind the scenes.
+    app_dummy();
+    
+    __state = state;
+    Game* game = Game::getInstance();
+    assert(game != NULL);
+    Platform* platform = Platform::create(game);
+    assert(platform != NULL);
+    platform->enterMessagePump();
+    Game::getInstance()->exit();
+    delete platform;
+    
+    // Android specific : the process needs to exit to 
+    // to trigger cleanup of global resources (such as game).
+    exit(0);
+}
+
+#endif

+ 20 - 0
gameplay/src/gameplay-main-ios.mm

@@ -0,0 +1,20 @@
+#ifdef __APPLE__
+
+#include "gameplay.h"
+
+using namespace gameplay;
+
+/**
+ * Main entry point.
+ */
+int main(int argc, char** argv)
+{
+    Game* game = Game::getInstance();
+    assert(game != NULL);
+    Platform* platform = Platform::create(game);
+    int result = platform->enterMessagePump();
+	delete platform;
+    return result;
+}
+
+#endif

+ 3 - 1
gameplay/src/gameplay-main-macos.mm

@@ -12,7 +12,9 @@ int main(int argc, char** argv)
     Game* game = Game::getInstance();
     assert(game != NULL);
     Platform* platform = Platform::create(game);
-    return platform->enterMessagePump();
+    int result = platform->enterMessagePump();
+	delete platform;
+    return result;
 }
 
 #endif

+ 39 - 0
gameplay/src/gameplay.dox

@@ -0,0 +1,39 @@
+/** \mainpage gameplay - 3D Game Framework
+ *
+ * \section intro Introduction
+ *
+ * The gameplay framework is a cross-platform, C++, 3D game framework that includes a runtime library, tools,
+ * and learning content that allows developers to write games for mobile and desktop platforms
+ * without worrying about platform details.
+ *
+ * \section License
+ *
+ * The project is open sourced under the Apache 2.0 license.
+ *
+ * \section Project Features
+ *
+ * The gameplay C++ runtime library offers a simple, well-defined, 3D game framework that's
+ * designed to get the most out of today's mobile and desktop platforms. Its purpose is to help you
+ * create stunning, world-class, games that harness the power and performance of the platform without
+ * being concerned about the platform details. This is accomplished through a set of C++ classes that
+ * are built on top of the OS, providing access to the hardware, graphics, and audio libraries
+ * (including OpenGL ES 2.0, and OpenAL 1.1). These classes allow you to:
+ *
+ * <b>Build your game without worrying about platform details</b>
+ * \li Drive your game with application initialization, update, and render callbacks.
+ * \li Control your game with touch screen, keyboard and accelerometer handlers.
+ * \li Manage your game with control over the device's file system.
+ *
+ * <b>Create game components with ease</b>
+ * \li Add visuals with scene, node, camera, lights, mesh, texture, sprite, materials, animation, and physics classes.
+ * \li Control and position the game using the viewport, camera, and audio listener.
+ * \li Manage fundamental elements such as fonts, colors, and curves.
+ * \li Update game data with built-in math classes for vectors, matrices, rays, planes, bounding volumes and their associated operations.
+ *
+ * <b>Improve your game and learn</b>
+ * \li Supports TrueType fonts and the Khronos COLLADA and Autodesk FBX interchange scene formats to import and binary encode your 3D game assets.
+ * \li Built-in materials and shaders to enhance your game's rendering.
+ * \li Both mobile and desktop platforms are supported for ease of portability and tooling options.
+ * \li Documentation, tutorials, and code samples are provided.
+ *
+ */

+ 2 - 4
gameplay/src/gameplay.h

@@ -4,6 +4,7 @@
 #include "Game.h"
 #include "Keyboard.h"
 #include "Touch.h"
+#include "Mouse.h"
 #include "FileSystem.h"
 #include "Package.h"
 
@@ -65,7 +66,4 @@
 #include "PhysicsHingeConstraint.h"
 #include "PhysicsSocketConstraint.h"
 #include "PhysicsSpringConstraint.h"
-#include "PhysicsRigidBody.h"
-
-
-
+#include "PhysicsRigidBody.h"