소스 검색

Merge pull request #113 from blackberry-gaming/next-rmadhavan

Android Port
Sean Paul Taylor 14 년 전
부모
커밋
484208c447
36개의 변경된 파일2408개의 추가작업 그리고 623개의 파일을 삭제
  1. 54 0
      .gitignore
  2. 31 0
      gameplay-newproject.bat
  3. 1 0
      gameplay-template/android/jni/Application.mk
  4. 62 0
      gameplay-template/android/jni/template.Android.mk
  5. 4 0
      gameplay-template/android/res/values/template.strings.xml
  6. 32 0
      gameplay-template/android/template.AndroidManifest.xml
  7. 92 0
      gameplay-template/android/template.build.xml
  8. 2 2
      gameplay-template/res/box.material
  9. 14 0
      gameplay/android/AndroidManifest.xml
  10. 85 0
      gameplay/android/build.xml
  11. 25 0
      gameplay/android/jni/Android.mk
  12. 2 0
      gameplay/android/jni/Application.mk
  13. 2 0
      gameplay/gameplay.vcxproj
  14. 6 0
      gameplay/gameplay.vcxproj.filters
  15. 65 1
      gameplay/src/AudioBuffer.cpp
  16. 14 1
      gameplay/src/AudioBuffer.h
  17. 138 1
      gameplay/src/AudioController.cpp
  18. 9 0
      gameplay/src/AudioController.h
  19. 238 3
      gameplay/src/AudioSource.cpp
  20. 21 0
      gameplay/src/AudioSource.h
  21. 44 2
      gameplay/src/Base.h
  22. 68 3
      gameplay/src/FileSystem.cpp
  23. 1 1
      gameplay/src/Font.cpp
  24. 1 0
      gameplay/src/Game.cpp
  25. 8 1
      gameplay/src/Game.h
  26. 5 0
      gameplay/src/Game.inl
  27. 2 1
      gameplay/src/PhysicsRigidBody.h
  28. 7 0
      gameplay/src/Platform.h
  29. 722 0
      gameplay/src/PlatformAndroid.cpp
  30. 603 598
      gameplay/src/PlatformMacOS.mm
  31. 11 3
      gameplay/src/PlatformQNX.cpp
  32. 5 0
      gameplay/src/PlatformWin32.cpp
  33. 1 1
      gameplay/src/Texture.cpp
  34. 1 1
      gameplay/src/VertexAttributeBinding.cpp
  35. 31 0
      gameplay/src/gameplay-main-android.cpp
  36. 1 4
      gameplay/src/gameplay.h

+ 54 - 0
.gitignore

@@ -77,3 +77,57 @@
 /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

+ 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

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

+ 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

+ 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

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

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

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

+ 138 - 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()
@@ -99,10 +165,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;

+ 44 - 2
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,7 +170,9 @@ extern void printError(const char* format, ...);
 #include <OpenAL/al.h>
 #include <OpenAL/alc.h>
 #endif
+
 #include <vorbis/vorbisfile.h>
+#endif
 
 // Image
 #include <png.h>
@@ -143,7 +181,7 @@ extern void printError(const char* format, ...);
 #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>
@@ -271,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

+ 68 - 3
gameplay/src/FileSystem.cpp

@@ -10,9 +10,55 @@
     #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()
@@ -106,10 +152,29 @@ 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)
     {

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

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

+ 8 - 1
gameplay/src/Game.h

@@ -185,7 +185,14 @@ 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.
      *

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

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

+ 722 - 0
gameplay/src/PlatformAndroid.cpp

@@ -0,0 +1,722 @@
+#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;
+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:
+            return Keyboard::KEY_LEFT_ALT;
+        case AKEYCODE_ALT_RIGHT:
+            return Keyboard::KEY_RIGHT_ALT;
+        case AKEYCODE_SHIFT_LEFT:
+            return Keyboard::KEY_LEFT_SHIFT;
+        case AKEYCODE_SHIFT_RIGHT:
+            return Keyboard::KEY_RIGHT_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;
+        default:
+            return Keyboard::KEY_NONE;
+    }
+}
+
+// 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);
+        int contactIndex = data >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+        Touch::TouchEvent touchEvent;
+        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;
+        }
+    
+        Game::getInstance()->touchEvent(touchEvent, AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0), contactIndex);
+        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));
+                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_SAVE_STATE:
+            // TODO
+            break;
+        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);
+    
+    // TODO: Determine initial orientation angle.
+    //orientation_direction_t direction;
+    //orientation_get(&direction, &__orientationAngle);
+
+    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 - 598
gameplay/src/PlatformMacOS.mm

@@ -1,598 +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* 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]);
-}
-
-}
-
-#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_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* 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

+ 11 - 3
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
 
@@ -688,7 +689,6 @@ int Platform::enterMessagePump()
     int eventType;
     int flags;
     int value;
-    int visible = 1;
     int position[2];
     int domain;
     mtouch_event_t touchEvent;
@@ -883,8 +883,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();
 
@@ -983,6 +983,14 @@ void Platform::swapBuffers()
         eglSwapBuffers(__eglDisplay, __eglSurface);
 }
 
+void Platform::displayKeyboard(bool display)
+{
+    if (display)
+        virtualkeyboard_show();
+    else
+        virtualkeyboard_hide();
+}
+
 }
 
 #endif

+ 5 - 0
gameplay/src/PlatformWin32.cpp

@@ -610,6 +610,11 @@ void Platform::swapBuffers()
         SwapBuffers(__hdc);
 }
 
+void Platform::displayKeyboard(bool display)
+{
+    // Do nothing.
+}
+
 }
 
 #endif

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

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

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

@@ -0,0 +1,31 @@
+#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)
+{
+    // Make sure glue isn't stripped.
+    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;
+    
+    // We need to exit the process to cleanup global resources.
+    exit(0);
+}
+
+#endif

+ 1 - 4
gameplay/src/gameplay.h

@@ -66,7 +66,4 @@
 #include "PhysicsHingeConstraint.h"
 #include "PhysicsSocketConstraint.h"
 #include "PhysicsSpringConstraint.h"
-#include "PhysicsRigidBody.h"
-
-
-
+#include "PhysicsRigidBody.h"